import { useState } from "react"; import { AlertCircle, Banknote, FileSignature, FileText, Train, TrainFront, UserCheck, Users, } from "lucide-react"; import { Alert, Badge, Button, Container, Skeleton, Stack, Tabs, } from "@mantine/core"; import { useQueryClient } from "@tanstack/react-query"; import { useAuth } from "@/auth/useAuth"; import { OverviewPageHeader } from "@/components/overview/OverviewPageHeader"; import { OverviewTabContent } from "@/components/overview/OverviewTabContent"; import "@/components/overview/overview.css"; import { QUERY_KEYS } from "@/constants/QUERY_KEYS"; import { useOverview } from "@/hooks/useOverview"; import { FREIGHT_PERMS, hasPermission } from "@/lib/permissions"; import type { OverviewRange, OverviewTabKey } from "@/types/overview"; const TAB_ITEMS: Array<{ value: OverviewTabKey; label: string; icon: typeof FileText; kpiKey: | "bookings" | "contracts" | "billing" | "operations" | "customers" | "staff"; metricKey: string; /** Any of these keys grants the tab. */ permission: string[]; }> = [ { value: "bookings", label: "Bookings", icon: FileText, kpiKey: "bookings", metricKey: "totalActive", permission: [FREIGHT_PERMS.bookings.view], }, { value: "contracts", label: "Contracts", icon: FileSignature, kpiKey: "contracts", metricKey: "totalActive", permission: [FREIGHT_PERMS.contracts.view], }, { value: "billing", label: "Billing", icon: Banknote, kpiKey: "billing", metricKey: "successfulPaymentsMtd", permission: [FREIGHT_PERMS.bookings.view, FREIGHT_PERMS.payments.view], }, { value: "operations", label: "Operations", icon: Train, kpiKey: "operations", metricKey: "trainsActive", permission: [ FREIGHT_PERMS.trainScheduling.view, FREIGHT_PERMS.warehouseInventory.view, FREIGHT_PERMS.firstMile.view, FREIGHT_PERMS.lastMile.view, ], }, { value: "fleet", label: "Fleet", icon: TrainFront, kpiKey: "operations", metricKey: "wagonsAvailable", permission: [ FREIGHT_PERMS.fleet.view, FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.trainScheduling.view, ], }, { value: "customers", label: "Customers", icon: Users, kpiKey: "customers", metricKey: "totalCustomers", permission: [FREIGHT_PERMS.customers.view], }, { value: "staff", label: "Staff", icon: UserCheck, kpiKey: "staff", metricKey: "activeEmployees", permission: [ FREIGHT_PERMS.admin, FREIGHT_PERMS.staff.roles.view, FREIGHT_PERMS.staff.employeeRegistration.view, FREIGHT_PERMS.staff.roleAssignment.view, ], }, ]; function HeaderSkeleton() { return ( ); } const OverviewPage = () => { const [range, setRange] = useState("30d"); const [activeTab, setActiveTab] = useState("bookings"); const queryClient = useQueryClient(); const { user } = useAuth(); const { data: summary, isLoading, isError, error, refetch, isFetching } = useOverview(range); // Permission-scoped view: only tabs the user may see; a restricted role // (e.g. operations) gets a summary 403 — that is not a connection problem. const visibleTabs = TAB_ITEMS.filter((tab) => tab.permission.some((key) => hasPermission(user, key)), ); const currentTab = visibleTabs.some((t) => t.value === activeTab) ? activeTab : visibleTabs[0]?.value; const accessDenied = (error as { response?: { status?: number } } | null)?.response?.status === 403; const handleRefresh = () => { void refetch(); void queryClient.invalidateQueries({ queryKey: QUERY_KEYS.OVERVIEW.ROOT }); }; const getTabBadge = (tab: (typeof TAB_ITEMS)[number]) => { if (!summary?.kpis) return 0; const group = summary.kpis[tab.kpiKey] as unknown as Record; return group[tab.metricKey] ?? 0; }; return ( {isLoading && !summary ? ( ) : ( )} {isError && !accessDenied && ( } color="red" title="Unable to load dashboard summary" variant="light" > Check your connection and try again. )} {visibleTabs.length === 0 && !isLoading && !isError && ( Your role has no access to any overview section. )} {visibleTabs.length > 0 && ( setActiveTab((value as OverviewTabKey) ?? visibleTabs[0].value) } variant="pills" color="edr-green" keepMounted={false} classNames={{ list: "ov-tablist", tab: "ov-tab" }} > {visibleTabs.map((tab) => { const Icon = tab.icon; const isActive = currentTab === tab.value; return ( } rightSection={ summary ? ( {getTabBadge(tab)} ) : undefined } > {tab.label} ); })} {visibleTabs.map((tab) => ( ))} )} ); }; export default OverviewPage;