Files
CRM_Montzar/apps/web/src/router/index.ts
T
Mahdi f5d68d83fc feat: add per-org SMS wallet with adm panel and header credit
Bill all platform SMS (including OTP), allow negative balance with manual/auto cutoffs, and support own-panel bypass without wallet debit.
2026-10-19 15:55:48 +03:30

603 lines
19 KiB
TypeScript

import { createRouter, createWebHistory } from 'vue-router';
import type { UserRole } from '@mento/shared';
import { useAuthStore } from '@/stores/auth';
import { usePlatformAuthStore } from '@/stores/platform-auth';
export const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/login',
name: 'login',
component: () => import('@/views/LoginView.vue'),
meta: { public: true },
},
{
path: '/register',
name: 'register',
component: () => import('@/views/RegisterView.vue'),
meta: { public: true },
},
{
path: '/f/:token',
name: 'public-lead-form',
component: () => import('@/views/LeadFormPublicView.vue'),
meta: { public: true },
},
{
path: '/p/:slug',
name: 'portal',
component: () => import('@/views/PortalView.vue'),
meta: { public: true },
},
{
path: '/adm',
name: 'adm-login',
component: () => import('@/views/adm/AdmLoginView.vue'),
meta: { public: true, platformPublic: true },
},
{
path: '/adm',
component: () => import('@/layouts/AdmLayout.vue'),
meta: { public: true, platform: true },
children: [
{
path: 'dashboard',
name: 'adm-dashboard',
component: () => import('@/views/adm/AdmDashboardView.vue'),
},
{
path: 'organizations',
name: 'adm-organizations',
component: () => import('@/views/adm/AdmOrganizationsView.vue'),
},
{
path: 'users',
name: 'adm-users',
component: () => import('@/views/adm/AdmUsersView.vue'),
},
{
path: 'admins',
name: 'adm-admins',
component: () => import('@/views/adm/AdmAdminsView.vue'),
},
{
path: 'sms',
name: 'adm-sms',
component: () => import('@/views/adm/AdmSmsView.vue'),
},
],
},
{
path: '/',
component: () => import('@/layouts/AppLayout.vue'),
children: [
{
path: '',
name: 'home',
component: () => import('@/views/HomeView.vue'),
},
{
path: 'profile',
name: 'profile',
component: () => import('@/views/ProfileView.vue'),
},
{
path: 'reports',
name: 'reports',
component: () => import('@/views/ReportsView.vue'),
},
{
path: 'behavior/disc',
name: 'disc-report',
component: () => import('@/views/DiscReportView.vue'),
},
{
path: 'sales/forecast',
name: 'sales-forecast',
component: () => import('@/views/SalesForecastView.vue'),
},
{
path: 'tickets',
name: 'tickets',
component: () => import('@/views/TicketsView.vue'),
},
{
path: 'conversations',
name: 'conversations',
component: () => import('@/views/ConversationsView.vue'),
},
{
path: 'tickets/new',
name: 'ticket-new',
component: () => import('@/views/TicketFormView.vue'),
},
{
path: 'tickets/:id',
name: 'ticket-detail',
component: () => import('@/views/TicketFormView.vue'),
},
{
path: 'activities',
name: 'activities',
component: () => import('@/views/ActivitiesView.vue'),
},
{
path: 'activities/calendar',
name: 'activities-calendar',
component: () => import('@/views/ActivitiesCalendarView.vue'),
},
{
path: 'activities/new',
name: 'activity-new',
component: () => import('@/views/ActivityFormView.vue'),
},
{
path: 'activities/:id/edit',
name: 'activity-edit',
component: () => import('@/views/ActivityFormView.vue'),
},
{
path: 'activities/:id',
name: 'activity-detail',
component: () => import('@/views/ActivityDetailView.vue'),
},
{
path: 'routines',
component: () => import('@/views/routines/RoutinesShellView.vue'),
children: [
{
path: '',
name: 'routines-today',
component: () => import('@/views/routines/RoutinesTodayView.vue'),
},
{
path: 'overdue',
name: 'routines-overdue',
component: () => import('@/views/routines/RoutinesOverdueView.vue'),
},
{
path: 'calendar',
name: 'routines-calendar',
component: () => import('@/views/routines/RoutinesCalendarView.vue'),
},
{
path: 'list',
name: 'routines-list',
component: () => import('@/views/routines/RoutinesListView.vue'),
},
{
path: 'me',
name: 'routines-me',
component: () => import('@/views/routines/RoutinesMyPerformanceView.vue'),
},
{
path: 'team',
name: 'routines-team',
component: () => import('@/views/routines/RoutinesTeamView.vue'),
},
{
path: 'manage',
name: 'routines-manage',
component: () => import('@/views/routines/RoutinesManageView.vue'),
},
{
path: 'manage/new',
name: 'routine-new',
component: () => import('@/views/routines/RoutineFormView.vue'),
},
{
path: 'manage/:id/edit',
name: 'routine-edit',
component: () => import('@/views/routines/RoutineFormView.vue'),
},
{
path: 'settings',
name: 'routines-settings',
component: () => import('@/views/routines/RoutinesSettingsView.vue'),
},
],
},
{
path: 'quotes',
name: 'quotes',
component: () => import('@/views/QuotesView.vue'),
},
{
path: 'quotes/new',
name: 'quote-new',
component: () => import('@/views/QuoteFormView.vue'),
},
{
path: 'quotes/:id',
name: 'quote-detail',
component: () => import('@/views/QuoteFormView.vue'),
},
{
path: 'invoices',
name: 'invoices',
component: () => import('@/views/InvoicesView.vue'),
},
{
path: 'invoices/new',
name: 'invoice-new',
component: () => import('@/views/InvoiceFormView.vue'),
},
{
path: 'invoices/:id',
name: 'invoice-detail',
component: () => import('@/views/InvoiceFormView.vue'),
},
{
path: 'inventory',
name: 'inventory',
component: () => import('@/views/InventoryView.vue'),
},
{
path: 'cheques',
name: 'cheques',
component: () => import('@/views/ChequesView.vue'),
},
{
path: 'finance',
name: 'finance',
component: () => import('@/views/treasury/TreasuryHomeView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/accounts',
name: 'finance-accounts',
component: () => import('@/views/treasury/TreasuryAccountsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/accounts/:id',
name: 'finance-account',
component: () => import('@/views/treasury/TreasuryAccountView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/incomes',
name: 'finance-incomes',
component: () => import('@/views/treasury/TreasuryIncomesView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/expenses',
name: 'finance-expenses',
component: () => import('@/views/treasury/TreasuryExpensesView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/receivables',
name: 'finance-receivables',
component: () => import('@/views/treasury/TreasuryReceivablesView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/payroll',
name: 'finance-payroll',
component: () => import('@/views/treasury/TreasuryPayrollView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/recurring',
name: 'finance-recurring',
component: () => import('@/views/treasury/TreasuryRecurringView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/reports',
name: 'finance-reports',
component: () => import('@/views/treasury/TreasuryReportsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/audit',
name: 'finance-audit',
component: () => import('@/views/treasury/TreasuryAuditView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'finance/categories',
name: 'finance-categories',
component: () => import('@/views/treasury/TreasuryCategoriesView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'reservations',
name: 'reservations',
component: () => import('@/views/ReservationsView.vue'),
},
{
path: 'commissions',
name: 'commissions',
component: () => import('@/views/CommissionsView.vue'),
},
{
path: 'pipeline',
name: 'pipeline',
component: () => import('@/views/PipelineBoardView.vue'),
},
{
path: 'leads',
name: 'leads',
component: () => import('@/views/LeadsView.vue'),
},
{
path: 'leads/board',
name: 'leads-board',
component: () => import('@/views/LeadBoardView.vue'),
},
{
path: 'leads/:id',
name: 'lead-detail',
component: () => import('@/views/LeadDetailView.vue'),
},
{
path: 'projects',
name: 'projects',
component: () => import('@/views/ProjectBoardView.vue'),
},
{
path: 'project-cards/new',
name: 'project-card-new',
component: () => import('@/views/ProjectCardFormView.vue'),
},
{
path: 'project-cards/:id',
name: 'project-card-detail',
component: () => import('@/views/ProjectCardFormView.vue'),
},
{
path: 'loyalty',
name: 'loyalty',
component: () => import('@/views/LoyaltyClubView.vue'),
},
{
path: 'club',
name: 'gamification',
component: () => import('@/views/gamification/GamificationView.vue'),
},
{
path: 'club/admin',
name: 'gamification-admin',
component: () => import('@/views/gamification/GamificationAdminView.vue'),
},
{
path: 'deals/new',
name: 'deal-new',
component: () => import('@/views/DealFormView.vue'),
},
{
path: 'deals/:id',
name: 'deal-detail',
component: () => import('@/views/DealFormView.vue'),
},
{
path: 'contacts',
name: 'contacts',
component: () => import('@/views/ContactsView.vue'),
},
{
path: 'notes',
name: 'notes',
component: () => import('@/views/NotesView.vue'),
},
{
path: 'attachments',
name: 'attachments',
component: () => import('@/views/AttachmentsView.vue'),
},
{
path: 'settings/automations',
name: 'automations',
component: () => import('@/views/AutomationsSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/message-automations',
name: 'message-automations',
component: () => import('@/views/MessageAutomationsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/lead-stages',
name: 'lead-stages',
component: () => import('@/views/LeadStagesSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/pipelines',
name: 'pipeline-settings',
component: () => import('@/views/PipelineSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[], module: 'pipeline' },
},
{
path: 'settings/lead-forms',
name: 'lead-forms',
component: () => import('@/views/LeadFormsSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/lead-forms/:id',
name: 'lead-form-builder',
component: () => import('@/views/LeadFormBuilderView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/team',
name: 'team-settings',
component: () => import('@/views/TeamSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/audit-logs',
name: 'audit-logs',
component: () => import('@/views/AuditLogsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/deletions',
name: 'deletion-archives',
component: () => import('@/views/DeletionArchivesView.vue'),
meta: { roles: ['owner'] as UserRole[] },
},
{
path: 'settings/public-api',
name: 'public-api',
component: () => import('@/views/PublicApiSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/sms-campaigns',
name: 'sms-campaigns',
component: () => import('@/views/SmsCampaignsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/sms-campaigns/:id/report',
name: 'sms-campaign-report',
component: () => import('@/views/SmsCampaignReportView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/custom-fields',
name: 'custom-fields',
component: () => import('@/views/CustomFieldsSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/products',
name: 'products',
component: () => import('@/views/ProductsSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/products/new',
name: 'product-new',
component: () => import('@/views/ProductFormView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/products/:id/edit',
name: 'product-edit',
component: () => import('@/views/ProductFormView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/currencies',
name: 'currencies',
component: () => import('@/views/CurrencySettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/tahesab',
name: 'tahesab-settings',
component: () => import('@/views/TahesabSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/woocommerce',
name: 'woocommerce-settings',
component: () => import('@/views/WooCommerceSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/montify',
name: 'montify-settings',
component: () => import('@/views/MontifySettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/voip',
name: 'voip-settings',
component: () => import('@/views/VoipSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'settings/faq',
name: 'faq-settings',
component: () => import('@/views/FaqSettingsView.vue'),
meta: { roles: ['owner', 'admin'] as UserRole[] },
},
{
path: 'contacts/people/new',
name: 'person-new',
component: () => import('@/views/PersonFormView.vue'),
},
{
path: 'contacts/people/:id/edit',
name: 'person-edit',
component: () => import('@/views/PersonFormView.vue'),
},
{
path: 'contacts/people/:id',
name: 'person-detail',
component: () => import('@/views/PersonProfileView.vue'),
},
{
path: 'contacts/companies/new',
name: 'company-new',
component: () => import('@/views/CompanyFormView.vue'),
},
{
path: 'contacts/companies/:id',
name: 'company-detail',
component: () => import('@/views/CompanyFormView.vue'),
},
],
},
],
});
router.beforeEach((to) => {
if (to.path === '/adm' || to.path.startsWith('/adm/')) {
const platform = usePlatformAuthStore();
if (to.meta.platformPublic) {
if (platform.isAuthenticated) return { name: 'adm-dashboard' };
return true;
}
if (!platform.isAuthenticated) return { name: 'adm-login' };
return true;
}
const auth = useAuthStore();
if (!to.meta.public && !auth.isAuthenticated) {
return { name: 'login', query: { redirect: to.fullPath } };
}
if (
to.meta.public &&
auth.isAuthenticated &&
(to.name === 'login' || to.name === 'register')
) {
return { name: 'home' };
}
if (String(to.path).startsWith('/behavior')) {
const modules = auth.user?.enabledModules;
if (modules && !modules.includes('behavior')) return { name: 'home' };
}
if (String(to.path).startsWith('/finance')) {
const modules = auth.user?.enabledModules;
if (modules && !modules.includes('treasury')) return { name: 'home' };
}
const roles = to.meta.roles as UserRole[] | undefined;
if (roles?.length) {
const role = auth.user?.role as UserRole | undefined;
if (role && roles.includes(role)) {
return true;
}
if (to.name === 'team-settings' && auth.user?.viewAs?.canOpenTeamSettings) {
return true;
}
if (String(to.path).startsWith('/finance')) {
const perms = auth.user?.permissions ?? [];
const payroll = to.path.startsWith('/finance/payroll');
if (payroll && perms.includes('treasury_payroll')) return true;
if (!payroll && (perms.includes('treasury') || perms.includes('treasury_payroll'))) return true;
}
return { name: 'home' };
}
return true;
});