484f89e2ee
NPM Installation / build (16.x, ubuntu-latest) (push) Waiting to run
NPM Installation / build (16.x, windows-latest) (push) Waiting to run
NPM Installation / build (17.x, ubuntu-latest) (push) Waiting to run
NPM Installation / build (17.x, windows-latest) (push) Waiting to run
NPM Installation / build (18.x, ubuntu-latest) (push) Waiting to run
NPM Installation / build (18.x, windows-latest) (push) Waiting to run
754 lines
22 KiB
JavaScript
754 lines
22 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: 'Website',
|
|
component: () => import('@/views/website/LandingPage.vue'),
|
|
meta: { guestLanding: true },
|
|
},
|
|
{
|
|
path: '/app',
|
|
name: 'Application',
|
|
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: '/forbidden',
|
|
name: 'Forbidden',
|
|
component: () => import('@/views/pages/Page403.vue'),
|
|
meta: { publicWithinApp: true },
|
|
},
|
|
{
|
|
path: '/my-applications',
|
|
name: 'Pengajuan Saya',
|
|
component: () => import('@/views/access-applications/MyApplications.vue'),
|
|
meta: { publicWithinApp: true },
|
|
},
|
|
{
|
|
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: '/customers/orders',
|
|
name: 'Customer List Order',
|
|
component: () => import('@/views/customers/Customers.vue'),
|
|
meta: { customerScope: 'orders' },
|
|
},
|
|
{
|
|
path: '/customers/orders/create',
|
|
name: 'Registrasi Customer',
|
|
component: () => import('@/views/customers/CustomerFormPage.vue'),
|
|
meta: { activeMenu: '/customers/orders' },
|
|
},
|
|
{
|
|
path: '/customers/orders/:id/edit',
|
|
name: 'Edit Customer',
|
|
component: () => import('@/views/customers/CustomerFormPage.vue'),
|
|
meta: { activeMenu: '/customers/orders' },
|
|
},
|
|
{
|
|
path: '/customers/:scope/:id/detail',
|
|
name: 'Detail Customer',
|
|
component: () => import('@/views/customers/CustomerDetailPage.vue'),
|
|
meta: { activeMenuBase: '/customers' },
|
|
},
|
|
{
|
|
path: '/customers/active',
|
|
name: 'Customer Aktif',
|
|
component: () => import('@/views/customers/Customers.vue'),
|
|
meta: { customerScope: 'active' },
|
|
},
|
|
{
|
|
path: '/customers/inactive',
|
|
name: 'Customer Tidak Aktif',
|
|
component: () => import('@/views/customers/Customers.vue'),
|
|
meta: { customerScope: 'inactive' },
|
|
},
|
|
{
|
|
path: '/customers/unmanaged',
|
|
name: 'Customer Unmanage',
|
|
component: () => import('@/views/customers/Customers.vue'),
|
|
meta: { customerScope: 'unmanaged' },
|
|
},
|
|
{
|
|
path: '/customers/trash',
|
|
name: 'Sampah Customer',
|
|
component: () => import('@/views/customers/Customers.vue'),
|
|
meta: { customerScope: 'trash' },
|
|
},
|
|
{
|
|
path: '/billing/profiles',
|
|
name: 'Profile Tagihan',
|
|
component: () => import('@/views/billing/BillingProfiles.vue'),
|
|
},
|
|
{
|
|
path: '/billing/running',
|
|
name: 'Tagihan Berjalan',
|
|
component: () => import('@/views/billing/Invoices.vue'),
|
|
meta: { invoiceScope: 'running' },
|
|
},
|
|
{
|
|
path: '/billing/overdue',
|
|
name: 'Tunggakan',
|
|
component: () => import('@/views/billing/Invoices.vue'),
|
|
meta: { invoiceScope: 'overdue' },
|
|
},
|
|
{
|
|
path: '/billing/paid',
|
|
name: 'Tagihan Lunas',
|
|
component: () => import('@/views/billing/Invoices.vue'),
|
|
meta: { invoiceScope: 'paid' },
|
|
},
|
|
{
|
|
path: '/topology/map',
|
|
name: 'Peta Jaringan',
|
|
component: () => import('@/views/topology/TopologyMap.vue'),
|
|
},
|
|
{
|
|
path: '/topology/devices',
|
|
name: 'Perangkat Topologi',
|
|
component: () => import('@/views/topology/TopologyResourceView.vue'),
|
|
meta: { topologyResource: 'node' },
|
|
},
|
|
{
|
|
path: '/topology/links',
|
|
name: 'Jalur Kabel',
|
|
component: () => import('@/views/topology/TopologyResourceView.vue'),
|
|
meta: { topologyResource: 'link' },
|
|
},
|
|
{
|
|
path: '/topology/network',
|
|
name: 'Topologi Jaringan',
|
|
component: () => import('@/views/topology/TopologyNetwork.vue'),
|
|
},
|
|
{
|
|
path: '/topology/device-types',
|
|
name: 'Kategori Perangkat',
|
|
component: () => import('@/views/topology/TopologyResourceView.vue'),
|
|
meta: { topologyResource: 'device-type' },
|
|
},
|
|
{
|
|
path: '/notifications-service/broadcasts',
|
|
name: 'Pesan Siaran',
|
|
component: () => import('@/views/notifications-service/NotificationBroadcasts.vue'),
|
|
},
|
|
{
|
|
path: '/notifications-service/system',
|
|
name: 'Notifikasi Aplikasi',
|
|
component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'),
|
|
meta: { notificationChannel: 'system' },
|
|
},
|
|
{
|
|
path: '/notifications-service/whatsapp-official',
|
|
name: 'WhatsApp Official',
|
|
component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'),
|
|
meta: { notificationChannel: 'whatsapp_official' },
|
|
},
|
|
{
|
|
path: '/notifications-service/whatsapp-unofficial',
|
|
name: 'WhatsApp Unofficial',
|
|
component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'),
|
|
meta: { notificationChannel: 'whatsapp_unofficial' },
|
|
},
|
|
{
|
|
path: '/notifications-service/email',
|
|
name: 'Email Notifikasi',
|
|
component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'),
|
|
meta: { notificationChannel: 'email' },
|
|
},
|
|
{
|
|
path: '/notifications-service/telegram',
|
|
name: 'Telegram Notifikasi',
|
|
component: () => import('@/views/notifications-service/NotificationChannelDashboard.vue'),
|
|
meta: { notificationChannel: 'telegram' },
|
|
},
|
|
{
|
|
path: '/payment-gateway/settings',
|
|
name: 'Setting Payment Gateway',
|
|
component: () => import('@/views/payment-gateway/PaymentGatewaySettings.vue'),
|
|
},
|
|
{
|
|
path: '/payment-gateway/logs',
|
|
name: 'Log Pembayaran',
|
|
component: () => import('@/views/payment-gateway/PaymentLogs.vue'),
|
|
},
|
|
{
|
|
path: '/deposit-balance',
|
|
alias: '/wallet',
|
|
name: 'Saldo Deposit',
|
|
component: () => import('@/views/wallet/WalletDashboard.vue'),
|
|
meta: { activeMenu: '/deposit-balance' },
|
|
},
|
|
{
|
|
path: '/voucher-hotspot/sales',
|
|
name: 'Jual Voucher Hotspot',
|
|
component: () => import('@/views/voucher-hotspot/VoucherSales.vue'),
|
|
},
|
|
{
|
|
path: '/voucher-hotspot/agents',
|
|
name: 'Agen Voucher',
|
|
component: () => import('@/views/voucher-hotspot/VoucherAgents.vue'),
|
|
},
|
|
{
|
|
path: '/voucher-hotspot/login-page',
|
|
name: 'Voucher LoginPage',
|
|
component: () => import('@/views/voucher-hotspot/VoucherLoginPage.vue'),
|
|
},
|
|
{
|
|
path: '/access-applications/tenants',
|
|
name: 'Pengajuan Tenant',
|
|
component: () => import('@/views/access-applications/ApplicationReviews.vue'),
|
|
meta: { applicationType: 'tenant' },
|
|
},
|
|
{
|
|
path: '/access-applications/agents',
|
|
name: 'Pengajuan Agen',
|
|
component: () => import('@/views/access-applications/ApplicationReviews.vue'),
|
|
meta: { applicationType: 'agent' },
|
|
},
|
|
{
|
|
path: '/access-applications/staff',
|
|
name: 'Pengajuan Staff',
|
|
component: () => import('@/views/access-applications/ApplicationReviews.vue'),
|
|
meta: { applicationType: 'staff' },
|
|
},
|
|
{
|
|
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 {
|
|
const menuStore = useMenuStore()
|
|
await menuStore.fetchMenus()
|
|
|
|
const publicWithinApp = to.matched.some((record) => record.meta.publicWithinApp)
|
|
const alwaysAllowed = ['/dashboard', '/profile', '/forbidden', '/my-applications']
|
|
|
|
if (!publicWithinApp && !alwaysAllowed.includes(to.path)) {
|
|
let accessUrl = to.meta.activeMenu || to.path
|
|
|
|
if (to.meta.activeMenuBase && to.params.scope) {
|
|
accessUrl = `${to.meta.activeMenuBase}/${to.params.scope}`
|
|
}
|
|
|
|
if (!menuStore.canAccessUrl(accessUrl)) {
|
|
return {
|
|
name: 'Forbidden',
|
|
query: { from: to.fullPath },
|
|
replace: true,
|
|
}
|
|
}
|
|
}
|
|
} catch {
|
|
useAuthStore().logout()
|
|
|
|
return {
|
|
name: 'Login',
|
|
query: {
|
|
redirect: to.fullPath,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
return true
|
|
})
|
|
|
|
export default router
|