From 8d94ffcf464f60890983c989376dcd09859436d4 Mon Sep 17 00:00:00 2001
From: Flatlogic Bot
Date: Sat, 4 Apr 2026 04:35:44 +0000
Subject: [PATCH] Autosave: 20260404-043544
---
frontend/src/components/AsideMenuItem.tsx | 36 +-
frontend/src/components/AsideMenuList.tsx | 3 +-
frontend/src/helpers/workspace.ts | 139 ++-
frontend/src/layouts/Authenticated.tsx | 9 +-
frontend/src/menuAside.ts | 1120 +++++++++++++--------
frontend/src/pages/compliance-desk.tsx | 7 +
frontend/src/pages/dashboard.tsx | 55 +-
frontend/src/pages/delivery-command.tsx | 7 +
frontend/src/pages/executive-command.tsx | 7 +
frontend/src/pages/executive-summary.tsx | 12 +
frontend/src/pages/financial-control.tsx | 7 +
frontend/src/pages/index.tsx | 56 +-
frontend/src/pages/login.tsx | 13 +-
frontend/src/pages/operations-command.tsx | 7 +
frontend/src/pages/platform-command.tsx | 7 +
frontend/src/pages/procurement-desk.tsx | 7 +
16 files changed, 1008 insertions(+), 484 deletions(-)
create mode 100644 frontend/src/pages/compliance-desk.tsx
create mode 100644 frontend/src/pages/delivery-command.tsx
create mode 100644 frontend/src/pages/executive-command.tsx
create mode 100644 frontend/src/pages/financial-control.tsx
create mode 100644 frontend/src/pages/operations-command.tsx
create mode 100644 frontend/src/pages/platform-command.tsx
create mode 100644 frontend/src/pages/procurement-desk.tsx
diff --git a/frontend/src/components/AsideMenuItem.tsx b/frontend/src/components/AsideMenuItem.tsx
index dbb09b2..8f494c3 100644
--- a/frontend/src/components/AsideMenuItem.tsx
+++ b/frontend/src/components/AsideMenuItem.tsx
@@ -13,6 +13,20 @@ type Props = {
isDropdownList?: boolean
}
+const getActiveView = (path: string) => new URL(path, location.href).pathname.split('/')[1]
+
+const itemMatchesActiveView = (menuItem: MenuAsideItem, activeView: string): boolean => {
+ if (menuItem.href && getActiveView(menuItem.href) === activeView) {
+ return true
+ }
+
+ if (!menuItem.menu?.length) {
+ return false
+ }
+
+ return menuItem.menu.some((nestedItem) => itemMatchesActiveView(nestedItem, activeView))
+}
+
const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
const [isLinkActive, setIsLinkActive] = useState(false)
const [isDropdownActive, setIsDropdownActive] = useState(false)
@@ -29,16 +43,20 @@ const AsideMenuItem = ({ item, isDropdownList = false }: Props) => {
const { asPath, isReady } = useRouter()
useEffect(() => {
- if (item.href && isReady) {
- const linkPathName = new URL(item.href, location.href).pathname + '/';
- const activePathname = new URL(asPath, location.href).pathname
-
- const activeView = activePathname.split('/')[1];
- const linkPathNameView = linkPathName.split('/')[1];
-
- setIsLinkActive(linkPathNameView === activeView);
+ if (!isReady) {
+ return
}
- }, [item.href, isReady, asPath])
+
+ const activeView = getActiveView(asPath)
+
+ if (item.href) {
+ setIsLinkActive(getActiveView(item.href) === activeView)
+ }
+
+ if (item.menu?.length) {
+ setIsDropdownActive(item.menu.some((nestedItem) => itemMatchesActiveView(nestedItem, activeView)))
+ }
+ }, [item.href, item.menu, isReady, asPath])
const asideMenuItemInnerContents = (
<>
diff --git a/frontend/src/components/AsideMenuList.tsx b/frontend/src/components/AsideMenuList.tsx
index 9ef6d84..6aa5420 100644
--- a/frontend/src/components/AsideMenuList.tsx
+++ b/frontend/src/components/AsideMenuList.tsx
@@ -3,7 +3,7 @@ import { MenuAsideItem } from '../interfaces'
import AsideMenuItem from './AsideMenuItem'
import { useAppSelector } from '../stores/hooks';
import { hasPermission } from '../helpers/userPermissions';
-import { getWorkspaceConfig, itemVisibleForRole } from '../helpers/workspace';
+import { getWorkspaceConfig, getWorkspaceRoute, itemVisibleForRole } from '../helpers/workspace';
type Props = {
menu: MenuAsideItem[]
@@ -32,6 +32,7 @@ export default function AsideMenuList({ menu, isDropdownList = false, className
if (displayItem.label === 'Role Workspace') {
displayItem.label = workspaceConfig.sidebarLabel;
+ displayItem.href = getWorkspaceRoute(roleName);
}
return (
diff --git a/frontend/src/helpers/workspace.ts b/frontend/src/helpers/workspace.ts
index 2bde41c..e6c584d 100644
--- a/frontend/src/helpers/workspace.ts
+++ b/frontend/src/helpers/workspace.ts
@@ -9,6 +9,18 @@ export const WORKSPACE_ROLES = {
public: 'Public',
} as const;
+export const WORKSPACE_ROUTES = {
+ [WORKSPACE_ROLES.superAdmin]: '/platform-command',
+ [WORKSPACE_ROLES.administrator]: '/operations-command',
+ [WORKSPACE_ROLES.directorGeneral]: '/executive-command',
+ [WORKSPACE_ROLES.financeDirector]: '/financial-control',
+ [WORKSPACE_ROLES.procurementLead]: '/procurement-desk',
+ [WORKSPACE_ROLES.complianceAuditLead]: '/compliance-desk',
+ [WORKSPACE_ROLES.projectDeliveryLead]: '/delivery-command',
+} as const;
+
+const ADMIN_DASHBOARD_ROLES = [WORKSPACE_ROLES.superAdmin, WORKSPACE_ROLES.administrator] as const;
+
export type WorkspaceMetricKey =
| 'approvedBudget'
| 'committedBudget'
@@ -197,114 +209,119 @@ const createWorkspaceConfig = ({
const workspaceConfigs: Record = {
[WORKSPACE_ROLES.superAdmin]: createWorkspaceConfig({
sidebarLabel: 'Platform Command',
- pageTitle: 'Super Administrator Workspace',
- eyebrow: 'FDSU ERP · Super Administrator workspace',
- heroTitle: 'Oversee tenants, access control, institutional activity, and platform-level risk from one command surface.',
+ pageTitle: 'Platform Command Workspace',
+ eyebrow: 'FDSU ERP · Tenant governance workspace',
+ heroTitle: 'Govern tenants, access policy, institutional activity, and platform-level risk from one command surface.',
heroDescription:
- 'This workspace prioritizes cross-organization visibility, access governance, auditability, and system stewardship while keeping drill-down access into the same ERP records used by operating teams.',
- primaryAction: { href: '/organizations/organizations-list', label: 'Review organizations' },
- secondaryAction: { href: '/users/users-list', label: 'Manage users' },
+ 'This workspace is for platform stewardship: organization oversight, role and permission control, auditability, and cross-tenant signals while preserving drill-down access into the underlying ERP records used by operating teams.',
+ primaryAction: { href: '/organizations/organizations-list', label: 'Review tenant registry' },
+ secondaryAction: { href: '/users/users-list', label: 'Open access control' },
highlightedMetricKeys: ['activeProjects', 'pendingApprovals', 'openRiskAlerts', 'contractsNearingExpiry', 'unreadNotifications', 'vendorComplianceAlerts'],
heroMetricKeys: ['activeProjects', 'pendingApprovals', 'openRiskAlerts', 'unreadNotifications'],
blockOrder: ['focus', 'watchlist', 'summary', 'approvalRisk', 'operations', 'delivery', 'actions'],
sectionCopy: {
approvalQueue: {
eyebrow: 'Governance queue',
- title: 'Approvals and escalations needing platform attention',
+ title: 'Escalations and approvals requiring governance attention',
actionLabel: 'Open governance queue',
},
riskPanel: {
eyebrow: 'Cross-cutting control flags',
- title: 'Platform-wide exceptions and institutional exposure',
+ title: 'Platform-wide exceptions, permission exposure, and institutional control risk',
},
recentNotifications: {
- eyebrow: 'Platform activity',
- title: 'Recent operational signals across tenants',
+ eyebrow: 'Cross-tenant activity',
+ title: 'Recent workflow and control signals across organizations',
actionLabel: 'Open activity feed',
},
quickActions: {
eyebrow: 'Platform actions',
- title: 'Jump directly into oversight and access-control work',
+ title: 'Jump directly into tenant oversight and permission-control work',
},
},
quickLinks: [
{
href: '/organizations/organizations-list',
label: 'Organization registry',
- description: 'Review tenant setup and coverage.',
+ description: 'Review tenant setup, coverage, and stewardship ownership.',
icon: 'organizations',
},
{
href: '/users/users-list',
label: 'User access',
- description: 'Manage accounts, roles, and ownership.',
+ description: 'Manage accounts, authority assignments, and platform ownership.',
icon: 'users',
},
{
- href: '/approvals/approvals-list',
- label: 'Escalation queue',
- description: 'Follow stalled approvals and control breaks.',
- icon: 'approvals',
+ href: '/roles/roles-list',
+ label: 'Role catalog',
+ description: 'Govern platform-wide role definitions and responsibility boundaries.',
+ icon: 'users',
},
{
- href: '/audit_logs/audit_logs-list',
- label: 'Audit trail',
- description: 'Trace administrative and ERP record changes.',
+ href: '/permissions/permissions-list',
+ label: 'Permission matrix',
+ description: 'Inspect and tighten permission coverage across the platform.',
icon: 'audit',
},
],
}),
[WORKSPACE_ROLES.administrator]: createWorkspaceConfig({
sidebarLabel: 'Operations Command',
- pageTitle: 'Administrator Workspace',
- eyebrow: 'FDSU ERP · Administrator workspace',
- heroTitle: 'Run tenant operations, workflow readiness, user support, and master data hygiene without losing ERP traceability.',
+ pageTitle: 'Operations Command Workspace',
+ eyebrow: 'FDSU ERP · Workflow operations workspace',
+ heroTitle: 'Run workflow operations, user support, record readiness, and day-to-day administrative follow-through.',
heroDescription:
- 'This workspace is tuned for organization-level administration: approval setup, departments, notifications, operational bottlenecks, and administrative follow-through across the shared ERP.',
- primaryAction: { href: '/approval_workflows/approval_workflows-list', label: 'Review workflows' },
- secondaryAction: { href: '/notifications/notifications-list', label: 'Check notifications' },
+ 'This workspace is for organization-level operations administration: grouped workflow controls, notices, master-data upkeep, and fast drill-down into procurement, delivery, finance, and records without forcing teams to scan a single dense sidebar.',
+ primaryAction: { href: '/approval_workflows/approval_workflows-list', label: 'Open workflow hub' },
+ secondaryAction: { href: '/notifications/notifications-list', label: 'Open notice center' },
highlightedMetricKeys: ['pendingApprovals', 'unreadNotifications', 'contractsNearingExpiry', 'openRiskAlerts', 'activeProjects', 'procurementPipeline'],
heroMetricKeys: ['pendingApprovals', 'unreadNotifications', 'procurementPipeline', 'contractsNearingExpiry'],
blockOrder: ['focus', 'summary', 'approvalRisk', 'watchlist', 'operations', 'delivery', 'actions'],
sectionCopy: {
approvalQueue: {
eyebrow: 'Operations queue',
- title: 'Approvals waiting on workflow administration',
+ title: 'Approvals waiting on routing, delegation, or admin follow-through',
actionLabel: 'Open workflow queue',
},
procurementQueue: {
eyebrow: 'Operational throughput',
- title: 'Requisitions that may require admin unblock',
+ title: 'Requisitions that may require workflow or setup unblock',
actionLabel: 'Open requisitions',
},
+ recentNotifications: {
+ eyebrow: 'Operational notices',
+ title: 'Recent admin-facing signals requiring follow-through',
+ actionLabel: 'Open notice center',
+ },
quickActions: {
eyebrow: 'Administrative actions',
- title: 'Go straight to configuration and operational follow-through',
+ title: 'Open grouped control areas without scanning the full ERP register',
},
},
quickLinks: [
{
href: '/approval_workflows/approval_workflows-list',
- label: 'Workflow setup',
- description: 'Review approval design and routing.',
+ label: 'Workflow hub',
+ description: 'Keep approval routes, steps, and queues ready for daily use.',
icon: 'approvals',
},
{
href: '/notifications/notifications-list',
- label: 'Notification center',
- description: 'Resolve unread system and user signals.',
+ label: 'Notice center',
+ description: 'Triage notifications, documents, and control signals in one stop.',
icon: 'notifications',
},
{
href: '/users/users-list',
- label: 'User support',
- description: 'Update access and account records.',
+ label: 'Admin records',
+ description: 'Jump into users, departments, provinces, and support clean-up.',
icon: 'users',
},
{
href: '/projects/projects-list',
- label: 'Project register',
- description: 'Verify record coverage and ownership.',
+ label: 'Delivery register',
+ description: 'Move from grouped navigation into active projects and contract follow-through.',
icon: 'projects',
},
],
@@ -616,6 +633,54 @@ export function getWorkspaceConfig(roleName?: string | null): WorkspaceConfig {
return workspaceConfigs[roleName] || workspaceConfigs[WORKSPACE_ROLES.projectDeliveryLead];
}
+export function getWorkspaceRoute(roleName?: string | null) {
+ if (!roleName) {
+ return '/executive-summary';
+ }
+
+ return WORKSPACE_ROUTES[roleName] || '/executive-summary';
+}
+
+export function normalizeInternalRedirectTarget(target?: string | string[] | null) {
+ const redirectTarget = Array.isArray(target) ? target[0] : target;
+
+ if (!redirectTarget || typeof redirectTarget !== 'string') {
+ return null;
+ }
+
+ if (!redirectTarget.startsWith('/') || redirectTarget.startsWith('//')) {
+ return null;
+ }
+
+ if (redirectTarget.startsWith('/login')) {
+ return null;
+ }
+
+ return redirectTarget;
+}
+
+export function getPostLoginRoute(roleName?: string | null, redirectTarget?: string | string[] | null) {
+ return normalizeInternalRedirectTarget(redirectTarget) || getWorkspaceRoute(roleName);
+}
+
+export function getLoginRoute(redirectTarget?: string | string[] | null) {
+ const safeRedirectTarget = normalizeInternalRedirectTarget(redirectTarget);
+
+ if (!safeRedirectTarget) {
+ return '/login';
+ }
+
+ return `/login?redirect=${encodeURIComponent(safeRedirectTarget)}`;
+}
+
+export function isDashboardRole(roleName?: string | null) {
+ if (!roleName) {
+ return false;
+ }
+
+ return ADMIN_DASHBOARD_ROLES.includes(roleName as (typeof ADMIN_DASHBOARD_ROLES)[number]);
+}
+
export function itemVisibleForRole(itemRoles?: string[], roleName?: string | null) {
if (!itemRoles?.length) {
return true;
diff --git a/frontend/src/layouts/Authenticated.tsx b/frontend/src/layouts/Authenticated.tsx
index 73d8391..3b5fb16 100644
--- a/frontend/src/layouts/Authenticated.tsx
+++ b/frontend/src/layouts/Authenticated.tsx
@@ -14,6 +14,7 @@ import { useRouter } from 'next/router'
import {findMe, logoutUser} from "../stores/authSlice";
import {hasPermission} from "../helpers/userPermissions";
+import { getLoginRoute } from "../helpers/workspace";
type Props = {
@@ -49,12 +50,14 @@ export default function LayoutAuthenticated({
};
useEffect(() => {
- dispatch(findMe());
if (!isTokenValid()) {
dispatch(logoutUser());
- router.push('/login');
+ router.replace(getLoginRoute(router.asPath));
+ return;
}
- }, [token, localToken]);
+
+ dispatch(findMe());
+ }, [dispatch, localToken, router, token]);
useEffect(() => {
diff --git a/frontend/src/menuAside.ts b/frontend/src/menuAside.ts
index 6b5a8e3..2d470b6 100644
--- a/frontend/src/menuAside.ts
+++ b/frontend/src/menuAside.ts
@@ -1,7 +1,705 @@
-import * as icon from '@mdi/js';
+import * as icon from '@mdi/js'
import { MenuAsideItem } from './interfaces'
import { WORKSPACE_ROLES } from './helpers/workspace'
+const optionalIcon = (name: string, fallback: string = icon.mdiTable): string => {
+ const iconSet = icon as Record
+
+ return iconSet[name] || fallback
+}
+
+const adminWorkspaceRoles = [WORKSPACE_ROLES.administrator]
+const sharedEntityRoles = [
+ WORKSPACE_ROLES.superAdmin,
+ WORKSPACE_ROLES.directorGeneral,
+ WORKSPACE_ROLES.financeDirector,
+ WORKSPACE_ROLES.procurementLead,
+ WORKSPACE_ROLES.complianceAuditLead,
+ WORKSPACE_ROLES.projectDeliveryLead,
+]
+
+const adminGroupedNavigation: MenuAsideItem[] = [
+ {
+ label: 'Workflow Readiness',
+ icon: icon.mdiSitemap,
+ roles: adminWorkspaceRoles,
+ withDevider: true,
+ menu: [
+ {
+ href: '/approval_workflows/approval_workflows-list',
+ label: 'Approval workflows',
+ icon: optionalIcon('mdiSitemap'),
+ permissions: 'READ_APPROVAL_WORKFLOWS',
+ },
+ {
+ href: '/approval_steps/approval_steps-list',
+ label: 'Approval steps',
+ icon: optionalIcon('mdiStairs'),
+ permissions: 'READ_APPROVAL_STEPS',
+ },
+ {
+ href: '/approvals/approvals-list',
+ label: 'Approvals',
+ icon: optionalIcon('mdiChecklist'),
+ permissions: 'READ_APPROVALS',
+ },
+ {
+ href: '/role_permissions/role_permissions-list',
+ label: 'Role access',
+ icon: optionalIcon('mdiLinkVariant'),
+ permissions: 'READ_ROLE_PERMISSIONS',
+ },
+ ],
+ },
+ {
+ label: 'Operational Notices',
+ icon: icon.mdiBellOutline,
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/notifications/notifications-list',
+ label: 'Notifications',
+ icon: optionalIcon('mdiBell'),
+ permissions: 'READ_NOTIFICATIONS',
+ },
+ {
+ href: '/audit_logs/audit_logs-list',
+ label: 'Audit logs',
+ icon: optionalIcon('mdiClipboardTextClock'),
+ permissions: 'READ_AUDIT_LOGS',
+ },
+ {
+ href: '/documents/documents-list',
+ label: 'Documents',
+ icon: optionalIcon('mdiFolderFile'),
+ permissions: 'READ_DOCUMENTS',
+ },
+ {
+ href: '/compliance_alerts/compliance_alerts-list',
+ label: 'Compliance alerts',
+ icon: optionalIcon('mdiShieldAlert'),
+ permissions: 'READ_COMPLIANCE_ALERTS',
+ },
+ ],
+ },
+ {
+ label: 'Administration',
+ icon: icon.mdiAccountGroup,
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/users/users-list',
+ label: 'Users',
+ icon: icon.mdiAccountGroup,
+ permissions: 'READ_USERS',
+ },
+ {
+ href: '/provinces/provinces-list',
+ label: 'Provinces',
+ icon: optionalIcon('mdiMapMarker'),
+ permissions: 'READ_PROVINCES',
+ },
+ {
+ href: '/departments/departments-list',
+ label: 'Departments',
+ icon: optionalIcon('mdiOfficeBuilding'),
+ permissions: 'READ_DEPARTMENTS',
+ },
+ ],
+ },
+ {
+ label: 'Budget & Planning',
+ icon: optionalIcon('mdiWalletOutline'),
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/fiscal_years/fiscal_years-list',
+ label: 'Fiscal years',
+ icon: optionalIcon('mdiCalendarRange'),
+ permissions: 'READ_FISCAL_YEARS',
+ },
+ {
+ href: '/funding_sources/funding_sources-list',
+ label: 'Funding sources',
+ icon: optionalIcon('mdiCashMultiple'),
+ permissions: 'READ_FUNDING_SOURCES',
+ },
+ {
+ href: '/budget_programs/budget_programs-list',
+ label: 'Budget programs',
+ icon: optionalIcon('mdiChartDonut'),
+ permissions: 'READ_BUDGET_PROGRAMS',
+ },
+ {
+ href: '/budget_lines/budget_lines-list',
+ label: 'Budget lines',
+ icon: optionalIcon('mdiFormatListBulleted'),
+ permissions: 'READ_BUDGET_LINES',
+ },
+ {
+ href: '/allocations/allocations-list',
+ label: 'Allocations',
+ icon: optionalIcon('mdiDatabaseArrowRight'),
+ permissions: 'READ_ALLOCATIONS',
+ },
+ {
+ href: '/budget_reallocations/budget_reallocations-list',
+ label: 'Reallocations',
+ icon: optionalIcon('mdiSwapHorizontal'),
+ permissions: 'READ_BUDGET_REALLOCATIONS',
+ },
+ ],
+ },
+ {
+ label: 'Procurement',
+ icon: icon.mdiGavel,
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/procurement_plans/procurement_plans-list',
+ label: 'Plans',
+ icon: optionalIcon('mdiClipboardList'),
+ permissions: 'READ_PROCUREMENT_PLANS',
+ },
+ {
+ href: '/requisitions/requisitions-list',
+ label: 'Requisitions',
+ icon: optionalIcon('mdiFileDocumentEdit'),
+ permissions: 'READ_REQUISITIONS',
+ },
+ {
+ href: '/tenders/tenders-list',
+ label: 'Tenders',
+ icon: optionalIcon('mdiGavel'),
+ permissions: 'READ_TENDERS',
+ },
+ {
+ href: '/vendors/vendors-list',
+ label: 'Vendors',
+ icon: optionalIcon('mdiTruckFast'),
+ permissions: 'READ_VENDORS',
+ },
+ {
+ href: '/vendor_compliance_documents/vendor_compliance_documents-list',
+ label: 'Vendor compliance',
+ icon: optionalIcon('mdiFileCertificate'),
+ permissions: 'READ_VENDOR_COMPLIANCE_DOCUMENTS',
+ },
+ {
+ href: '/bids/bids-list',
+ label: 'Bids',
+ icon: optionalIcon('mdiFileSign'),
+ permissions: 'READ_BIDS',
+ },
+ {
+ href: '/bid_evaluations/bid_evaluations-list',
+ label: 'Bid evaluations',
+ icon: optionalIcon('mdiClipboardCheck'),
+ permissions: 'READ_BID_EVALUATIONS',
+ },
+ {
+ href: '/awards/awards-list',
+ label: 'Awards',
+ icon: optionalIcon('mdiTrophyAward'),
+ permissions: 'READ_AWARDS',
+ },
+ ],
+ },
+ {
+ label: 'Delivery & Contracts',
+ icon: icon.mdiChartTimelineVariant,
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/programs/programs-list',
+ label: 'Programs',
+ icon: optionalIcon('mdiViewGridOutline'),
+ permissions: 'READ_PROGRAMS',
+ },
+ {
+ href: '/projects/projects-list',
+ label: 'Projects',
+ icon: optionalIcon('mdiBriefcaseCheck'),
+ permissions: 'READ_PROJECTS',
+ },
+ {
+ href: '/project_milestones/project_milestones-list',
+ label: 'Milestones',
+ icon: optionalIcon('mdiTimelineCheck'),
+ permissions: 'READ_PROJECT_MILESTONES',
+ },
+ {
+ href: '/risks/risks-list',
+ label: 'Risks',
+ icon: optionalIcon('mdiAlertOctagon'),
+ permissions: 'READ_RISKS',
+ },
+ {
+ href: '/issues/issues-list',
+ label: 'Issues',
+ icon: optionalIcon('mdiBug'),
+ permissions: 'READ_ISSUES',
+ },
+ {
+ href: '/field_verifications/field_verifications-list',
+ label: 'Field checks',
+ icon: optionalIcon('mdiMapMarkerCheck'),
+ permissions: 'READ_FIELD_VERIFICATIONS',
+ },
+ {
+ href: '/contracts/contracts-list',
+ label: 'Contracts',
+ icon: optionalIcon('mdiFileDocumentOutline'),
+ permissions: 'READ_CONTRACTS',
+ },
+ {
+ href: '/contract_amendments/contract_amendments-list',
+ label: 'Amendments',
+ icon: optionalIcon('mdiFileReplaceOutline'),
+ permissions: 'READ_CONTRACT_AMENDMENTS',
+ },
+ {
+ href: '/contract_milestones/contract_milestones-list',
+ label: 'Contract milestones',
+ icon: optionalIcon('mdiTimelineCheck'),
+ permissions: 'READ_CONTRACT_MILESTONES',
+ },
+ ],
+ },
+ {
+ label: 'Grants & Beneficiaries',
+ icon: optionalIcon('mdiHandCoin'),
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/grants/grants-list',
+ label: 'Grants',
+ icon: optionalIcon('mdiHandCoin'),
+ permissions: 'READ_GRANTS',
+ },
+ {
+ href: '/beneficiaries/beneficiaries-list',
+ label: 'Beneficiaries',
+ icon: optionalIcon('mdiAccountGroup'),
+ permissions: 'READ_BENEFICIARIES',
+ },
+ {
+ href: '/grant_applications/grant_applications-list',
+ label: 'Applications',
+ icon: optionalIcon('mdiFileDocumentMultiple'),
+ permissions: 'READ_GRANT_APPLICATIONS',
+ },
+ {
+ href: '/grant_evaluations/grant_evaluations-list',
+ label: 'Evaluations',
+ icon: optionalIcon('mdiStarCheck'),
+ permissions: 'READ_GRANT_EVALUATIONS',
+ },
+ {
+ href: '/grant_tranches/grant_tranches-list',
+ label: 'Tranches',
+ icon: optionalIcon('mdiCashCheck'),
+ permissions: 'READ_GRANT_TRANCHES',
+ },
+ ],
+ },
+ {
+ label: 'Finance & Payments',
+ icon: optionalIcon('mdiCashCheck'),
+ roles: adminWorkspaceRoles,
+ menu: [
+ {
+ href: '/expense_categories/expense_categories-list',
+ label: 'Expense categories',
+ icon: optionalIcon('mdiTagMultiple'),
+ permissions: 'READ_EXPENSE_CATEGORIES',
+ },
+ {
+ href: '/invoices/invoices-list',
+ label: 'Invoices',
+ icon: optionalIcon('mdiReceiptText'),
+ permissions: 'READ_INVOICES',
+ },
+ {
+ href: '/payment_requests/payment_requests-list',
+ label: 'Payment requests',
+ icon: optionalIcon('mdiCashFast'),
+ permissions: 'READ_PAYMENT_REQUESTS',
+ },
+ {
+ href: '/payment_batches/payment_batches-list',
+ label: 'Payment batches',
+ icon: optionalIcon('mdiPackageVariantClosed'),
+ permissions: 'READ_PAYMENT_BATCHES',
+ },
+ {
+ href: '/payments/payments-list',
+ label: 'Payments',
+ icon: optionalIcon('mdiBankTransfer'),
+ permissions: 'READ_PAYMENTS',
+ },
+ {
+ href: '/obligations/obligations-list',
+ label: 'Obligations',
+ icon: optionalIcon('mdiBookArrowDown'),
+ permissions: 'READ_OBLIGATIONS',
+ },
+ {
+ href: '/ledger_entries/ledger_entries-list',
+ label: 'Ledger entries',
+ icon: optionalIcon('mdiBookOpenPageVariant'),
+ permissions: 'READ_LEDGER_ENTRIES',
+ },
+ ],
+ },
+]
+
+const sharedEntityNavigation: MenuAsideItem[] = [
+ {
+ href: '/users/users-list',
+ label: 'Users',
+ roles: [WORKSPACE_ROLES.superAdmin],
+ icon: icon.mdiAccountGroup,
+ permissions: 'READ_USERS',
+ },
+ {
+ href: '/roles/roles-list',
+ label: 'Roles',
+ roles: [WORKSPACE_ROLES.superAdmin],
+ icon: optionalIcon('mdiShieldAccountVariantOutline'),
+ permissions: 'READ_ROLES',
+ },
+ {
+ href: '/permissions/permissions-list',
+ label: 'Permissions',
+ roles: [WORKSPACE_ROLES.superAdmin],
+ icon: optionalIcon('mdiShieldAccountOutline'),
+ permissions: 'READ_PERMISSIONS',
+ },
+ {
+ href: '/organizations/organizations-list',
+ label: 'Organizations',
+ roles: [WORKSPACE_ROLES.superAdmin],
+ icon: optionalIcon('mdiDomain'),
+ permissions: 'READ_ORGANIZATIONS',
+ },
+ {
+ href: '/provinces/provinces-list',
+ label: 'Provinces',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiMapMarker'),
+ permissions: 'READ_PROVINCES',
+ },
+ {
+ href: '/departments/departments-list',
+ label: 'Departments',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiOfficeBuilding'),
+ permissions: 'READ_DEPARTMENTS',
+ },
+ {
+ href: '/role_permissions/role_permissions-list',
+ label: 'Role permissions',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiLinkVariant'),
+ permissions: 'READ_ROLE_PERMISSIONS',
+ },
+ {
+ href: '/approval_workflows/approval_workflows-list',
+ label: 'Approval workflows',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiSitemap'),
+ permissions: 'READ_APPROVAL_WORKFLOWS',
+ },
+ {
+ href: '/approval_steps/approval_steps-list',
+ label: 'Approval steps',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiStairs'),
+ permissions: 'READ_APPROVAL_STEPS',
+ },
+ {
+ href: '/approvals/approvals-list',
+ label: 'Approvals',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiChecklist'),
+ permissions: 'READ_APPROVALS',
+ },
+ {
+ href: '/notifications/notifications-list',
+ label: 'Notifications',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiBell'),
+ permissions: 'READ_NOTIFICATIONS',
+ },
+ {
+ href: '/audit_logs/audit_logs-list',
+ label: 'Audit logs',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiClipboardTextClock'),
+ permissions: 'READ_AUDIT_LOGS',
+ },
+ {
+ href: '/fiscal_years/fiscal_years-list',
+ label: 'Fiscal years',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiCalendarRange'),
+ permissions: 'READ_FISCAL_YEARS',
+ },
+ {
+ href: '/funding_sources/funding_sources-list',
+ label: 'Funding sources',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiCashMultiple'),
+ permissions: 'READ_FUNDING_SOURCES',
+ },
+ {
+ href: '/budget_programs/budget_programs-list',
+ label: 'Budget programs',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiChartDonut'),
+ permissions: 'READ_BUDGET_PROGRAMS',
+ },
+ {
+ href: '/budget_lines/budget_lines-list',
+ label: 'Budget lines',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFormatListBulleted'),
+ permissions: 'READ_BUDGET_LINES',
+ },
+ {
+ href: '/allocations/allocations-list',
+ label: 'Allocations',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiDatabaseArrowRight'),
+ permissions: 'READ_ALLOCATIONS',
+ },
+ {
+ href: '/budget_reallocations/budget_reallocations-list',
+ label: 'Budget reallocations',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiSwapHorizontal'),
+ permissions: 'READ_BUDGET_REALLOCATIONS',
+ },
+ {
+ href: '/procurement_plans/procurement_plans-list',
+ label: 'Procurement plans',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiClipboardList'),
+ permissions: 'READ_PROCUREMENT_PLANS',
+ },
+ {
+ href: '/requisitions/requisitions-list',
+ label: 'Requisitions',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFileDocumentEdit'),
+ permissions: 'READ_REQUISITIONS',
+ },
+ {
+ href: '/tenders/tenders-list',
+ label: 'Tenders',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiGavel'),
+ permissions: 'READ_TENDERS',
+ },
+ {
+ href: '/vendors/vendors-list',
+ label: 'Vendors',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiTruckFast'),
+ permissions: 'READ_VENDORS',
+ },
+ {
+ href: '/vendor_compliance_documents/vendor_compliance_documents-list',
+ label: 'Vendor compliance documents',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFileCertificate'),
+ permissions: 'READ_VENDOR_COMPLIANCE_DOCUMENTS',
+ },
+ {
+ href: '/bids/bids-list',
+ label: 'Bids',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFileSign'),
+ permissions: 'READ_BIDS',
+ },
+ {
+ href: '/bid_evaluations/bid_evaluations-list',
+ label: 'Bid evaluations',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiClipboardCheck'),
+ permissions: 'READ_BID_EVALUATIONS',
+ },
+ {
+ href: '/awards/awards-list',
+ label: 'Awards',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiTrophyAward'),
+ permissions: 'READ_AWARDS',
+ },
+ {
+ href: '/programs/programs-list',
+ label: 'Programs',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiViewGridOutline'),
+ permissions: 'READ_PROGRAMS',
+ },
+ {
+ href: '/projects/projects-list',
+ label: 'Projects',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiBriefcaseCheck'),
+ permissions: 'READ_PROJECTS',
+ },
+ {
+ href: '/project_milestones/project_milestones-list',
+ label: 'Project milestones',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiTimelineCheck'),
+ permissions: 'READ_PROJECT_MILESTONES',
+ },
+ {
+ href: '/risks/risks-list',
+ label: 'Risks',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiAlertOctagon'),
+ permissions: 'READ_RISKS',
+ },
+ {
+ href: '/issues/issues-list',
+ label: 'Issues',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiBug'),
+ permissions: 'READ_ISSUES',
+ },
+ {
+ href: '/field_verifications/field_verifications-list',
+ label: 'Field verifications',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiMapMarkerCheck'),
+ permissions: 'READ_FIELD_VERIFICATIONS',
+ },
+ {
+ href: '/contracts/contracts-list',
+ label: 'Contracts',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFileDocumentOutline'),
+ permissions: 'READ_CONTRACTS',
+ },
+ {
+ href: '/contract_amendments/contract_amendments-list',
+ label: 'Contract amendments',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFileReplaceOutline'),
+ permissions: 'READ_CONTRACT_AMENDMENTS',
+ },
+ {
+ href: '/contract_milestones/contract_milestones-list',
+ label: 'Contract milestones',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiTimelineCheck'),
+ permissions: 'READ_CONTRACT_MILESTONES',
+ },
+ {
+ href: '/grants/grants-list',
+ label: 'Grants',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiHandCoin'),
+ permissions: 'READ_GRANTS',
+ },
+ {
+ href: '/beneficiaries/beneficiaries-list',
+ label: 'Beneficiaries',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiAccountGroup'),
+ permissions: 'READ_BENEFICIARIES',
+ },
+ {
+ href: '/grant_applications/grant_applications-list',
+ label: 'Grant applications',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFileDocumentMultiple'),
+ permissions: 'READ_GRANT_APPLICATIONS',
+ },
+ {
+ href: '/grant_evaluations/grant_evaluations-list',
+ label: 'Grant evaluations',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiStarCheck'),
+ permissions: 'READ_GRANT_EVALUATIONS',
+ },
+ {
+ href: '/grant_tranches/grant_tranches-list',
+ label: 'Grant tranches',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiCashCheck'),
+ permissions: 'READ_GRANT_TRANCHES',
+ },
+ {
+ href: '/expense_categories/expense_categories-list',
+ label: 'Expense categories',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiTagMultiple'),
+ permissions: 'READ_EXPENSE_CATEGORIES',
+ },
+ {
+ href: '/invoices/invoices-list',
+ label: 'Invoices',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiReceiptText'),
+ permissions: 'READ_INVOICES',
+ },
+ {
+ href: '/payment_requests/payment_requests-list',
+ label: 'Payment requests',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiCashFast'),
+ permissions: 'READ_PAYMENT_REQUESTS',
+ },
+ {
+ href: '/payment_batches/payment_batches-list',
+ label: 'Payment batches',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiPackageVariantClosed'),
+ permissions: 'READ_PAYMENT_BATCHES',
+ },
+ {
+ href: '/payments/payments-list',
+ label: 'Payments',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiBankTransfer'),
+ permissions: 'READ_PAYMENTS',
+ },
+ {
+ href: '/obligations/obligations-list',
+ label: 'Obligations',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiBookArrowDown'),
+ permissions: 'READ_OBLIGATIONS',
+ },
+ {
+ href: '/ledger_entries/ledger_entries-list',
+ label: 'Ledger entries',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiBookOpenPageVariant'),
+ permissions: 'READ_LEDGER_ENTRIES',
+ },
+ {
+ href: '/documents/documents-list',
+ label: 'Documents',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiFolderFile'),
+ permissions: 'READ_DOCUMENTS',
+ },
+ {
+ href: '/compliance_alerts/compliance_alerts-list',
+ label: 'Compliance alerts',
+ roles: sharedEntityRoles,
+ icon: optionalIcon('mdiShieldAlert'),
+ permissions: 'READ_COMPLIANCE_ALERTS',
+ },
+]
+
const menuAside: MenuAsideItem[] = [
{
href: '/executive-summary',
@@ -20,7 +718,11 @@ const menuAside: MenuAsideItem[] = [
{
href: '/dashboard',
icon: icon.mdiViewDashboardOutline,
- label: 'Platform Widgets',
+ label: 'Admin Widgets',
+ labelByRole: {
+ [WORKSPACE_ROLES.superAdmin]: 'Platform Widgets',
+ [WORKSPACE_ROLES.administrator]: 'Operations Widgets',
+ },
roles: [WORKSPACE_ROLES.superAdmin, WORKSPACE_ROLES.administrator],
},
{
@@ -38,21 +740,7 @@ const menuAside: MenuAsideItem[] = [
permissions: 'READ_USERS',
roles: [WORKSPACE_ROLES.superAdmin],
},
- {
- href: '/approval_workflows/approval_workflows-list',
- label: 'Workflow Readiness',
- icon: icon.mdiSitemap,
- permissions: 'READ_APPROVAL_WORKFLOWS',
- roles: [WORKSPACE_ROLES.administrator],
- withDevider: true,
- },
- {
- href: '/notifications/notifications-list',
- label: 'Operational Notices',
- icon: icon.mdiBellOutline,
- permissions: 'READ_NOTIFICATIONS',
- roles: [WORKSPACE_ROLES.administrator],
- },
+ ...adminGroupedNavigation,
{
href: '/projects/projects-list',
label: 'Strategic Portfolio',
@@ -128,412 +816,18 @@ const menuAside: MenuAsideItem[] = [
permissions: 'READ_PROJECT_MILESTONES',
roles: [WORKSPACE_ROLES.projectDeliveryLead],
},
-
- {
- href: '/users/users-list',
- label: 'Users',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiAccountGroup ?? icon.mdiTable,
- permissions: 'READ_USERS'
- },
- {
- href: '/roles/roles-list',
- label: 'Roles',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiShieldAccountVariantOutline ?? icon.mdiTable,
- permissions: 'READ_ROLES'
- },
- {
- href: '/permissions/permissions-list',
- label: 'Permissions',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiShieldAccountOutline ?? icon.mdiTable,
- permissions: 'READ_PERMISSIONS'
- },
- {
- href: '/organizations/organizations-list',
- label: 'Organizations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_ORGANIZATIONS'
- },
- {
- href: '/provinces/provinces-list',
- label: 'Provinces',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiMapMarker' in icon ? icon['mdiMapMarker' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PROVINCES'
- },
- {
- href: '/departments/departments-list',
- label: 'Departments',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiOfficeBuilding' in icon ? icon['mdiOfficeBuilding' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_DEPARTMENTS'
- },
- {
- href: '/role_permissions/role_permissions-list',
- label: 'Role permissions',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiLinkVariant' in icon ? icon['mdiLinkVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_ROLE_PERMISSIONS'
- },
- {
- href: '/approval_workflows/approval_workflows-list',
- label: 'Approval workflows',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiSitemap' in icon ? icon['mdiSitemap' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_APPROVAL_WORKFLOWS'
- },
- {
- href: '/approval_steps/approval_steps-list',
- label: 'Approval steps',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiStairs' in icon ? icon['mdiStairs' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_APPROVAL_STEPS'
- },
- {
- href: '/approvals/approvals-list',
- label: 'Approvals',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiChecklist' in icon ? icon['mdiChecklist' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_APPROVALS'
- },
- {
- href: '/notifications/notifications-list',
- label: 'Notifications',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiBell' in icon ? icon['mdiBell' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_NOTIFICATIONS'
- },
- {
- href: '/audit_logs/audit_logs-list',
- label: 'Audit logs',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiClipboardTextClock' in icon ? icon['mdiClipboardTextClock' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_AUDIT_LOGS'
- },
- {
- href: '/fiscal_years/fiscal_years-list',
- label: 'Fiscal years',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiCalendarRange' in icon ? icon['mdiCalendarRange' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_FISCAL_YEARS'
- },
- {
- href: '/funding_sources/funding_sources-list',
- label: 'Funding sources',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiCashMultiple' in icon ? icon['mdiCashMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_FUNDING_SOURCES'
- },
- {
- href: '/budget_programs/budget_programs-list',
- label: 'Budget programs',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiChartDonut' in icon ? icon['mdiChartDonut' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_BUDGET_PROGRAMS'
- },
- {
- href: '/budget_lines/budget_lines-list',
- label: 'Budget lines',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFormatListBulleted' in icon ? icon['mdiFormatListBulleted' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_BUDGET_LINES'
- },
- {
- href: '/allocations/allocations-list',
- label: 'Allocations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiDatabaseArrowRight' in icon ? icon['mdiDatabaseArrowRight' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_ALLOCATIONS'
- },
- {
- href: '/budget_reallocations/budget_reallocations-list',
- label: 'Budget reallocations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiSwapHorizontal' in icon ? icon['mdiSwapHorizontal' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_BUDGET_REALLOCATIONS'
- },
- {
- href: '/procurement_plans/procurement_plans-list',
- label: 'Procurement plans',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiClipboardList' in icon ? icon['mdiClipboardList' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PROCUREMENT_PLANS'
- },
- {
- href: '/requisitions/requisitions-list',
- label: 'Requisitions',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFileDocumentEdit' in icon ? icon['mdiFileDocumentEdit' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_REQUISITIONS'
- },
- {
- href: '/tenders/tenders-list',
- label: 'Tenders',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiGavel' in icon ? icon['mdiGavel' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_TENDERS'
- },
- {
- href: '/vendors/vendors-list',
- label: 'Vendors',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiTruckFast' in icon ? icon['mdiTruckFast' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_VENDORS'
- },
- {
- href: '/vendor_compliance_documents/vendor_compliance_documents-list',
- label: 'Vendor compliance documents',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFileCertificate' in icon ? icon['mdiFileCertificate' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_VENDOR_COMPLIANCE_DOCUMENTS'
- },
- {
- href: '/bids/bids-list',
- label: 'Bids',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFileSign' in icon ? icon['mdiFileSign' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_BIDS'
- },
- {
- href: '/bid_evaluations/bid_evaluations-list',
- label: 'Bid evaluations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiClipboardCheck' in icon ? icon['mdiClipboardCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_BID_EVALUATIONS'
- },
- {
- href: '/awards/awards-list',
- label: 'Awards',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiTrophyAward' in icon ? icon['mdiTrophyAward' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_AWARDS'
- },
- {
- href: '/programs/programs-list',
- label: 'Programs',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiViewGridOutline' in icon ? icon['mdiViewGridOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PROGRAMS'
- },
- {
- href: '/projects/projects-list',
- label: 'Projects',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiBriefcaseCheck' in icon ? icon['mdiBriefcaseCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PROJECTS'
- },
- {
- href: '/project_milestones/project_milestones-list',
- label: 'Project milestones',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFlagCheckered' in icon ? icon['mdiFlagCheckered' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PROJECT_MILESTONES'
- },
- {
- href: '/risks/risks-list',
- label: 'Risks',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiAlertOctagon' in icon ? icon['mdiAlertOctagon' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_RISKS'
- },
- {
- href: '/issues/issues-list',
- label: 'Issues',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiBug' in icon ? icon['mdiBug' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_ISSUES'
- },
- {
- href: '/field_verifications/field_verifications-list',
- label: 'Field verifications',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiMapMarkerCheck' in icon ? icon['mdiMapMarkerCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_FIELD_VERIFICATIONS'
- },
- {
- href: '/contracts/contracts-list',
- label: 'Contracts',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFileDocumentOutline' in icon ? icon['mdiFileDocumentOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_CONTRACTS'
- },
- {
- href: '/contract_amendments/contract_amendments-list',
- label: 'Contract amendments',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFileReplaceOutline' in icon ? icon['mdiFileReplaceOutline' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_CONTRACT_AMENDMENTS'
- },
- {
- href: '/contract_milestones/contract_milestones-list',
- label: 'Contract milestones',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiTimelineCheck' in icon ? icon['mdiTimelineCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_CONTRACT_MILESTONES'
- },
- {
- href: '/grants/grants-list',
- label: 'Grants',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiHandCoin' in icon ? icon['mdiHandCoin' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_GRANTS'
- },
- {
- href: '/beneficiaries/beneficiaries-list',
- label: 'Beneficiaries',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiAccountGroup' in icon ? icon['mdiAccountGroup' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_BENEFICIARIES'
- },
- {
- href: '/grant_applications/grant_applications-list',
- label: 'Grant applications',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFileDocumentMultiple' in icon ? icon['mdiFileDocumentMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_GRANT_APPLICATIONS'
- },
- {
- href: '/grant_evaluations/grant_evaluations-list',
- label: 'Grant evaluations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiStarCheck' in icon ? icon['mdiStarCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_GRANT_EVALUATIONS'
- },
- {
- href: '/grant_tranches/grant_tranches-list',
- label: 'Grant tranches',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiCashCheck' in icon ? icon['mdiCashCheck' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_GRANT_TRANCHES'
- },
- {
- href: '/expense_categories/expense_categories-list',
- label: 'Expense categories',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiTagMultiple' in icon ? icon['mdiTagMultiple' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_EXPENSE_CATEGORIES'
- },
- {
- href: '/invoices/invoices-list',
- label: 'Invoices',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiReceiptText' in icon ? icon['mdiReceiptText' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_INVOICES'
- },
- {
- href: '/payment_requests/payment_requests-list',
- label: 'Payment requests',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiCashFast' in icon ? icon['mdiCashFast' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PAYMENT_REQUESTS'
- },
- {
- href: '/payment_batches/payment_batches-list',
- label: 'Payment batches',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiPackageVariantClosed' in icon ? icon['mdiPackageVariantClosed' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PAYMENT_BATCHES'
- },
- {
- href: '/payments/payments-list',
- label: 'Payments',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiBankTransfer' in icon ? icon['mdiBankTransfer' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_PAYMENTS'
- },
- {
- href: '/obligations/obligations-list',
- label: 'Obligations',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiBookArrowDown' in icon ? icon['mdiBookArrowDown' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_OBLIGATIONS'
- },
- {
- href: '/ledger_entries/ledger_entries-list',
- label: 'Ledger entries',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiBookOpenPageVariant' in icon ? icon['mdiBookOpenPageVariant' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_LEDGER_ENTRIES'
- },
- {
- href: '/documents/documents-list',
- label: 'Documents',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiFolderFile' in icon ? icon['mdiFolderFile' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_DOCUMENTS'
- },
- {
- href: '/compliance_alerts/compliance_alerts-list',
- label: 'Compliance alerts',
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
- // @ts-ignore
- icon: 'mdiShieldAlert' in icon ? icon['mdiShieldAlert' as keyof typeof icon] : icon.mdiTable ?? icon.mdiTable,
- permissions: 'READ_COMPLIANCE_ALERTS'
- },
+ ...sharedEntityNavigation,
{
href: '/profile',
label: 'Profile',
icon: icon.mdiAccountCircle,
},
-
-
{
href: '/api-docs',
target: '_blank',
label: 'Swagger API',
icon: icon.mdiFileCode,
- permissions: 'READ_API_DOCS'
+ permissions: 'READ_API_DOCS',
},
]
diff --git a/frontend/src/pages/compliance-desk.tsx b/frontend/src/pages/compliance-desk.tsx
new file mode 100644
index 0000000..e767487
--- /dev/null
+++ b/frontend/src/pages/compliance-desk.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const ComplianceDeskPage: any = ExecutiveSummaryPage;
+
+ComplianceDeskPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default ComplianceDeskPage;
diff --git a/frontend/src/pages/dashboard.tsx b/frontend/src/pages/dashboard.tsx
index 1747e62..1edfeaa 100644
--- a/frontend/src/pages/dashboard.tsx
+++ b/frontend/src/pages/dashboard.tsx
@@ -1,16 +1,19 @@
import * as icon from '@mdi/js';
import Head from 'next/head'
+import { useRouter } from 'next/router'
import React from 'react'
import axios from 'axios';
import type { ReactElement } from 'react'
import LayoutAuthenticated from '../layouts/Authenticated'
import SectionMain from '../components/SectionMain'
+import CardBox from '../components/CardBox'
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton'
import BaseIcon from "../components/BaseIcon";
import { getPageTitle } from '../config'
import Link from "next/link";
import { hasPermission } from "../helpers/userPermissions";
+import { getWorkspaceRoute, isDashboardRole } from '../helpers/workspace';
import { fetchWidgets } from '../stores/roles/rolesSlice';
import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator';
import { SmartWidget } from '../components/SmartWidget/SmartWidget';
@@ -18,6 +21,7 @@ import { SmartWidget } from '../components/SmartWidget/SmartWidget';
import { useAppDispatch, useAppSelector } from '../stores/hooks';
const Dashboard = () => {
const dispatch = useAppDispatch();
+ const router = useRouter();
const iconsColor = useAppSelector((state) => state.style.iconsColor);
const corners = useAppSelector((state) => state.style.corners);
const cardsStyle = useAppSelector((state) => state.style.cardsStyle);
@@ -83,7 +87,22 @@ const Dashboard = () => {
const { isFetchingQuery } = useAppSelector((state) => state.openAi);
const { rolesWidgets, loading } = useAppSelector((state) => state.roles);
-
+ const roleName = currentUser?.app_role?.name;
+ const dashboardAllowed = isDashboardRole(roleName);
+ const workspaceRoute = getWorkspaceRoute(roleName);
+ const dashboardCopy = roleName === 'Super Administrator'
+ ? {
+ pageTitle: 'Platform Widgets',
+ eyebrow: 'Super Administrator widget surface',
+ description: 'Configure and review widgets for tenant governance, access control, and cross-organization oversight.',
+ widgetLabelFallback: 'Super Administrator',
+ }
+ : {
+ pageTitle: 'Operations Widgets',
+ eyebrow: 'Administrator operations surface',
+ description: 'Configure and review widgets for workflow readiness, operational notices, and day-to-day administrative follow-through.',
+ widgetLabelFallback: 'Administrator',
+ };
const organizationId = currentUser?.organizations?.id;
@@ -116,6 +135,12 @@ const Dashboard = () => {
async function getWidgets(roleId) {
await dispatch(fetchWidgets(roleId));
}
+ React.useEffect(() => {
+ if (!currentUser?.id || dashboardAllowed) return;
+
+ router.replace(workspaceRoute);
+ }, [currentUser?.id, dashboardAllowed, router, workspaceRoute]);
+
React.useEffect(() => {
if (!currentUser) return;
loadData().then();
@@ -127,20 +152,42 @@ const Dashboard = () => {
getWidgets(widgetsRole?.role?.value || '').then();
}, [widgetsRole?.role?.value]);
+ if (currentUser?.id && !dashboardAllowed) {
+ return (
+
+
+
+
Role workspace redirect
+
This dashboard is reserved for Super Administrators and Administrators.
+
Redirecting you to your role workspace now.
+
+ Go to workspace now
+
+
+
+
+ )
+ }
+
return (
<>
- {getPageTitle('Overview')}
+ {getPageTitle(dashboardCopy.pageTitle)}
{''}
+
+
+ {dashboardCopy.eyebrow}
+ {dashboardCopy.description}
+
{hasPermission(currentUser, 'CREATE_ROLES') && {
{!!rolesWidgets.length &&
hasPermission(currentUser, 'CREATE_ROLES') && (
- {`${widgetsRole?.role?.label || 'Users'}'s widgets`}
+ {`${widgetsRole?.role?.label || dashboardCopy.widgetLabelFallback}'s widgets`}
)}
diff --git a/frontend/src/pages/delivery-command.tsx b/frontend/src/pages/delivery-command.tsx
new file mode 100644
index 0000000..4149abf
--- /dev/null
+++ b/frontend/src/pages/delivery-command.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const DeliveryCommandPage: any = ExecutiveSummaryPage;
+
+DeliveryCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default DeliveryCommandPage;
diff --git a/frontend/src/pages/executive-command.tsx b/frontend/src/pages/executive-command.tsx
new file mode 100644
index 0000000..94c20a3
--- /dev/null
+++ b/frontend/src/pages/executive-command.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const ExecutiveCommandPage: any = ExecutiveSummaryPage;
+
+ExecutiveCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default ExecutiveCommandPage;
diff --git a/frontend/src/pages/executive-summary.tsx b/frontend/src/pages/executive-summary.tsx
index b87f24a..7119192 100644
--- a/frontend/src/pages/executive-summary.tsx
+++ b/frontend/src/pages/executive-summary.tsx
@@ -12,6 +12,7 @@ import {
} from '@mdi/js';
import axios from 'axios';
import Head from 'next/head';
+import { useRouter } from 'next/router';
import Link from 'next/link';
import React, { ReactElement, ReactNode, useEffect, useMemo, useState } from 'react';
import BaseButton from '../components/BaseButton';
@@ -23,6 +24,7 @@ import SectionMain from '../components/SectionMain';
import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton';
import {
getWorkspaceConfig,
+ getWorkspaceRoute,
type WorkspaceDetailBlockKey,
type WorkspaceMetricKey,
type WorkspaceQuickLinkIconKey,
@@ -343,12 +345,22 @@ const SummaryMetric = ({
);
const ExecutiveSummaryPage = () => {
+ const router = useRouter();
const { currentUser } = useAppSelector((state) => state.auth);
const [data, setData] = useState(defaultResponse);
const [loading, setLoading] = useState(true);
const [errorMessage, setErrorMessage] = useState('');
+ const workspaceRoute = useMemo(() => getWorkspaceRoute(currentUser?.app_role?.name), [currentUser?.app_role?.name]);
const workspaceConfig = useMemo(() => getWorkspaceConfig(currentUser?.app_role?.name), [currentUser?.app_role?.name]);
+ useEffect(() => {
+ if (!currentUser?.id || router.pathname !== '/executive-summary' || workspaceRoute === '/executive-summary') {
+ return;
+ }
+
+ router.replace(workspaceRoute);
+ }, [currentUser?.id, router, workspaceRoute]);
+
useEffect(() => {
const fetchSummary = async () => {
try {
diff --git a/frontend/src/pages/financial-control.tsx b/frontend/src/pages/financial-control.tsx
new file mode 100644
index 0000000..371839c
--- /dev/null
+++ b/frontend/src/pages/financial-control.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const FinancialControlPage: any = ExecutiveSummaryPage;
+
+FinancialControlPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default FinancialControlPage;
diff --git a/frontend/src/pages/index.tsx b/frontend/src/pages/index.tsx
index 35df893..983a691 100644
--- a/frontend/src/pages/index.tsx
+++ b/frontend/src/pages/index.tsx
@@ -12,13 +12,17 @@ import {
import type { ReactElement } from 'react';
import Head from 'next/head';
import Link from 'next/link';
-import React from 'react';
+import { useRouter } from 'next/router';
+import React, { useEffect, useMemo } from 'react';
import BaseButton from '../components/BaseButton';
import BaseDivider from '../components/BaseDivider';
import BaseIcon from '../components/BaseIcon';
import CardBox from '../components/CardBox';
-import LayoutGuest from '../layouts/Guest';
import { getPageTitle } from '../config';
+import { getLoginRoute, getWorkspaceRoute } from '../helpers/workspace';
+import LayoutGuest from '../layouts/Guest';
+import { findMe } from '../stores/authSlice';
+import { useAppDispatch, useAppSelector } from '../stores/hooks';
const moduleCards = [
{
@@ -51,6 +55,34 @@ const controlPoints = [
];
export default function HomePage() {
+ const router = useRouter();
+ const dispatch = useAppDispatch();
+ const { currentUser, token } = useAppSelector((state) => state.auth);
+
+ const workspaceRoute = useMemo(
+ () => getWorkspaceRoute(currentUser?.app_role?.name),
+ [currentUser?.app_role?.name],
+ );
+
+ useEffect(() => {
+ const existingToken = token || (typeof window !== 'undefined' ? localStorage.getItem('token') : '');
+
+ if (existingToken && !currentUser?.id) {
+ dispatch(findMe());
+ }
+ }, [currentUser?.id, dispatch, token]);
+
+ useEffect(() => {
+ if (currentUser?.id) {
+ router.replace(workspaceRoute);
+ }
+ }, [currentUser?.id, router, workspaceRoute]);
+
+ const getProtectedHref = (href: string) => (currentUser?.id ? href : getLoginRoute(href));
+
+ const primaryWorkspaceHref = currentUser?.id ? workspaceRoute : getLoginRoute('/executive-summary');
+ const dashboardHref = currentUser?.id ? '/dashboard' : getLoginRoute('/dashboard');
+
return (
<>
@@ -65,8 +97,8 @@ export default function HomePage() {
FDSU ERP
-
-
+
+
@@ -85,9 +117,9 @@ export default function HomePage() {
-
-
-
+
+
+
@@ -112,7 +144,7 @@ export default function HomePage() {
Access
-
Public landing with links into the secured admin workspace
+
Public landing with role-aware sign-in routing into the secured workspace
@@ -159,10 +191,10 @@ export default function HomePage() {
Quick access
{[
- { href: '/executive-summary', icon: mdiWalletOutline, title: 'Executive summary', text: 'Operational overview, approval queue, risk panel, and rollout indicators.' },
- { href: '/requisitions/requisitions-list', icon: mdiClipboardClockOutline, title: 'Requisitions', text: 'Create, list, and review procurement requests.' },
- { href: '/contracts/contracts-list', icon: mdiFileDocumentOutline, title: 'Contracts', text: 'Open the contract register and milestone detail views.' },
- { href: '/vendors/vendors-list', icon: mdiCheckDecagramOutline, title: 'Vendor master', text: 'Access qualification, banking data, compliance, and related history.' },
+ { href: currentUser?.id ? workspaceRoute : getLoginRoute('/executive-summary'), icon: mdiWalletOutline, title: 'Executive summary', text: 'Operational overview, approval queue, risk panel, and rollout indicators.' },
+ { href: getProtectedHref('/requisitions/requisitions-list'), icon: mdiClipboardClockOutline, title: 'Requisitions', text: 'Create, list, and review procurement requests.' },
+ { href: getProtectedHref('/contracts/contracts-list'), icon: mdiFileDocumentOutline, title: 'Contracts', text: 'Open the contract register and milestone detail views.' },
+ { href: getProtectedHref('/vendors/vendors-list'), icon: mdiCheckDecagramOutline, title: 'Vendor master', text: 'Access qualification, banking data, compliance, and related history.' },
].map((item) => (
diff --git a/frontend/src/pages/login.tsx b/frontend/src/pages/login.tsx
index 0285176..c2f856f 100644
--- a/frontend/src/pages/login.tsx
+++ b/frontend/src/pages/login.tsx
@@ -21,6 +21,7 @@ import { useAppDispatch, useAppSelector } from '../stores/hooks';
import Link from 'next/link';
import {toast, ToastContainer} from "react-toastify";
import { getPexelsImage, getPexelsVideo } from '../helpers/pexels'
+import { getPostLoginRoute } from '../helpers/workspace';
export default function Login() {
const router = useRouter();
@@ -58,16 +59,18 @@ export default function Login() {
}, []);
// Fetch user data
useEffect(() => {
- if (token) {
+ const existingToken = token || (typeof window !== 'undefined' ? localStorage.getItem('token') : '');
+
+ if (existingToken) {
dispatch(findMe());
}
- }, [token, dispatch]);
- // Redirect to dashboard if user is logged in
+ }, [dispatch, token]);
+ // Redirect to the intended deep link or the role workspace if user is logged in
useEffect(() => {
if (currentUser?.id) {
- router.push('/dashboard');
+ router.replace(getPostLoginRoute(currentUser?.app_role?.name, router.query.redirect));
}
- }, [currentUser?.id, router]);
+ }, [currentUser?.app_role?.name, currentUser?.id, router, router.query.redirect]);
// Show error message if there is one
useEffect(() => {
if (errorMessage){
diff --git a/frontend/src/pages/operations-command.tsx b/frontend/src/pages/operations-command.tsx
new file mode 100644
index 0000000..243aa0f
--- /dev/null
+++ b/frontend/src/pages/operations-command.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const OperationsCommandPage: any = ExecutiveSummaryPage;
+
+OperationsCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default OperationsCommandPage;
diff --git a/frontend/src/pages/platform-command.tsx b/frontend/src/pages/platform-command.tsx
new file mode 100644
index 0000000..ecc59f8
--- /dev/null
+++ b/frontend/src/pages/platform-command.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const PlatformCommandPage: any = ExecutiveSummaryPage;
+
+PlatformCommandPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default PlatformCommandPage;
diff --git a/frontend/src/pages/procurement-desk.tsx b/frontend/src/pages/procurement-desk.tsx
new file mode 100644
index 0000000..c156a74
--- /dev/null
+++ b/frontend/src/pages/procurement-desk.tsx
@@ -0,0 +1,7 @@
+import ExecutiveSummaryPage from './executive-summary';
+
+const ProcurementDeskPage: any = ExecutiveSummaryPage;
+
+ProcurementDeskPage.getLayout = (ExecutiveSummaryPage as any).getLayout;
+
+export default ProcurementDeskPage;