vue login basic

install depen

npm install axios
npm install chart.js
npm install js-cookie
npm install lodash
npm install luxon
npm install numeral
npm install pinia
npm install pinia-plugin-persistedstate
npm install vue-router

npm install tailwindcss @tailwindcss/vite
//vite.config.ts


import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
  plugins: [
    tailwindcss(),
  ],
})
// css

@import "tailwindcss";

edit App.vue

<script setup>
import { RouterLink, RouterView } from "vue-router";
</script>

<template>
  <RouterView />
</template>

edit main.ts/js


import { createApp } from 'vue'
import { createPinia } from 'pinia'

import './index.css'

import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(createPinia())
app.use(router)

app.mount('#app')
# force artinya jika folder atau filenya sudah tidak ada ,tidak akan error dan di overwrite
mkdir src/helpers -Force; New-Item -ItemType File src/helpers/errorHelper.js -Force
New-Item -ItemType File src/helpers/format.js -Force
New-Item -ItemType File src/helpers/permissionHelper.js -Force

mkdir src/plugins -Force; New-Item -ItemType File src/plugins/axios.js -Force
mkdir src/layouts -Force; New-Item -ItemType File src/layouts/Auth.vue -Force
New-Item -ItemType File src/layouts/Main.vue -Force

mkdir src/views -Force; New-Item -ItemType File src/views/Login.vue -Force
New-Item -ItemType File src/views/Dashboard.vue -Force

mkdir src/stores -Force; New-Item -ItemType File src/stores/auth.js -Force

New-Item -ItemType File src/index.css -Force

mkdir src/router -Force; New-Item -ItemType File src/router/index.js -Force


// src/roter/index.ts

import Auth from "@/layouts/Auth.vue";
import Main from "@/layouts/Main.vue";
import Dashboard from "@/views/Dashboard.vue";
import Login from "@/views/Login.vue";
import { createRouter, createWebHistory } from "vue-router";
import { useAuthStore } from "@/stores/auth";
import HeadOfFamilies from "@/views/head-of-family/HeadOfFamilies.vue";
import HeadOfFamily from "@/views/head-of-family/HeadOfFamily.vue";
import HeadOfFamilyCreate from "@/views/head-of-family/HeadOfFamilyCreate.vue";
import SocialAssistances from "@/views/social-assistance/SocialAssistances.vue";
import SocialAssistance from "@/views/social-assistance/SocialAssistance.vue";
import SocialAssistanceEdit from "@/views/social-assistance/SocialAssistanceEdit.vue";
import SocialAssistanceCreate from "@/views/social-assistance/SocialAssistanceCreate.vue";
import SocialAssistanceRecipients from "@/views/social-assistance-recipient/socialAssistanceRecipients.vue";
import SocialAssistanceRecipient from "@/views/social-assistance-recipient/SocialAssistanceRecipient.vue";
import Developments from "@/views/development/developments.vue";
import Development from "@/views/development/development.vue";
import DevelopmentCreate from "@/views/development/developmentCreate.vue";
import DevelopmentEdit from "@/views/development/developmentEdit.vue";
import Events from "@/views/Event/Events.vue";
import Event from "@/views/Event/Event.vue";
import EventEdit from "@/views/Event/EventEdit.vue";
import EventCreate from "@/views/Event/EventCreate.vue";
import Profile from "@/views/profile/Profile.vue";
import ProfileCreate from "@/views/profile/ProfileCreate.vue";
import FamilyMembers from "@/views/family-member/FamilyMembers.vue";
import FamilyMember from "@/views/family-member/FamilyMember.vue";
import FamilyMemberCreate from "@/views/family-member/FamilyMemberCreate.vue";

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: "/",
      component: Main,
      children: [
        {
          path: "",
          name: "dashboard",
          component: Dashboard,
          meta: { requiresAuth: true, permission: "dashboard-menu" },
        },
        {
          path: "family-member/:id",
          name: "manage-family-member",
          component: FamilyMember,
          meta: { requiresAuth: true, permission: "family-member-list" },
        },
        {
          path: "family-member",
          name: "family-member",
          component: FamilyMembers,
          meta: { requiresAuth: true, permission: "family-member-list" },
        },
        {
          path: "family-member/create",
          name: "create-family-member",
          component: FamilyMemberCreate,
          meta: { requiresAuth: true, permission: "family-member-create" },
        },
        {
          path: "head-of-family",
          name: "head-of-family",
          component: HeadOfFamilies,
          meta: { requiresAuth: true, permission: "head-of-family-list" },
        },
        {
          path: "head-of-family/:id", // ✅ fix param route
          name: "manage-head-of-family",
          component: HeadOfFamily,
          meta: { requiresAuth: true, permission: "head-of-family-list" },
        },
        {
          path: "head-of-family/create",
          name: "create-head-of-family",
          component: HeadOfFamilyCreate,
          meta: { requiresAuth: true, permission: "head-of-family-create" },
        },
        {
          path: "social-assistance",
          name: "social-assistance",
          component: SocialAssistances,
          meta: { requiresAuth: true, permission: "social-assistance-list" },
        },

        {
          path: "social-assistance/:id", // ✅ fix param route
          name: "manage-social-assistance",
          component: SocialAssistance,
          meta: { requiresAuth: true, permission: "social-assistance-list" },
        },
        {
          path: "social-assistance/create",
          name: "create-social-assistance",
          component: SocialAssistanceCreate,
          meta: { requiresAuth: true, permission: "social-assistance-create" },
        },
        {
          path: "social-assistance/edit/:id", // ✅ fix param route
          name: "edit-social-assistance",
          component: SocialAssistanceEdit,
          meta: { requiresAuth: true, permission: "social-assistance-edit" },
        },

        {
          path: "social-assistance-recipient",
          name: "social-assistance-recipient",
          component: SocialAssistanceRecipients,
          meta: {
            requiresAuth: true,
            permission: "social-assistance-recipient-list",
          },
        },
        {
          path: "social-assistance-recipient/:id", // ✅ fix param route
          name: "manage-social-assistance-recipient",
          component: SocialAssistanceRecipient,
          meta: { requiresAuth: true, permission: "social-assistance-list" },
        },
        {
          path: "development",
          name: "development",
          component: Developments,
          meta: { requiresAuth: true, permission: "development-list" },
        },
        {
          path: "development/:id", // ✅ fix param route
          name: "manage-development",
          component: Development,
          meta: { requiresAuth: true, permission: "development-list" },
        },
        {
          path: "development/create",
          name: "create-development",
          component: DevelopmentCreate,
          meta: { requiresAuth: true, permission: "development-create" },
        },
        {
          path: "development/edit/:id", // ✅ fix param route
          name: "edit-development",
          component: DevelopmentEdit,
          meta: { requiresAuth: true, permission: "development-edit" },
        },
        {
          path: "event",
          name: "event",
          component: Events,
          meta: { requiresAuth: true, permission: "event-list" },
        },
        {
          path: "event/:id",
          name: "manage-event",
          component: Event,
          meta: { requiresAuth: true, permission: "event-list" },
        },

        {
          path: "event/create",
          name: "create-event",
          component: EventCreate,
          meta: { requiresAuth: true, permission: "event-create" },
        },

        {
          path: "event/edit/:id",
          name: "edit-event",
          component: EventEdit,
          meta: { requiresAuth: true, permission: "event-edit" },
        },
        {
          path: "profile",
          name: "profile",
          component: Profile,
          meta: { requiresAuth: true, permission: "profile-menu" },
        },
        {
          path: "profile/create",
          name: "create-profile",
          component: ProfileCreate,
          meta: { requiresAuth: true, permission: "profile-create" },
        },
      ],
    },
    {
      path: "/login",
      component: Auth,
      children: [
        {
          path: "",
          name: "login",
          component: Login,
          meta: { requiresUnauth: true },
        },
      ],
    },
    // {
    //   path: "/403", // ✅ tambahin biar nggak error
    //   name: "Error 403",
    //   component: () => import("@/views/errors/Error403.vue"),
    // },
  ],
});

router.beforeEach(async (to, from, next) => {
  const authStore = useAuthStore();

  if (to.meta.requiresAuth) {
    if (authStore.isAuthenticated) {
      try {
        if (!authStore.user) {
          await authStore.checkAuth();
        }

        const userPermissions = authStore.user?.permissions || [];

        if (
          to.meta.permission &&
          !userPermissions.includes(to.meta.permission)
        ) {
          next({ name: "Error 403" });
          return;
        }

        next();
      } catch (error) {
        next({ name: "login" });
      }
    } else {
      next({ name: "login" });
    }
  } else if (to.meta.requiresUnauth && authStore.isAuthenticated) {
    next({ name: "dashboard" });
  } else {
    next();
  }
});

export default router;

// src/stores/auth.ts
import { handleError } from "@/helpers/errorHelper";
import { axiosInstance } from "@/plugins/axios";
import router from "@/router";
import Cookies from "js-cookie";
import { defineStore } from "pinia";

export const useAuthStore = defineStore("auth", {
  state: () => ({
    user: null,
    token: Cookies.get("token") || null, // ✅ simpan di state
    loading: false,
    error: null,
    success: null,
  }),
  getters: {
    isAuthenticated: state => !!state.token, // ✅ gampang cek login
  },
  actions: {
    async login(credentials) {
      this.loading = true;
      try {
        const response = await axiosInstance.post("/login", credentials);
        const token = response.data.token;

        this.token = token; // ✅ simpan ke state
        Cookies.set("token", token);
        axiosInstance.defaults.headers.common["Authorization"] = `Bearer ${token}`;

        this.success = "Login successful";
        await router.push({ name: "dashboard" });
      } catch (error) {
        this.error = handleError(error);
      } finally {
        this.loading = false;
      }
    },

    async logout() {
      this.loading = true;
      try {
        await axiosInstance.post("/logout");

        this.token = null; // ✅ clear dari state
        Cookies.remove("token");
        delete axiosInstance.defaults.headers.common["Authorization"]; // ✅ clear header

        this.user = null;
        this.error = null;
        this.success = "Logout successful";

        await router.push({ name: "login" });
      } catch (error) {
        this.error = handleError(error);
      } finally {
        this.loading = false;
      }
    },

    async checkAuth() {
      this.loading = true;
      try {
        const response = await axiosInstance.get("/me");
        this.user = response.data.data;
        return this.user;
      } catch (error) {
        if (error.response && error.response.status === 401) {
          await this.logout();
        }
      } finally {
        this.loading = false;
      }
    },
  },
});
// Main.vue
<script setup lang="ts"></script>

<template>
  <div class="flex flex1">
    <div id="Main-Container" class="flex flex-col flex-1">
      <div
        id="Content"
        class="relative flec flex-col flex-1 gap-[14px] p6 pb-[30px] w-full shrink-0"
      >
        <RouterView />
      </div>
    </div>
  </div>
</template>
// Auth.vue
<script setup lang="ts">
import { RouterView } from 'vue-router';
</script>
<template>
    <main class="flex min-h-screen">
        <RouterView />
    </main>
</template>
// axios.js
import axios from 'axios';
import Cookies from 'js-cookie';

axios.defaults.baseURL = 'http://localhost:8000/api';
axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
axios.defaults.headers.common['Accept'] = 'application/json';
// axios.defaults.headers.common['Content-Type'] = 'application/json';
axios.defaults.headers.common['Content-Type'] = 'multipart/form-data';


axios.interceptors.request.use(
  config => {
    const token = Cookies.get('token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  error => Promise.reject(error)
);

export const axiosInstance = axios;

errorHelper.js

export function handleError(error) {
  if (error.response && error.response.status === 422) {
    return error.response.data.errors;
  } else if (error.response && error.response.status === 401) {
    return error.response.data.message;
  } else if (error.response && error.response.status === 400) {
    return error.response.data.message;
  } else if (error.response && error.response.status === 500) {
    return error.response.data.message;
  } else {
    alert("Something went wrong, Please try again later");
  }
}

format.js

import numeral from 'numeral';
import { DateTime } from 'luxon';

export function formatRupiah(value) {
    return numeral(value).format('0,0[.]00');
}

export function parseRupiah(value) {
    return numeral(value).value();
}

export function formatPercentage(value) {
    return numeral(value).format('0,0[.]00%');
}

export function formatDate(date) {
    const options = { day: 'numeric', month: 'long', year: 'numeric' };

    return new Date(date).toLocaleDateString('id-ID', options);
}

export function formatDateTime(date) {
    const options = { day: 'numeric', month: 'long', year: 'numeric', hour: 'numeric', minute: 'numeric' };

    return new Date(date).toLocaleDateString('id-ID', options);
}

export function formatToClientTimezone(date) {
    const originalDate = DateTime.fromISO(date, { zone: 'utc' });

    const timezone =  Intl.DateTimeFormat().resolvedOptions().timeZone;

    return originalDate
    .setZone(timezone)
    .setLocale('id')
    .toFormat('dd LLLL yyyy, HH:mm');
    
}

export function ucfirst(string) {
    return string ? string.charAt(0).toUpperCase() + string.slice(1) : '';
}

permissionHelper.js

import { useAuthStore } from '@/stores/auth'

export const can = permission => {
    const authStore = useAuthStore()

    const userPermissions = authStore.user?.permissions || []

    return userPermissions.includes(permission)

}
import axios from "axios";
import Cookies from "js-cookie";

const token = Cookies.get("token");

axios.defaults.baseURL = "http://localhost:8000/api";
axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
axios.defaults.headers.common["Content-Type"] = "multipart/form-data";
axios.defaults.headers.common["Accept"] = "application/json";
axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;

axios.interceptors.request.use((config) => {
  const token = Cookies.get("token");
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }

  return config;
});

export const axiosInstance = axios;
<script setup>
import { RouterView } from "vue-router";
</script>

<template>
  <main class="flex min-h-screen">
    <RouterView />
  </main>
</template>

Leave a Reply