Files
manja_dev_ui/src/router/index.js
T
Wian Drs 73a32380e7
NPM Installation / build (16.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (16.x, windows-latest) (push) Has been cancelled
NPM Installation / build (17.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (17.x, windows-latest) (push) Has been cancelled
NPM Installation / build (18.x, ubuntu-latest) (push) Has been cancelled
NPM Installation / build (18.x, windows-latest) (push) Has been cancelled
base struktur update
2026-07-27 16:25:37 +07:00

532 lines
15 KiB
JavaScript

/**
* router/index.js - Vue Router Configuration
*
* This file configures the application routing using Vue Router 5.
* It defines all routes and navigation structure for the SPA.
*
* Routing Features:
* - Hash-based routing (createWebHashHistory) for static hosting compatibility
* - Lazy loading for all route components (code splitting)
* - Nested routes for layout-based navigation
* - Automatic scroll to top on navigation
*
* Route Structure:
* - Protected routes: Wrapped in DefaultLayout with sidebar and header
* - Public routes: Login, Register, 404, 500 pages without layout
*
* Adding New Routes:
* 1. Import component (use dynamic import for code splitting)
* 2. Add route object to appropriate section
* 3. Update _nav.js for sidebar navigation (if needed)
*
* @see https://router.vuejs.org/
*/
import { h, resolveComponent } from 'vue'
import { createRouter, createWebHashHistory } from 'vue-router'
import DefaultLayout from '@/layouts/DefaultLayout'
import { useAuthStore } from '@/stores/auth'
import { useMenuStore } from '@/stores/menu'
import { hasToken } from '@/utils/session'
/**
* Application routes configuration
* @type {Array<Object>}
*/
const routes = [
{
path: '/',
name: 'Home',
component: DefaultLayout,
redirect: '/dashboard',
meta: {
requiresAuth: true,
},
children: [
{
path: '/dashboard',
name: 'Dashboard',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "dashboard" */ '@/views/dashboard/Dashboard.vue'),
},
{
path: '/profile',
name: 'Profile',
component: () => import('@/views/profile/Profile.vue'),
},
{
path: '/tickets',
name: 'Tickets',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
redirect: '/tickets/ticket',
children: [
{
path: '/tickets/ticket',
name: 'Ticket',
component: () => import('@/views/tickets/Ticket.vue'),
},
{
path: '/tickets/ticket-type',
alias: '/ticket-types',
name: 'Ticket Type',
component: () => import('@/views/tickets/TicketType.vue'),
},
{
path: '/tickets/ticket-materials',
alias: '/materials',
name: 'Ticket Materials',
component: () => import('@/views/tickets/TicketMaterials.vue'),
},
{
path: '/tickets/ticket-incident',
alias: '/ticket-incident-types',
name: 'Ticket Incident',
component: () => import('@/views/tickets/TicketIncident.vue'),
},
{
path: '/tickets/approved',
alias: '/tickets/approval',
name: 'Approved',
component: () => import('@/views/tickets/Approved.vue'),
},
{
path: '/tickets/rejected',
name: 'Rejected',
component: () => import('@/views/tickets/Rejected.vue'),
},
],
},
{
path: '/theme',
name: 'Theme',
redirect: '/theme/typography',
},
{
path: '/theme/colors',
name: 'Colors',
component: () => import('@/views/theme/Colors.vue'),
},
{
path: '/theme/typography',
name: 'Typography',
component: () => import('@/views/theme/Typography.vue'),
},
{
path: '/base',
name: 'Base',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
redirect: '/base/breadcrumbs',
children: [
{
path: '/base/accordion',
name: 'Accordion',
component: () => import('@/views/base/Accordion.vue'),
},
{
path: '/base/breadcrumbs',
name: 'Breadcrumbs',
component: () => import('@/views/base/Breadcrumbs.vue'),
},
{
path: '/base/cards',
name: 'Cards',
component: () => import('@/views/base/Cards.vue'),
},
{
path: '/base/carousels',
name: 'Carousels',
component: () => import('@/views/base/Carousels.vue'),
},
{
path: '/base/chips',
name: 'Chips',
component: () => import('@/views/base/Chips.vue'),
},
{
path: '/base/collapses',
name: 'Collapses',
component: () => import('@/views/base/Collapses.vue'),
},
{
path: '/base/list-groups',
name: 'List Groups',
component: () => import('@/views/base/ListGroups.vue'),
},
{
path: '/base/navs',
name: 'Navs',
component: () => import('@/views/base/Navs.vue'),
},
{
path: '/base/paginations',
name: 'Paginations',
component: () => import('@/views/base/Paginations.vue'),
},
{
path: '/base/placeholders',
name: 'Placeholders',
component: () => import('@/views/base/Placeholders.vue'),
},
{
path: '/base/popovers',
name: 'Popovers',
component: () => import('@/views/base/Popovers.vue'),
},
{
path: '/base/progress',
name: 'Progress',
component: () => import('@/views/base/Progress.vue'),
},
{
path: '/base/spinners',
name: 'Spinners',
component: () => import('@/views/base/Spinners.vue'),
},
{
path: '/base/tables',
name: 'Tables',
component: () => import('@/views/base/Tables.vue'),
},
{
path: '/base/tabs',
name: 'Tabs',
component: () => import('@/views/base/Tabs.vue'),
},
{
path: '/base/tooltips',
name: 'Tooltips',
component: () => import('@/views/base/Tooltips.vue'),
},
],
},
{
path: '/buttons',
name: 'Buttons',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
redirect: '/buttons/standard-buttons',
children: [
{
path: '/buttons/standard-buttons',
name: 'Button Component',
component: () => import('@/views/buttons/Buttons.vue'),
},
{
path: '/buttons/dropdowns',
name: 'Dropdowns',
component: () => import('@/views/buttons/Dropdowns.vue'),
},
{
path: '/buttons/button-groups',
name: 'Button Groups',
component: () => import('@/views/buttons/ButtonGroups.vue'),
},
],
},
{
path: '/forms',
name: 'Forms',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
redirect: '/forms/form-control',
children: [
{
path: '/forms/form-control',
name: 'Form Control',
component: () => import('@/views/forms/FormControl.vue'),
},
{
path: '/forms/select',
name: 'Select',
component: () => import('@/views/forms/Select.vue'),
},
{
path: '/forms/checks-radios',
name: 'Checks & Radios',
component: () => import('@/views/forms/ChecksRadios.vue'),
},
{
path: '/forms/chip-input',
name: 'Chip Input',
component: () => import('@/views/forms/ChipInput.vue'),
},
{
path: '/forms/range',
name: 'Range',
component: () => import('@/views/forms/Range.vue'),
},
{
path: '/forms/input-group',
name: 'Input Group',
component: () => import('@/views/forms/InputGroup.vue'),
},
{
path: '/forms/floating-labels',
name: 'Floating Labels',
component: () => import('@/views/forms/FloatingLabels.vue'),
},
{
path: '/forms/layout',
name: 'Layout',
component: () => import('@/views/forms/Layout.vue'),
},
{
path: '/forms/validation',
name: 'Validation',
component: () => import('@/views/forms/Validation.vue'),
},
],
},
{
path: '/charts',
name: 'Charts',
component: () => import('@/views/charts/Charts.vue'),
},
{
path: '/icons',
name: 'Icons',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
redirect: '/icons/coreui-icons',
children: [
{
path: '/icons/coreui-icons',
name: 'CoreUI Icons',
component: () => import('@/views/icons/CoreUIIcons.vue'),
},
{
path: '/icons/brands',
name: 'Brands',
component: () => import('@/views/icons/Brands.vue'),
},
{
path: '/icons/flags',
name: 'Flags',
component: () => import('@/views/icons/Flags.vue'),
},
],
},
{
path: '/notifications',
name: 'Notifications',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
redirect: '/notifications/alerts',
children: [
{
path: '/notifications/alerts',
name: 'Alerts',
component: () => import('@/views/notifications/Alerts.vue'),
},
{
path: '/notifications/badges',
name: 'Badges',
component: () => import('@/views/notifications/Badges.vue'),
},
{
path: '/notifications/modals',
name: 'Modals',
component: () => import('@/views/notifications/Modals.vue'),
},
{
path: '/notifications/toasts',
name: 'Toasts',
component: () => import('@/views/notifications/Toasts.vue'),
},
],
},
{
path: '/group-menus',
name: 'Group Menus',
component: () => import('@/views/group-menus/GroupMenus.vue'),
},
{
path: '/group-menus/:id/menus',
name: 'Group Menu Permissions',
component: () => import('@/views/group-menus/GroupMenuPermissions.vue'),
meta: { activeMenu: '/group-menus' },
},
{
path: '/nas/mikrotik',
name: 'NAS Mikrotik',
component: () => import('@/views/nas/NasResourceView.vue'),
meta: { nasResource: 'mikrotik' },
},
{
path: '/nas/package-profiles',
name: 'NAS Profile Paket',
component: () => import('@/views/nas/NasResourceView.vue'),
meta: { nasResource: 'package-profile' },
},
{
path: '/nas/olt',
name: 'NAS OLT',
component: () => import('@/views/nas/NasResourceView.vue'),
meta: { nasResource: 'olt' },
},
{
path: '/nas/webfig',
name: 'NAS Webfig',
component: () => import('@/views/nas/NasResourceView.vue'),
meta: { nasResource: 'webfig' },
},
{
path: '/users',
name: 'Users',
component: () => import('@/views/users/Users.vue'),
},
{
path: '/tenants',
name: 'Tenants',
component: () => import('@/views/tenants/Tenants.vue'),
},
{
path: '/wilayah/desa',
name: 'Wilayah Desa',
component: () => import('@/views/wilayah/Wilayah.vue'),
meta: { tingkat: 'desa' },
},
{
path: '/wilayah/kecamatan',
name: 'Wilayah Kecamatan',
component: () => import('@/views/wilayah/Wilayah.vue'),
meta: { tingkat: 'kecamatan' },
},
{
path: '/wilayah/kabupaten',
name: 'Wilayah Kabupaten',
component: () => import('@/views/wilayah/Wilayah.vue'),
meta: { tingkat: 'kabupaten' },
},
{
path: '/wilayah/provinsi',
name: 'Wilayah Provinsi',
component: () => import('@/views/wilayah/Wilayah.vue'),
meta: { tingkat: 'provinsi' },
},
{
path: '/widgets',
name: 'Widgets',
component: () => import('@/views/widgets/Widgets.vue'),
},
],
},
{
path: '/pages',
redirect: '/pages/404',
name: 'Pages',
component: {
render() {
return h(resolveComponent('router-view'))
},
},
children: [
{
path: '404',
name: 'Page404',
component: () => import('@/views/pages/Page404'),
},
{
path: '500',
name: 'Page500',
component: () => import('@/views/pages/Page500'),
},
{
path: 'login',
name: 'Login',
component: () => import('@/views/pages/Login'),
meta: {
guestOnly: true,
},
},
{
path: 'register',
name: 'Register',
component: () => import('@/views/pages/Register'),
meta: {
guestOnly: true,
},
},
{
path: 'verify-code',
name: 'VerifyCode',
component: () => import('@/views/pages/VerifyCode'),
meta: {
guestOnly: true,
},
},
],
},
]
const router = createRouter({
history: createWebHashHistory(import.meta.env.BASE_URL),
routes,
scrollBehavior() {
// always scroll to top
return { top: 0 }
},
})
router.beforeEach(async (to) => {
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth)
const guestOnly = to.matched.some((record) => record.meta.guestOnly)
const authenticated = hasToken()
if (requiresAuth && !authenticated) {
return {
name: 'Login',
query: {
redirect: to.fullPath,
},
}
}
if (guestOnly && authenticated) {
return { name: 'Dashboard' }
}
if (requiresAuth) {
try {
await useMenuStore().fetchMenus()
} catch {
useAuthStore().logout()
return {
name: 'Login',
query: {
redirect: to.fullPath,
},
}
}
}
return true
})
export default router