Skip to content

05. Designing Dashboard

We know that the core of web app designing is CSS. Bootstrap is a popular CSS framework which provides ready made UI components and a way to customize them. There are other frameworks specific to Vue, such as PrimeVue, which offers a rich set of native Vue UI components that you can use to design your web app. In this development, we will stick to bootstrap and custom core CSS.

The more you practice, the more you learn! We will design the admin dashboard using Bootstrap and will follow the same approach to design the rest of the app. I will also write some custom CSS so that we can directly design using custom CSS if you are comfortable with it.

Page layout

First we create a basic layout including header, sidebar, main content, and footer.

Basic layout

js filename=static/components/DashCompo.js

const DashCompo = {
  name: "DashCompo",
  template: `
    <div>
      <nav class="navbar navbar-expand-lg navbar-dark bg-success">
        <div class="container">
          <a class="navbar-brand" href="#">Eat Fresh</a>
          <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"
            aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
            <span class="navbar-toggler-icon"></span>
          </button>
          <div class="collapse navbar-collapse" id="navbarNav">
            <ul class="navbar-nav ms-auto">
              <li class="nav-item">
                <a class="nav-link pointer-on-hover" @click="$emit('home')">Home</a>
              </li>
              <slot name="menu"></slot>
              <li class="nav-item">
                <a class="nav-link pointer-on-hover" @click="logout">logout</a>
              </li>
            </ul>

            <!-- Search Bar -->
            <form class="d-flex ms-auto" @submit.prevent="search">
              <input class="form-control me-2" type="search" v-model="query" placeholder="Search" aria-label="Search">
              <button class="btn btn-outline-light" type="submit">Search</button>
            </form>
          </div>
        </div>
      </nav>
      <main>
        <div class="sidebar bg-black">
          <div class="sidebar-icon mb-5">
            <!-- Add your icon or content here -->
          </div>
          <!-- Add any sidebar content here -->
        </div>
        <router-view></router-view>
      </main>
      <footer class="bg-success text-white text-center py-2">
        &copy; 2023 Eat Fresh. All rights reserved.
      </footer>
    </div>
      `,
  data() {
    return {
      ...
    };
  },
  methods: {
    ...
  },
  mounted() {
    ...
  },
};
export default DashCompo;

Now let's design the different dashboards for different roles.

Admin Dashboard

js filename=static/views/AdminApp.js

import DashCompo from "../components/DashCompo.js";

const AdminApp = {
  name: "AdminApp",
  template: `
    <DashCompo @home="home" @notifi="notifi">
      <template v-slot:menu>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="createCat">Add category</a>
          </li>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="createPro">Add product</a>
          </li>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="managers">Managers</a>
          </li>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="stats">stats</a>
          </li>
      </template>
      <template v-slot:edit>
        <a class="pointer-on-hover" @click="editCat(category.id)">edit</a>
      </template>
    </DashCompo>
    `,
  components: {
    DashCompo,
  },
  methods: {
    ...
  },
  mounted() {
    ...
  },
};
export default AdminApp;

Note Whenever we inherit a template into anothe template use bind the parent template to new component in the components option.

  components: {
    DashCompo,
  },

Manager Dashboard

js filename=static/views/ManagerApp.js

const ManagerApp = {
    name: "ManagerApp",
    template: `
    <DashCompo @home="home" @notifi="notifi">
      <template v-slot:menu>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="createCat">Add category</a>
          </li>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="createPro">Add product</a>
          </li>
          <li class="nav-item">
              <a class="nav-link pointer-on-hover" @click="stats">stats</a>
          </li>
      </template>
      <template v-slot:edit>
        <a class="pointer-on-hover" @click="editCat(category.id)">edit</a>
      </template>
    </DashCompo>
    `,
    components: {
        DashCompo,
    },
    data() {
        return {};
    },
    methods: {
        home() {
            // do something
        },
    },
    mounted() {
        // write your code here
    },
};
export default ManagerApp;

User Dashboard

js filename=static/views/UserApp.js

const UserApp = {
  name: "UserApp",
  template: `
  <DashCompo @home="home" @notifi="notifi">
    <template v-slot:menu>
      <li class="nav-item">
          <a class="nav-link pointer-on-hover" @click="orders">Your Orders</a>
      </li>
      <li class="nav-item">
          <a class="nav-link pointer-on-hover" @click="shareViaWhatsApp" >Refer</a>
      </li>
    </template>
  </DashCompo>
  `,
  components: {
    DashCompo,
  },
  data() {
    return {};
  },
  methods: {
    home() {
      // do something
    },
  },
  mounted() {
    // write your code here
  },
};
export default UserApp;

Now since we have created the major dashboards for each role required for this application. We need to create the routes for each dashboard.

Let's create the routes and add them to the paths.


js filename=static/routes/index.js
import HomeView from "../views/HomeView.js";
import LoginCompo from "../views/LoginCompo.js";
import RegisterCompo from "../views/RegisterCompo.js";
import AdminApp from "../views/AdminApp.js"; // [tl! add:start]
import ManagerApp from "../views/ManagerApp.js";
import UserApp from "../views/UserApp.js"; // [tl! add:end]
const router = VueRouter.createRouter({
  history: VueRouter.createWebHistory(),
  routes: [
    { path: "/app", component: HomeView },
    { path: "/app/login", component: LoginCompo },
    { path: "/app/register", component: RegisterCompo },
    { path: "/app/admin", component: AdminApp }, // [tl! add:start]
    { path: "/app/manager", component: ManagerApp },
    { path: "/app/user", component: UserApp }, // [tl! add:end]
  ],
});
export default router;

State Management

We will utilize Vuex for state management to efficiently handle the data for each dashboard. As a centralized state management library, Vuex provides a single source of truth for application data, which is accessible by any component. This centralization facilitates the implementation of reactive programming, ensuring that any updates to the application's state are immediately reflected across the user interface. By leveraging Vuex, we can maintain a clean separation of concerns, enhance code maintainability, and improve the overall scalability of our application.

Again we will use CDN link for using Vuex in out Vue app. Let's First create the common structure of the file. We will create a folder store inside the static folder and then create a file index.js inside the store folder.


js filename=static/store/index.js
const store = new Vuex.Store({
  state: {
    // write your code here
  },
  getters: {
    // write your code here
  },
  mutations: {
    // write your code here
  },
  actions: {
    // write your code here
  },
});
export default store;

For more information about Vuex, you can refer to the official documentationvuex.vuejs.org/guide/. Now we need to configure it with our main Vue app. Let's update the code in the index.js file.

html filename= templates/index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Eat Fresh</title>
  <link href="https:/cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
  <script src="https:/cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css"
    crossorigin="anonymous" />
  <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
  <script src="https://unpkg.com/vue-router@4.0.15/dist/vue-router.global.js"></script>
  <script src="https://unpkg.com/vuex@4.0.0/dist/vuex.global.js"></script>
  <!-- [tl! add] -->
  <link rel="stylesheet" href="../static/style.css" />
</head>

<body>
  <div id="app">
    <component is="router-view"></component>
  </div>

  <script type="module">
    import store from "../static/store/index.js"; // [tl! add]
    import router from "../static/router/index.js";

    const { createApp } = Vue;
    const app = createApp({});

    app.use(router);
    app.use(store); // [tl! add]
    var tooltipTriggerList = [].slice.call(
      document.querySelectorAll('[data-bs-toggle="tooltip"]')
    );
    var tooltipList = tooltipTriggerList.map(function (
      tooltipTriggerEl
    ) {
      return new bootstrap.Tooltip(tooltipTriggerEl);
    });
    app.mount("#app");
  </script>
</body>

</html>

The above configuration of Vue Router and Vuex will allow us to manage the state of our application and define routes for the different components. Next, we will focus on implementing the features of our application.

Testing it out

Now that we have configured our application with Vue Router and Vue Store. Let's run the server and check if the application is working fine. Also commit the changes.

python3 app.py

Commit the changes to git.

git add .
git commit -m "Implemented role-based dashboards with Vuex and Vue Router integration"

Deleting a chirp

Continue to admin features...