Vue.js Admin Panel Tutorial | CoreUI Setup And Login API.

Vue.js Admin Panel

Let’s set up Vue.js Admin Panel and create login functionality with CoreUI, the only Open Source Bootstrap And Vue 3 Admin Dashboard.

What is CoreUI? And Why?

Because it’s Bootstrap Admin Dashboard Template, Vue 3 UI Components Library, mainly having everything ready to start developing your admin application. I have used it before but the React version, it’s saved so much time. So I have decided to give it a shot on Vue.js. I have built Vue 3 admin panel and used it in a couple of projects but I have wasted my time in inventing the wheel. From A full stack perspective, We should capture any chance of saving time.

CoreUI Installation.

I forked the CoreUI repository on GitHub. If you don’t know how to fork it, at the top right you will find a button named “fork” with a number next to it, Just click the little arrow and click “create a new fork” from the drop-down menu. Now the repository has been created on your account. You can clone it to your local machine. I’m using Windows 10 and Nodejs 18.

In the Root folder of the project run the below command to install the necessary packages.

npm install

Let’s test our installation by running the server and open the gaven URL in the browser, for me it was http://localhost:8080/ . It should open the dashboard home page.

npm run serve

Adding Authentication Functions In Vuex Store.

Vue applications by default support .env file. so let’s create one in the project root folder. for development in the root folder add .env.development. Now we will create two variables one for the backend URL and another for Base URL of the admin app.

I’m using laravel as backend you can watch the tutorial of Laravel Authentication on my youtube channel or article here

VUE_APP_BACKEND_URL= http://127.0.0.1:8000/api/en
BASE_URL =  http://localhost:8080/

Setting up the store src\store\index.js and add the below code or functions next to the existed theme codes in the file.

import { createStore } from 'vuex'
/* eslint-disable */
export default createStore({
  state: {
     //
    // Add
    token:'', //access token from backend
    name:'', // the user name from backend
    backendUrl: process.env.VUE_APP_BACKEND_URL,
    
  
  },
  mutations: {
    //
   // Add
   
    initialiseStore(state) {
         if(localStorage.getItem('token')){
          state.token = localStorage.getItem('token');
         }
         if(localStorage.getItem('name')){
          state.name = localStorage.getItem('name');
         }
         if(state.token == ""){
           return false;
         }
         return true;
    },
    saveLogin(state,LoginData){
      state.token =LoginData.token;
      state.name =LoginData.name;

      localStorage.setItem('token', LoginData.token);
      localStorage.setItem('name', LoginData.name);
    
    },

    Logout(state){
      state.token ="";
      state.name ="";

      localStorage.removeItem('token');
      localStorage.removeItem('name');
    }
  },
 //
})
  • In the state object, we add the globe variable that we will be using across the app and it will affect the app. such as token for the access token from the backend and the user name as well as the backend URL from the .env.development file.
  • initialiseStore this function we run when we start the app to check if the user logged in or not. we use localStorage for saving the login data in the browser for the next time otherwise, when the user closes the app the login data will be lost and the user must log in again.
  • saveLogin we will use it when the user logged in for saving the data in the store and localStorage as well.
  • Logout is for removing all the logged-in data so that the user will be logged out.

Vue router Check Login Middleware

We need to redirct the user to login page if not logged in or logged out.

First of all we need to initiate our vuex store to check if the login status via adding initialiseStore to src\App.vue

<template>
  <router-view />
</template>

<script>
export default {
  mounted() {
    this.$store.commit('initialiseStore')
  },
}
</script>
<style lang="scss">
// Import Main styles for this application
@import 'styles/style';
</style>

Create a new folder name it “auth “and copy page\Login.vue file to it, we will added later but now let’s modify the login route and add route rule so that if the user is not logging, the router redirect to login page.

import store from '@/store'
//
 {
   path: 'login',
   name: 'Login',
   component: () => import('@/views/auth/Login'), // modify here
 },

//at the end of the file
router.beforeEach((to, from, next) => {
  let token = store.state.token
  console.log('router ==' + token)
  if (to.name !== 'Login' && !token) next({ name: 'Login' })
  else next()
})

export default router

Login Page

Let’s install axios and vue-validation for call the Login API form the backend and login form validation.

npm install axios
npm install @vuelidate/core @vuelidate/validators

Let add the script for login page.

<script>
/* eslint-disable */
import axios from 'axios'
import useVuelidate from '@vuelidate/core'
import { required, email } from '@vuelidate/validators'
import SubmitButton from '@/components/SubmitButton.vue'
export default {
  name: 'Login',
  setup() {
    return { v$: useVuelidate() }
  },
  components:{SubmitButton},
  data() {
    return {
      form: {
        email: '',
        password: '',
      },
      errorMgs: '', // to show error message
      ShowError: false, // flag to toggle errorMgs
      isSendingForm: false, 
    }
  },
  validations() {
    return {
      form: {
        email: {
          required,
          email,
        },
        password: {
          required,
        },
      },
    }
  },
  methods: {
    setTouched(theModel) {
      if(theModel == 'email' || theModel == 'all' )
        {this.v$.form.email.$touch()}

      if(theModel == 'password' || theModel == 'all')      
 {this.v$.form.password.$touch()} 
    },
    onSubmit(event) {
      event.preventDefault()
      this.setTouched('all');
      if (!this.v$.$invalid) 
      {
       this.isSendingForm = true;
       axios.post(
        this.$store.state.backendUrl+'/login',this.form, 
        {
          headers: {"Content-Type": "application/json",}
        })
        .then((response) => {
          console.log(response);
           // saving the login data after success
          this.$store.commit('saveLogin',
          {
            "token":response.data.token,
            "name":response.data.name,
          });
          this.$router.push('/')
          this.isSendingForm = false;
       
        })
        .catch( (error) => {
          // login failed
          console.log(error);
          this.ShowError=true;
          this.errorMgs = error.response.data.error;
          this.isSendingForm = false;
        });
      
      }
    },
  },
}
</script>
  • In setup() we declare $v to be used for useVuelidate() for validation.
  • The SubmitButton component, we will create later.
  • Then we add the form data object in addition to the flags for showing error messages and sending the form.
  • validations() for setting up the form validation inputs.
  • setTouched(theModel) We use it with @input method on the input fields for validation.
  • In onSubmit the if (!this.v$.$invalid) to check if there are no validation errors. When we send the login data this.isSendingForm = true; then we use axios.post to submit the form and this.$store.commit('saveLogin') saving the login data response if successful of .catch( (error) showing an error if failed.

Let’s add our HTML

Creating submit button with spinner src\components\SubmitButton.vue

<template>
  <!-- eslint-disable -->
 <CButton color="primary" type="submit" class="px-4">
    <div v-if="isSendingForm" class="spinner-border spinner-border-sm" role="status">
        <span class="visually-hidden">Loading...</span>
    </div> 
    {{title}}
 </CButton>
</template>

<script>
/* eslint-disable */ 
export default{
    name: "SubmitButton",
    props: ['title','isSendingForm'],
}

</script>

Adding the login page HTML

<template>
  <!-- eslint-disable -->
  <div class="bg-light min-vh-100 d-flex flex-row align-items-center">
    <CContainer>
      <CRow class="justify-content-center">
        <CCol :md="8">
          <CCardGroup>
            <CCard class="p-4">
              <CCardBody>
                <CAlert color="danger"
                   :visible="ShowError" 
                   dismissible @close="() => { ShowError = false }">
                   {{ errorMgs }}
                </CAlert>
                <CForm @submit="onSubmit">
                  <h1>Login</h1>
                  <p class="text-medium-emphasis">
                     Sign In to your account
                  </p>
                  <CInputGroup class="mb-3">
                    <CInputGroupText>
                      <CIcon icon="cil-user" />
                    </CInputGroupText>
                    <CFormInput
                      v-model.trim="form.email"
                      @input="setTouched('email')"
                      type="email"
                      placeholder="Email"
                      autocomplete="email"
                      feedback="Please provide a valid email."
                      :invalid="v$.form.email.$error"
                    />
                  </CInputGroup>
                  <CInputGroup class="mb-4">
                    <CInputGroupText>
                      <CIcon icon="cil-lock-locked" />
                    </CInputGroupText>
                    <CFormInput
                      v-model.trim="form.password"
                      @input="setTouched('password')"
                      type="password"
                      placeholder="Password"
                      autocomplete="current-password"
                      feedback="Please provide a valid password."
                      :invalid="v$.form.password.$error"
                    />
                  </CInputGroup>
                  <CRow>
                    <CCol :xs="6">
                      <SubmitButton
                      title="Login"
                      :isSendingForm="isSendingForm"
                      />
                 
                    </CCol>
                    <CCol :xs="6" class="text-right">
                      <CButton color="link" class="px-0">
                        Forgot password?
                      </CButton>
                    </CCol>
                  </CRow>
                </CForm>
              </CCardBody>
            </CCard>
            <CCard class="text-white bg-primary py-5" style="width: 44%">
              <CCardBody class="text-center">
                <div>
                  <h2>Sign up</h2>
                  <p>
                    Lorem ipsum dolor sit amet, consectetur adipisicing elit,
                    sed do eiusmod tempor incididunt ut labore et dolore magna
                    aliqua.
                  </p>
                  <CButton color="light" variant="outline" class="mt-3">
                    Register Now!
                  </CButton>
                </div>
              </CCardBody>
            </CCard>
          </CCardGroup>
        </CCol>
      </CRow>
    </CContainer>
  </div>
</template>

We added the error message inside alert component CAlert. In CFormInput we adding the v-model for the email and password as well as the @input to setTouched so that we can validate the inputs. In feedback attribute, we add the error validation message. Finally we show the invalid error on the input via :invalid. The SubmitButton that’s a reusable component that should be used with all forms.

Logout

Let add the logout button, If you click the user image at the top right you will find logout link in the dropdown menu. So let’s add the logout method in src\components\AppHeaderDropdownAccnt.vue

  <CDropdownItem @click="logout()"> <CIcon icon="cil-lock-locked" /> Logout </CDropdownItem>
methods: {
    logout() {
      this.$store.commit('Logout')
      this.$router.push('/login')
    },
  },

That’s all for now I will continue building this admin panel in a series of tutorials here. thank you.

Next tutorial: Vue.js Admin Panel Tutorial | Reset Password

Leave a Reply

Your email address will not be published. Required fields are marked *