import * as icon from '@mdi/js' import Head from 'next/head' import Link from 'next/link' import axios from 'axios' import React from 'react' import { useRouter } from 'next/router' import type { ReactElement } from 'react' import LayoutAuthenticated from '../layouts/Authenticated' import SectionMain from '../components/SectionMain' import CardBox from '../components/CardBox' import BaseButton from '../components/BaseButton' import BaseButtons from '../components/BaseButtons' import BaseIcon from '../components/BaseIcon' import NotificationBar from '../components/NotificationBar' import SectionTitleLineWithButton from '../components/SectionTitleLineWithButton' import { WidgetCreator } from '../components/WidgetCreator/WidgetCreator' import { SmartWidget } from '../components/SmartWidget/SmartWidget' import { getPageTitle } from '../config' import { hasPermission } from '../helpers/userPermissions' import { getWorkspaceRoute, isDashboardRole } from '../helpers/workspace' import { useAppDispatch, useAppSelector } from '../stores/hooks' import { fetchWidgets } from '../stores/roles/rolesSlice' type DashboardEntityCard = { entity: string label: string description: string href: string permission: string iconPath: string } type DashboardSection = { title: string eyebrow: string description: string cards: DashboardEntityCard[] } type DashboardRoleConfig = { pageTitle: string eyebrow: string description: string primaryAction: { href: string label: string } secondaryAction: { href: string label: string } sections: DashboardSection[] } const DASHBOARD_CONFIG_BY_ROLE: Record = { 'Super Administrator': { pageTitle: 'Platform Widgets', eyebrow: 'Super Administrator widget surface', description: 'Review the platform through a governance lens: tenants, users, roles, permissions, workflow configuration, and audit visibility.', primaryAction: { href: '/organizations/organizations-list', label: 'Review organizations', }, secondaryAction: { href: '/executive-summary', label: 'Open workspace', }, sections: [ { title: 'Platform governance', eyebrow: 'Control surface', description: 'The structural entities that shape tenants, users, and role design across the platform.', cards: [ { entity: 'organizations', label: 'Organizations', description: 'Track tenant coverage and ownership.', href: '/organizations/organizations-list', permission: 'READ_ORGANIZATIONS', iconPath: icon.mdiDomain, }, { entity: 'users', label: 'Users', description: 'Inspect who can access the platform.', href: '/users/users-list', permission: 'READ_USERS', iconPath: icon.mdiAccountGroup, }, { entity: 'roles', label: 'Roles', description: 'Review responsibility boundaries.', href: '/roles/roles-list', permission: 'READ_ROLES', iconPath: icon.mdiShieldAccountVariantOutline, }, { entity: 'permissions', label: 'Permissions', description: 'Validate the permission catalogue.', href: '/permissions/permissions-list', permission: 'READ_PERMISSIONS', iconPath: icon.mdiShieldAccountOutline, }, { entity: 'role_permissions', label: 'Role permissions', description: 'Inspect how permissions map to roles.', href: '/role_permissions/role_permissions-list', permission: 'READ_ROLE_PERMISSIONS', iconPath: icon.mdiLinkVariant, }, ], }, { title: 'Workflow and observability', eyebrow: 'Readiness and traceability', description: 'Templates, notices, and audit trails that help supervise tenant readiness without owning operational workflows.', cards: [ { entity: 'approval_workflows', label: 'Approval workflows', description: 'Monitor routing templates and approvals design.', href: '/approval_workflows/approval_workflows-list', permission: 'READ_APPROVAL_WORKFLOWS', iconPath: icon.mdiSitemap, }, { entity: 'approval_steps', label: 'Approval steps', description: 'Check stage definitions and progression rules.', href: '/approval_steps/approval_steps-list', permission: 'READ_APPROVAL_STEPS', iconPath: icon.mdiStairs, }, { entity: 'notifications', label: 'Notifications', description: 'Review platform-wide message flow.', href: '/notifications/notifications-list', permission: 'READ_NOTIFICATIONS', iconPath: icon.mdiBellOutline, }, { entity: 'audit_logs', label: 'Audit logs', description: 'Investigate system activity and changes.', href: '/audit_logs/audit_logs-list', permission: 'READ_AUDIT_LOGS', iconPath: icon.mdiClipboardTextClock, }, ], }, ], }, Administrator: { pageTitle: 'Operations Widgets', eyebrow: 'Administrator operations surface', description: 'Keep the organization ready to operate by focusing on users, master data, approval routing, administrative notices, and records assurance.', primaryAction: { href: '/approval_workflows/approval_workflows-list', label: 'Review workflow setup', }, secondaryAction: { href: '/executive-summary', label: 'Open workspace', }, sections: [ { title: 'Organization setup', eyebrow: 'People and reference records', description: 'The administrative entities that keep the organization usable, searchable, and ready for day-to-day work.', cards: [ { entity: 'users', label: 'Users', description: 'Support staff access and account readiness.', href: '/users/users-list', permission: 'READ_USERS', iconPath: icon.mdiAccountGroup, }, { entity: 'provinces', label: 'Provinces', description: 'Maintain the delivery geography master list.', href: '/provinces/provinces-list', permission: 'READ_PROVINCES', iconPath: icon.mdiMapMarker, }, { entity: 'departments', label: 'Departments', description: 'Keep internal organization structure aligned.', href: '/departments/departments-list', permission: 'READ_DEPARTMENTS', iconPath: icon.mdiOfficeBuilding, }, { entity: 'documents', label: 'Documents', description: 'Review supporting records and shared files.', href: '/documents/documents-list', permission: 'READ_DOCUMENTS', iconPath: icon.mdiFolderFile, }, ], }, { title: 'Workflow control', eyebrow: 'Routing and follow-through', description: 'The operating controls that help the Administrator keep approvals, notifications, and control exceptions from drifting.', cards: [ { entity: 'approval_workflows', label: 'Approval workflows', description: 'Inspect routing patterns and gaps.', href: '/approval_workflows/approval_workflows-list', permission: 'READ_APPROVAL_WORKFLOWS', iconPath: icon.mdiSitemap, }, { entity: 'approval_steps', label: 'Approval steps', description: 'Maintain stage definitions and owners.', href: '/approval_steps/approval_steps-list', permission: 'READ_APPROVAL_STEPS', iconPath: icon.mdiStairs, }, { entity: 'approvals', label: 'Approvals', description: 'Review the active approval queue.', href: '/approvals/approvals-list', permission: 'READ_APPROVALS', iconPath: icon.mdiChecklist, }, { entity: 'role_permissions', label: 'Role access', description: 'Check whether access design matches responsibility.', href: '/role_permissions/role_permissions-list', permission: 'READ_ROLE_PERMISSIONS', iconPath: icon.mdiLinkVariant, }, { entity: 'notifications', label: 'Notifications', description: 'Track operational signals requiring follow-up.', href: '/notifications/notifications-list', permission: 'READ_NOTIFICATIONS', iconPath: icon.mdiBellOutline, }, { entity: 'audit_logs', label: 'Audit logs', description: 'Investigate who changed what and when.', href: '/audit_logs/audit_logs-list', permission: 'READ_AUDIT_LOGS', iconPath: icon.mdiClipboardTextClock, }, { entity: 'compliance_alerts', label: 'Compliance alerts', description: 'Spot records that need administrative intervention.', href: '/compliance_alerts/compliance_alerts-list', permission: 'READ_COMPLIANCE_ALERTS', iconPath: icon.mdiShieldAlertOutline, }, ], }, ], }, } const SectionHeader = ({ eyebrow, title, description, action, }: { eyebrow: string title: string description?: string action?: React.ReactNode }) => (

{eyebrow}

{title}

{description ?

{description}

: null}
{action}
) const DashboardCard = ({ label, description, count, href, iconPath, }: DashboardEntityCard & { count: number | string | null }) => { const numericCount = typeof count === 'number' const displayValue = count === null ? '—' : count return (

{label}

{description}

Current records

{displayValue}

{numericCount ? 'Live' : 'Unavailable'}
) } const Dashboard = () => { const dispatch = useAppDispatch() const router = useRouter() const { currentUser } = useAppSelector((state) => state.auth) 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 dashboardConfig = DASHBOARD_CONFIG_BY_ROLE[roleName] || DASHBOARD_CONFIG_BY_ROLE.Administrator const availableSections = React.useMemo( () => dashboardConfig.sections .map((section) => ({ ...section, cards: section.cards.filter((card) => hasPermission(currentUser, card.permission)), })) .filter((section) => section.cards.length > 0), [currentUser, dashboardConfig], ) const [entityCounts, setEntityCounts] = React.useState>({}) const [loadError, setLoadError] = React.useState('') const [widgetsRole, setWidgetsRole] = React.useState({ role: { value: '', label: '' }, }) const totalVisibleEntities = availableSections.reduce((sum, section) => sum + section.cards.length, 0) const totalLoadedRecords = Object.values(entityCounts).reduce((sum, value) => { if (typeof value === 'number') { return sum + value } return sum }, 0) React.useEffect(() => { if (!currentUser?.id || dashboardAllowed) return router.replace(workspaceRoute) }, [currentUser?.id, dashboardAllowed, router, workspaceRoute]) React.useEffect(() => { if (!currentUser?.app_role?.id) return setWidgetsRole({ role: { value: currentUser.app_role.id, label: currentUser.app_role.name, }, }) }, [currentUser?.app_role?.id, currentUser?.app_role?.name]) React.useEffect(() => { if (!widgetsRole?.role?.value) return dispatch(fetchWidgets(widgetsRole.role.value)) }, [dispatch, widgetsRole?.role?.value]) React.useEffect(() => { if (!currentUser || !dashboardAllowed) return let mounted = true const loadCounts = async () => { try { setLoadError('') const cards = availableSections.flatMap((section) => section.cards) const requests = cards.map((card) => axios.get(`/${card.entity}/count`)) const results = await Promise.allSettled(requests) const nextCounts: Record = {} results.forEach((result, index) => { const card = cards[index] if (result.status === 'fulfilled') { nextCounts[card.entity] = result.value.data.count return } console.error(`Failed to load ${card.entity} count`, result.reason) nextCounts[card.entity] = 'Error' }) if (mounted) { setEntityCounts(nextCounts) } } catch (error: any) { console.error('Failed to load dashboard counts', error) if (mounted) { setLoadError(error?.response?.data?.message || 'Unable to load dashboard counts right now.') } } } loadCounts() return () => { mounted = false } }, [availableSections, currentUser, dashboardAllowed]) 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(dashboardConfig.pageTitle)}

{dashboardConfig.eyebrow}

{dashboardConfig.pageTitle}

{dashboardConfig.description}

Visible entity cards

{totalVisibleEntities}

Only role-approved modules are surfaced here.

Records represented

{totalLoadedRecords}

Combined count across the visible control surface.

Saved widgets

{rolesWidgets.length}

Role-specific widgets remain available below.

Current operator

{currentUser?.firstName || currentUser?.email || 'Authenticated user'}

{roleName || 'Operational access'}

Dashboard intent

This page is now a focused control surface instead of a generic entity dump. If a module does not belong to this role, it stays out of view.

Next best action

{dashboardConfig.primaryAction.label}
{loadError ? {loadError} : null} Widget editing enabled ) : null } /> {hasPermission(currentUser, 'CREATE_ROLES') ? ( ) : null}
{(isFetchingQuery || loading) && (

Loading widgets…

Refreshing saved role insights.

)} {rolesWidgets.map((widget) => ( ))} {!rolesWidgets.length && !(isFetchingQuery || loading) ? (
No widgets are saved for this role yet. Use the widget creator to add focused insights, or continue with the curated entity sections below.
) : null}
{availableSections.map((section) => (
{section.cards.map((card) => ( ))}
))}
) } Dashboard.getLayout = function getLayout(page: ReactElement) { return {page} } export default Dashboard