{"version":3,"sources":["../call-control-sdk/lib/pages/wfmStaffing/index.tsx","../call-control-sdk/lib/pages/wfmStaffing/LiveTab.tsx","../call-control-sdk/lib/pages/wfmStaffing/SlotDetailDrawer.tsx","../call-control-sdk/lib/pages/wfmStaffing/tableBits.tsx","../call-control-sdk/lib/pages/wfmStaffing/types.ts","../call-control-sdk/lib/pages/wfmStaffing/forecastTypes.ts","../call-control-sdk/lib/pages/wfmStaffing/Markdown.tsx","../call-control-sdk/lib/pages/wfmStaffing/useForecast.ts","../call-control-sdk/lib/pages/wfmStaffing/columnInfo.ts","../call-control-sdk/lib/pages/wfmStaffing/IntervalTab.tsx","../call-control-sdk/lib/pages/wfmStaffing/StaffingChart.tsx","../call-control-sdk/lib/pages/wfmStaffing/filterControls.tsx","../call-control-sdk/lib/pages/wfmStaffing/useStaffing.ts","../call-control-sdk/lib/pages/wfmStaffing/ForecastTab.tsx","../call-control-sdk/lib/pages/wfmStaffing/ForecastChart.tsx","../call-control-sdk/lib/pages/wfmStaffing/ForecastExplainDrawer.tsx","../call-control-sdk/lib/pages/wfmStaffing/useLiveStaffing.ts","../call-control-sdk/lib/pages/wfmStaffing/useStaffingAlerts.ts"],"sourcesContent":["import React, { useEffect, useState } from \"react\";\r\nimport { Box, Tab, Tabs, Typography } from \"@mui/material\";\r\n\r\nimport PageHeader from \"../common/components/PageHeader\";\r\nimport { SDKProvider } from \"../../components/SDKProvider\";\r\nimport { useGetAuthorizationToken } from \"../../hooks/useGetAuthorizationToken\";\r\nimport LiveTab from \"./LiveTab\";\r\nimport IntervalTab from \"./IntervalTab\";\r\nimport ForecastTab from \"./ForecastTab\";\r\nimport { useLiveStaffing } from \"./useLiveStaffing\";\r\nimport { useStaffingAlerts } from \"./useStaffingAlerts\";\r\n\r\nconst FONT = \"'Inter', 'Segoe UI', 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif\";\r\n\r\nconst COLORS = {\r\n\tteal: \"#1A5F6C\",\r\n\tgrey: \"#7a8599\",\r\n} as const;\r\n\r\nconst tabsSx = {\r\n\tminHeight: 36,\r\n\tpx: 1,\r\n\t// The page header already carries 14px of its own below the subtitle.\r\n\tmt: -1,\r\n\tborderBottom: \"1px solid #eef1f6\",\r\n\t\"& .MuiTab-root\": {\r\n\t\ttextTransform: \"none\", fontWeight: 600, fontSize: \"0.78rem\", fontFamily: FONT,\r\n\t\tminHeight: 36, py: 0, px: 1.5, color: \"#4a5568\",\r\n\t},\r\n\t\"& .Mui-selected\": { color: `${COLORS.teal} !important` },\r\n\t\"& .MuiTabs-indicator\": { backgroundColor: COLORS.teal, height: 2, borderRadius: \"2px 2px 0 0\" },\r\n};\r\n\r\ntype TabKey = \"live\" | \"interval\" | \"forecast\";\r\n\r\n/** Long enough that a quiet queue still has arrivals to measure. */\r\nconst DEFAULT_WINDOW_MINUTES = 30;\r\n\r\nconst TABS: { key: TabKey; label: string }[] = [\r\n\t{ key: \"live\", label: \"Live\" },\r\n\t{ key: \"interval\", label: \"By interval\" },\r\n\t{ key: \"forecast\", label: \"Forecast\" },\r\n];\r\n\r\n/* ═══════════════════════ DEEP LINK ═══════════════════════\r\n * ?tab=live|interval, so a link opens the same view. Read and written through\r\n * the address bar directly: this page lives in the SDK, which has no router of\r\n * its own, and replaceState changes nothing the host router routes on.\r\n */\r\n\r\nfunction readTab(): TabKey | null {\r\n\tconst tab = new URLSearchParams(window.location.search).get(\"tab\");\r\n\treturn tab === \"live\" || tab === \"interval\" || tab === \"forecast\" ? tab : null;\r\n}\r\n\r\nfunction writeTab(tab: TabKey): void {\r\n\tconst q = new URLSearchParams(window.location.search);\r\n\tq.set(\"tab\", tab);\r\n\twindow.history.replaceState(null, \"\", `${window.location.pathname}?${q.toString()}`);\r\n}\r\n\r\n/* ═══════════════════════ PAGE ═══════════════════════ */\r\n\r\nfunction WfmStaffingContent() {\r\n\t// SDK initialization finishes after this route can mount — wait for the token so\r\n\t// the first request goes out authenticated.\r\n\tconst authorizationToken = useGetAuthorizationToken();\r\n\tconst [tab, setTab] = useState<TabKey>(() => readTab() ?? \"live\");\r\n\tconst [windowMinutes, setWindowMinutes] = useState(DEFAULT_WINDOW_MINUTES);\r\n\r\n\tuseEffect(() => { writeTab(tab); }, [tab]);\r\n\r\n\t// Polled here rather than inside the Live tab, so a supervisor reading the\r\n\t// interval history is still told when a queue goes short.\r\n\tconst live = useLiveStaffing(windowMinutes, !!authorizationToken);\r\n\tuseStaffingAlerts(live.data?.queues, !!authorizationToken);\r\n\r\n\treturn (\r\n\t\t<Box sx={{ height: \"100%\", display: \"flex\", flexDirection: \"column\", fontFamily: FONT }}>\r\n\t\t\t<PageHeader\r\n\t\t\t\ttitle=\"Workforce Management\"\r\n\t\t\t\tsubtitle=\"Agents each queue needs against the agents on shift, inbound and campaign work together\"\r\n\t\t\t/>\r\n\r\n\t\t\t<Tabs value={tab} onChange={(_, v: TabKey) => setTab(v)} sx={tabsSx}>\r\n\t\t\t\t{TABS.map((t) => (\r\n\t\t\t\t\t<Tab key={t.key} value={t.key} label={t.label} />\r\n\t\t\t\t))}\r\n\t\t\t</Tabs>\r\n\r\n\t\t\t<Box sx={{ flex: 1, minHeight: 0, overflow: \"auto\", pt: 2 }}>\r\n\t\t\t\t{tab === \"live\" ? (\r\n\t\t\t\t\t<LiveTab\r\n\t\t\t\t\t\twindowMinutes={windowMinutes}\r\n\t\t\t\t\t\tonWindowChange={setWindowMinutes}\r\n\t\t\t\t\t\tdata={live.data}\r\n\t\t\t\t\t\tloading={live.loading}\r\n\t\t\t\t\t\terror={live.error}\r\n\t\t\t\t\t\trefresh={live.refresh}\r\n\t\t\t\t\t/>\r\n\t\t\t\t) : tab === \"interval\" ? (\r\n\t\t\t\t\t<IntervalTab enabled={!!authorizationToken} />\r\n\t\t\t\t) : (\r\n\t\t\t\t\t<ForecastTab enabled={!!authorizationToken} />\r\n\t\t\t\t)}\r\n\t\t\t</Box>\r\n\t\t</Box>\r\n\t);\r\n}\r\n\r\nconst WfmStaffing: React.FC = () => (\r\n\t<SDKProvider>\r\n\t\t<WfmStaffingContent />\r\n\t</SDKProvider>\r\n);\r\n\r\nexport default WfmStaffing;\r\n","import { useMemo, useState } from \"react\";\r\nimport { Alert, Box, CircularProgress, IconButton, Tooltip, Typography } from \"@mui/material\";\r\nimport RefreshIcon from \"@mui/icons-material/Refresh\";\r\nimport type { MRT_ColumnDef } from \"material-react-table\";\r\nimport dayjs from \"dayjs\";\r\n\r\nimport AppTable from \"../common/components/AppTable\";\r\nimport SlotDetailDrawer from \"./SlotDetailDrawer\";\r\nimport { SDKButton } from \"../../components/SDKButton\";\r\nimport { LIVE_WINDOW_OPTIONS, type LiveQueueRow, type LiveStaffingResponse } from \"./types\";\r\nimport {\r\n\tCOLORS, DASH, FONT, StatusChip, cell, cellBaseSx, count, decimal, pct0, tabularSx, withInfo,\r\n} from \"./tableBits\";\r\nimport { liveInfo } from \"./columnInfo\";\r\n\r\n/** Understaffed queues first, worst shortfall at the top. */\r\nfunction sortRows(rows: LiveQueueRow[]): LiveQueueRow[] {\r\n\treturn [...rows].sort((a, b) => {\r\n\t\tconst aShort = a.status === \"UNDERSTAFFED\" ? 0 : 1;\r\n\t\tconst bShort = b.status === \"UNDERSTAFFED\" ? 0 : 1;\r\n\t\tif (aShort !== bShort) return aShort - bShort;\r\n\t\tif (aShort === 0) return b.shortfall - a.shortfall;\r\n\t\treturn a.queueName.localeCompare(b.queueName);\r\n\t});\r\n}\r\n\r\n/* ═══════════════════════ TAB ═══════════════════════ */\r\n\r\ninterface LiveTabProps {\r\n\twindowMinutes: number;\r\n\tonWindowChange: (minutes: number) => void;\r\n\t/** Polled by the page, not here: the staffing toasts need it on both tabs. */\r\n\tdata: LiveStaffingResponse | null;\r\n\tloading: boolean;\r\n\terror: string;\r\n\trefresh: () => void;\r\n}\r\n\r\nexport default function LiveTab({ windowMinutes, onWindowChange, data, loading, error, refresh }: LiveTabProps) {\r\n\tconst rows = useMemo(() => sortRows(data?.queues ?? []), [data]);\r\n\r\n\t/** The queue whose drawer is open. */\r\n\tconst [queueRow, setQueueRow] = useState<LiveQueueRow | null>(null);\r\n\r\n\tconst columns = useMemo<MRT_ColumnDef<LiveQueueRow>[]>(() => {\r\n\t\tconst info = liveInfo({ ...data?.settings, windowMinutes: data?.windowMinutes ?? windowMinutes });\r\n\t\treturn [\r\n\t\t\t{\r\n\t\t\t\tid: \"queueName\",\r\n\t\t\t\t...withInfo(\"Queue\", info.queue),\r\n\t\t\t\taccessorFn: (r) => r.queueName,\r\n\t\t\t\tCell: ({ row }) => row.original.queueName || DASH,\r\n\t\t\t\t...cell({ fontSize: \"0.73rem\", color: \"#4a5568\" }),\r\n\t\t\t\tmuiTableBodyCellProps: { align: \"left\" as const, sx: { ...cellBaseSx, fontSize: \"0.73rem\", fontWeight: 600, color: \"#4a5568\" } },\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"status\",\r\n\t\t\t\t...withInfo(\"Status\", info.status),\r\n\t\t\t\taccessorFn: (r) => r.status,\r\n\t\t\t\tCell: ({ row }) => (\r\n\t\t\t\t\t<StatusChip\r\n\t\t\t\t\t\tstatus={row.original.status}\r\n\t\t\t\t\t\tshortfall={row.original.shortfall}\r\n\t\t\t\t\t\tsurplus={row.original.surplus}\r\n\t\t\t\t\t/>\r\n\t\t\t\t),\r\n\t\t\t\t...cell({}),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"callsPerHour\",\r\n\t\t\t\t...withInfo(\"Inbound/h\", info.inboundPerHour),\r\n\t\t\t\taccessorFn: (r) => r.callsPerHour,\r\n\t\t\t\tCell: ({ row }) => decimal(row.original.callsPerHour),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 600, color: COLORS.blue }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"outboundCallsPerHour\",\r\n\t\t\t\t...withInfo(\"Campaign/h\", info.campaignPerHour),\r\n\t\t\t\taccessorFn: (r) => r.outboundCallsPerHour,\r\n\t\t\t\tCell: ({ row }) => decimal(row.original.outboundCallsPerHour),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"waitingNow\",\r\n\t\t\t\t...withInfo(\"Waiting\", info.waiting),\r\n\t\t\t\taccessorFn: (r) => r.waitingNow,\r\n\t\t\t\tCell: ({ row }) => count(row.original.waitingNow),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 600, color: COLORS.navy }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"requiredAgents\",\r\n\t\t\t\t...withInfo(\"Inbound needed\", info.inboundNeeded),\r\n\t\t\t\taccessorFn: (r) => r.requiredAgents,\r\n\t\t\t\tCell: ({ row }) => count(row.original.requiredAgents),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 600, color: \"#4a5568\" }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"requiredTotalAgents\",\r\n\t\t\t\t...withInfo(\"Total needed\", info.totalNeeded),\r\n\t\t\t\taccessorFn: (r) => r.requiredTotalAgents,\r\n\t\t\t\tCell: ({ row }) => count(row.original.requiredTotalAgents),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 700, color: COLORS.navy }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"staffedAgents\",\r\n\t\t\t\t...withInfo(\"Staffed\", info.staffed),\r\n\t\t\t\taccessorFn: (r) => r.staffedAgents,\r\n\t\t\t\t// Fractional: an agent serving three queues counts as a third in each.\r\n\t\t\t\tCell: ({ row }) => decimal(row.original.staffedAgents),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 700, color: COLORS.green }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"idleAgents\",\r\n\t\t\t\t...withInfo(\"Idle\", info.idle),\r\n\t\t\t\taccessorFn: (r) => r.idleAgents,\r\n\t\t\t\tCell: ({ row }) => count(row.original.idleAgents),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 600, color: \"#4a5568\" }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"onBreakAgents\",\r\n\t\t\t\t...withInfo(\"On break\", info.onBreak),\r\n\t\t\t\taccessorFn: (r) => r.onBreakAgents,\r\n\t\t\t\tCell: ({ row }) => count(row.original.onBreakAgents),\r\n\t\t\t\t...cell({ ...tabularSx, fontWeight: 600, color: COLORS.grey }),\r\n\t\t\t},\r\n\t\t\t{\r\n\t\t\t\tid: \"expectedServiceLevel\",\r\n\t\t\t\t...withInfo(\"Expected SLA\", info.expectedSla),\r\n\t\t\t\taccessorFn: (r) => r.expectedServiceLevel,\r\n\t\t\t\t// Already net of agents tied up on campaign calls.\r\n\t\t\t\tCell: ({ row }) => {\r\n\t\t\t\t\tconst value = row.original.expectedServiceLevel;\r\n\t\t\t\t\tconst target = data?.settings?.slaTarget;\r\n\t\t\t\t\tconst colour = typeof value !== \"number\" || typeof target !== \"number\"\r\n\t\t\t\t\t\t? \"#4a5568\"\r\n\t\t\t\t\t\t: value >= target ? COLORS.green : value >= target - 0.1 ? \"#c47c00\" : \"#cc2a2a\";\r\n\t\t\t\t\treturn <Box component=\"span\" sx={{ color: colour, fontWeight: 700 }}>{pct0(value)}</Box>;\r\n\t\t\t\t},\r\n\t\t\t\t...cell(tabularSx),\r\n\t\t\t},\r\n\t\t];\r\n\t}, [data?.settings, data?.windowMinutes, windowMinutes]);\r\n\r\n\tconst settingsLine = data?.settings\r\n\t\t? `Target ${Math.round(data.settings.slaTarget * 100)}% within ${data.settings.slaSeconds} s`\r\n\t\t\t+ ` · max occupancy ${Math.round(data.settings.maxOccupancy * 100)}%`\r\n\t\t\t+ (data.asOf ? ` · as of ${dayjs(data.asOf).format(\"HH:mm\")}` : \"\")\r\n\t\t: \"\";\r\n\r\n\treturn (\r\n\t\t<Box sx={{ px: 1, pb: 2 }}>\r\n\t\t\t<Box sx={{ display: \"flex\", alignItems: \"center\", gap: 1, flexWrap: \"wrap\", mb: 1 }}>\r\n\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.78rem\", fontWeight: 600, color: \"#4a5568\" }}>\r\n\t\t\t\t\tArrivals over\r\n\t\t\t\t</Typography>\r\n\t\t\t\t{LIVE_WINDOW_OPTIONS.map((opt) => (\r\n\t\t\t\t\t<SDKButton\r\n\t\t\t\t\t\tkey={opt.minutes}\r\n\t\t\t\t\t\tvariant={windowMinutes === opt.minutes ? \"primary\" : \"outlined\"}\r\n\t\t\t\t\t\tonClick={() => onWindowChange(opt.minutes)}\r\n\t\t\t\t\t\tsx={{ height: 28, minWidth: 0, px: 1.25 }}\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t{opt.label}\r\n\t\t\t\t\t</SDKButton>\r\n\t\t\t\t))}\r\n\r\n\t\t\t\t<Box sx={{ flex: 1 }} />\r\n\r\n\t\t\t\t{settingsLine && (\r\n\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.72rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t\t{settingsLine}\r\n\t\t\t\t\t</Typography>\r\n\t\t\t\t)}\r\n\t\t\t\t<Tooltip title=\"Refresh\" arrow>\r\n\t\t\t\t\t<span>\r\n\t\t\t\t\t\t<IconButton size=\"small\" onClick={refresh} disabled={loading}>\r\n\t\t\t\t\t\t\t{loading ? <CircularProgress size={14} /> : <RefreshIcon sx={{ fontSize: 16 }} />}\r\n\t\t\t\t\t\t</IconButton>\r\n\t\t\t\t\t</span>\r\n\t\t\t\t</Tooltip>\r\n\t\t\t</Box>\r\n\r\n\t\t\t{error && (\r\n\t\t\t\t<Alert severity=\"error\" sx={{ mb: 1, fontFamily: FONT, fontSize: \"0.78rem\" }}>{error}</Alert>\r\n\t\t\t)}\r\n\r\n\t\t\t<AppTable\r\n\t\t\t\tcolumns={columns}\r\n\t\t\t\tdata={rows}\r\n\t\t\t\tlayoutMode=\"semantic\"\r\n\t\t\t\tenableTopToolbar={false}\r\n\t\t\t\tenableBottomToolbar={false}\r\n\t\t\t\tenablePagination={false}\r\n\t\t\t\tenableColumnActions={false}\r\n\t\t\t\tenableColumnFilters={false}\r\n\t\t\t\tenableColumnDragging={false}\r\n\t\t\t\tenableHiding={false}\r\n\t\t\t\tenableDensityToggle={false}\r\n\t\t\t\tenableFullScreenToggle={false}\r\n\t\t\t\tenableStickyHeader\r\n\t\t\t\tstate={{ isLoading: loading && !data }}\r\n\t\t\t\tlocalization={{\r\n\t\t\t\t\tnoRecordsToDisplay: `No agents on shift and no calls in the last ${data?.windowMinutes ?? windowMinutes} min.`,\r\n\t\t\t\t} as any}\r\n\t\t\t\tmuiCircularProgressProps={{ sx: { color: COLORS.blue }, size: 28 }}\r\n\t\t\t\tmuiTablePaperProps={{ elevation: 0, sx: { borderRadius: \"8px\", border: \"1px solid #e0e7ef\", overflow: \"hidden\", boxShadow: \"none\" } }}\r\n\t\t\t\tmuiTableProps={{ sx: { fontFamily: FONT, border: \"none\", borderRadius: 0, boxShadow: \"none\" } }}\r\n\t\t\t\tmuiTableContainerProps={{ sx: { maxHeight: \"calc(100vh - 300px)\" } }}\r\n\t\t\t\tmuiTableHeadProps={{ sx: { boxShadow: \"none\" } }}\r\n\t\t\t\tmuiTableHeadRowProps={{ sx: { boxShadow: \"none\" } }}\r\n\t\t\t\t// A short queue is the thing this page exists to show, so it is tinted\r\n\t\t\t\t// rather than left to the chip alone.\r\n\t\t\t\t// A queue holds more than its columns show; the rest is in the drawer.\r\n\t\t\t\tmuiTableBodyRowProps={({ row }) => ({\r\n\t\t\t\t\tonClick: () => setQueueRow(row.original),\r\n\t\t\t\t\tsx: {\r\n\t\t\t\t\t\tcursor: \"pointer\",\r\n\t\t\t\t\t\tbackgroundColor: row.original.status === \"UNDERSTAFFED\" ? \"#fdf4f4\" : \"#fff\",\r\n\t\t\t\t\t\t\"&:hover\": { backgroundColor: row.original.status === \"UNDERSTAFFED\" ? \"#fbe9e9\" : \"#f0f7f8\" },\r\n\t\t\t\t\t},\r\n\t\t\t\t})}\r\n\t\t\t/>\r\n\r\n\t\t\t<SlotDetailDrawer\r\n\t\t\t\tselection={queueRow\r\n\t\t\t\t\t? { kind: \"live\", row: queueRow, windowMinutes: data?.windowMinutes ?? windowMinutes, asOf: data?.asOf }\r\n\t\t\t\t\t: null}\r\n\t\t\t\tslotMinutes={windowMinutes}\r\n\t\t\t\tonClose={() => setQueueRow(null)}\r\n\t\t\t/>\r\n\t\t</Box>\r\n\t);\r\n}\r\n","import { useEffect, useMemo, useState } from \"react\";\r\nimport {\r\n\tAccordion, AccordionDetails, AccordionSummary,\r\n\tBox, Chip, CircularProgress, Drawer, FormControlLabel, IconButton, Switch, Tooltip, Typography,\r\n} from \"@mui/material\";\r\nimport { alpha } from \"@mui/material/styles\";\r\nimport CloseIcon from \"@mui/icons-material/Close\";\r\nimport ExpandMoreIcon from \"@mui/icons-material/ExpandMore\";\r\nimport dayjs from \"dayjs\";\r\n\r\nimport { COLORS, DASH, FONT, count, decimal, duration, pct1 } from \"./tableBits\";\r\nimport { STATUS_STYLE, type IntervalRow, type LiveQueueRow, type StaffingStatus } from \"./types\";\r\nimport { LOW_HISTORY_WEEKS, type ForecastIntervalRow } from \"./forecastTypes\";\r\nimport Markdown from \"./Markdown\";\r\nimport { getForecastExplain } from \"./useForecast\";\r\n\r\n/** Sits below the app bar, like the report and form submission panels. */\r\nconst APP_HEADER_H = 60;\r\n\r\ninterface Field {\r\n\tlabel: string;\r\n\tvalue: string;\r\n\t/** Empty fields are the ones the toggle hides. */\r\n\tempty: boolean;\r\n\tcolor?: string;\r\n\thint?: string;\r\n}\r\n\r\ninterface Section {\r\n\ttitle: string;\r\n\taccent: string;\r\n\tfields: Field[];\r\n}\r\n\r\n/** A value counts as empty when the API had nothing to say — not when it said zero. */\r\nconst field = (label: string, raw: unknown, value: string, extra: Partial<Field> = {}): Field => ({\r\n\tlabel,\r\n\tvalue,\r\n\tempty: raw === null || raw === undefined || value === DASH,\r\n\t...extra,\r\n});\r\n\r\nconst erlangs = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? `${v.toFixed(2)} E` : DASH;\r\n\r\nconst STATUS_LABEL: Record<StaffingStatus, string> = {\r\n\tUNDERSTAFFED: \"Understaffed\",\r\n\tOK: \"OK\",\r\n\tOVERSTAFFED: \"Overstaffed\",\r\n\tNO_DATA: \"No data\",\r\n};\r\n\r\n/* ═══════════════════════ SECTIONS ═══════════════════════ */\r\n\r\nfunction staffingSections(row: IntervalRow): Section[] {\r\n\treturn [\r\n\t\t{\r\n\t\t\ttitle: \"Calls\",\r\n\t\t\taccent: COLORS.navy,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Offered\", row.offered, count(row.offered)),\r\n\t\t\t\tfield(\"Answered\", row.answered, count(row.answered), { color: COLORS.green }),\r\n\t\t\t\tfield(\"Answered within SLA\", row.answeredWithinSla, count(row.answeredWithinSla), {\r\n\t\t\t\t\tcolor: COLORS.green,\r\n\t\t\t\t\thint: \"Picked up inside the queue's service level target.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Abandoned\", row.abandoned, count(row.abandoned), { color: COLORS.red }),\r\n\t\t\t\tfield(\"Service level\", row.serviceLevel, pct1(row.serviceLevel), { color: COLORS.blue }),\r\n\t\t\t],\r\n\t\t},\r\n\t\t{\r\n\t\t\ttitle: \"Workload\",\r\n\t\t\taccent: COLORS.teal,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Average handle time\", row.avgHandleSeconds, duration(row.avgHandleSeconds), {\r\n\t\t\t\t\thint: \"Talk plus wrap-up per answered call. Falls back to the range's average when the slot has too few calls of its own.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Inbound work\", row.trafficErlangs, erlangs(row.trafficErlangs), {\r\n\t\t\t\t\thint: \"Hours of talking per hour. 6 E means six hours of calls arrive every hour.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Campaign calls\", row.outboundCalls, decimal(row.outboundCalls), {\r\n\t\t\t\t\tcolor: COLORS.teal,\r\n\t\t\t\t\thint: \"A campaign spread over two queues counts half on each.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Campaign work\", row.outboundErlangs, erlangs(row.outboundErlangs), { color: COLORS.teal }),\r\n\t\t\t],\r\n\t\t},\r\n\t\t{\r\n\t\t\ttitle: \"Agents\",\r\n\t\t\taccent: COLORS.blue,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Inbound needed\", row.requiredAgents, count(row.requiredAgents), {\r\n\t\t\t\t\thint: \"Erlang-C on the inbound calls alone.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Total needed\", row.requiredTotalAgents, count(row.requiredTotalAgents), {\r\n\t\t\t\t\tcolor: COLORS.navy,\r\n\t\t\t\t\thint: \"Inbound plus the campaign work on top. Status and gap compare against this.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Staffed\", row.staffedAgents, decimal(row.staffedAgents), {\r\n\t\t\t\t\tcolor: COLORS.green,\r\n\t\t\t\t\thint: \"Averaged over the slot, and an agent in three queues counts as a third in each.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Distinct agents seen\", row.agentsSeen, count(row.agentsSeen)),\r\n\t\t\t\tfield(\"Gap\", row.staffingGap, decimal(row.staffingGap), {\r\n\t\t\t\t\tcolor: (row.staffingGap ?? 0) < 0 ? COLORS.red : COLORS.green,\r\n\t\t\t\t\thint: \"Staffed minus total needed. Negative is short.\",\r\n\t\t\t\t}),\r\n\t\t\t],\r\n\t\t},\r\n\t];\r\n}\r\n\r\nfunction forecastSections(row: ForecastIntervalRow): Section[] {\r\n\tconst sections: Section[] = [\r\n\t\t{\r\n\t\t\ttitle: \"Expected\",\r\n\t\t\taccent: COLORS.navy,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Expected inbound calls\", row.forecastCalls, decimal(row.forecastCalls), {\r\n\t\t\t\t\tcolor: COLORS.blue,\r\n\t\t\t\t\thint: \"0.4 means about one call every couple of weeks in this slot.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Handle time\", row.forecastHandleSeconds, duration(row.forecastHandleSeconds)),\r\n\t\t\t\tfield(\"Weeks of history\", row.weeksUsed, typeof row.weeksUsed === \"number\" ? `${row.weeksUsed} weeks` : DASH, {\r\n\t\t\t\t\tcolor: (row.weeksUsed ?? 0) < LOW_HISTORY_WEEKS ? COLORS.amber : undefined,\r\n\t\t\t\t\thint: \"Past weeks that had calls in this slot. Days the queue took none are skipped rather than counted as zero.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Expected campaign calls\", row.forecastOutboundCalls, decimal(row.forecastOutboundCalls), { color: COLORS.teal }),\r\n\t\t\t\tfield(\"Campaign work\", row.outboundErlangs, erlangs(row.outboundErlangs), { color: COLORS.teal }),\r\n\t\t\t],\r\n\t\t},\r\n\t\t{\r\n\t\t\ttitle: \"Agents to roster\",\r\n\t\t\taccent: COLORS.blue,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Inbound needed\", row.requiredAgents, count(row.requiredAgents), {\r\n\t\t\t\t\thint: \"Erlang-C on the expected inbound calls alone.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Total needed\", row.requiredTotalAgents, count(row.requiredTotalAgents), {\r\n\t\t\t\t\tcolor: COLORS.navy,\r\n\t\t\t\t\thint: \"The number to roster: inbound plus the campaign work on top.\",\r\n\t\t\t\t}),\r\n\t\t\t],\r\n\t\t},\r\n\t];\r\n\r\n\t// Only a slot that has fully passed has anything to compare against.\r\n\tif (row.isPast) {\r\n\t\tsections.push({\r\n\t\t\ttitle: \"What happened\",\r\n\t\t\taccent: COLORS.green,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Actual inbound calls\", row.actualCalls, count(row.actualCalls), { color: COLORS.blue }),\r\n\t\t\t\tfield(\"Actual campaign calls\", row.actualOutboundCalls, decimal(row.actualOutboundCalls), { color: COLORS.teal }),\r\n\t\t\t\tfield(\"Staffed\", row.actualStaffedAgents, decimal(row.actualStaffedAgents), { color: COLORS.green }),\r\n\t\t\t\tfield(\r\n\t\t\t\t\t\"Rostered to forecast\",\r\n\t\t\t\t\trow.status,\r\n\t\t\t\t\trow.status ? STATUS_LABEL[row.status] ?? row.status : DASH,\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcolor: row.status ? STATUS_STYLE[row.status]?.color : undefined,\r\n\t\t\t\t\t\thint: \"Whether staffing matched what this forecast called for — not whether it matched the calls that actually came.\",\r\n\t\t\t\t\t},\r\n\t\t\t\t),\r\n\t\t\t],\r\n\t\t});\r\n\t}\r\n\r\n\treturn sections;\r\n}\r\n\r\nfunction liveSections(row: LiveQueueRow): Section[] {\r\n\treturn [\r\n\t\t{\r\n\t\t\ttitle: \"Demand\",\r\n\t\t\taccent: COLORS.navy,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Calls in the window\", row.arrivalsInWindow, count(row.arrivalsInWindow), {\r\n\t\t\t\t\thint: \"What the per-hour rate beside it is worked out from.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Inbound per hour\", row.callsPerHour, decimal(row.callsPerHour), { color: COLORS.blue }),\r\n\t\t\t\tfield(\"Waiting now\", row.waitingNow, count(row.waitingNow), {\r\n\t\t\t\t\tcolor: (row.waitingNow ?? 0) > 0 ? COLORS.amber : undefined,\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Average handle time\", row.avgHandleSeconds, duration(row.avgHandleSeconds), {\r\n\t\t\t\t\thint: \"Talk plus wrap-up per answered call, over the last few days.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Campaign calls per hour\", row.outboundCallsPerHour, decimal(row.outboundCallsPerHour), { color: COLORS.teal }),\r\n\t\t\t\tfield(\"Campaign work\", row.outboundErlangs, erlangs(row.outboundErlangs), {\r\n\t\t\t\t\tcolor: COLORS.teal,\r\n\t\t\t\t\thint: \"Hours of agent time per hour that campaign calls are taking.\",\r\n\t\t\t\t}),\r\n\t\t\t],\r\n\t\t},\r\n\t\t{\r\n\t\t\ttitle: \"Agents\",\r\n\t\t\taccent: COLORS.blue,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Inbound needed\", row.requiredAgents, count(row.requiredAgents), {\r\n\t\t\t\t\thint: \"Erlang-C on the inbound calls alone.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Total needed\", row.requiredTotalAgents, count(row.requiredTotalAgents), {\r\n\t\t\t\t\tcolor: COLORS.navy,\r\n\t\t\t\t\thint: \"Inbound plus the campaign work on top. Status, shortfall and surplus compare against this.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Staffed\", row.staffedAgents, decimal(row.staffedAgents), {\r\n\t\t\t\t\tcolor: COLORS.green,\r\n\t\t\t\t\thint: \"This queue's share: an agent serving three queues counts as a third in each.\",\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Idle\", row.idleAgents, count(row.idleAgents)),\r\n\t\t\t\tfield(\"On break\", row.onBreakAgents, count(row.onBreakAgents), { color: COLORS.grey }),\r\n\t\t\t\tfield(\"Shortfall\", row.shortfall, decimal(row.shortfall), {\r\n\t\t\t\t\tcolor: (row.shortfall ?? 0) > 0 ? COLORS.red : undefined,\r\n\t\t\t\t}),\r\n\t\t\t\tfield(\"Surplus\", row.surplus, decimal(row.surplus), {\r\n\t\t\t\t\tcolor: (row.surplus ?? 0) > 0 ? COLORS.amber : undefined,\r\n\t\t\t\t}),\r\n\t\t\t],\r\n\t\t},\r\n\t\t{\r\n\t\t\ttitle: \"Service level\",\r\n\t\t\taccent: COLORS.teal,\r\n\t\t\tfields: [\r\n\t\t\t\tfield(\"Expected service level\", row.expectedServiceLevel, pct1(row.expectedServiceLevel), {\r\n\t\t\t\t\tcolor: COLORS.blue,\r\n\t\t\t\t\thint: \"What the agents on shift would answer within target, once campaign calls have taken their share.\",\r\n\t\t\t\t}),\r\n\t\t\t],\r\n\t\t},\r\n\t];\r\n}\r\n\r\n/* ═══════════════════════ DRAWER ═══════════════════════ */\r\n\r\ntype SlotRow =\r\n\t| { kind: \"staffing\"; row: IntervalRow }\r\n\t| { kind: \"forecast\"; row: ForecastIntervalRow }\r\n\t/** No slot of its own: a live row is \"right now\", over the arrivals window. */\r\n\t| { kind: \"live\"; row: LiveQueueRow; windowMinutes: number; asOf?: string };\r\n\r\n/**\r\n * Everything one slot holds, in a panel rather than more columns.\r\n *\r\n * The slot tables already carry eleven columns; the figures behind them — the\r\n * Erlangs, the distinct agent count, the weeks of history — belong here, where\r\n * they can be read with a sentence of explanation beside them.\r\n */\r\nexport default function SlotDetailDrawer({\r\n\tselection, slotMinutes, onClose,\r\n}: {\r\n\tselection: SlotRow | null;\r\n\tslotMinutes: number;\r\n\tonClose: () => void;\r\n}) {\r\n\t// Off by default: a quiet slot is mostly dashes, and hiding them leaves the\r\n\t// handful of numbers that matter.\r\n\tconst [showEmpty, setShowEmpty] = useState(false);\r\n\r\n\t/**\r\n\t * Everything is open to begin with — except on a forecast slot, where only\r\n\t * the first is, so the explanation underneath is reachable without scrolling\r\n\t * past three open sections. Nothing else on this page has one.\r\n\t */\r\n\tconst [openMap, setOpenMap] = useState<Record<string, boolean>>({});\r\n\tconst openByDefault = (index: number) =>\r\n\t\t(selection?.kind === \"forecast\" ? index === 0 : true);\r\n\tconst isOpen = (title: string, index: number) => openMap[title] ?? openByDefault(index);\r\n\tconst toggleSection = (title: string, index: number) =>\r\n\t\tsetOpenMap((prev) => ({ ...prev, [title]: !(prev[title] ?? openByDefault(index)) }));\r\n\r\n\tconst sections = useMemo(() => {\r\n\t\tif (!selection) return [];\r\n\t\tif (selection.kind === \"staffing\") return staffingSections(selection.row);\r\n\t\tif (selection.kind === \"live\") return liveSections(selection.row);\r\n\t\treturn forecastSections(selection.row);\r\n\t}, [selection]);\r\n\r\n\tconst visible = sections\r\n\t\t.map((s) => ({ ...s, fields: showEmpty ? s.fields : s.fields.filter((f) => !f.empty) }))\r\n\t\t.filter((s) => s.fields.length > 0);\r\n\r\n\tconst hiddenCount = sections.reduce((n, s) => n + s.fields.filter((f) => f.empty).length, 0);\r\n\r\n\t/*\r\n\t * The forecast's own account of this slot, under the sections. The figures\r\n\t * say what the forecast is; this says why, in the words a supervisor would\r\n\t * use to defend the roster built from it.\r\n\t */\r\n\tconst [explanation, setExplanation] = useState<string>(\"\");\r\n\tconst [explaining, setExplaining] = useState(false);\r\n\r\n\tuseEffect(() => {\r\n\t\tsetOpenMap({});\r\n\t\tsetExplanation(\"\");\r\n\r\n\t\tif (selection?.kind !== \"forecast\") return;\r\n\t\tconst at = selection.row.intervalStart ? dayjs(selection.row.intervalStart) : null;\r\n\t\tif (!at || !at.isValid()) return;\r\n\r\n\t\tlet live = true;\r\n\t\tsetExplaining(true);\r\n\t\tgetForecastExplain({\r\n\t\t\tday: at.format(\"YYYY-MM-DD\"),\r\n\t\t\tqueueId: selection.row.queueId,\r\n\t\t\tslot: at.format(\"HH:mm\"),\r\n\t\t\tintervalMinutes: slotMinutes,\r\n\t\t})\r\n\t\t\t.then((res) => { if (live) setExplanation(res.summaryMarkdown || res.summary || \"\"); })\r\n\t\t\t// A missing explanation is not worth an error: the figures above stand\r\n\t\t\t// on their own.\r\n\t\t\t.catch(() => { if (live) setExplanation(\"\"); })\r\n\t\t\t.finally(() => { if (live) setExplaining(false); });\r\n\t\treturn () => { live = false; };\r\n\t}, [selection, slotMinutes]);\r\n\r\n\tconst row = selection?.row;\r\n\tconst status = row?.status as StaffingStatus | null | undefined;\r\n\r\n\t/** When this is about: a slot's window, or \"right now\" for a live row. */\r\n\tconst when = (() => {\r\n\t\tif (!selection) return DASH;\r\n\t\tif (selection.kind === \"live\") {\r\n\t\t\tconst asOf = selection.asOf ? dayjs(selection.asOf) : null;\r\n\t\t\treturn `Last ${selection.windowMinutes} min`\r\n\t\t\t\t+ (asOf && asOf.isValid() ? ` · as of ${asOf.format(\"HH:mm\")}` : \"\");\r\n\t\t}\r\n\t\tconst start = selection.row.intervalStart ? dayjs(selection.row.intervalStart) : null;\r\n\t\tif (!start || !start.isValid()) return DASH;\r\n\t\treturn `${start.format(\"dddd, D MMMM YYYY\")} · ${start.format(\"HH:mm\")}–${start.add(slotMinutes, \"minute\").format(\"HH:mm\")}`;\r\n\t})();\r\n\r\n\treturn (\r\n\t\t<Drawer\r\n\t\t\tanchor=\"right\"\r\n\t\t\topen={!!selection}\r\n\t\t\tonClose={onClose}\r\n\t\t\tslotProps={{\r\n\t\t\t\tpaper: {\r\n\t\t\t\t\tsx: {\r\n\t\t\t\t\t\twidth: { xs: \"100%\", sm: 460 },\r\n\t\t\t\t\t\ttop: `${APP_HEADER_H}px`,\r\n\t\t\t\t\t\theight: `calc(100% - ${APP_HEADER_H}px)`,\r\n\t\t\t\t\t\tboxShadow: \"-4px 0 24px rgba(0,0,0,0.12)\",\r\n\t\t\t\t\t\tborderLeft: \"1px solid #e0e6ed\",\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t}}\r\n\t\t>\r\n\t\t\t{row && (\r\n\t\t\t\t<Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\", bgcolor: \"#fafbfc\", fontFamily: FONT }}>\r\n\t\t\t\t\t{/* Header */}\r\n\t\t\t\t\t<Box sx={{ px: 2.5, pt: 2.5, pb: 2, borderBottom: \"1px solid #eee\", bgcolor: \"#fff\" }}>\r\n\t\t\t\t\t\t<Box sx={{ display: \"flex\", alignItems: \"flex-start\", justifyContent: \"space-between\", gap: 1 }}>\r\n\t\t\t\t\t\t\t<Box sx={{ minWidth: 0, flex: 1 }}>\r\n\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontWeight: 700, fontSize: \"1.05rem\", color: \"#1a1a1a\", lineHeight: 1.25 }}>\r\n\t\t\t\t\t\t\t\t\t{row.queueName || `Queue ${row.queueId}`}\r\n\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.78rem\", color: COLORS.grey, mt: 0.3 }}>\r\n\t\t\t\t\t\t\t\t\t{when}\r\n\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t<Tooltip title=\"Close\">\r\n\t\t\t\t\t\t\t\t<IconButton\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tonClick={onClose}\r\n\t\t\t\t\t\t\t\t\tsx={{ color: \"#555\", bgcolor: \"#f2f4f7\", \"&:hover\": { bgcolor: \"#e4e7eb\", color: \"#111\" } }}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t<CloseIcon fontSize=\"small\" />\r\n\t\t\t\t\t\t\t\t</IconButton>\r\n\t\t\t\t\t\t\t</Tooltip>\r\n\t\t\t\t\t\t</Box>\r\n\r\n\t\t\t\t\t\t{/* Chips and the toggle share a row — the toggle on its own line pushed\r\n\t\t\t\t\t\t    the first section below the fold. */}\r\n\t\t\t\t\t\t<Box sx={{ display: \"flex\", alignItems: \"center\", flexWrap: \"wrap\", gap: 0.75, mt: 1.25 }}>\r\n\t\t\t\t\t\t\t{status && (\r\n\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tlabel={STATUS_LABEL[status] ?? status}\r\n\t\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\theight: 22, fontSize: \"0.68rem\", fontWeight: 700,\r\n\t\t\t\t\t\t\t\t\t\tbackgroundColor: STATUS_STYLE[status]?.bg,\r\n\t\t\t\t\t\t\t\t\t\tcolor: STATUS_STYLE[status]?.color,\r\n\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t{selection?.kind === \"forecast\" && (\r\n\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tlabel={selection.row.isPast ? \"Past slot\" : \"Still to come\"}\r\n\t\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\theight: 22, fontSize: \"0.68rem\", fontWeight: 700,\r\n\t\t\t\t\t\t\t\t\t\tbackgroundColor: selection.row.isPast ? \"#eceeed\" : \"#e6f0f2\",\r\n\t\t\t\t\t\t\t\t\t\tcolor: selection.row.isPast ? COLORS.grey : COLORS.teal,\r\n\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t{selection?.kind === \"forecast\" && (selection.row.weeksUsed ?? 0) < LOW_HISTORY_WEEKS && (\r\n\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tlabel=\"Low history\"\r\n\t\t\t\t\t\t\t\t\tsx={{ height: 22, fontSize: \"0.68rem\", fontWeight: 700, backgroundColor: \"#fbf0d6\", color: COLORS.amber }}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t\t<Box sx={{ flex: 1 }} />\r\n\r\n\t\t\t\t\t\t\t<FormControlLabel\r\n\t\t\t\t\t\t\t\tsx={{ mr: 0 }}\r\n\t\t\t\t\t\t\t\tcontrol={\r\n\t\t\t\t\t\t\t\t\t<Switch\r\n\t\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\t\tchecked={showEmpty}\r\n\t\t\t\t\t\t\t\t\t\tonChange={(e) => setShowEmpty(e.target.checked)}\r\n\t\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t\tlabel={\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.73rem\", color: \"#4a5568\", whiteSpace: \"nowrap\" }}>\r\n\t\t\t\t\t\t\t\t\t\tShow empty{hiddenCount > 0 && !showEmpty ? ` (${hiddenCount})` : \"\"}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t</Box>\r\n\r\n\t\t\t\t\t{/* Sections */}\r\n\t\t\t\t\t<Box sx={{ flex: 1, overflowY: \"auto\", px: 2, py: 2, display: \"flex\", flexDirection: \"column\", gap: 1.5 }}>\r\n\t\t\t\t\t\t{visible.length === 0 && (\r\n\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t\t\t\tNothing recorded here.\r\n\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t{visible.map((section, index) => (\r\n\t\t\t\t\t\t\t/* Same shape as the report and form submission drawers: a tinted\r\n\t\t\t\t\t\t\t   header bar, an accent edge, and the chevron on the right. */\r\n\t\t\t\t\t\t\t<Accordion\r\n\t\t\t\t\t\t\t\tkey={section.title}\r\n\t\t\t\t\t\t\t\texpanded={isOpen(section.title, index)}\r\n\t\t\t\t\t\t\t\tonChange={() => toggleSection(section.title, index)}\r\n\t\t\t\t\t\t\t\tdisableGutters\r\n\t\t\t\t\t\t\t\televation={0}\r\n\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\"&:before\": { display: \"none\" },\r\n\t\t\t\t\t\t\t\t\tborder: \"1px solid #eef2f3\",\r\n\t\t\t\t\t\t\t\t\tborderLeft: `4px solid ${section.accent}`,\r\n\t\t\t\t\t\t\t\t\tborderRadius: \"8px !important\",\r\n\t\t\t\t\t\t\t\t\toverflow: \"hidden\",\r\n\t\t\t\t\t\t\t\t\t// Keep full height and let the drawer scroll. In a flex column an\r\n\t\t\t\t\t\t\t\t\t// Accordion is shrunk to fit instead, which clips its own rows.\r\n\t\t\t\t\t\t\t\t\tflexShrink: 0,\r\n\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t<AccordionSummary\r\n\t\t\t\t\t\t\t\t\texpandIcon={<ExpandMoreIcon sx={{ color: section.accent }} />}\r\n\t\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\tminHeight: 42,\r\n\t\t\t\t\t\t\t\t\t\tbgcolor: alpha(section.accent, 0.06),\r\n\t\t\t\t\t\t\t\t\t\t\"&:hover\": { bgcolor: alpha(section.accent, 0.1) },\r\n\t\t\t\t\t\t\t\t\t\t\"& .MuiAccordionSummary-content\": { my: 0.75, alignItems: \"center\", gap: 1 },\r\n\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontWeight: 700, fontSize: \"0.85rem\", color: section.accent }}>\r\n\t\t\t\t\t\t\t\t\t\t{section.title}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t{/* A collapsed section still says how much it is holding. */}\r\n\t\t\t\t\t\t\t\t\t{!isOpen(section.title, index) && (\r\n\t\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.72rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t\t\t\t\t\t\t{section.fields.length} {section.fields.length === 1 ? \"field\" : \"fields\"}\r\n\t\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t\t</AccordionSummary>\r\n\t\t\t\t\t\t\t\t<AccordionDetails sx={{ bgcolor: \"#fff\", px: 2, py: 1.5 }}>\r\n\t\t\t\t\t\t\t\t\t<Box sx={{ display: \"grid\", gridTemplateColumns: \"1fr auto\", rowGap: 0.85, columnGap: 2 }}>\r\n\t\t\t\t\t\t\t\t\t\t{section.fields.map((f) => (\r\n\t\t\t\t\t\t\t\t\t\t\t<Box key={f.label} sx={{ display: \"contents\" }}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.76rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{f.hint ? (\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Tooltip arrow title={f.hint}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Box component=\"span\" sx={{ borderBottom: \"1px dotted #c3cbd4\", cursor: \"help\" }}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{f.label}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Tooltip>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t) : f.label}\r\n\t\t\t\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<Typography\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfontFamily: FONT, fontSize: \"0.8rem\", fontWeight: 600,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tfontVariantNumeric: \"tabular-nums\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: f.empty ? COLORS.grey : (f.color || \"#1a1a1a\"),\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\ttextAlign: \"right\",\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{f.value}\r\n\t\t\t\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t\t\t\t))}\r\n\t\t\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t\t</AccordionDetails>\r\n\t\t\t\t\t\t\t</Accordion>\r\n\t\t\t\t\t\t))}\r\n\t\t\t\t\t{(explaining || explanation) && (\r\n\t\t\t\t\t\t\t<Box sx={{ border: \"1px solid #e0e7ef\", borderRadius: \"8px\", backgroundColor: \"#fff\", p: 1.5, flexShrink: 0 }}>\r\n\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontWeight: 700, fontSize: \"0.85rem\", color: COLORS.navy, mb: 0.75 }}>\r\n\t\t\t\t\t\t\t\t\tWhy this forecast?\r\n\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t{explaining\r\n\t\t\t\t\t\t\t\t\t? <CircularProgress size={16} sx={{ color: COLORS.blue }} />\r\n\t\t\t\t\t\t\t\t\t: <Markdown text={explanation} />}\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\t\t\t\t\t</Box>\r\n\t\t\t\t</Box>\r\n\t\t\t)}\r\n\t\t</Drawer>\r\n\t);\r\n}\r\n","import { Box, Chip, Tooltip } from \"@mui/material\";\r\nimport InfoOutlinedIcon from \"@mui/icons-material/InfoOutlined\";\r\nimport { headCellSx } from \"../common/utils/tableStyles\";\r\nimport { STATUS_STYLE, type StaffingStatus } from \"./types\";\r\n\r\n/* Shared between the Live and By interval tables, so the two read as one page. */\r\n\r\nexport const FONT = \"'Inter', 'Segoe UI', 'Roboto', -apple-system, BlinkMacSystemFont, sans-serif\";\r\nexport const DASH = \"–\";\r\n\r\nexport const COLORS = {\r\n\tnavy: \"#0d2a56\",\r\n\tblue: \"#1565c8\",\r\n\tteal: \"#1A5F6C\",\r\n\tgreen: \"#0a9a62\",\r\n\tamber: \"#c47c00\",\r\n\tred: \"#cc2a2a\",\r\n\tgrey: \"#7a8599\",\r\n} as const;\r\n\r\nexport const cellBaseSx = {\r\n\tfontFamily: FONT,\r\n\tfontSize: \"0.75rem\",\r\n\tborderBottom: \"1px solid #eef1f6\",\r\n\twhiteSpace: \"nowrap\",\r\n\t\"&&\": { px: 1.5, py: 1.25 },\r\n};\r\n\r\nexport const headSx = {\r\n\t...(headCellSx as object),\r\n\t...cellBaseSx,\r\n\tfontSize: \"13px\",\r\n\tverticalAlign: \"middle\",\r\n\ttextAlign: \"center\",\r\n\tborderBottom: \"1px solid #dfe5ee\",\r\n\t\"& .Mui-TableHeadCell-Content\": { justifyContent: \"center\" },\r\n\t\"&&\": { px: 1.5, py: 0.6 },\r\n};\r\n\r\nexport const tabularSx = { fontSize: \"0.75rem\", fontVariantNumeric: \"tabular-nums\" as const };\r\n\r\n/** Centred head and body cells, with per-column body styling. */\r\nexport const cell = (sx: Record<string, unknown> = {}) => ({\r\n\tmuiTableHeadCellProps: { align: \"center\" as const, sx: headSx },\r\n\tmuiTableBodyCellProps: { align: \"center\" as const, sx: { ...cellBaseSx, textAlign: \"center\", ...sx } },\r\n});\r\n\r\n/** A header with an ⓘ that says what the column means. Spread into a column def. */\r\nexport const withInfo = (header: string, info: string) => ({\r\n\theader,\r\n\tHeader: () => (\r\n\t\t<Tooltip arrow title={info}>\r\n\t\t\t<Box component=\"span\" sx={{ display: \"inline-flex\", alignItems: \"center\", gap: 0.4, cursor: \"pointer\" }}>\r\n\t\t\t\t{header}\r\n\t\t\t\t<InfoOutlinedIcon sx={{ fontSize: 14, color: COLORS.grey }} />\r\n\t\t\t</Box>\r\n\t\t</Tooltip>\r\n\t),\r\n});\r\n\r\n/* ═══════════════════════ FORMATTING ═══════════════════════\r\n * Rates are fractions. Null means \"not known\" and must never read as zero.\r\n */\r\n\r\nexport const pct0 = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? `${(v * 100).toFixed(0)}%` : DASH;\r\n\r\nexport const pct1 = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? `${(v * 100).toFixed(1)}%` : DASH;\r\n\r\nexport const count = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? v.toLocaleString() : DASH;\r\n\r\n/** Averages carry a decimal: 0.4 staffed agents is one agent for 40% of the slot. */\r\nexport const decimal = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? v.toFixed(1) : DASH;\r\n\r\n/** Signed, because the direction is the point: -0.6 is short, +1.2 is spare. */\r\nexport const signed = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? `${v > 0 ? \"+\" : v < 0 ? \"−\" : \"\"}${Math.abs(v).toFixed(1)}` : DASH;\r\n\r\n/** \"±29%\" — a forecast error has no useful direction, only a size. */\r\nexport const plusMinusPct = (v: number | null | undefined) =>\r\n\ttypeof v === \"number\" ? `±${Math.round(Math.abs(v) * 100)}%` : DASH;\r\n\r\n/** \"1m 12s\" — handle time is read as minutes once it passes one. */\r\nexport function duration(seconds: number | null | undefined): string {\r\n\tif (typeof seconds !== \"number\") return DASH;\r\n\tif (seconds < 60) return `${seconds.toFixed(0)}s`;\r\n\tconst mins = Math.floor(seconds / 60);\r\n\treturn `${mins}m ${Math.round(seconds - mins * 60)}s`;\r\n}\r\n\r\n/* ═══════════════════════ STATUS ═══════════════════════ */\r\n\r\nconst STATUS_TOOLTIP: Record<StaffingStatus, string> = {\r\n\tUNDERSTAFFED: \"Inbound callers came and fewer agents were staffed than the queue needs\",\r\n\tOK: \"Enough agents for the inbound and campaign work\",\r\n\tOVERSTAFFED: \"More agents than needed — the spare ones can take campaign calls\",\r\n\tNO_DATA: \"Calls arrived but no handle time is known yet\",\r\n};\r\n\r\n/** \"Short 5\" / \"Surplus 1\" — the number is what the chip is for. */\r\nexport function StatusChip({\r\n\tstatus, shortfall, surplus, tooltip,\r\n}: {\r\n\tstatus: StaffingStatus;\r\n\tshortfall?: number | null;\r\n\tsurplus?: number | null;\r\n\t/** The forecast tab compares against the forecast, so it says so itself. */\r\n\ttooltip?: string;\r\n}) {\r\n\tconst style = STATUS_STYLE[status] ?? STATUS_STYLE.NO_DATA;\r\n\tconst label = status === \"UNDERSTAFFED\"\r\n\t\t? `Short ${typeof shortfall === \"number\" ? shortfall : \"\"}`.trim()\r\n\t\t: status === \"OVERSTAFFED\"\r\n\t\t\t? `Surplus ${typeof surplus === \"number\" ? surplus : \"\"}`.trim()\r\n\t\t\t: status === \"NO_DATA\" ? \"No data\" : \"OK\";\r\n\r\n\treturn (\r\n\t\t<Tooltip arrow title={tooltip ?? STATUS_TOOLTIP[status] ?? \"\"}>\r\n\t\t\t<Chip\r\n\t\t\t\tsize=\"small\"\r\n\t\t\t\tlabel={label}\r\n\t\t\t\tsx={{\r\n\t\t\t\t\theight: 20, fontSize: \"0.66rem\", fontWeight: 700,\r\n\t\t\t\t\tbackgroundColor: style.bg, color: style.color,\r\n\t\t\t\t}}\r\n\t\t\t/>\r\n\t\t</Tooltip>\r\n\t);\r\n}\r\n","/**\r\n * Workforce management — how many agents a queue needs against how many are on\r\n * shift. The backend does the staffing maths; the UI only displays it.\r\n *\r\n * Every rate is a FRACTION (0.8 is 80%). `null` means \"not known\", which is not\r\n * zero: a queue with no answered call yet has an unknown requirement, not a\r\n * requirement of none.\r\n */\r\n\r\nexport type StaffingStatus = \"UNDERSTAFFED\" | \"OK\" | \"OVERSTAFFED\" | \"NO_DATA\";\r\n\r\n/** Tenant-wide targets, echoed in every response so the UI never hard-codes them. */\r\nexport interface WfmSettings {\r\n\t/** Answer within this many seconds to count towards the service level. */\r\n\tslaSeconds: number;\r\n\tslaTarget: number;\r\n\t/** The share of their shift agents may spend working — headroom for the bursts. */\r\n\tmaxOccupancy: number;\r\n\tintervalMinutes: number;\r\n}\r\n\r\n/** One queue, right now. */\r\nexport interface LiveQueueRow {\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\tarrivalsInWindow: number;\r\n\tcallsPerHour: number;\r\n\twaitingNow: number;\r\n\tavgHandleSeconds: number | null;\r\n\t/** Agents inbound alone needs to hit the target. */\r\n\trequiredAgents: number | null;\r\n\toutboundCallsPerHour: number;\r\n\toutboundErlangs: number;\r\n\t/**\r\n\t * Inbound plus campaign work. Campaign calls first use the idle time inbound\r\n\t * agents already have, so this only exceeds `requiredAgents` once that\r\n\t * headroom runs out. Status, gap, shortfall and surplus all use this number.\r\n\t */\r\n\trequiredTotalAgents: number | null;\r\n\tstaffedAgents: number;\r\n\tidleAgents: number;\r\n\tonBreakAgents: number;\r\n\t/** Against the total. */\r\n\tshortfall: number;\r\n\tsurplus: number;\r\n\t/** What the service level would be at the current staffing. */\r\n\texpectedServiceLevel: number | null;\r\n\tstatus: StaffingStatus;\r\n}\r\n\r\nexport interface LiveStaffingResponse {\r\n\tsettings: WfmSettings;\r\n\twindowMinutes: number;\r\n\t/** Local wall-clock, no offset — do not convert time zones. */\r\n\tasOf: string;\r\n\tqueues: LiveQueueRow[];\r\n}\r\n\r\n/** How far back arrivals are counted. A quiet queue needs a longer window to say anything. */\r\nexport const LIVE_WINDOW_OPTIONS = [\r\n\t{ minutes: 15, label: \"15 min\" },\r\n\t{ minutes: 30, label: \"30 min\" },\r\n\t{ minutes: 60, label: \"60 min\" },\r\n\t{ minutes: 120, label: \"120 min\" },\r\n] as const;\r\n\r\n/* ═══════════════════════ BY INTERVAL ═══════════════════════ */\r\n\r\n/** One 15/30/60-minute slot for one queue. */\r\nexport interface IntervalRow {\r\n\t/** Local wall-clock with no offset — do not convert time zones. */\r\n\tintervalStart: string;\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\toffered: number;\r\n\tanswered: number;\r\n\tansweredWithinSla: number;\r\n\tabandoned: number;\r\n\tserviceLevel: number | null;\r\n\tavgHandleSeconds: number | null;\r\n\ttrafficErlangs: number | null;\r\n\trequiredAgents: number | null;\r\n\t/** Fractional: a campaign spread over two queues counts half on each. */\r\n\toutboundCalls: number;\r\n\toutboundErlangs: number;\r\n\trequiredTotalAgents: number | null;\r\n\t/** An average over the slot: 0.4 is one agent present for 40% of it. */\r\n\tstaffedAgents: number;\r\n\tagentsSeen?: number;\r\n\t/** Staffed minus total needed. Negative is short. */\r\n\tstaffingGap: number | null;\r\n\tstatus: StaffingStatus;\r\n}\r\n\r\n/** One day for one queue — a roll-up of that day's intervals. */\r\nexport interface DaySummaryRow {\r\n\tday: string;\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\toffered: number;\r\n\tanswered: number;\r\n\tansweredWithinSla: number;\r\n\tabandoned: number;\r\n\tserviceLevel: number | null;\r\n\toutboundCalls: number;\r\n\tpeakRequiredAgents: number | null;\r\n\tpeakIntervalStart: string | null;\r\n\tavgStaffedAgents: number | null;\r\n\tintervals: number;\r\n\tintervalsUnderstaffed: number;\r\n}\r\n\r\n/**\r\n * GET /wfm/staffing — the day table, paged by the server.\r\n *\r\n * Intervals are no longer part of this response: a day's slots are fetched on\r\n * demand when a row is clicked, so a month of queues costs one page of rows\r\n * rather than thousands of slots nobody has asked to see.\r\n */\r\nexport interface StaffingDaysPage {\r\n\tsettings: WfmSettings;\r\n\trows: DaySummaryRow[];\r\n\ttotal: number;\r\n}\r\n\r\n/** GET /wfm/staffing/intervals — one queue, one day, at most 96 slots. */\r\nexport interface StaffingIntervalsResponse {\r\n\tsettings: WfmSettings;\r\n\tday: string;\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\t/** The day's roll-up, for the panel header — the same shape as a day row. */\r\n\tsummary: DaySummaryRow | null;\r\n\tintervals: IntervalRow[];\r\n}\r\n\r\nexport interface StaffingIntervalsParams {\r\n\t/** YYYY-MM-DD. */\r\n\tday: string;\r\n\tqueueId: number;\r\n\tintervalMinutes: number;\r\n\t/** Carried over from the table so the slots match the rows they came from. */\r\n\ttimeFrom?: string;\r\n\ttimeTo?: string;\r\n}\r\n\r\nexport interface StaffingParams {\r\n\t/** YYYY-MM-DD, both required. */\r\n\tstartDate: string;\r\n\tendDate: string;\r\n\t/** 15, 30 or 60. */\r\n\tintervalMinutes: number;\r\n\t/** Omit for every queue. */\r\n\tqueueIds?: number[];\r\n\t/** ISO numbers, 1 = Mon … 7 = Sun. Omit for every day. */\r\n\tweekdays?: number[];\r\n\t/** HH:MM. Keeps slots starting at or after `from` and before `to`. */\r\n\ttimeFrom?: string;\r\n\ttimeTo?: string;\r\n\t/** 1-based, as the API counts pages. */\r\n\tpage?: number;\r\n\tpageSize?: number;\r\n\t/** Matches the queue name. */\r\n\tsearch?: string;\r\n}\r\n\r\n/** Server limits, mirrored so the filters can refuse before the round trip. */\r\nexport const WFM_LIMITS = {\r\n\tmaxDays: 31,\r\n\tmaxQueues: 100,\r\n} as const;\r\n\r\nexport const INTERVAL_OPTIONS = [15, 30, 60] as const;\r\n\r\n/** ISO weekday numbers, as the API wants them. */\r\nexport const WEEKDAYS = [\r\n\t{ iso: 1, label: \"Mon\" },\r\n\t{ iso: 2, label: \"Tue\" },\r\n\t{ iso: 3, label: \"Wed\" },\r\n\t{ iso: 4, label: \"Thu\" },\r\n\t{ iso: 5, label: \"Fri\" },\r\n\t{ iso: 6, label: \"Sat\" },\r\n\t{ iso: 7, label: \"Sun\" },\r\n] as const;\r\n\r\n/** A queue as the filter needs it. Collected from the rows: there is no queue list endpoint on this feature, and `queues:read` is a permission a WFM user may not hold. */\r\nexport interface QueueChoice {\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n}\r\n\r\nexport const STATUS_STYLE: Record<StaffingStatus, { color: string; bg: string }> = {\r\n\tUNDERSTAFFED: { color: \"#cc2a2a\", bg: \"#fdecea\" },\r\n\tOK: { color: \"#0a9a62\", bg: \"#e3f3e8\" },\r\n\t// Not an error: spare agents take campaign calls.\r\n\tOVERSTAFFED: { color: \"#c47c00\", bg: \"#fbf0d6\" },\r\n\tNO_DATA: { color: \"#7a8599\", bg: \"#eceeed\" },\r\n};\r\n\r\n","import type { StaffingStatus, WfmSettings } from \"./types\";\r\n\r\n/**\r\n * GET /wfm/forecast — expected calls and agents for the coming slots, and for\r\n * past ones the forecast next to what actually happened.\r\n *\r\n * Each slot is predicted from the same weekday and time over the last few\r\n * weeks, recent weeks counting more. Today is never used as history and a past\r\n * day only uses the weeks before it, so a day's forecast never changes after\r\n * the fact — which is what makes the accuracy figures worth reading.\r\n */\r\n\r\n/** The forecast's own settings, on top of the staffing ones. */\r\nexport interface ForecastSettings extends WfmSettings {\r\n\t/** Weeks of history behind each slot — a tenant setting, 4 to 12. */\r\n\tforecastWeeks: number;\r\n}\r\n\r\n/** One queue on one day — the roster view. */\r\nexport interface ForecastDayRow {\r\n\tday: string;\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\tforecastCalls: number | null;\r\n\tforecastOutboundCalls: number | null;\r\n\tpeakRequiredAgents: number | null;\r\n\tpeakIntervalStart: string | null;\r\n\t/** Total agent time to roster that day, campaign work included. */\r\n\trequiredAgentHours: number | null;\r\n\r\n\t/* Past days only — null for anything still to come. */\r\n\tactualCalls: number | null;\r\n\tactualOutboundCalls: number | null;\r\n\tactualStaffedAgentHours: number | null;\r\n\t/** Fraction: 0.29 is ±29%. Inbound only. */\r\n\tforecastError: number | null;\r\n\tintervalsUnderstaffed: number | null;\r\n}\r\n\r\n/** One slot for one queue. */\r\nexport interface ForecastIntervalRow {\r\n\t/** Local wall-clock with no offset — do not convert time zones. */\r\n\tintervalStart: string;\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\t/** Fractional on purpose: 0.4 is about one call every couple of weeks in this slot. */\r\n\tforecastCalls: number | null;\r\n\tforecastHandleSeconds: number | null;\r\n\trequiredAgents: number | null;\r\n\t/** Weeks that really contributed — a holiday contributes nothing rather than a zero. */\r\n\tweeksUsed: number | null;\r\n\tforecastOutboundCalls: number | null;\r\n\toutboundErlangs: number | null;\r\n\t/** The number to roster: inbound plus the campaign work on top. */\r\n\trequiredTotalAgents: number | null;\r\n\r\n\t/** False for the slot in progress; it becomes past once it ends. */\r\n\tisPast: boolean;\r\n\tactualCalls: number | null;\r\n\tactualOutboundCalls: number | null;\r\n\tactualStaffedAgents: number | null;\r\n\t/**\r\n\t * Past slots only, and it answers a different question from the other tabs:\r\n\t * whether staffing matched what the FORECAST called for, not what the calls\r\n\t * turned out to need.\r\n\t */\r\n\tstatus: StaffingStatus | null;\r\n}\r\n\r\n/**\r\n * GET /wfm/forecast — the day table, paged by the server.\r\n *\r\n * Slots are no longer part of this response: a day's are fetched when its row\r\n * is clicked, so four weeks of queues costs a page of rows rather than every\r\n * slot in the month.\r\n */\r\nexport interface ForecastDaysPage {\r\n\tsettings: ForecastSettings;\r\n\tgeneratedAt: string;\r\n\trows: ForecastDayRow[];\r\n\ttotal: number;\r\n}\r\n\r\n/** GET /wfm/forecast/intervals — one queue, one day. */\r\nexport interface ForecastIntervalsResponse {\r\n\tsettings: ForecastSettings;\r\n\tday: string;\r\n\tqueueId: number;\r\n\tqueueName: string;\r\n\t/** The day's roll-up for the panel header — the same shape as a day row. */\r\n\tsummary: ForecastDayRow | null;\r\n\tintervals: ForecastIntervalRow[];\r\n}\r\n\r\nexport interface ForecastParams {\r\n\t/** YYYY-MM-DD, both required. */\r\n\tstartDate: string;\r\n\tendDate: string;\r\n\tintervalMinutes: number;\r\n\tqueueIds?: number[];\r\n\tweekdays?: number[];\r\n\ttimeFrom?: string;\r\n\ttimeTo?: string;\r\n\t/** 1-based, as the API counts pages. */\r\n\tpage?: number;\r\n\tpageSize?: number;\r\n\t/** Matches the queue name. */\r\n\tsearch?: string;\r\n}\r\n\r\nexport interface ForecastIntervalsParams {\r\n\t/** YYYY-MM-DD. */\r\n\tday: string;\r\n\tqueueId: number;\r\n\tintervalMinutes: number;\r\n\ttimeFrom?: string;\r\n\ttimeTo?: string;\r\n}\r\n\r\n/** Server limits, mirrored so the filters can refuse before the round trip. */\r\nexport const FORECAST_LIMITS = {\r\n\tmaxDays: 31,\r\n\t/** The forecast runs out of usable history beyond four weeks ahead. */\r\n\tmaxDaysAhead: 28,\r\n\tmaxQueues: 100,\r\n} as const;\r\n\r\n/** Below this many weeks of history a slot's prediction is worth a caveat. */\r\nexport const LOW_HISTORY_WEEKS = 4;\r\n\r\n/** Where the forecast error stops being good enough to roster against. */\r\nexport const FORECAST_ERROR_BANDS = { good: 0.15, fair: 0.3 } as const;\r\n\r\n/** Explains the prediction in the tenant's own numbers rather than the defaults. */\r\nexport function forecastTooltip(settings: ForecastSettings | undefined): string {\r\n\tconst weeks = settings?.forecastWeeks ?? 8;\r\n\tconst target = Math.round((settings?.slaTarget ?? 0.8) * 100);\r\n\tconst seconds = settings?.slaSeconds ?? 20;\r\n\treturn `Expected calls, based on the same weekday and time over the last ${weeks} weeks, `\r\n\t\t+ `recent weeks counting more. Agents needed are sized to answer ${target}% of inbound calls `\r\n\t\t+ `within ${seconds} seconds, plus the campaign calls active campaigns will make while their leads last.`;\r\n}\r\n\r\n\r\n/* ═══════════════════════ \"WHY?\" ═══════════════════════\r\n * GET /wfm/forecast/explain — how one slot's numbers were arrived at.\r\n *\r\n * Every field is optional in practice: the panel shows what the server sends\r\n * and leaves out the rest, rather than printing a column of dashes.\r\n */\r\n\r\n/** Where the handle time behind the forecast came from. */\r\nexport type HandleSource = \"SLOT_HISTORY\" | \"QUEUE_28_DAYS\" | \"NONE\";\r\n\r\nexport const HANDLE_SOURCE_LABEL: Record<HandleSource, string> = {\r\n\tSLOT_HISTORY: \"this slot's own calls\",\r\n\tQUEUE_28_DAYS: \"queue average, last 28 days\",\r\n\tNONE: \"not known yet\",\r\n};\r\n\r\n/** One past week behind the prediction. */\r\nexport interface ExplainWeek {\r\n\t/** The week's own date — `day`, as the API names it. */\r\n\tday: string;\r\n\t/** Recent weeks count for more: 8 for last week down to 1. */\r\n\tweight: number;\r\n\tcalls: number | null;\r\n\t/** False for a day the queue took no calls — skipped rather than averaged in as zero. */\r\n\tused: boolean;\r\n\tnote?: string | null;\r\n}\r\n\r\nexport interface ExplainInbound {\r\n\tweeks: ExplainWeek[];\r\n\t/** The result of the formula — the same number the slot row carries. */\r\n\texpectedCalls?: number | null;\r\n\t/** The sum written out, e.g. \"(7×0 + 6×0 + 5×2 + 4×0) ÷ (7+6+5+4) = 0.45\". */\r\n\tformula?: string | null;\r\n\thandleSeconds?: number | null;\r\n\thandleSource?: HandleSource | null;\r\n\t/** How many answered calls that handle time rests on. */\r\n\thandleCalls?: number | null;\r\n}\r\n\r\nexport interface ExplainCampaign {\r\n\tname: string;\r\n\t/** What the campaign's own weekly pattern suggests. */\r\n\tpatternCalls?: number | null;\r\n\t/** Its share that reaches this queue. */\r\n\tcallsToQueue?: number | null;\r\n\tnote?: string | null;\r\n}\r\n\r\n/** One rung of the Erlang-C ladder: what N agents would achieve. */\r\nexport interface ExplainSizingOption {\r\n\tagents: number;\r\n\tserviceLevel: number | null;\r\n\toccupancy: number | null;\r\n}\r\n\r\nexport interface ExplainSizing {\r\n\toptions: ExplainSizingOption[];\r\n\t/** Agent-hours per hour of work the sizing was done for. */\r\n\tinboundErlangs?: number | null;\r\n\toutboundErlangs?: number | null;\r\n\t/** The rung chosen for inbound — the first that clears the target. */\r\n\tinboundAgents?: number | null;\r\n\t/** How the campaign work was added on top to reach the total. */\r\n\ttotalRule?: string | null;\r\n}\r\n\r\nexport interface ExplainActuals {\r\n\tcalls?: number | null;\r\n\t/** `campaignCalls` here, not `outboundCalls` as the slot rows call it. */\r\n\tcampaignCalls?: number | null;\r\n\tstaffedAgents?: number | null;\r\n\tstatus?: string | null;\r\n}\r\n\r\nexport interface ForecastExplainResponse {\r\n\t/** Complete on its own: the whole explanation in a paragraph. Plain text,\r\n\t *  for tooltips and anywhere formatting cannot go. */\r\n\tsummary?: string | null;\r\n\t/** The same explanation with light formatting — what the panel renders. */\r\n\tsummaryMarkdown?: string | null;\r\n\tinbound?: ExplainInbound | null;\r\n\tcampaigns?: ExplainCampaign[] | null;\r\n\tsizing?: ExplainSizing | null;\r\n\t/** Past slots only. */\r\n\tactuals?: ExplainActuals | null;\r\n\t/** The same slot the intervals call returns, so the header needs no second request. */\r\n\trow?: ForecastIntervalRow | null;\r\n}\r\n\r\nexport interface ForecastExplainParams {\r\n\t/** YYYY-MM-DD. */\r\n\tday: string;\r\n\tqueueId: number;\r\n\t/** The row's start time, HH:MM — it has to land on an interval boundary. */\r\n\tslot: string;\r\n\tintervalMinutes: number;\r\n}\r\n","import { Fragment, type ReactNode } from \"react\";\r\nimport { Box, Typography } from \"@mui/material\";\r\n\r\nimport { FONT } from \"./tableBits\";\r\n\r\n/**\r\n * The small slice of Markdown the WFM explanations use.\r\n *\r\n * Deliberately not a library: this renders server-written prose inside the\r\n * SDK, which is vendored into the host app, and a parser is a dependency the\r\n * whole bundle would carry for one paragraph. It covers paragraphs, line\r\n * breaks, bullet lists, **bold**, *italic* and `code` — swap in react-markdown\r\n * if the summaries ever grow tables or links.\r\n *\r\n * Safe by construction: every piece becomes a React node, so nothing is ever\r\n * handed to dangerouslySetInnerHTML.\r\n */\r\n\r\n/** `**bold**`, `*italic*`, `_italic_` and `` `code` ``, innermost first. */\r\nfunction renderInline(text: string, keyPrefix: string): ReactNode[] {\r\n\tconst out: ReactNode[] = [];\r\n\t// One pass, longest marker first so ** is never read as two single *.\r\n\tconst pattern = /(\\*\\*[^*]+\\*\\*|__[^_]+__|\\*[^*\\n]+\\*|_[^_\\n]+_|`[^`\\n]+`)/g;\r\n\tlet last = 0;\r\n\tlet match: RegExpExecArray | null;\r\n\tlet i = 0;\r\n\r\n\twhile ((match = pattern.exec(text)) !== null) {\r\n\t\tif (match.index > last) out.push(text.slice(last, match.index));\r\n\t\tconst token = match[0];\r\n\t\tconst key = `${keyPrefix}-${i++}`;\r\n\r\n\t\tif (token.startsWith(\"**\") || token.startsWith(\"__\")) {\r\n\t\t\tout.push(<Box key={key} component=\"span\" sx={{ fontWeight: 700, color: \"#1a1a1a\" }}>{token.slice(2, -2)}</Box>);\r\n\t\t} else if (token.startsWith(\"`\")) {\r\n\t\t\tout.push(\r\n\t\t\t\t<Box\r\n\t\t\t\t\tkey={key}\r\n\t\t\t\t\tcomponent=\"code\"\r\n\t\t\t\t\tsx={{\r\n\t\t\t\t\t\tfontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\r\n\t\t\t\t\t\tfontSize: \"0.92em\",\r\n\t\t\t\t\t\tbackgroundColor: \"#f1f4f7\",\r\n\t\t\t\t\t\tborderRadius: \"4px\",\r\n\t\t\t\t\t\tpx: 0.5,\r\n\t\t\t\t\t}}\r\n\t\t\t\t>\r\n\t\t\t\t\t{token.slice(1, -1)}\r\n\t\t\t\t</Box>,\r\n\t\t\t);\r\n\t\t} else {\r\n\t\t\tout.push(<Box key={key} component=\"span\" sx={{ fontStyle: \"italic\" }}>{token.slice(1, -1)}</Box>);\r\n\t\t}\r\n\t\tlast = match.index + token.length;\r\n\t}\r\n\r\n\tif (last < text.length) out.push(text.slice(last));\r\n\treturn out;\r\n}\r\n\r\nexport default function Markdown({ text, sx }: { text: string; sx?: object }) {\r\n\t// Blank lines separate paragraphs; a list is a run of lines starting with - or *.\r\n\tconst blocks = text.replace(/\\r\\n/g, \"\\n\").split(/\\n{2,}/);\r\n\r\n\treturn (\r\n\t\t<Box sx={{ display: \"flex\", flexDirection: \"column\", gap: 1, ...sx }}>\r\n\t\t\t{blocks.map((block, bi) => {\r\n\t\t\t\tconst lines = block.split(\"\\n\").filter((l) => l.trim().length > 0);\r\n\t\t\t\tconst isList = lines.length > 0 && lines.every((l) => /^\\s*[-*+]\\s+/.test(l));\r\n\r\n\t\t\t\tif (isList) {\r\n\t\t\t\t\treturn (\r\n\t\t\t\t\t\t<Box key={bi} component=\"ul\" sx={{ m: 0, pl: 2.5, display: \"flex\", flexDirection: \"column\", gap: 0.4 }}>\r\n\t\t\t\t\t\t\t{lines.map((line, li) => (\r\n\t\t\t\t\t\t\t\t<Box\r\n\t\t\t\t\t\t\t\t\tkey={li}\r\n\t\t\t\t\t\t\t\t\tcomponent=\"li\"\r\n\t\t\t\t\t\t\t\t\tsx={{ fontFamily: FONT, fontSize: \"0.8rem\", lineHeight: 1.65, color: \"#334155\" }}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t{renderInline(line.replace(/^\\s*[-*+]\\s+/, \"\"), `${bi}-${li}`)}\r\n\t\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t))}\r\n\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t);\r\n\t\t\t\t}\r\n\r\n\t\t\t\treturn (\r\n\t\t\t\t\t<Typography key={bi} sx={{ fontFamily: FONT, fontSize: \"0.8rem\", lineHeight: 1.65, color: \"#334155\" }}>\r\n\t\t\t\t\t\t{lines.map((line, li) => (\r\n\t\t\t\t\t\t\t<Fragment key={li}>\r\n\t\t\t\t\t\t\t\t{li > 0 && <br />}\r\n\t\t\t\t\t\t\t\t{renderInline(line, `${bi}-${li}`)}\r\n\t\t\t\t\t\t\t</Fragment>\r\n\t\t\t\t\t\t))}\r\n\t\t\t\t\t</Typography>\r\n\t\t\t\t);\r\n\t\t\t})}\r\n\t\t</Box>\r\n\t);\r\n}\r\n","import axiosInstance from \"../../services/axios\";\r\nimport { END_POINT } from \"../../services/endPoint\";\r\nimport type {\r\n\tForecastDaysPage,\r\n\tForecastExplainParams,\r\n\tForecastExplainResponse,\r\n\tForecastIntervalsParams,\r\n\tForecastIntervalsResponse,\r\n\tForecastParams,\r\n} from \"./forecastTypes\";\r\n\r\n/**\r\n * Query params are snake_case; `queue_ids` and `weekdays` repeat once per value.\r\n * `page` and `pageSize` are the standard paginated pair and keep their casing.\r\n */\r\nfunction buildQs(params: ForecastParams): string {\r\n\tconst qs = new URLSearchParams();\r\n\tqs.append(\"start_date\", params.startDate);\r\n\tqs.append(\"end_date\", params.endDate);\r\n\tqs.append(\"interval_minutes\", String(params.intervalMinutes));\r\n\tfor (const id of params.queueIds ?? []) qs.append(\"queue_ids\", String(id));\r\n\tfor (const day of params.weekdays ?? []) qs.append(\"weekdays\", String(day));\r\n\tif (params.timeFrom) qs.append(\"time_from\", params.timeFrom);\r\n\tif (params.timeTo) qs.append(\"time_to\", params.timeTo);\r\n\tif (params.page != null) qs.append(\"page\", String(params.page));\r\n\tif (params.pageSize != null) qs.append(\"pageSize\", String(params.pageSize));\r\n\tif (params.search) qs.append(\"search\", params.search);\r\n\treturn `?${qs.toString()}`;\r\n}\r\n\r\n/** One page of the forecast day table. */\r\nexport async function getForecastDays(params: ForecastParams): Promise<ForecastDaysPage> {\r\n\tconst res = await axiosInstance.get(`${END_POINT.WFM_FORECAST}${buildQs(params)}`);\r\n\tconst body = res.data as any;\r\n\tconst rows = Array.isArray(body?.data) ? body.data : [];\r\n\treturn {\r\n\t\tsettings: body?.settings,\r\n\t\tgeneratedAt: body?.generatedAt,\r\n\t\trows,\r\n\t\t// Falls back to the page itself, so a server that omits the count still pages.\r\n\t\ttotal: typeof body?.total === \"number\" ? body.total : rows.length,\r\n\t};\r\n}\r\n\r\n/** The forecast slots behind one day of one queue, fetched when its row is clicked. */\r\nexport async function getForecastIntervals(params: ForecastIntervalsParams): Promise<ForecastIntervalsResponse> {\r\n\tconst qs = new URLSearchParams();\r\n\tqs.append(\"day\", params.day);\r\n\tqs.append(\"queue_id\", String(params.queueId));\r\n\tqs.append(\"interval_minutes\", String(params.intervalMinutes));\r\n\tif (params.timeFrom) qs.append(\"time_from\", params.timeFrom);\r\n\tif (params.timeTo) qs.append(\"time_to\", params.timeTo);\r\n\tconst res = await axiosInstance.get(`${END_POINT.WFM_FORECAST_INTERVALS}?${qs.toString()}`);\r\n\tconst body = res.data as any;\r\n\treturn {\r\n\t\tsettings: body?.settings,\r\n\t\tday: body?.day,\r\n\t\tqueueId: body?.queueId,\r\n\t\tqueueName: body?.queueName,\r\n\t\tsummary: body?.summary ?? null,\r\n\t\tintervals: Array.isArray(body?.intervals) ? body.intervals : [],\r\n\t};\r\n}\r\n\r\n/** How one slot's numbers were arrived at. */\r\nexport async function getForecastExplain(params: ForecastExplainParams): Promise<ForecastExplainResponse> {\r\n\tconst qs = new URLSearchParams();\r\n\tqs.append(\"day\", params.day);\r\n\tqs.append(\"queue_id\", String(params.queueId));\r\n\tqs.append(\"slot\", params.slot);\r\n\tqs.append(\"interval_minutes\", String(params.intervalMinutes));\r\n\tconst res = await axiosInstance.get(`${END_POINT.WFM_FORECAST_EXPLAIN}?${qs.toString()}`);\r\n\treturn (res.data ?? {}) as ForecastExplainResponse;\r\n}\r\n","import type { WfmSettings } from \"./types\";\r\n\r\n/**\r\n * What each column means, in the tenant's own numbers. One place so the four\r\n * tables never drift apart in how they explain the same thing.\r\n */\r\n\r\nexport interface InfoValues extends Partial<WfmSettings> {\r\n\t/** Weeks of history behind each forecast slot. */\r\n\tforecastWeeks?: number;\r\n\t/** The live tab's arrival window. */\r\n\twindowMinutes?: number;\r\n}\r\n\r\n/** Falls back to the same defaults `forecastTooltip` uses until settings load. */\r\nfunction words(v: InfoValues) {\r\n\tconst target = `${Math.round((v.slaTarget ?? 0.8) * 100)}%`;\r\n\tconst seconds = v.slaSeconds ?? 20;\r\n\treturn {\r\n\t\ttarget,\r\n\t\tseconds,\r\n\t\tweeks: v.forecastWeeks ?? 8,\r\n\t\tslot: v.intervalMinutes ?? 30,\r\n\t\twindow: v.windowMinutes ?? 30,\r\n\t\t/** Dropped rather than guessed when the tenant's cap is not known yet. */\r\n\t\toccupancy: typeof v.maxOccupancy === \"number\"\r\n\t\t\t? ` without anyone more than ${Math.round(v.maxOccupancy * 100)}% busy`\r\n\t\t\t: \"\",\r\n\t};\r\n}\r\n\r\nexport function forecastDayInfo(v: InfoValues) {\r\n\tconst w = words(v);\r\n\treturn {\r\n\t\tqueue: \"The inbound queue this row is for.\",\r\n\t\tdate: \"The day being forecast. Past days also show what actually happened, so you can compare.\",\r\n\t\texpectedInbound: `Inbound calls expected on this queue for the whole day, based on the same weekday over the last ${w.weeks} weeks (recent weeks count more).`,\r\n\t\texpectedCampaign: \"Campaign (outbound) calls expected to reach this queue's agents that day. Only active or scheduled campaigns, inside their calling hours, while leads last.\",\r\n\t\tpeakAgents: \"The most agents needed at one time that day (inbound + campaign), and when. Roster at least this many for the busiest slot.\",\r\n\t\tagentHours: `Total agent time needed across the day: agents needed in each ${w.slot}-minute slot, added up. Use this to plan shifts.`,\r\n\t\tactualInbound: \"Inbound calls that really arrived in the slots that are already over. \\\"–\\\" until a slot has passed.\",\r\n\t\tactualCampaign: \"Campaign calls that really reached this queue's agents in the slots that are already over.\",\r\n\t\tforecastError: \"How far the inbound forecast was from what happened in the finished slots (lower is better). \\\"–\\\" when no calls actually arrived.\",\r\n\t\tstaffedHours: \"Agent time actually logged in and not on break during the finished slots. An agent in several queues counts as a share in each.\",\r\n\t\tshortIntervals: \"Finished slots where fewer agents were staffed than the forecast needed while inbound calls were expected.\",\r\n\t};\r\n}\r\n\r\nexport function forecastSlotInfo(v: InfoValues) {\r\n\tconst w = words(v);\r\n\treturn {\r\n\t\ttime: `The ${w.slot}-minute slot.`,\r\n\t\texpectedInbound: `Inbound calls expected in this slot: the same weekday and time over the last ${w.weeks} weeks, recent weeks counting more.`,\r\n\t\thandleTime: \"Expected time per call (talk + wrap-up), from this slot's past calls, or the queue's 28-day average when the slot has fewer than 10 calls.\",\r\n\t\tinboundNeeded: `Agents needed for inbound calls alone, to answer ${w.target} within ${w.seconds} seconds${w.occupancy}.`,\r\n\t\thistory: \"How many past weeks this slot's forecast is based on. \\\"Low history\\\" (under 4 weeks) means little data: treat the number with caution. Days the queue was closed are skipped.\",\r\n\t\texpectedCampaign: \"Campaign calls expected to reach this queue's agents in this slot. Past slots show what history predicted, even if the campaign is now paused, so it can be compared with what actually happened.\",\r\n\t\ttotalNeeded: \"Agents to roster for this slot: inbound plus campaign work. Campaign calls first use the spare time inbound agents have between calls, so the total is often less than the two added together.\",\r\n\t\tactualInbound: \"Inbound calls that really arrived (finished slots only).\",\r\n\t\tactualCampaign: \"Campaign calls that really reached these agents (finished slots only).\",\r\n\t\tstaffed: \"Average agents logged in and not on break during the slot (finished slots only). 0.5 = one agent for half the slot; an agent in several queues counts as a share in each.\",\r\n\t\tstatus: \"Finished slots only. Short = fewer staffed than the forecast needed while inbound calls were expected; OK = enough; Surplus = at least 20% more than needed; No data = calls expected but no handle time known yet.\",\r\n\t};\r\n}\r\n\r\nexport function intervalDayInfo(v: InfoValues) {\r\n\tconst w = words(v);\r\n\treturn {\r\n\t\tdate: \"The day these figures cover.\",\r\n\t\tqueue: \"The inbound queue. Calls are counted only in the queue they belong to.\",\r\n\t\tcalls: \"Inbound calls that entered this queue and finished (answered, abandoned, timed out or transferred).\",\r\n\t\tsla: `Share of inbound calls answered within ${w.seconds} seconds. Target: ${w.target}.`,\r\n\t\tabandoned: \"Inbound callers who hung up before an agent answered.\",\r\n\t\tcampaignCalls: \"Campaign (outbound) calls that reached this queue's agents.\",\r\n\t\tpeakNeeded: \"The most agents needed at one time that day (inbound + campaign), and when.\",\r\n\t\tavgStaffed: \"Average agents logged in and not on break across the day's active slots. An agent in several queues counts as a share in each.\",\r\n\t\tshortIntervals: \"Slots where fewer agents were staffed than needed while inbound calls were coming in.\",\r\n\t};\r\n}\r\n\r\nexport function intervalSlotInfo(v: InfoValues) {\r\n\tconst w = words(v);\r\n\treturn {\r\n\t\ttime: `The ${w.slot}-minute slot.`,\r\n\t\tcalls: \"Inbound calls that entered the queue in this slot.\",\r\n\t\tsla: `Share answered within ${w.seconds} seconds. \"–\" when no calls arrived.`,\r\n\t\tavgHandle: \"Average talk + wrap-up per answered call. Falls back to the queue's 28-day average when the slot has fewer than 10 answered calls.\",\r\n\t\tinboundNeeded: `Agents that inbound calls alone needed, to answer ${w.target} within ${w.seconds} seconds.`,\r\n\t\tcampaignCalls: \"Campaign calls that reached this queue's agents. A campaign shared by several of an agent's queues is split between them.\",\r\n\t\ttotalNeeded: \"Agents needed for inbound and campaign work together.\",\r\n\t\tstaffed: \"Average agents logged in and not on break during the slot (0.5 = one agent for half the slot).\",\r\n\t\tgap: \"Staffed minus total needed. Negative (red) = short.\",\r\n\t\tstatus: \"Short = fewer staffed than needed while inbound calls came in; OK = enough; Surplus = at least 20% spare; No data = calls came but no handle time is known. A slot with only campaign calls is never Short.\",\r\n\t};\r\n}\r\n\r\nexport function liveInfo(v: InfoValues) {\r\n\tconst w = words(v);\r\n\treturn {\r\n\t\tqueue: \"The inbound queue.\",\r\n\t\tstatus: \"Right now: Short = fewer agents on shift than needed; OK = enough; Surplus = spare agents (free for campaign calls); No data = calls but no handle time known yet.\",\r\n\t\tinboundPerHour: `Inbound calls per hour, based on the last ${w.window} minutes.`,\r\n\t\tcampaignPerHour: `Campaign calls per hour reaching this queue's agents, based on the last ${w.window} minutes.`,\r\n\t\twaiting: \"Callers waiting in the queue right now.\",\r\n\t\tinboundNeeded: `Agents needed now for inbound calls alone, to answer ${w.target} within ${w.seconds} seconds.`,\r\n\t\ttotalNeeded: \"Agents needed now for inbound and campaign work together.\",\r\n\t\tstaffed: \"Agents of this queue logged in and not on break right now (only those allowed to take inbound calls). An agent in several queues counts as a share in each, e.g. 1.3.\",\r\n\t\tidle: \"Agents of this queue who are free right now.\",\r\n\t\tonBreak: \"Agents of this queue on break right now.\",\r\n\t\texpectedSla: `Share of inbound calls expected to be answered within ${w.seconds} seconds with the agents on shift now, after allowing for those busy on campaign calls.`,\r\n\t};\r\n}\r\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\r\nimport {\r\n  Alert,\r\n  Box,\r\n  CircularProgress,\r\n  IconButton,\r\n  Typography,\r\n} from \"@mui/material\";\r\nimport ArrowBackIcon from \"@mui/icons-material/ArrowBack\";\r\nimport RefreshIcon from \"@mui/icons-material/Refresh\";\r\nimport type { MRT_ColumnDef } from \"material-react-table\";\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport { LocalizationProvider } from \"@mui/x-date-pickers/LocalizationProvider\";\r\nimport { AdapterDayjs } from \"@mui/x-date-pickers/AdapterDayjs\";\r\n\r\nimport AppButton from \"../common/components/AppButton\";\r\nimport AppTable from \"../common/components/AppTable\";\r\nimport SearchBar from \"../common/components/SearchBar\";\r\nimport ReportFilterBar, {\r\n  type QuickRange,\r\n} from \"../common/components/ReportFilterBar\";\r\nimport StaffingChart from \"./StaffingChart\";\r\nimport SlotDetailDrawer from \"./SlotDetailDrawer\";\r\nimport {\r\n  FILTER_HEIGHT,\r\n  IntervalSelect,\r\n  QueueMultiSelect,\r\n  WeekdayAndTimeFilters,\r\n} from \"./filterControls\";\r\nimport {\r\n  getStaffingDays,\r\n  getStaffingIntervals,\r\n  staffingErrorMessage,\r\n} from \"./useStaffing\";\r\nimport {\r\n  WFM_LIMITS,\r\n  type DaySummaryRow,\r\n  type IntervalRow,\r\n  type QueueChoice,\r\n  type StaffingDaysPage,\r\n  type StaffingIntervalsResponse,\r\n} from \"./types\";\r\nimport {\r\n  COLORS,\r\n  DASH,\r\n  FONT,\r\n  StatusChip,\r\n  cell,\r\n  cellBaseSx,\r\n  count,\r\n  decimal,\r\n  duration,\r\n  pct1,\r\n  signed,\r\n  tabularSx,\r\n  withInfo,\r\n} from \"./tableBits\";\r\nimport { intervalDayInfo, intervalSlotInfo } from \"./columnInfo\";\r\n\r\n/** The same ladder the rest of the app's tables offer. */\r\nconst PAGE_SIZE_OPTIONS = [10, 15, 25, 50, 100];\r\nconst DEFAULT_PAGE_SIZE = 10;\r\n\r\ninterface IntervalTabProps {\r\n  enabled: boolean;\r\n}\r\n\r\nexport default function IntervalTab({ enabled }: IntervalTabProps) {\r\n  // The guide's default: the last seven days, well inside the 31-day cap.\r\n  const [startDate, setStartDate] = useState<Dayjs | null>(\r\n    dayjs().subtract(6, \"day\").startOf(\"day\"),\r\n  );\r\n  const [endDate, setEndDate] = useState<Dayjs | null>(dayjs());\r\n  const [quickRange, setQuickRange] = useState(\"\");\r\n  const [intervalMinutes, setIntervalMinutes] = useState<number>(30);\r\n  const [selectedQueues, setSelectedQueues] = useState<number[]>([]);\r\n  const [weekdays, setWeekdays] = useState<number[]>([]);\r\n  const [timeFrom, setTimeFrom] = useState(\"\");\r\n  const [timeTo, setTimeTo] = useState(\"\");\r\n  /** What is typed, and what has been sent — typing shouldn't fire a request a letter. */\r\n  const [searchInput, setSearchInput] = useState(\"\");\r\n  const [search, setSearch] = useState(\"\");\r\n\r\n  const [data, setData] = useState<StaffingDaysPage | null>(null);\r\n  const [loading, setLoading] = useState(false);\r\n  const [error, setError] = useState(\"\");\r\n  const [daysPagination, setDaysPagination] = useState({\r\n    pageIndex: 0,\r\n    pageSize: DEFAULT_PAGE_SIZE,\r\n  });\r\n\r\n  /* The slots behind the clicked day, fetched on their own. */\r\n  const [slotsData, setSlotsData] = useState<StaffingIntervalsResponse | null>(\r\n    null,\r\n  );\r\n  const [slotsLoading, setSlotsLoading] = useState(false);\r\n  const [slotsError, setSlotsError] = useState(\"\");\r\n  /** The slot whose drawer is open. */\r\n  const [slotRow, setSlotRow] = useState<IntervalRow | null>(null);\r\n\r\n  /** The day row clicked in the summary — the intervals below narrow to it. */\r\n  const [focusedDay, setFocusedDay] = useState<{\r\n    day: string;\r\n    queueId: number;\r\n    queueName: string;\r\n  } | null>(null);\r\n\r\n  /*\r\n   * The queue filter is built from the rows themselves: this feature has no\r\n   * queue list endpoint, and the one on the queues master needs `queues:read`,\r\n   * which a WFM login may not hold. Kept from the last unfiltered load, so\r\n   * narrowing to one queue doesn't collapse the list to that queue.\r\n   */\r\n  const [queueChoices, setQueueChoices] = useState<QueueChoice[]>([]);\r\n  const hasQueueFilter = selectedQueues.length > 0;\r\n\r\n  /** Null when the filters are usable, otherwise why they aren't. */\r\n  const filterError = useMemo(() => {\r\n    if (!startDate || !endDate) return \"Pick a start and end date\";\r\n    if (startDate.isAfter(endDate)) return \"Start date must be before end date\";\r\n    if (endDate.diff(startDate, \"day\") + 1 > WFM_LIMITS.maxDays) {\r\n      return `The range cannot be longer than ${WFM_LIMITS.maxDays} days`;\r\n    }\r\n    if (selectedQueues.length > WFM_LIMITS.maxQueues) {\r\n      return `Pick at most ${WFM_LIMITS.maxQueues} queues`;\r\n    }\r\n    if (timeFrom && timeTo && timeFrom >= timeTo)\r\n      return \"Time from must be before time to\";\r\n    return null;\r\n  }, [startDate, endDate, selectedQueues.length, timeFrom, timeTo]);\r\n\r\n  const buildParams = useCallback(\r\n    () => ({\r\n      startDate: startDate!.format(\"YYYY-MM-DD\"),\r\n      endDate: endDate!.format(\"YYYY-MM-DD\"),\r\n      intervalMinutes,\r\n      queueIds: selectedQueues.length > 0 ? selectedQueues : undefined,\r\n      weekdays: weekdays.length > 0 ? [...weekdays].sort() : undefined,\r\n      timeFrom: timeFrom || undefined,\r\n      timeTo: timeTo || undefined,\r\n      // The API counts pages from 1; MRT counts from 0.\r\n      page: daysPagination.pageIndex + 1,\r\n      pageSize: daysPagination.pageSize,\r\n      search: search.trim() || undefined,\r\n    }),\r\n    [\r\n      startDate,\r\n      endDate,\r\n      intervalMinutes,\r\n      selectedQueues,\r\n      weekdays,\r\n      timeFrom,\r\n      timeTo,\r\n      daysPagination,\r\n      search,\r\n    ],\r\n  );\r\n\r\n  /** Drops a reply that a later request has already superseded. */\r\n  const requestId = useRef(0);\r\n\r\n  const fetchStaffing = useCallback(async () => {\r\n    if (!enabled) return;\r\n    if (filterError) {\r\n      setError(filterError);\r\n      setData(null);\r\n      return;\r\n    }\r\n    const id = ++requestId.current;\r\n    setLoading(true);\r\n    setError(\"\");\r\n    try {\r\n      const res = await getStaffingDays(buildParams());\r\n      if (requestId.current !== id) return;\r\n      setData(res);\r\n      // The focused day belongs to the previous result set.\r\n      setFocusedDay(null);\r\n      setSlotsData(null);\r\n    } catch (err) {\r\n      if (requestId.current !== id) return;\r\n      setData(null);\r\n      setError(\r\n        staffingErrorMessage(err, \"Could not load staffing by interval\"),\r\n      );\r\n    } finally {\r\n      if (requestId.current === id) setLoading(false);\r\n    }\r\n  }, [enabled, filterError, buildParams]);\r\n\r\n  useEffect(() => {\r\n    fetchStaffing();\r\n  }, [fetchStaffing]);\r\n\r\n  /*\r\n   * Merged rather than replaced: one page of a paginated table holds only the\r\n   * queues on that page, so replacing would shrink the filter as you page\r\n   * through. Only grown while no queue filter is applied.\r\n   */\r\n  useEffect(() => {\r\n    if (hasQueueFilter || !data) return;\r\n    setQueueChoices((previous) => {\r\n      const seen = new Map(previous.map((q) => [q.queueId, q.queueName]));\r\n      const before = seen.size;\r\n      for (const row of data.rows) seen.set(row.queueId, row.queueName);\r\n      if (seen.size === before) return previous;\r\n      return [...seen]\r\n        .map(([queueId, queueName]) => ({ queueId, queueName }))\r\n        .sort((a, b) => a.queueName.localeCompare(b.queueName));\r\n    });\r\n  }, [data, hasQueueFilter]);\r\n\r\n  /*\r\n   * Typing waits for a pause before it becomes a request — the search runs on\r\n   * the server, and a letter a request would mean six round trips for \"telugu\".\r\n   */\r\n  useEffect(() => {\r\n    const timer = window.setTimeout(() => {\r\n      setSearch(searchInput);\r\n      setDaysPagination((p) =>\r\n        p.pageIndex === 0 ? p : { ...p, pageIndex: 0 },\r\n      );\r\n    }, 400);\r\n    return () => window.clearTimeout(timer);\r\n  }, [searchInput]);\r\n\r\n  /* Any filter change goes back to page 1, in the same update as the filter. */\r\n  const resetPage = () =>\r\n    setDaysPagination((p) => (p.pageIndex === 0 ? p : { ...p, pageIndex: 0 }));\r\n  const handleStartChange = (v: Dayjs | null) => {\r\n    setStartDate(v);\r\n    resetPage();\r\n  };\r\n  const handleEndChange = (v: Dayjs | null) => {\r\n    setEndDate(v);\r\n    resetPage();\r\n  };\r\n\r\n  /**\r\n   * The slots behind one day of one queue. Its own call now: the day table is\r\n   * paged, so the slots for every row would be far more than anyone asked to\r\n   * see. At most 96 rows come back, which is why this table has no paging.\r\n   */\r\n  const slotsRequestId = useRef(0);\r\n\r\n  const openDay = useCallback(\r\n    async (row: DaySummaryRow) => {\r\n      const id = ++slotsRequestId.current;\r\n      setFocusedDay({\r\n        day: row.day,\r\n        queueId: row.queueId,\r\n        queueName: row.queueName,\r\n      });\r\n      setSlotsLoading(true);\r\n      setSlotsError(\"\");\r\n      setSlotsData(null);\r\n      try {\r\n        const res = await getStaffingIntervals({\r\n          day: row.day,\r\n          queueId: row.queueId,\r\n          intervalMinutes,\r\n          timeFrom: timeFrom || undefined,\r\n          timeTo: timeTo || undefined,\r\n        });\r\n        if (slotsRequestId.current !== id) return;\r\n        setSlotsData(res);\r\n      } catch (err) {\r\n        if (slotsRequestId.current !== id) return;\r\n        setSlotsError(\r\n          staffingErrorMessage(err, \"Could not load this day's intervals\"),\r\n        );\r\n      } finally {\r\n        if (slotsRequestId.current === id) setSlotsLoading(false);\r\n      }\r\n    },\r\n    [intervalMinutes, timeFrom, timeTo],\r\n  );\r\n\r\n  const closeDay = useCallback(() => {\r\n    // Bumped so a reply still in flight is dropped rather than reopening the panel.\r\n    slotsRequestId.current += 1;\r\n    setFocusedDay(null);\r\n    setSlotsData(null);\r\n    setSlotsError(\"\");\r\n    setSlotsLoading(false);\r\n  }, []);\r\n\r\n  /* ── Day summary columns ── */\r\n\r\n  const slaTarget = data?.settings?.slaTarget;\r\n\r\n  /** Service level is a floor: at or above target is green, within 10 points amber. */\r\n  const slaColour = useCallback(\r\n    (value: number | null | undefined) => {\r\n      if (typeof value !== \"number\" || typeof slaTarget !== \"number\")\r\n        return \"#4a5568\";\r\n      if (value >= slaTarget) return COLORS.green;\r\n      return value >= slaTarget - 0.1 ? COLORS.amber : COLORS.red;\r\n    },\r\n    [slaTarget],\r\n  );\r\n\r\n  const dayInfo = useMemo(\r\n    () => intervalDayInfo({ ...data?.settings }),\r\n    [data?.settings],\r\n  );\r\n\r\n  const dayColumns = useMemo<MRT_ColumnDef<DaySummaryRow>[]>(\r\n    () => [\r\n      {\r\n        id: \"day\",\r\n        ...withInfo(\"Date\", dayInfo.date),\r\n        accessorFn: (r) => r.day,\r\n        Cell: ({ row }) =>\r\n          row.original.day\r\n            ? dayjs(row.original.day).format(\"ddd D MMM YYYY\")\r\n            : DASH,\r\n        ...cell({ fontSize: \"0.73rem\", color: \"#4a5568\" }),\r\n        muiTableBodyCellProps: {\r\n          align: \"left\" as const,\r\n          sx: { ...cellBaseSx, fontSize: \"0.73rem\", color: \"#4a5568\" },\r\n        },\r\n      },\r\n      {\r\n        id: \"queueName\",\r\n        ...withInfo(\"Queue\", dayInfo.queue),\r\n        accessorFn: (r) => r.queueName,\r\n        Cell: ({ row }) => row.original.queueName || DASH,\r\n        ...cell({ fontSize: \"0.73rem\", fontWeight: 600, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"offered\",\r\n        ...withInfo(\"Calls\", dayInfo.calls),\r\n        accessorFn: (r) => r.offered,\r\n        Cell: ({ row }) => count(row.original.offered),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.navy }),\r\n      },\r\n      {\r\n        id: \"serviceLevel\",\r\n        ...withInfo(\"SLA %\", dayInfo.sla),\r\n        accessorFn: (r) => r.serviceLevel,\r\n        Cell: ({ row }) => (\r\n          <Box\r\n            component=\"span\"\r\n            sx={{\r\n              color: slaColour(row.original.serviceLevel),\r\n              fontWeight: 700,\r\n            }}>\r\n            {pct1(row.original.serviceLevel)}\r\n          </Box>\r\n        ),\r\n        ...cell(tabularSx),\r\n      },\r\n      {\r\n        id: \"abandoned\",\r\n        ...withInfo(\"Abandoned\", dayInfo.abandoned),\r\n        accessorFn: (r) => r.abandoned,\r\n        Cell: ({ row }) => count(row.original.abandoned),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.red }),\r\n      },\r\n      {\r\n        id: \"outboundCalls\",\r\n        ...withInfo(\"Campaign calls\", dayInfo.campaignCalls),\r\n        accessorFn: (r) => r.outboundCalls,\r\n        // Whole number here: a day's worth of halves adds up to something real.\r\n        Cell: ({ row }) =>\r\n          count(\r\n            typeof row.original.outboundCalls === \"number\"\r\n              ? Math.round(row.original.outboundCalls)\r\n              : null,\r\n          ),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n      },\r\n      {\r\n        id: \"peakRequiredAgents\",\r\n        ...withInfo(\"Peak needed\", dayInfo.peakNeeded),\r\n        accessorFn: (r) => r.peakRequiredAgents,\r\n        // \"3 at 15:00\" — the worst slot of the day and when it was.\r\n        Cell: ({ row }) => {\r\n          const peak = row.original.peakRequiredAgents;\r\n          if (typeof peak !== \"number\") return DASH;\r\n          const at = row.original.peakIntervalStart\r\n            ? dayjs(row.original.peakIntervalStart)\r\n            : null;\r\n          return at && at.isValid()\r\n            ? `${peak} at ${at.format(\"HH:mm\")}`\r\n            : String(peak);\r\n        },\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.navy }),\r\n      },\r\n      {\r\n        id: \"avgStaffedAgents\",\r\n        ...withInfo(\"Avg staffed\", dayInfo.avgStaffed),\r\n        accessorFn: (r) => r.avgStaffedAgents,\r\n        Cell: ({ row }) => decimal(row.original.avgStaffedAgents),\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.green }),\r\n      },\r\n      {\r\n        id: \"intervalsUnderstaffed\",\r\n        ...withInfo(\"Short intervals\", dayInfo.shortIntervals),\r\n        accessorFn: (r) => r.intervalsUnderstaffed,\r\n        Cell: ({ row }) => {\r\n          const short = row.original.intervalsUnderstaffed ?? 0;\r\n          return (\r\n            <Box\r\n              component=\"span\"\r\n              sx={{\r\n                color: short > 0 ? COLORS.red : \"#4a5568\",\r\n                fontWeight: short > 0 ? 700 : 600,\r\n              }}>\r\n              {`${short} of ${row.original.intervals ?? 0}`}\r\n            </Box>\r\n          );\r\n        },\r\n        ...cell(tabularSx),\r\n      },\r\n    ],\r\n    [slaColour, dayInfo],\r\n  );\r\n\r\n  /* ── Interval columns ── */\r\n\r\n  // The slot's length comes from the response, so the end time matches the data\r\n  // rather than a filter the user may have changed since.\r\n  const slotMinutes =\r\n    slotsData?.settings?.intervalMinutes ??\r\n    data?.settings?.intervalMinutes ??\r\n    intervalMinutes;\r\n\r\n  const slotInfo = useMemo(\r\n    () =>\r\n      intervalSlotInfo({\r\n        ...(slotsData?.settings ?? data?.settings),\r\n        intervalMinutes: slotMinutes,\r\n      }),\r\n    [slotsData?.settings, data?.settings, slotMinutes],\r\n  );\r\n\r\n  const columns = useMemo<MRT_ColumnDef<IntervalRow>[]>(\r\n    () => [\r\n      {\r\n        id: \"intervalStart\",\r\n        ...withInfo(\"Time\", slotInfo.time),\r\n        accessorFn: (r) => r.intervalStart,\r\n        // Local wall-clock with no offset — parsed as local, never converted.\r\n        Cell: ({ row }) => {\r\n          const start = row.original.intervalStart\r\n            ? dayjs(row.original.intervalStart)\r\n            : null;\r\n          if (!start || !start.isValid()) return DASH;\r\n          return `${start.format(\"ddd D MMM, HH:mm\")}–${start.add(slotMinutes, \"minute\").format(\"HH:mm\")}`;\r\n        },\r\n        ...cell({ fontSize: \"0.73rem\", color: \"#4a5568\" }),\r\n        muiTableBodyCellProps: {\r\n          align: \"left\" as const,\r\n          sx: { ...cellBaseSx, fontSize: \"0.73rem\", color: \"#4a5568\" },\r\n        },\r\n      },\r\n      {\r\n        id: \"queueName\",\r\n        header: \"Queue\",\r\n        accessorFn: (r) => r.queueName,\r\n        Cell: ({ row }) => row.original.queueName || DASH,\r\n        ...cell({ fontSize: \"0.73rem\", fontWeight: 600, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"offered\",\r\n        ...withInfo(\"Calls\", slotInfo.calls),\r\n        accessorFn: (r) => r.offered,\r\n        Cell: ({ row }) => count(row.original.offered),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.navy }),\r\n      },\r\n      {\r\n        id: \"serviceLevel\",\r\n        ...withInfo(\"SLA %\", slotInfo.sla),\r\n        accessorFn: (r) => r.serviceLevel,\r\n        Cell: ({ row }) => pct1(row.original.serviceLevel),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.blue }),\r\n      },\r\n      {\r\n        id: \"avgHandleSeconds\",\r\n        ...withInfo(\"Avg handle\", slotInfo.avgHandle),\r\n        accessorFn: (r) => r.avgHandleSeconds,\r\n        Cell: ({ row }) => duration(row.original.avgHandleSeconds),\r\n        ...cell({ ...tabularSx, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"requiredAgents\",\r\n        ...withInfo(\"Inbound needed\", slotInfo.inboundNeeded),\r\n        accessorFn: (r) => r.requiredAgents,\r\n        Cell: ({ row }) => count(row.original.requiredAgents),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"outboundCalls\",\r\n        ...withInfo(\"Campaign calls\", slotInfo.campaignCalls),\r\n        accessorFn: (r) => r.outboundCalls,\r\n        // Fractional on purpose: a campaign on two queues counts half on each.\r\n        Cell: ({ row }) => decimal(row.original.outboundCalls),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n      },\r\n      {\r\n        id: \"requiredTotalAgents\",\r\n        ...withInfo(\"Total needed\", slotInfo.totalNeeded),\r\n        accessorFn: (r) => r.requiredTotalAgents,\r\n        Cell: ({ row }) => count(row.original.requiredTotalAgents),\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.navy }),\r\n      },\r\n      {\r\n        id: \"staffedAgents\",\r\n        ...withInfo(\"Staffed\", slotInfo.staffed),\r\n        accessorFn: (r) => r.staffedAgents,\r\n        // An average over the slot: 0.4 is one agent present for 40% of it.\r\n        Cell: ({ row }) => decimal(row.original.staffedAgents),\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.green }),\r\n      },\r\n      {\r\n        id: \"staffingGap\",\r\n        ...withInfo(\"Gap\", slotInfo.gap),\r\n        accessorFn: (r) => r.staffingGap,\r\n        Cell: ({ row }) => {\r\n          const gap = row.original.staffingGap;\r\n          return (\r\n            <Box\r\n              component=\"span\"\r\n              sx={{\r\n                color:\r\n                  typeof gap === \"number\" && gap < 0 ? COLORS.red : \"#4a5568\",\r\n                fontWeight: 700,\r\n              }}>\r\n              {signed(gap)}\r\n            </Box>\r\n          );\r\n        },\r\n        ...cell(tabularSx),\r\n      },\r\n      {\r\n        id: \"status\",\r\n        ...withInfo(\"Status\", slotInfo.status),\r\n        accessorFn: (r) => r.status,\r\n        Cell: ({ row }) => <StatusChip status={row.original.status} />,\r\n        ...cell({}),\r\n      },\r\n    ],\r\n    [slotMinutes, slotInfo],\r\n  );\r\n\r\n  /** Whatever the opened day returned — one queue, one day, so always chartable. */\r\n  const rows: IntervalRow[] = slotsData?.intervals ?? [];\r\n  const chartQueue = slotsData?.queueName ?? focusedDay?.queueName ?? null;\r\n\r\n  /*\r\n   * A day opens as its own view rather than a panel under the table. Appearing\r\n   * below the fold reads as \"nothing happened\", and the filters and the day\r\n   * table are no use while reading one day's slots.\r\n   */\r\n  if (focusedDay) {\r\n    const summary = slotsData?.summary;\r\n    return (\r\n      <Box sx={{ px: 1, pb: 2 }}>\r\n        <Box\r\n          sx={{\r\n            display: \"flex\",\r\n            alignItems: \"center\",\r\n            gap: 1.25,\r\n            flexWrap: \"wrap\",\r\n            mb: 1.5,\r\n          }}>\r\n          <IconButton\r\n            onClick={closeDay}\r\n            size=\"small\"\r\n            sx={{\r\n              width: 30,\r\n              height: 30,\r\n              flexShrink: 0,\r\n              borderRadius: \"6px\",\r\n              backgroundColor: \"action.hover\",\r\n              color: \"text.secondary\",\r\n              \"&:hover\": {\r\n                backgroundColor: \"action.selected\",\r\n                color: \"text.primary\",\r\n              },\r\n            }}>\r\n            <ArrowBackIcon sx={{ fontSize: 15 }} />\r\n          </IconButton>\r\n          <Box sx={{ minWidth: 0 }}>\r\n            <Typography\r\n              sx={{\r\n                fontFamily: FONT,\r\n                fontSize: \"1rem\",\r\n                fontWeight: 700,\r\n                color: COLORS.navy,\r\n                lineHeight: 1.3,\r\n              }}>\r\n              {slotsData?.queueName ?? focusedDay.queueName} ·{\" \"}\r\n              {dayjs(focusedDay.day).format(\"ddd D MMM YYYY\")}\r\n            </Typography>\r\n            {/* The day's own roll-up, from the response rather than the row,\r\n\t\t\t\t\t\t    so the header agrees with the slots under it. */}\r\n            {summary && (\r\n              <Typography\r\n                sx={{\r\n                  fontFamily: FONT,\r\n                  fontSize: \"0.74rem\",\r\n                  color: COLORS.grey,\r\n                  mt: 0.2,\r\n                }}>\r\n                {count(summary.offered)} calls · SLA{\" \"}\r\n                {pct1(summary.serviceLevel)}\r\n                {typeof summary.peakRequiredAgents === \"number\" &&\r\n                  ` · peak ${summary.peakRequiredAgents}` +\r\n                    (summary.peakIntervalStart\r\n                      ? ` at ${dayjs(summary.peakIntervalStart).format(\"HH:mm\")}`\r\n                      : \"\")}\r\n                {` · ${summary.intervalsUnderstaffed ?? 0} of ${summary.intervals ?? 0} intervals short`}\r\n              </Typography>\r\n            )}\r\n          </Box>\r\n          {slotsLoading && (\r\n            <CircularProgress size={16} sx={{ color: COLORS.blue }} />\r\n          )}\r\n          <Box sx={{ flex: 1 }} />\r\n          <AppButton\r\n            variant=\"outlined\"\r\n            onClick={closeDay}\r\n            sx={{ height: FILTER_HEIGHT }}>\r\n            Back to days\r\n          </AppButton>\r\n        </Box>\r\n\r\n        {slotsError && (\r\n          <Alert\r\n            severity=\"error\"\r\n            sx={{ mb: 1, fontFamily: FONT, fontSize: \"0.78rem\" }}\r\n            onClose={() => setSlotsError(\"\")}>\r\n            {slotsError}\r\n          </Alert>\r\n        )}\r\n\r\n        {chartQueue && rows.length > 0 && (\r\n          <StaffingChart\r\n            rows={rows}\r\n            intervalMinutes={slotMinutes}\r\n            queueName={chartQueue}\r\n          />\r\n        )}\r\n\r\n        <AppTable\r\n          columns={columns}\r\n          data={rows}\r\n          layoutMode=\"semantic\"\r\n          enableTopToolbar={false}\r\n          // At most 96 slots come back, so there is nothing to page.\r\n          enableBottomToolbar={false}\r\n          enablePagination={false}\r\n          enableColumnActions={false}\r\n          enableColumnFilters={false}\r\n          enableColumnDragging={false}\r\n          enableHiding={false}\r\n          enableDensityToggle={false}\r\n          enableFullScreenToggle={false}\r\n          enableStickyHeader\r\n          state={{ isLoading: slotsLoading }}\r\n          localization={\r\n            {\r\n              noRecordsToDisplay: \"No calls or staffed agents in this day.\",\r\n            } as any\r\n          }\r\n          muiCircularProgressProps={{ sx: { color: COLORS.blue }, size: 28 }}\r\n          muiTablePaperProps={{\r\n            elevation: 0,\r\n            sx: {\r\n              borderRadius: \"8px\",\r\n              border: \"1px solid #e0e7ef\",\r\n              overflow: \"hidden\",\r\n              boxShadow: \"none\",\r\n            },\r\n          }}\r\n          muiTableProps={{\r\n            sx: {\r\n              fontFamily: FONT,\r\n              border: \"none\",\r\n              borderRadius: 0,\r\n              boxShadow: \"none\",\r\n            },\r\n          }}\r\n          muiTableContainerProps={{ sx: { maxHeight: \"calc(100vh - 320px)\" } }}\r\n          muiTableHeadProps={{ sx: { boxShadow: \"none\" } }}\r\n          muiTableHeadRowProps={{ sx: { boxShadow: \"none\" } }}\r\n          // A slot holds more than its columns show; the rest is in the drawer.\r\n          muiTableBodyRowProps={({ row }) => ({\r\n            onClick: () => setSlotRow(row.original),\r\n            sx: {\r\n              cursor: \"pointer\",\r\n              backgroundColor:\r\n                row.original.status === \"UNDERSTAFFED\" ? \"#fdf4f4\" : \"#fff\",\r\n              \"&:hover\": {\r\n                backgroundColor:\r\n                  row.original.status === \"UNDERSTAFFED\"\r\n                    ? \"#fbe9e9\"\r\n                    : \"#f0f7f8\",\r\n              },\r\n            },\r\n          })}\r\n        />\r\n\r\n        <SlotDetailDrawer\r\n          selection={slotRow ? { kind: \"staffing\", row: slotRow } : null}\r\n          slotMinutes={slotMinutes}\r\n          onClose={() => setSlotRow(null)}\r\n        />\r\n      </Box>\r\n    );\r\n  }\r\n\r\n  return (\r\n    <LocalizationProvider dateAdapter={AdapterDayjs}>\r\n      {/* No bottom padding: the day table ends in its own pagination bar, and\r\n          the gap under it only pushed the page into a scroll. */}\r\n      <Box sx={{ px: 1 }}>\r\n        <ReportFilterBar\r\n          startDate={startDate}\r\n          endDate={endDate}\r\n          onStartChange={handleStartChange}\r\n          onEndChange={handleEndChange}\r\n          // Whole days only — the time-of-day filter below is the finer cut.\r\n          showTime={false}\r\n          quickRange={quickRange}\r\n          onQuickRangeChange={(r: QuickRange) => setQuickRange(r)}\r\n          extra={\r\n            <>\r\n              <IntervalSelect\r\n                value={intervalMinutes}\r\n                onChange={(m) => {\r\n                  setIntervalMinutes(m);\r\n                  resetPage();\r\n                }}\r\n              />\r\n              <QueueMultiSelect\r\n                choices={queueChoices}\r\n                value={selectedQueues}\r\n                onChange={(ids) => {\r\n                  setSelectedQueues(ids);\r\n                  resetPage();\r\n                }}\r\n              />\r\n              <SearchBar\r\n                value={searchInput}\r\n                onChange={setSearchInput}\r\n                placeholder=\"Search queue...\"\r\n                width={170}\r\n                borderRadius=\"6px\"\r\n                sx={{\r\n                  \"& .MuiOutlinedInput-root\": {\r\n                    height: FILTER_HEIGHT,\r\n                    fontSize: 13,\r\n                  },\r\n                }}\r\n              />\r\n              <AppButton\r\n                startIcon={\r\n                  <RefreshIcon sx={{ fontSize: \"0.9rem !important\" }} />\r\n                }\r\n                disabled={loading}\r\n                onClick={fetchStaffing}\r\n                sx={{ height: FILTER_HEIGHT }}>\r\n                Refresh\r\n              </AppButton>\r\n            </>\r\n          }\r\n        />\r\n\r\n        <WeekdayAndTimeFilters\r\n          weekdays={weekdays}\r\n          onWeekdaysChange={(days) => {\r\n            setWeekdays(days);\r\n            resetPage();\r\n          }}\r\n          timeFrom={timeFrom}\r\n          timeTo={timeTo}\r\n          onTimeFromChange={(v) => {\r\n            setTimeFrom(v);\r\n            resetPage();\r\n          }}\r\n          onTimeToChange={(v) => {\r\n            setTimeTo(v);\r\n            resetPage();\r\n          }}\r\n        />\r\n\r\n        {error && (\r\n          <Alert\r\n            severity=\"error\"\r\n            sx={{ my: 1, fontFamily: FONT, fontSize: \"0.78rem\" }}\r\n            onClose={() => setError(\"\")}>\r\n            {error}\r\n          </Alert>\r\n        )}\r\n\r\n        {/* ── Daily summary ──────────────────────────────────────────────\r\n\t\t\t\t    One row per queue per day, as the roll-up of the intervals below.\r\n\t\t\t\t    A row is a way in: click it and the intervals narrow to that day. */}\r\n        <Box\r\n          sx={{\r\n            display: \"flex\",\r\n            alignItems: \"center\",\r\n            gap: 1,\r\n            flexWrap: \"wrap\",\r\n            mx: 1,\r\n            mt: 1,\r\n            mb: 0.8,\r\n          }}>\r\n          <Typography\r\n            sx={{\r\n              fontFamily: FONT,\r\n              fontSize: \"0.85rem\",\r\n              fontWeight: 700,\r\n              color: COLORS.navy,\r\n            }}>\r\n            Daily summary\r\n          </Typography>\r\n          <Typography\r\n            sx={{ fontFamily: FONT, fontSize: \"0.72rem\", color: COLORS.grey }}>\r\n            Click a day to open its intervals\r\n          </Typography>\r\n        </Box>\r\n\r\n        <AppTable\r\n          columns={dayColumns}\r\n          data={data?.rows ?? []}\r\n          layoutMode=\"semantic\"\r\n          enableTopToolbar={false}\r\n          enableColumnActions={false}\r\n          enableColumnFilters={false}\r\n          enableColumnDragging={false}\r\n          enableHiding={false}\r\n          enableDensityToggle={false}\r\n          enableFullScreenToggle={false}\r\n          enableStickyHeader\r\n          // Paged by the server: MRT is told the total and asks for a page\r\n          // rather than slicing rows it does not have.\r\n          manualPagination\r\n          rowCount={data?.total ?? 0}\r\n          onPaginationChange={setDaysPagination}\r\n          muiPaginationProps={{ rowsPerPageOptions: PAGE_SIZE_OPTIONS }}\r\n          state={{ pagination: daysPagination, isLoading: loading }}\r\n          localization={\r\n            {\r\n              noRecordsToDisplay: \"No calls or staffed agents in this range.\",\r\n            } as any\r\n          }\r\n          muiCircularProgressProps={{ sx: { color: COLORS.blue }, size: 28 }}\r\n          muiTablePaperProps={{\r\n            elevation: 0,\r\n            sx: {\r\n              borderRadius: \"8px\",\r\n              border: \"1px solid #e0e7ef\",\r\n              overflow: \"hidden\",\r\n              boxShadow: \"none\",\r\n              //   mb: 2,\r\n            },\r\n          }}\r\n          muiTableProps={{\r\n            sx: {\r\n              fontFamily: FONT,\r\n              border: \"none\",\r\n              borderRadius: 0,\r\n              boxShadow: \"none\",\r\n            },\r\n          }}\r\n          // The whole tab is the day table now, so it takes the height the\r\n          // panel underneath it used to leave behind.\r\n          muiTableContainerProps={{ sx: { maxHeight: \"calc(100vh - 360px)\" } }}\r\n          muiTableHeadProps={{ sx: { boxShadow: \"none\" } }}\r\n          muiTableHeadRowProps={{ sx: { boxShadow: \"none\" } }}\r\n          // A row is a way in: clicking it opens that day's own view. No\r\n          // selected state to show — the list is gone once one is open.\r\n          muiTableBodyRowProps={({ row }) => ({\r\n            onClick: () => openDay(row.original),\r\n            sx: {\r\n              cursor: \"pointer\",\r\n              backgroundColor:\r\n                (row.original.intervalsUnderstaffed ?? 0) > 0\r\n                  ? \"#fdf4f4\"\r\n                  : \"#fff\",\r\n              \"&:hover\": {\r\n                backgroundColor:\r\n                  (row.original.intervalsUnderstaffed ?? 0) > 0\r\n                    ? \"#fbe9e9\"\r\n                    : \"#f0f7f8\",\r\n              },\r\n            },\r\n          })}\r\n          muiBottomToolbarProps={{\r\n            sx: {\r\n              borderTop: \"1px solid #eef1f6\",\r\n              boxShadow: \"none\",\r\n              fontFamily: FONT,\r\n              \"& .MuiTablePagination-displayedRows, & .MuiTablePagination-selectLabel, & .MuiInputBase-root\":\r\n                { fontFamily: FONT, fontSize: \"0.73rem\" },\r\n            },\r\n          }}\r\n        />\r\n      </Box>\r\n    </LocalizationProvider>\r\n  );\r\n}\r\n","import { useMemo } from \"react\";\r\nimport { Box, Paper, Typography } from \"@mui/material\";\r\nimport {\r\n\tArea,\r\n\tCartesianGrid,\r\n\tComposedChart,\r\n\tLegend,\r\n\tLine,\r\n\tResponsiveContainer,\r\n\tTooltip,\r\n\tXAxis,\r\n\tYAxis,\r\n} from \"recharts\";\r\nimport dayjs from \"dayjs\";\r\n\r\nimport { COLORS, FONT } from \"./tableBits\";\r\nimport type { IntervalRow } from \"./types\";\r\n\r\n/** The slot's own window plus each series, rather than a bare timestamp. */\r\nfunction ChartTooltip({ active, payload, label, intervalMinutes }: any) {\r\n\tif (!active || !payload?.length) return null;\r\n\tconst start = dayjs(label);\r\n\treturn (\r\n\t\t<Box sx={{ backgroundColor: \"#fff\", border: \"1px solid #e2e8f0\", borderRadius: \"8px\", px: 1.25, py: 0.75, boxShadow: \"0 6px 18px rgba(15,23,42,0.12)\" }}>\r\n\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.7rem\", color: COLORS.grey, mb: 0.3 }}>\r\n\t\t\t\t{`${start.format(\"ddd D MMM, HH:mm\")}–${start.add(intervalMinutes, \"minute\").format(\"HH:mm\")}`}\r\n\t\t\t</Typography>\r\n\t\t\t{payload.map((entry: any) => (\r\n\t\t\t\t<Typography key={entry.dataKey} sx={{ fontFamily: FONT, fontSize: \"0.73rem\", fontWeight: 600, color: entry.color }}>\r\n\t\t\t\t\t{entry.name}: {typeof entry.value === \"number\" ? entry.value.toFixed(1) : \"–\"}\r\n\t\t\t\t</Typography>\r\n\t\t\t))}\r\n\t\t</Box>\r\n\t);\r\n}\r\n\r\ninterface ChartPoint {\r\n\tt: number;\r\n\tneeded: number | null;\r\n\tinbound: number | null;\r\n\tstaffed: number | null;\r\n}\r\n\r\n/**\r\n * Agents needed against agents on shift, for one queue.\r\n *\r\n * Needed is drawn as a step: it is a decision that holds for the whole slot,\r\n * not a reading that slides between them. Staffed is an area under it, so a\r\n * short slot is the gap left uncovered — visible without reading the table.\r\n */\r\nexport default function StaffingChart({\r\n\trows, intervalMinutes, queueName,\r\n}: {\r\n\trows: IntervalRow[];\r\n\tintervalMinutes: number;\r\n\tqueueName: string;\r\n}) {\r\n\tconst points = useMemo<ChartPoint[]>(() => {\r\n\t\tconst sorted = [...rows]\r\n\t\t\t.filter((r) => r.intervalStart && dayjs(r.intervalStart).isValid())\r\n\t\t\t.sort((a, b) => dayjs(a.intervalStart).valueOf() - dayjs(b.intervalStart).valueOf());\r\n\r\n\t\tconst out: ChartPoint[] = [];\r\n\t\tlet previous: number | null = null;\r\n\r\n\t\tfor (const row of sorted) {\r\n\t\t\tconst t = dayjs(row.intervalStart).valueOf();\r\n\t\t\t/*\r\n\t\t\t * Slots only exist where the queue was open, so a closed night is\r\n\t\t\t * missing rather than zero. A null spacer breaks the line there —\r\n\t\t\t * without it the chart draws straight across the gap and invents a\r\n\t\t\t * shift nobody worked.\r\n\t\t\t */\r\n\t\t\tif (previous !== null && t - previous > intervalMinutes * 60_000 * 1.5) {\r\n\t\t\t\tout.push({ t: previous + 1, needed: null, inbound: null, staffed: null });\r\n\t\t\t}\r\n\t\t\tout.push({\r\n\t\t\t\tt,\r\n\t\t\t\tneeded: typeof row.requiredTotalAgents === \"number\" ? row.requiredTotalAgents : null,\r\n\t\t\t\tinbound: typeof row.requiredAgents === \"number\" ? row.requiredAgents : null,\r\n\t\t\t\tstaffed: typeof row.staffedAgents === \"number\" ? row.staffedAgents : null,\r\n\t\t\t});\r\n\t\t\tprevious = t;\r\n\t\t}\r\n\t\treturn out;\r\n\t}, [rows, intervalMinutes]);\r\n\r\n\tif (points.length === 0) return null;\r\n\r\n\treturn (\r\n\t\t<Paper\r\n\t\t\televation={0}\r\n\t\t\tsx={{ borderRadius: \"8px\", border: \"1px solid #e0e7ef\", p: { xs: 1.5, sm: 2 }, mb: 2 }}\r\n\t\t>\r\n\t\t\t<Box sx={{ display: \"flex\", alignItems: \"center\", gap: 1, flexWrap: \"wrap\", mb: 1 }}>\r\n\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.85rem\", fontWeight: 700, color: COLORS.navy }}>\r\n\t\t\t\t\tNeeded vs staffed\r\n\t\t\t\t</Typography>\r\n\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.72rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t{queueName} · {intervalMinutes} min slots\r\n\t\t\t\t</Typography>\r\n\t\t\t</Box>\r\n\r\n\t\t\t<Box sx={{ height: 240 }}>\r\n\t\t\t\t<ResponsiveContainer width=\"100%\" height=\"100%\">\r\n\t\t\t\t\t<ComposedChart data={points} margin={{ top: 8, right: 12, bottom: 4, left: -18 }}>\r\n\t\t\t\t\t\t<CartesianGrid stroke=\"#eef1f6\" vertical={false} />\r\n\t\t\t\t\t\t<XAxis\r\n\t\t\t\t\t\t\tdataKey=\"t\"\r\n\t\t\t\t\t\t\ttype=\"number\"\r\n\t\t\t\t\t\t\tscale=\"time\"\r\n\t\t\t\t\t\t\tdomain={[\"dataMin\", \"dataMax\"]}\r\n\t\t\t\t\t\t\ttickFormatter={(t: number) => dayjs(t).format(\"HH:mm\")}\r\n\t\t\t\t\t\t\ttick={{ fontFamily: FONT, fontSize: 10, fill: COLORS.grey }}\r\n\t\t\t\t\t\t\tstroke=\"#cfd8e3\"\r\n\t\t\t\t\t\t\tminTickGap={28}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<YAxis\r\n\t\t\t\t\t\t\tallowDecimals={false}\r\n\t\t\t\t\t\t\ttick={{ fontFamily: FONT, fontSize: 10, fill: COLORS.grey }}\r\n\t\t\t\t\t\t\tstroke=\"#cfd8e3\"\r\n\t\t\t\t\t\t\twidth={44}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<Tooltip content={<ChartTooltip intervalMinutes={intervalMinutes} />} />\r\n\t\t\t\t\t\t<Legend wrapperStyle={{ fontFamily: FONT, fontSize: \"0.72rem\" }} />\r\n\r\n\t\t\t\t\t\t{/* Drawn first so the step lines sit on top of it. */}\r\n\t\t\t\t\t\t<Area\r\n\t\t\t\t\t\t\tname=\"Staffed\"\r\n\t\t\t\t\t\t\ttype=\"stepAfter\"\r\n\t\t\t\t\t\t\tdataKey=\"staffed\"\r\n\t\t\t\t\t\t\tstroke={COLORS.green}\r\n\t\t\t\t\t\t\tfill={COLORS.green}\r\n\t\t\t\t\t\t\tfillOpacity={0.14}\r\n\t\t\t\t\t\t\tstrokeWidth={1.5}\r\n\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\tconnectNulls={false}\r\n\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<Line\r\n\t\t\t\t\t\t\tname=\"Inbound needed\"\r\n\t\t\t\t\t\t\ttype=\"stepAfter\"\r\n\t\t\t\t\t\t\tdataKey=\"inbound\"\r\n\t\t\t\t\t\t\tstroke={COLORS.blue}\r\n\t\t\t\t\t\t\tstrokeWidth={1}\r\n\t\t\t\t\t\t\tstrokeOpacity={0.5}\r\n\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\tconnectNulls={false}\r\n\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<Line\r\n\t\t\t\t\t\t\tname=\"Total needed\"\r\n\t\t\t\t\t\t\ttype=\"stepAfter\"\r\n\t\t\t\t\t\t\tdataKey=\"needed\"\r\n\t\t\t\t\t\t\tstroke={COLORS.navy}\r\n\t\t\t\t\t\t\tstrokeWidth={2}\r\n\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\tconnectNulls={false}\r\n\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t</ComposedChart>\r\n\t\t\t\t</ResponsiveContainer>\r\n\t\t\t</Box>\r\n\t\t</Paper>\r\n\t);\r\n}\r\n","import { useState } from \"react\";\r\nimport { Box, Checkbox, Chip, FormControl, InputLabel, MenuItem, Select, TextField, Typography } from \"@mui/material\";\r\n\r\nimport AppButton from \"../common/components/AppButton\";\r\nimport MenuSearchField from \"../common/components/MenuSearchField\";\r\nimport { COLORS, FONT } from \"./tableBits\";\r\nimport { INTERVAL_OPTIONS, WEEKDAYS, type QueueChoice } from \"./types\";\r\n\r\n/* The filters the By interval and Forecast tabs share. Only the date range and\r\n   its quick buttons differ between them, so those stay with each tab. */\r\n\r\nexport const FILTER_HEIGHT = 32;\r\n\r\nexport function IntervalSelect({\r\n\tvalue, onChange,\r\n}: {\r\n\tvalue: number;\r\n\tonChange: (minutes: number) => void;\r\n}) {\r\n\treturn (\r\n\t\t<FormControl size=\"small\" sx={{ minWidth: 120 }}>\r\n\t\t\t<InputLabel sx={{ fontSize: \"0.76rem\" }}>Interval</InputLabel>\r\n\t\t\t<Select\r\n\t\t\t\tlabel=\"Interval\"\r\n\t\t\t\tvalue={String(value)}\r\n\t\t\t\tonChange={(e) => onChange(Number(e.target.value))}\r\n\t\t\t\tsx={{ fontSize: \"0.76rem\", height: FILTER_HEIGHT, borderRadius: \"6px\" }}\r\n\t\t\t>\r\n\t\t\t\t{INTERVAL_OPTIONS.map((m) => (\r\n\t\t\t\t\t<MenuItem key={m} value={String(m)} sx={{ fontSize: \"0.76rem\" }}>{m} min</MenuItem>\r\n\t\t\t\t))}\r\n\t\t\t</Select>\r\n\t\t</FormControl>\r\n\t);\r\n}\r\n\r\nexport function QueueMultiSelect({\r\n\tchoices, value, onChange,\r\n}: {\r\n\tchoices: QueueChoice[];\r\n\tvalue: number[];\r\n\tonChange: (ids: number[]) => void;\r\n}) {\r\n\tconst [search, setSearch] = useState(\"\");\r\n\tconst query = search.trim().toLowerCase();\r\n\tconst visible = query ? choices.filter((q) => q.queueName.toLowerCase().includes(query)) : choices;\r\n\tconst allVisibleSelected = visible.length > 0 && visible.every((q) => value.includes(q.queueId));\r\n\r\n\treturn (\r\n\t\t<FormControl size=\"small\" sx={{ minWidth: 150 }}>\r\n\t\t\t<InputLabel sx={{ fontSize: \"0.76rem\" }}>All Queues</InputLabel>\r\n\t\t\t<Select\r\n\t\t\t\tmultiple\r\n\t\t\t\tlabel=\"All Queues\"\r\n\t\t\t\tvalue={value}\r\n\t\t\t\tsx={{ fontSize: \"0.76rem\", height: FILTER_HEIGHT, borderRadius: \"6px\" }}\r\n\t\t\t\tMenuProps={{\r\n\t\t\t\t\tvariant: \"menu\",\r\n\t\t\t\t\tautoFocus: false,\r\n\t\t\t\t\tPaperProps: { sx: { maxHeight: 280, \"& .MuiMenuItem-root\": { fontSize: \"0.76rem\", minHeight: 32, py: 0.3 } } },\r\n\t\t\t\t}}\r\n\t\t\t\tonClose={() => setSearch(\"\")}\r\n\t\t\t\tonChange={(e) => {\r\n\t\t\t\t\tconst ids = e.target.value as number[];\r\n\t\t\t\t\tif (ids.includes(-1)) {\r\n\t\t\t\t\t\tonChange(allVisibleSelected\r\n\t\t\t\t\t\t\t? value.filter((id) => !visible.some((q) => q.queueId === id))\r\n\t\t\t\t\t\t\t: [...new Set([...value, ...visible.map((q) => q.queueId)])]);\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tonChange(ids);\r\n\t\t\t\t\t}\r\n\t\t\t\t}}\r\n\t\t\t\trenderValue={(selected) => {\r\n\t\t\t\t\tconst ids = selected as number[];\r\n\t\t\t\t\tif (ids.length === 0 || ids.length === choices.length) return \"All Queues\";\r\n\t\t\t\t\tif (ids.length === 1) return choices.find((q) => q.queueId === ids[0])?.queueName ?? \"1 Queue\";\r\n\t\t\t\t\treturn `${ids.length} Queue(s)`;\r\n\t\t\t\t}}\r\n\t\t\t>\r\n\t\t\t\t<MenuSearchField value={search} onChange={setSearch} placeholder=\"Search queue...\" />\r\n\t\t\t\t{visible.length > 0 && (\r\n\t\t\t\t\t<MenuItem value={-1}>\r\n\t\t\t\t\t\t<Checkbox\r\n\t\t\t\t\t\t\tchecked={allVisibleSelected}\r\n\t\t\t\t\t\t\tindeterminate={visible.some((q) => value.includes(q.queueId)) && !allVisibleSelected}\r\n\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\tSelect all\r\n\t\t\t\t\t</MenuItem>\r\n\t\t\t\t)}\r\n\t\t\t\t{visible.length === 0 && <MenuItem disabled>No queues found</MenuItem>}\r\n\t\t\t\t{visible.map((q) => (\r\n\t\t\t\t\t<MenuItem key={q.queueId} value={q.queueId}>\r\n\t\t\t\t\t\t<Checkbox checked={value.includes(q.queueId)} size=\"small\" />\r\n\t\t\t\t\t\t{q.queueName}\r\n\t\t\t\t\t</MenuItem>\r\n\t\t\t\t))}\r\n\t\t\t</Select>\r\n\t\t</FormControl>\r\n\t);\r\n}\r\n\r\n/**\r\n * Weekdays and time of day together: the pair that answers \"is Monday morning\r\n * always short\", which a plain date range cannot.\r\n */\r\nexport function WeekdayAndTimeFilters({\r\n\tweekdays, onWeekdaysChange, timeFrom, timeTo, onTimeFromChange, onTimeToChange,\r\n}: {\r\n\tweekdays: number[];\r\n\tonWeekdaysChange: (days: number[]) => void;\r\n\ttimeFrom: string;\r\n\ttimeTo: string;\r\n\tonTimeFromChange: (value: string) => void;\r\n\tonTimeToChange: (value: string) => void;\r\n}) {\r\n\tconst timeField = (label: string, value: string, onChange: (v: string) => void) => (\r\n\t\t<TextField\r\n\t\t\ttype=\"time\"\r\n\t\t\tsize=\"small\"\r\n\t\t\tlabel={label}\r\n\t\t\tvalue={value}\r\n\t\t\tonChange={(e) => onChange(e.target.value)}\r\n\t\t\tslotProps={{ inputLabel: { shrink: true, sx: { fontSize: \"0.76rem\" } } }}\r\n\t\t\tsx={{ width: 120, \"& .MuiOutlinedInput-root\": { height: FILTER_HEIGHT, borderRadius: \"6px\", fontSize: \"0.76rem\" } }}\r\n\t\t/>\r\n\t);\r\n\r\n\treturn (\r\n\t\t<Box sx={{ display: \"flex\", alignItems: \"center\", gap: 1, flexWrap: \"wrap\", px: 1, py: 0.6 }}>\r\n\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.75rem\", fontWeight: 600, color: \"#4a5568\" }}>\r\n\t\t\t\tWeekdays\r\n\t\t\t</Typography>\r\n\t\t\t{WEEKDAYS.map((d) => {\r\n\t\t\t\tconst on = weekdays.includes(d.iso);\r\n\t\t\t\treturn (\r\n\t\t\t\t\t<Chip\r\n\t\t\t\t\t\tkey={d.iso}\r\n\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\tlabel={d.label}\r\n\t\t\t\t\t\tonClick={() => onWeekdaysChange(on ? weekdays.filter((x) => x !== d.iso) : [...weekdays, d.iso])}\r\n\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\theight: 24, fontSize: \"0.7rem\", fontWeight: 700, cursor: \"pointer\",\r\n\t\t\t\t\t\t\tbackgroundColor: on ? COLORS.teal : \"#eef1f6\",\r\n\t\t\t\t\t\t\tcolor: on ? \"#fff\" : \"#4a5568\",\r\n\t\t\t\t\t\t\t\"&:hover\": { backgroundColor: on ? COLORS.teal : \"#e2e8f0\" },\r\n\t\t\t\t\t\t}}\r\n\t\t\t\t\t/>\r\n\t\t\t\t);\r\n\t\t\t})}\r\n\t\t\t{weekdays.length > 0 && (\r\n\t\t\t\t<AppButton variant=\"outlined\" onClick={() => onWeekdaysChange([])} sx={{ height: 24, minWidth: 0, px: 1 }}>\r\n\t\t\t\t\tAll days\r\n\t\t\t\t</AppButton>\r\n\t\t\t)}\r\n\r\n\t\t\t<Box sx={{ width: 12 }} />\r\n\t\t\t{timeField(\"Time from\", timeFrom, onTimeFromChange)}\r\n\t\t\t{timeField(\"Time to\", timeTo, onTimeToChange)}\r\n\t\t\t{(timeFrom || timeTo) && (\r\n\t\t\t\t<AppButton\r\n\t\t\t\t\tvariant=\"outlined\"\r\n\t\t\t\t\tonClick={() => { onTimeFromChange(\"\"); onTimeToChange(\"\"); }}\r\n\t\t\t\t\tsx={{ height: 24, minWidth: 0, px: 1 }}\r\n\t\t\t\t>\r\n\t\t\t\t\tAll day\r\n\t\t\t\t</AppButton>\r\n\t\t\t)}\r\n\t\t</Box>\r\n\t);\r\n}\r\n","import axiosInstance from \"../../services/axios\";\r\nimport { END_POINT } from \"../../services/endPoint\";\r\nimport type {\r\n\tStaffingDaysPage,\r\n\tStaffingIntervalsParams,\r\n\tStaffingIntervalsResponse,\r\n\tStaffingParams,\r\n} from \"./types\";\r\n\r\n/**\r\n * Query params are snake_case, like the rest of this API's query strings.\r\n * `queue_ids` and `weekdays` repeat once per value; the API reads them as lists.\r\n * `page` and `pageSize` are the standard paginated pair and keep their casing.\r\n */\r\nfunction buildQs(params: StaffingParams): string {\r\n\tconst qs = new URLSearchParams();\r\n\tqs.append(\"start_date\", params.startDate);\r\n\tqs.append(\"end_date\", params.endDate);\r\n\tqs.append(\"interval_minutes\", String(params.intervalMinutes));\r\n\tfor (const id of params.queueIds ?? []) qs.append(\"queue_ids\", String(id));\r\n\tfor (const day of params.weekdays ?? []) qs.append(\"weekdays\", String(day));\r\n\tif (params.timeFrom) qs.append(\"time_from\", params.timeFrom);\r\n\tif (params.timeTo) qs.append(\"time_to\", params.timeTo);\r\n\tif (params.page != null) qs.append(\"page\", String(params.page));\r\n\tif (params.pageSize != null) qs.append(\"pageSize\", String(params.pageSize));\r\n\tif (params.search) qs.append(\"search\", params.search);\r\n\treturn `?${qs.toString()}`;\r\n}\r\n\r\n/** One page of the day table. The server caches it for 60s, so paging is cheap. */\r\nexport async function getStaffingDays(params: StaffingParams): Promise<StaffingDaysPage> {\r\n\tconst res = await axiosInstance.get(`${END_POINT.WFM_STAFFING}${buildQs(params)}`);\r\n\tconst body = res.data as any;\r\n\tconst rows = Array.isArray(body?.data) ? body.data : [];\r\n\treturn {\r\n\t\tsettings: body?.settings,\r\n\t\trows,\r\n\t\t// Falls back to the page itself, so a server that omits the count still pages.\r\n\t\ttotal: typeof body?.total === \"number\" ? body.total : rows.length,\r\n\t};\r\n}\r\n\r\n/** The slots behind one day of one queue, fetched when its row is clicked. */\r\nexport async function getStaffingIntervals(params: StaffingIntervalsParams): Promise<StaffingIntervalsResponse> {\r\n\tconst qs = new URLSearchParams();\r\n\tqs.append(\"day\", params.day);\r\n\tqs.append(\"queue_id\", String(params.queueId));\r\n\tqs.append(\"interval_minutes\", String(params.intervalMinutes));\r\n\tif (params.timeFrom) qs.append(\"time_from\", params.timeFrom);\r\n\tif (params.timeTo) qs.append(\"time_to\", params.timeTo);\r\n\tconst res = await axiosInstance.get(`${END_POINT.WFM_STAFFING_INTERVALS}?${qs.toString()}`);\r\n\tconst body = res.data as any;\r\n\treturn {\r\n\t\tsettings: body?.settings,\r\n\t\tday: body?.day,\r\n\t\tqueueId: body?.queueId,\r\n\t\tqueueName: body?.queueName,\r\n\t\tsummary: body?.summary ?? null,\r\n\t\tintervals: Array.isArray(body?.intervals) ? body.intervals : [],\r\n\t};\r\n}\r\n\r\n/**\r\n * The API's own reason for refusing — a 422 carries a readable `detail`\r\n * (\"Report range cannot exceed 31 days\"), which belongs next to the filters\r\n * rather than behind a generic failure message.\r\n */\r\nexport function staffingErrorMessage(err: unknown, fallback: string): string {\r\n\tconst detail = (err as any)?.response?.data?.detail ?? (err as any)?.response?.data?.message;\r\n\tif (typeof detail === \"string\" && detail) return detail;\r\n\t// FastAPI validation errors: a list of { msg }.\r\n\tif (Array.isArray(detail) && detail.length > 0) {\r\n\t\treturn detail.map((d: any) => d?.msg).filter(Boolean).join(\"; \") || fallback;\r\n\t}\r\n\treturn fallback;\r\n}\r\n","import { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\r\nimport {\r\n  Alert,\r\n  Box,\r\n  Chip,\r\n  CircularProgress,\r\n  IconButton,\r\n  Tooltip,\r\n  Typography,\r\n} from \"@mui/material\";\r\nimport HelpOutlineIcon from \"@mui/icons-material/HelpOutlineOutlined\";\r\nimport InfoOutlinedIcon from \"@mui/icons-material/InfoOutlined\";\r\nimport ArrowBackIcon from \"@mui/icons-material/ArrowBack\";\r\nimport RefreshIcon from \"@mui/icons-material/Refresh\";\r\nimport type { MRT_ColumnDef } from \"material-react-table\";\r\nimport dayjs, { Dayjs } from \"dayjs\";\r\nimport { LocalizationProvider } from \"@mui/x-date-pickers/LocalizationProvider\";\r\nimport { AdapterDayjs } from \"@mui/x-date-pickers/AdapterDayjs\";\r\nimport { DatePicker } from \"@mui/x-date-pickers/DatePicker\";\r\n\r\nimport AppButton from \"../common/components/AppButton\";\r\nimport AppTable from \"../common/components/AppTable\";\r\nimport SearchBar from \"../common/components/SearchBar\";\r\nimport ForecastChart from \"./ForecastChart\";\r\nimport SlotDetailDrawer from \"./SlotDetailDrawer\";\r\nimport ForecastExplainDrawer from \"./ForecastExplainDrawer\";\r\nimport {\r\n  FILTER_HEIGHT,\r\n  IntervalSelect,\r\n  QueueMultiSelect,\r\n  WeekdayAndTimeFilters,\r\n} from \"./filterControls\";\r\nimport { getForecastDays, getForecastIntervals } from \"./useForecast\";\r\nimport { staffingErrorMessage } from \"./useStaffing\";\r\nimport {\r\n  FORECAST_ERROR_BANDS,\r\n  FORECAST_LIMITS,\r\n  LOW_HISTORY_WEEKS,\r\n  forecastTooltip,\r\n  type ForecastDayRow,\r\n  type ForecastIntervalRow,\r\n  type ForecastDaysPage,\r\n  type ForecastExplainParams,\r\n  type ForecastIntervalsResponse,\r\n} from \"./forecastTypes\";\r\nimport type { QueueChoice } from \"./types\";\r\nimport {\r\n  COLORS,\r\n  DASH,\r\n  FONT,\r\n  StatusChip,\r\n  cell,\r\n  cellBaseSx,\r\n  count,\r\n  decimal,\r\n  duration,\r\n  plusMinusPct,\r\n  tabularSx,\r\n  withInfo,\r\n} from \"./tableBits\";\r\nimport { forecastDayInfo, forecastSlotInfo } from \"./columnInfo\";\r\n\r\n/** The same ladder the rest of the app's tables offer. */\r\nconst PAGE_SIZE_OPTIONS = [10, 15, 25, 50, 100];\r\nconst DEFAULT_PAGE_SIZE = 10;\r\n\r\n/** Matches the shared date pickers, which cannot be reused here: they clamp to today. */\r\nconst pickerSx = {\r\n  width: 150,\r\n  \"& .MuiOutlinedInput-root\": {\r\n    borderRadius: \"6px\",\r\n    height: FILTER_HEIGHT,\r\n    px: 0.5,\r\n  },\r\n  \"& .MuiInputBase-input\": { py: 0, px: \"4px\", fontSize: \"0.72rem\" },\r\n  \"& .MuiInputAdornment-root .MuiSvgIcon-root\": { fontSize: \"0.8rem\" },\r\n  \"& .MuiOutlinedInput-notchedOutline\": { borderColor: \"#c0c8d4\" },\r\n  \"& .MuiPickersSectionList-root\": { py: \"4px\", px: 0, fontSize: 12 },\r\n};\r\n\r\n/** The three ranges a supervisor actually wants: the week ahead, the month ahead, and last week to check the forecast against. */\r\n/** The other tabs ask \"were we short\"; this one asks whether the roster matched the forecast. */\r\nconst SLOT_STATUS_TOOLTIP =\r\n  \"Compares the agents on shift with the agents this forecast called for — whether the roster \" +\r\n  \"followed the forecast, not whether it matched the calls that actually came.\";\r\n\r\nconst QUICK_RANGES = [\r\n  {\r\n    key: \"next7\",\r\n    label: \"Next 7 days\",\r\n    start: () => dayjs().startOf(\"day\"),\r\n    end: () => dayjs().add(6, \"day\").startOf(\"day\"),\r\n  },\r\n  {\r\n    key: \"next28\",\r\n    label: \"Next 4 weeks\",\r\n    start: () => dayjs().startOf(\"day\"),\r\n    end: () => dayjs().add(27, \"day\").startOf(\"day\"),\r\n  },\r\n  {\r\n    key: \"lastWeek\",\r\n    label: \"Last week\",\r\n    start: () => dayjs().subtract(7, \"day\").startOf(\"day\"),\r\n    end: () => dayjs().subtract(1, \"day\").startOf(\"day\"),\r\n  },\r\n] as const;\r\n\r\ninterface ForecastTabProps {\r\n  enabled: boolean;\r\n}\r\n\r\nexport default function ForecastTab({ enabled }: ForecastTabProps) {\r\n  // The week ahead, which is what a roster is written for.\r\n  const [startDate, setStartDate] = useState<Dayjs | null>(\r\n    dayjs().startOf(\"day\"),\r\n  );\r\n  const [endDate, setEndDate] = useState<Dayjs | null>(\r\n    dayjs().add(6, \"day\").startOf(\"day\"),\r\n  );\r\n  const [quickRange, setQuickRange] = useState<string>(\"next7\");\r\n  const [intervalMinutes, setIntervalMinutes] = useState(30);\r\n  const [selectedQueues, setSelectedQueues] = useState<number[]>([]);\r\n  const [weekdays, setWeekdays] = useState<number[]>([]);\r\n  const [timeFrom, setTimeFrom] = useState(\"\");\r\n  const [timeTo, setTimeTo] = useState(\"\");\r\n  /** What is typed, and what has been sent — typing shouldn't fire a request a letter. */\r\n  const [searchInput, setSearchInput] = useState(\"\");\r\n  const [search, setSearch] = useState(\"\");\r\n\r\n  const [data, setData] = useState<ForecastDaysPage | null>(null);\r\n  const [loading, setLoading] = useState(false);\r\n  const [error, setError] = useState(\"\");\r\n  const [daysPagination, setDaysPagination] = useState({\r\n    pageIndex: 0,\r\n    pageSize: DEFAULT_PAGE_SIZE,\r\n  });\r\n\r\n  /* The slots behind the clicked day, fetched on their own. */\r\n  const [slotsData, setSlotsData] = useState<ForecastIntervalsResponse | null>(\r\n    null,\r\n  );\r\n  const [slotsLoading, setSlotsLoading] = useState(false);\r\n  const [slotsError, setSlotsError] = useState(\"\");\r\n  /** The slot whose drawer is open. */\r\n  const [slotRow, setSlotRow] = useState<ForecastIntervalRow | null>(null);\r\n  /** The slot whose \"Why?\" panel is open. */\r\n  const [explain, setExplain] = useState<ForecastExplainParams | null>(null);\r\n\r\n  /** The day row clicked — the interval view below narrows to it. */\r\n  const [focusedDay, setFocusedDay] = useState<{\r\n    day: string;\r\n    queueId: number;\r\n    queueName: string;\r\n  } | null>(null);\r\n\r\n  /* Built from the rows, like the By interval tab: this feature has no queue\r\n\t   list endpoint, and `queues:read` is a permission a WFM login may not hold. */\r\n  const [queueChoices, setQueueChoices] = useState<QueueChoice[]>([]);\r\n  const hasQueueFilter = selectedQueues.length > 0;\r\n\r\n  const maxEnd = useMemo(\r\n    () => dayjs().add(FORECAST_LIMITS.maxDaysAhead, \"day\").startOf(\"day\"),\r\n    [],\r\n  );\r\n\r\n  /** Null when the filters are usable, otherwise why they aren't. */\r\n  const filterError = useMemo(() => {\r\n    if (!startDate || !endDate) return \"Pick a start and end date\";\r\n    if (startDate.isAfter(endDate)) return \"Start date must be before end date\";\r\n    if (endDate.diff(startDate, \"day\") + 1 > FORECAST_LIMITS.maxDays) {\r\n      return `The range cannot be longer than ${FORECAST_LIMITS.maxDays} days`;\r\n    }\r\n    if (endDate.isAfter(maxEnd)) {\r\n      return `The forecast only reaches ${FORECAST_LIMITS.maxDaysAhead} days ahead`;\r\n    }\r\n    if (selectedQueues.length > FORECAST_LIMITS.maxQueues) {\r\n      return `Pick at most ${FORECAST_LIMITS.maxQueues} queues`;\r\n    }\r\n    if (timeFrom && timeTo && timeFrom >= timeTo)\r\n      return \"Time from must be before time to\";\r\n    return null;\r\n  }, [startDate, endDate, maxEnd, selectedQueues.length, timeFrom, timeTo]);\r\n\r\n  const buildParams = useCallback(\r\n    () => ({\r\n      startDate: startDate!.format(\"YYYY-MM-DD\"),\r\n      endDate: endDate!.format(\"YYYY-MM-DD\"),\r\n      intervalMinutes,\r\n      queueIds: selectedQueues.length > 0 ? selectedQueues : undefined,\r\n      weekdays: weekdays.length > 0 ? [...weekdays].sort() : undefined,\r\n      timeFrom: timeFrom || undefined,\r\n      timeTo: timeTo || undefined,\r\n      // The API counts pages from 1; MRT counts from 0.\r\n      page: daysPagination.pageIndex + 1,\r\n      pageSize: daysPagination.pageSize,\r\n      search: search.trim() || undefined,\r\n    }),\r\n    [\r\n      startDate,\r\n      endDate,\r\n      intervalMinutes,\r\n      selectedQueues,\r\n      weekdays,\r\n      timeFrom,\r\n      timeTo,\r\n      daysPagination,\r\n      search,\r\n    ],\r\n  );\r\n\r\n  /** Drops a reply that a later request has already superseded. */\r\n  const requestId = useRef(0);\r\n\r\n  const fetchForecast = useCallback(async () => {\r\n    if (!enabled) return;\r\n    if (filterError) {\r\n      setError(filterError);\r\n      setData(null);\r\n      return;\r\n    }\r\n    const id = ++requestId.current;\r\n    setLoading(true);\r\n    setError(\"\");\r\n    try {\r\n      const res = await getForecastDays(buildParams());\r\n      if (requestId.current !== id) return;\r\n      setData(res);\r\n      // The focused day belongs to the previous result set.\r\n      setFocusedDay(null);\r\n      setSlotsData(null);\r\n    } catch (err) {\r\n      if (requestId.current !== id) return;\r\n      setData(null);\r\n      setError(staffingErrorMessage(err, \"Could not load the forecast\"));\r\n    } finally {\r\n      if (requestId.current === id) setLoading(false);\r\n    }\r\n  }, [enabled, filterError, buildParams]);\r\n\r\n  useEffect(() => {\r\n    fetchForecast();\r\n  }, [fetchForecast]);\r\n\r\n  /*\r\n   * Merged rather than replaced: one page of a paginated table holds only the\r\n   * queues on that page, so replacing would shrink the filter as you page.\r\n   */\r\n  useEffect(() => {\r\n    if (hasQueueFilter || !data) return;\r\n    setQueueChoices((previous) => {\r\n      const seen = new Map(previous.map((q) => [q.queueId, q.queueName]));\r\n      const before = seen.size;\r\n      for (const row of data.rows) seen.set(row.queueId, row.queueName);\r\n      if (seen.size === before) return previous;\r\n      return [...seen]\r\n        .map(([queueId, queueName]) => ({ queueId, queueName }))\r\n        .sort((a, b) => a.queueName.localeCompare(b.queueName));\r\n    });\r\n  }, [data, hasQueueFilter]);\r\n\r\n  /* Typing waits for a pause before it becomes a request. */\r\n  useEffect(() => {\r\n    const timer = window.setTimeout(() => {\r\n      setSearch(searchInput);\r\n      setDaysPagination((p) =>\r\n        p.pageIndex === 0 ? p : { ...p, pageIndex: 0 },\r\n      );\r\n    }, 400);\r\n    return () => window.clearTimeout(timer);\r\n  }, [searchInput]);\r\n\r\n  /* ── Day table ── */\r\n\r\n  const days = data?.rows ?? [];\r\n\r\n  /**\r\n   * A range entirely in the future has nothing to compare against, so the five\r\n   * \"actual\" columns are dropped rather than shown as a column of dashes.\r\n   */\r\n  const hasActuals = useMemo(\r\n    () =>\r\n      days.some(\r\n        (d) =>\r\n          d.actualCalls != null ||\r\n          d.actualStaffedAgentHours != null ||\r\n          d.forecastError != null,\r\n      ),\r\n    [days],\r\n  );\r\n\r\n  /** Under 15% is worth rostering against; past 30% the forecast is a hint, not a plan. */\r\n  const errorColour = (value: number | null | undefined) => {\r\n    if (typeof value !== \"number\") return \"#4a5568\";\r\n    const size = Math.abs(value);\r\n    if (size < FORECAST_ERROR_BANDS.good) return COLORS.green;\r\n    return size <= FORECAST_ERROR_BANDS.fair ? COLORS.amber : COLORS.red;\r\n  };\r\n\r\n  const today = dayjs().format(\"YYYY-MM-DD\");\r\n\r\n  const dayInfo = useMemo(\r\n    () =>\r\n      forecastDayInfo({\r\n        ...data?.settings,\r\n        intervalMinutes: data?.settings?.intervalMinutes ?? intervalMinutes,\r\n      }),\r\n    [data?.settings, intervalMinutes],\r\n  );\r\n\r\n  const dayColumns = useMemo<MRT_ColumnDef<ForecastDayRow>[]>(() => {\r\n    const base: MRT_ColumnDef<ForecastDayRow>[] = [\r\n      {\r\n        id: \"day\",\r\n        ...withInfo(\"Date\", dayInfo.date),\r\n        accessorFn: (r) => r.day,\r\n        Cell: ({ row }) => {\r\n          if (!row.original.day) return DASH;\r\n          const isToday = row.original.day === today;\r\n          return (\r\n            <Box\r\n              sx={{ display: \"inline-flex\", alignItems: \"center\", gap: 0.75 }}>\r\n              {dayjs(row.original.day).format(\"ddd D MMM\")}\r\n              {isToday && (\r\n                <Chip\r\n                  size=\"small\"\r\n                  label=\"Today\"\r\n                  sx={{\r\n                    height: 18,\r\n                    fontSize: \"0.62rem\",\r\n                    fontWeight: 700,\r\n                    backgroundColor: \"#e6f0f2\",\r\n                    color: COLORS.teal,\r\n                  }}\r\n                />\r\n              )}\r\n            </Box>\r\n          );\r\n        },\r\n        ...cell({ fontSize: \"0.73rem\", color: \"#4a5568\" }),\r\n        muiTableBodyCellProps: {\r\n          align: \"left\" as const,\r\n          sx: { ...cellBaseSx, fontSize: \"0.73rem\", color: \"#4a5568\" },\r\n        },\r\n      },\r\n      {\r\n        id: \"queueName\",\r\n        ...withInfo(\"Queue\", dayInfo.queue),\r\n        accessorFn: (r) => r.queueName,\r\n        Cell: ({ row }) => row.original.queueName || DASH,\r\n        ...cell({ fontSize: \"0.73rem\", fontWeight: 600, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"forecastCalls\",\r\n        ...withInfo(\"Expected inbound\", dayInfo.expectedInbound),\r\n        accessorFn: (r) => r.forecastCalls,\r\n        // Rounded here: a day's total is read as calls, not as a decimal.\r\n        Cell: ({ row }) =>\r\n          count(\r\n            typeof row.original.forecastCalls === \"number\"\r\n              ? Math.round(row.original.forecastCalls)\r\n              : null,\r\n          ),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.blue }),\r\n      },\r\n      {\r\n        id: \"forecastOutboundCalls\",\r\n        ...withInfo(\"Expected campaign\", dayInfo.expectedCampaign),\r\n        accessorFn: (r) => r.forecastOutboundCalls,\r\n        Cell: ({ row }) =>\r\n          count(\r\n            typeof row.original.forecastOutboundCalls === \"number\"\r\n              ? Math.round(row.original.forecastOutboundCalls)\r\n              : null,\r\n          ),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n      },\r\n      {\r\n        id: \"peakRequiredAgents\",\r\n        ...withInfo(\"Peak agents\", dayInfo.peakAgents),\r\n        accessorFn: (r) => r.peakRequiredAgents,\r\n        // \"3 at 11:00\" — the busiest slot and when it lands.\r\n        Cell: ({ row }) => {\r\n          const peak = row.original.peakRequiredAgents;\r\n          if (typeof peak !== \"number\") return DASH;\r\n          const at = row.original.peakIntervalStart\r\n            ? dayjs(row.original.peakIntervalStart)\r\n            : null;\r\n          return at && at.isValid()\r\n            ? `${peak} at ${at.format(\"HH:mm\")}`\r\n            : String(peak);\r\n        },\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.navy }),\r\n      },\r\n      {\r\n        id: \"requiredAgentHours\",\r\n        ...withInfo(\"Agent-hours needed\", dayInfo.agentHours),\r\n        accessorFn: (r) => r.requiredAgentHours,\r\n        // The rostering number: how much agent time the day takes in total.\r\n        Cell: ({ row }) =>\r\n          typeof row.original.requiredAgentHours === \"number\"\r\n            ? `${row.original.requiredAgentHours.toFixed(1)} h`\r\n            : DASH,\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.navy }),\r\n      },\r\n    ];\r\n\r\n    if (!hasActuals) return base;\r\n\r\n    return [\r\n      ...base,\r\n      {\r\n        id: \"actualCalls\",\r\n        ...withInfo(\"Actual inbound\", dayInfo.actualInbound),\r\n        accessorFn: (r) => r.actualCalls,\r\n        Cell: ({ row }) => count(row.original.actualCalls),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.blue }),\r\n      },\r\n      {\r\n        id: \"actualOutboundCalls\",\r\n        ...withInfo(\"Actual campaign\", dayInfo.actualCampaign),\r\n        accessorFn: (r) => r.actualOutboundCalls,\r\n        Cell: ({ row }) =>\r\n          count(\r\n            typeof row.original.actualOutboundCalls === \"number\"\r\n              ? Math.round(row.original.actualOutboundCalls)\r\n              : null,\r\n          ),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n      },\r\n      {\r\n        id: \"forecastError\",\r\n        ...withInfo(\"Forecast error\", dayInfo.forecastError),\r\n        accessorFn: (r) => r.forecastError,\r\n        // Inbound only, and unsigned: how far out it was, not which way.\r\n        Cell: ({ row }) => (\r\n          <Box\r\n            component=\"span\"\r\n            sx={{\r\n              color: errorColour(row.original.forecastError),\r\n              fontWeight: 700,\r\n            }}>\r\n            {plusMinusPct(row.original.forecastError)}\r\n          </Box>\r\n        ),\r\n        ...cell(tabularSx),\r\n      },\r\n      {\r\n        id: \"actualStaffedAgentHours\",\r\n        ...withInfo(\"Staffed hours\", dayInfo.staffedHours),\r\n        accessorFn: (r) => r.actualStaffedAgentHours,\r\n        Cell: ({ row }) =>\r\n          typeof row.original.actualStaffedAgentHours === \"number\"\r\n            ? `${row.original.actualStaffedAgentHours.toFixed(1)} h`\r\n            : DASH,\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.green }),\r\n      },\r\n      {\r\n        id: \"intervalsUnderstaffed\",\r\n        ...withInfo(\"Short intervals\", dayInfo.shortIntervals),\r\n        accessorFn: (r) => r.intervalsUnderstaffed,\r\n        Cell: ({ row }) => {\r\n          const short = row.original.intervalsUnderstaffed;\r\n          if (typeof short !== \"number\") return DASH;\r\n          return (\r\n            <Box\r\n              component=\"span\"\r\n              sx={{\r\n                color: short > 0 ? COLORS.red : \"#4a5568\",\r\n                fontWeight: short > 0 ? 700 : 600,\r\n              }}>\r\n              {count(short)}\r\n            </Box>\r\n          );\r\n        },\r\n        ...cell(tabularSx),\r\n      },\r\n    ];\r\n    // Rebuilt when the actual columns come or go — the column set depends on it.\r\n  }, [hasActuals, today, dayInfo]);\r\n\r\n  /* ── Slot table ── */\r\n\r\n  /**\r\n   * The slots behind one day of one queue. Its own call now: the day table is\r\n   * paged, so carrying every row's slots would be far more than anyone asked\r\n   * to see.\r\n   */\r\n  const slotsRequestId = useRef(0);\r\n\r\n  const openDay = useCallback(\r\n    async (row: ForecastDayRow) => {\r\n      const id = ++slotsRequestId.current;\r\n      setFocusedDay({\r\n        day: row.day,\r\n        queueId: row.queueId,\r\n        queueName: row.queueName,\r\n      });\r\n      setSlotsLoading(true);\r\n      setSlotsError(\"\");\r\n      setSlotsData(null);\r\n      try {\r\n        const res = await getForecastIntervals({\r\n          day: row.day,\r\n          queueId: row.queueId,\r\n          intervalMinutes,\r\n          timeFrom: timeFrom || undefined,\r\n          timeTo: timeTo || undefined,\r\n        });\r\n        if (slotsRequestId.current !== id) return;\r\n        setSlotsData(res);\r\n      } catch (err) {\r\n        if (slotsRequestId.current !== id) return;\r\n        setSlotsError(\r\n          staffingErrorMessage(err, \"Could not load this day's slots\"),\r\n        );\r\n      } finally {\r\n        if (slotsRequestId.current === id) setSlotsLoading(false);\r\n      }\r\n    },\r\n    [intervalMinutes, timeFrom, timeTo],\r\n  );\r\n\r\n  const closeDay = useCallback(() => {\r\n    // Bumped so a reply still in flight is dropped rather than reopening the panel.\r\n    slotsRequestId.current += 1;\r\n    setFocusedDay(null);\r\n    setSlotsData(null);\r\n    setSlotsError(\"\");\r\n    setSlotsLoading(false);\r\n  }, []);\r\n\r\n  // Clicking a day narrows the slots to it. Filtered here rather than\r\n  // refetched: the response already holds every slot in range.\r\n  const slots: ForecastIntervalRow[] = slotsData?.intervals ?? [];\r\n\r\n  /** Past slots carry what actually happened; a pure-future day drops those columns. */\r\n  const slotsHaveActuals = slots.some((r) => r.isPast);\r\n\r\n  const slotMinutes =\r\n    slotsData?.settings?.intervalMinutes ??\r\n    data?.settings?.intervalMinutes ??\r\n    intervalMinutes;\r\n\r\n  const slotInfo = useMemo(\r\n    () =>\r\n      forecastSlotInfo({\r\n        ...(slotsData?.settings ?? data?.settings),\r\n        intervalMinutes: slotMinutes,\r\n      }),\r\n    [slotsData?.settings, data?.settings, slotMinutes],\r\n  );\r\n\r\n  /** Everything that opens the workings goes through here, from a timestamp. */\r\n  const explainAt = useCallback(\r\n    (\r\n      timestamp: number | string | null | undefined,\r\n      queueId: number | undefined,\r\n    ) => {\r\n      const at = timestamp == null ? null : dayjs(timestamp);\r\n      if (!at || !at.isValid() || queueId == null) return;\r\n      setExplain({\r\n        day: at.format(\"YYYY-MM-DD\"),\r\n        queueId,\r\n        slot: at.format(\"HH:mm\"),\r\n        intervalMinutes: slotMinutes,\r\n      });\r\n    },\r\n    [slotMinutes],\r\n  );\r\n\r\n  const slotColumns = useMemo<MRT_ColumnDef<ForecastIntervalRow>[]>(() => {\r\n    const base: MRT_ColumnDef<ForecastIntervalRow>[] = [\r\n      {\r\n        id: \"intervalStart\",\r\n        ...withInfo(\"Time\", slotInfo.time),\r\n        accessorFn: (r) => r.intervalStart,\r\n        // Local wall-clock, never converted. The date is dropped once a single\r\n        // day is in view — it is in the chip above.\r\n        Cell: ({ row }) => {\r\n          const start = row.original.intervalStart\r\n            ? dayjs(row.original.intervalStart)\r\n            : null;\r\n          if (!start || !start.isValid()) return DASH;\r\n          const window = `${start.format(\"HH:mm\")}–${start.add(slotMinutes, \"minute\").format(\"HH:mm\")}`;\r\n          return focusedDay\r\n            ? window\r\n            : `${start.format(\"ddd D MMM\")}, ${window}`;\r\n        },\r\n        ...cell({ fontSize: \"0.73rem\", color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"queueName\",\r\n        header: \"Queue\",\r\n        accessorFn: (r) => r.queueName,\r\n        Cell: ({ row }) => row.original.queueName || DASH,\r\n        ...cell({ fontSize: \"0.73rem\", fontWeight: 600, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"forecastCalls\",\r\n        ...withInfo(\"Expected inbound\", slotInfo.expectedInbound),\r\n        accessorFn: (r) => r.forecastCalls,\r\n        // One decimal, because 0.4 means \"about one call every couple of\r\n        // weeks in this slot\" — rounding that to zero throws it away.\r\n        Cell: ({ row }) => decimal(row.original.forecastCalls),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.blue }),\r\n      },\r\n      {\r\n        id: \"forecastHandleSeconds\",\r\n        ...withInfo(\"Handle time\", slotInfo.handleTime),\r\n        accessorFn: (r) => r.forecastHandleSeconds,\r\n        Cell: ({ row }) => duration(row.original.forecastHandleSeconds),\r\n        ...cell({ ...tabularSx, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"requiredAgents\",\r\n        ...withInfo(\"Inbound needed\", slotInfo.inboundNeeded),\r\n        accessorFn: (r) => r.requiredAgents,\r\n        Cell: ({ row }) => count(row.original.requiredAgents),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: \"#4a5568\" }),\r\n      },\r\n      {\r\n        id: \"weeksUsed\",\r\n        ...withInfo(\"History\", slotInfo.history),\r\n        accessorFn: (r) => r.weeksUsed,\r\n        // A prediction from two weeks is a guess worth labelling as one.\r\n        Cell: ({ row }) => {\r\n          const weeks = row.original.weeksUsed;\r\n          if (typeof weeks !== \"number\") return DASH;\r\n          if (weeks >= LOW_HISTORY_WEEKS) return `${weeks} wks`;\r\n          return (\r\n            <Tooltip\r\n              arrow\r\n              title={`Only ${weeks} past week${weeks === 1 ? \"\" : \"s\"} had calls in this slot`}>\r\n              <Chip\r\n                size=\"small\"\r\n                label=\"Low history\"\r\n                sx={{\r\n                  height: 18,\r\n                  fontSize: \"0.62rem\",\r\n                  fontWeight: 700,\r\n                  backgroundColor: \"#eceeed\",\r\n                  color: COLORS.grey,\r\n                }}\r\n              />\r\n            </Tooltip>\r\n          );\r\n        },\r\n        ...cell(tabularSx),\r\n      },\r\n      {\r\n        id: \"forecastOutboundCalls\",\r\n        ...withInfo(\"Expected campaign\", slotInfo.expectedCampaign),\r\n        accessorFn: (r) => r.forecastOutboundCalls,\r\n        Cell: ({ row }) => decimal(row.original.forecastOutboundCalls),\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n      },\r\n      {\r\n        id: \"requiredTotalAgents\",\r\n        ...withInfo(\"Total needed\", slotInfo.totalNeeded),\r\n        accessorFn: (r) => r.requiredTotalAgents,\r\n        Cell: ({ row }) => count(row.original.requiredTotalAgents),\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.navy }),\r\n      },\r\n    ];\r\n\r\n    /** Opens the workings for this slot. First column, where the eye starts. */\r\n    const whyColumn: MRT_ColumnDef<ForecastIntervalRow> = {\r\n      id: \"why\",\r\n      header: \"Why\",\r\n      enableSorting: false,\r\n      accessorFn: () => \"\",\r\n      Cell: ({ row }) => {\r\n        const start = row.original.intervalStart\r\n          ? dayjs(row.original.intervalStart)\r\n          : null;\r\n        if (!start || !start.isValid()) return null;\r\n        return (\r\n          <Tooltip arrow title=\"Why this forecast?\">\r\n            <IconButton\r\n              size=\"small\"\r\n              onClick={(e) => {\r\n                // The row opens the detail drawer; this opens the workings.\r\n                e.stopPropagation();\r\n                explainAt(row.original.intervalStart, row.original.queueId);\r\n              }}\r\n              sx={{ color: COLORS.grey, \"&:hover\": { color: COLORS.teal } }}>\r\n              <HelpOutlineIcon sx={{ fontSize: 16 }} />\r\n            </IconButton>\r\n          </Tooltip>\r\n        );\r\n      },\r\n      ...cell({}),\r\n    };\r\n\r\n    if (!slotsHaveActuals) return [whyColumn, ...base];\r\n\r\n    return [\r\n      whyColumn,\r\n      ...base,\r\n      {\r\n        id: \"actualCalls\",\r\n        ...withInfo(\"Actual inbound\", slotInfo.actualInbound),\r\n        accessorFn: (r) => r.actualCalls,\r\n        Cell: ({ row }) =>\r\n          row.original.isPast ? count(row.original.actualCalls) : DASH,\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.blue }),\r\n      },\r\n      {\r\n        id: \"actualOutboundCalls\",\r\n        ...withInfo(\"Actual campaign\", slotInfo.actualCampaign),\r\n        accessorFn: (r) => r.actualOutboundCalls,\r\n        Cell: ({ row }) =>\r\n          row.original.isPast\r\n            ? decimal(row.original.actualOutboundCalls)\r\n            : DASH,\r\n        ...cell({ ...tabularSx, fontWeight: 600, color: COLORS.teal }),\r\n      },\r\n      {\r\n        id: \"actualStaffedAgents\",\r\n        ...withInfo(\"Staffed\", slotInfo.staffed),\r\n        accessorFn: (r) => r.actualStaffedAgents,\r\n        Cell: ({ row }) =>\r\n          row.original.isPast\r\n            ? decimal(row.original.actualStaffedAgents)\r\n            : DASH,\r\n        ...cell({ ...tabularSx, fontWeight: 700, color: COLORS.green }),\r\n      },\r\n      {\r\n        id: \"status\",\r\n        ...withInfo(\"Status\", slotInfo.status),\r\n        accessorFn: (r) => r.status,\r\n        Cell: ({ row }) =>\r\n          row.original.isPast && row.original.status ? (\r\n            <StatusChip\r\n              status={row.original.status}\r\n              tooltip={SLOT_STATUS_TOOLTIP}\r\n            />\r\n          ) : (\r\n            DASH\r\n          ),\r\n        ...cell({}),\r\n      },\r\n    ];\r\n    // Rebuilt when the actual columns come or go, and when the day in view changes.\r\n  }, [slotsHaveActuals, slotMinutes, focusedDay, slotInfo, explainAt]);\r\n\r\n  const applyQuickRange = (key: string) => {\r\n    const range = QUICK_RANGES.find((r) => r.key === key);\r\n    if (!range) return;\r\n    setQuickRange(key);\r\n    setStartDate(range.start());\r\n    setEndDate(range.end());\r\n  };\r\n\r\n  /** A manual date edit is no longer one of the quick ranges. */\r\n  const handleStartChange = (v: Dayjs | null) => {\r\n    setStartDate(v);\r\n    setQuickRange(\"\");\r\n  };\r\n  const handleEndChange = (v: Dayjs | null) => {\r\n    setEndDate(v);\r\n    setQuickRange(\"\");\r\n  };\r\n\r\n  /*\r\n   * A day opens as its own view rather than a panel under the table. Appearing\r\n   * below the fold reads as \"nothing happened\", and the filters and the day\r\n   * table are no use while reading one day's slots.\r\n   */\r\n  if (focusedDay) {\r\n    const summary = slotsData?.summary;\r\n    return (\r\n      <Box sx={{ px: 1, pb: 2 }}>\r\n        <Box\r\n          sx={{\r\n            display: \"flex\",\r\n            alignItems: \"center\",\r\n            gap: 1.25,\r\n            flexWrap: \"wrap\",\r\n            mb: 1.5,\r\n          }}>\r\n          <IconButton\r\n            onClick={closeDay}\r\n            size=\"small\"\r\n            sx={{\r\n              width: 30,\r\n              height: 30,\r\n              flexShrink: 0,\r\n              borderRadius: \"6px\",\r\n              backgroundColor: \"action.hover\",\r\n              color: \"text.secondary\",\r\n              \"&:hover\": {\r\n                backgroundColor: \"action.selected\",\r\n                color: \"text.primary\",\r\n              },\r\n            }}>\r\n            <ArrowBackIcon sx={{ fontSize: 15 }} />\r\n          </IconButton>\r\n          <Box sx={{ minWidth: 0 }}>\r\n            <Typography\r\n              sx={{\r\n                fontFamily: FONT,\r\n                fontSize: \"1rem\",\r\n                fontWeight: 700,\r\n                color: COLORS.navy,\r\n                lineHeight: 1.3,\r\n              }}>\r\n              {slotsData?.queueName ?? focusedDay.queueName} ·{\" \"}\r\n              {dayjs(focusedDay.day).format(\"ddd D MMM YYYY\")}\r\n            </Typography>\r\n            {/* The day's own roll-up, from the response rather than the row,\r\n\t\t\t\t\t\t    so the header agrees with the slots under it. */}\r\n            {summary && (\r\n              <Typography\r\n                sx={{\r\n                  fontFamily: FONT,\r\n                  fontSize: \"0.74rem\",\r\n                  color: COLORS.grey,\r\n                  mt: 0.2,\r\n                }}>\r\n                {decimal(summary.forecastCalls)} calls expected\r\n                {typeof summary.requiredAgentHours === \"number\" &&\r\n                  ` · ${summary.requiredAgentHours.toFixed(1)} agent-hours to roster`}\r\n                {typeof summary.peakRequiredAgents === \"number\" && (\r\n                  <>\r\n                    {\" · peak \"}\r\n                    {/* The busiest slot is the one a supervisor questions first,\r\n                        so it opens its own workings. */}\r\n                    {summary.peakIntervalStart ? (\r\n                      <Box\r\n                        component=\"span\"\r\n                        onClick={() =>\r\n                          explainAt(\r\n                            summary.peakIntervalStart,\r\n                            focusedDay.queueId,\r\n                          )\r\n                        }\r\n                        sx={{\r\n                          cursor: \"pointer\",\r\n                          color: COLORS.teal,\r\n                          fontWeight: 700,\r\n                          borderBottom: \"1px dotted\",\r\n                          \"&:hover\": { color: COLORS.navy },\r\n                        }}>\r\n                        {summary.peakRequiredAgents} at{\" \"}\r\n                        {dayjs(summary.peakIntervalStart).format(\"HH:mm\")}\r\n                      </Box>\r\n                    ) : (\r\n                      summary.peakRequiredAgents\r\n                    )}\r\n                  </>\r\n                )}\r\n                {typeof summary.forecastError === \"number\" &&\r\n                  ` · forecast error ${plusMinusPct(summary.forecastError)}`}\r\n              </Typography>\r\n            )}\r\n          </Box>\r\n          {slotsLoading && (\r\n            <CircularProgress size={16} sx={{ color: COLORS.blue }} />\r\n          )}\r\n          <Box sx={{ flex: 1 }} />\r\n          <AppButton\r\n            variant=\"outlined\"\r\n            onClick={closeDay}\r\n            sx={{ height: FILTER_HEIGHT }}>\r\n            Back to days\r\n          </AppButton>\r\n        </Box>\r\n\r\n        {slotsError && (\r\n          <Alert\r\n            severity=\"error\"\r\n            sx={{ mb: 1, fontFamily: FONT, fontSize: \"0.78rem\" }}\r\n            onClose={() => setSlotsError(\"\")}>\r\n            {slotsError}\r\n          </Alert>\r\n        )}\r\n\r\n        {slots.length > 0 && (\r\n          <ForecastChart\r\n            rows={slots}\r\n            slotMinutes={slotMinutes}\r\n            queueName={slotsData?.queueName ?? focusedDay.queueName}\r\n            day={focusedDay.day}\r\n            onSlotClick={(t) => explainAt(t, focusedDay.queueId)}\r\n          />\r\n        )}\r\n\r\n        <AppTable\r\n          // Keyed on the column set itself: MRT keeps the order it first saw, so\r\n          // a column arriving — or moving to the front — needs a remount rather\r\n          // than just a new array.\r\n          key={slotColumns.map((c) => c.id).join(\"|\")}\r\n          columns={slotColumns}\r\n          data={slots}\r\n          layoutMode=\"semantic\"\r\n          enableTopToolbar={false}\r\n          // One day of one queue: 96 slots at most, so nothing to page.\r\n          enableBottomToolbar={false}\r\n          enablePagination={false}\r\n          enableColumnActions={false}\r\n          enableColumnFilters={false}\r\n          enableColumnDragging={false}\r\n          enableHiding={false}\r\n          enableDensityToggle={false}\r\n          enableFullScreenToggle={false}\r\n          enableStickyHeader\r\n          state={{ isLoading: slotsLoading }}\r\n          localization={\r\n            {\r\n              noRecordsToDisplay:\r\n                \"No slots with expected or actual calls in this day.\",\r\n            } as any\r\n          }\r\n          muiCircularProgressProps={{ sx: { color: COLORS.blue }, size: 28 }}\r\n          muiTablePaperProps={{\r\n            elevation: 0,\r\n            sx: {\r\n              borderRadius: \"8px\",\r\n              border: \"1px solid #e0e7ef\",\r\n              overflow: \"hidden\",\r\n              boxShadow: \"none\",\r\n            },\r\n          }}\r\n          muiTableProps={{\r\n            sx: {\r\n              fontFamily: FONT,\r\n              border: \"none\",\r\n              borderRadius: 0,\r\n              boxShadow: \"none\",\r\n            },\r\n          }}\r\n          muiTableContainerProps={{ sx: { maxHeight: \"calc(100vh - 320px)\" } }}\r\n          muiTableHeadProps={{ sx: { boxShadow: \"none\" } }}\r\n          muiTableHeadRowProps={{ sx: { boxShadow: \"none\" } }}\r\n          // A slot holds more than its columns show; the rest is in the drawer.\r\n          muiTableBodyRowProps={({ row }) => ({\r\n            onClick: () => setSlotRow(row.original),\r\n            sx: {\r\n              cursor: \"pointer\",\r\n              backgroundColor:\r\n                row.original.status === \"UNDERSTAFFED\" ? \"#fdf4f4\" : \"#fff\",\r\n              \"&:hover\": {\r\n                backgroundColor:\r\n                  row.original.status === \"UNDERSTAFFED\"\r\n                    ? \"#fbe9e9\"\r\n                    : \"#f0f7f8\",\r\n              },\r\n            },\r\n          })}\r\n        />\r\n\r\n        <SlotDetailDrawer\r\n          selection={slotRow ? { kind: \"forecast\", row: slotRow } : null}\r\n          slotMinutes={slotMinutes}\r\n          onClose={() => setSlotRow(null)}\r\n        />\r\n\r\n        <ForecastExplainDrawer\r\n          params={explain}\r\n          queueName={slotsData?.queueName ?? focusedDay.queueName}\r\n          onClose={() => setExplain(null)}\r\n        />\r\n      </Box>\r\n    );\r\n  }\r\n\r\n  return (\r\n    <LocalizationProvider dateAdapter={AdapterDayjs}>\r\n      {/* No bottom padding: the day table ends in its own pagination bar, and\r\n\t\t\t    the gap under it only pushed the page into a scroll. */}\r\n      <Box sx={{ px: 1 }}>\r\n        <Box\r\n          sx={{\r\n            display: \"flex\",\r\n            alignItems: \"center\",\r\n            gap: 1,\r\n            flexWrap: \"wrap\",\r\n            rowGap: 1,\r\n            px: 1,\r\n            py: 0.6,\r\n          }}>\r\n          {/* Its own pickers rather than the shared DateRangePicker, which\r\n\t\t\t\t\t    clamps every value back to today — fine for a report, useless\r\n\t\t\t\t\t    for a forecast. */}\r\n          <DatePicker\r\n            value={startDate}\r\n            onChange={(v) => handleStartChange(v as Dayjs | null)}\r\n            maxDate={maxEnd}\r\n            slotProps={{ textField: { size: \"small\" as const, sx: pickerSx } }}\r\n          />\r\n          <Typography\r\n            sx={{ fontFamily: FONT, fontSize: \"0.75rem\", color: COLORS.grey }}>\r\n            to\r\n          </Typography>\r\n          <DatePicker\r\n            value={endDate}\r\n            onChange={(v) => handleEndChange(v as Dayjs | null)}\r\n            minDate={startDate ?? undefined}\r\n            maxDate={maxEnd}\r\n            slotProps={{ textField: { size: \"small\" as const, sx: pickerSx } }}\r\n          />\r\n\r\n          {QUICK_RANGES.map((r) => (\r\n            <AppButton\r\n              key={r.key}\r\n              variant={quickRange === r.key ? \"primary\" : \"outlined\"}\r\n              onClick={() => applyQuickRange(r.key)}\r\n              sx={{ height: FILTER_HEIGHT, whiteSpace: \"nowrap\" }}>\r\n              {r.label}\r\n            </AppButton>\r\n          ))}\r\n\r\n          <IntervalSelect\r\n            value={intervalMinutes}\r\n            onChange={setIntervalMinutes}\r\n          />\r\n          <QueueMultiSelect\r\n            choices={queueChoices}\r\n            value={selectedQueues}\r\n            onChange={setSelectedQueues}\r\n          />\r\n\r\n          <SearchBar\r\n            value={searchInput}\r\n            onChange={setSearchInput}\r\n            placeholder=\"Search queue...\"\r\n            width={170}\r\n            borderRadius=\"6px\"\r\n            sx={{\r\n              \"& .MuiOutlinedInput-root\": {\r\n                height: FILTER_HEIGHT,\r\n                fontSize: 13,\r\n              },\r\n            }}\r\n          />\r\n\r\n          <AppButton\r\n            startIcon={\r\n              loading ? (\r\n                <CircularProgress size={14} sx={{ color: \"#fff\" }} />\r\n              ) : (\r\n                <RefreshIcon sx={{ fontSize: \"0.9rem !important\" }} />\r\n              )\r\n            }\r\n            disabled={loading}\r\n            onClick={fetchForecast}\r\n            sx={{ height: FILTER_HEIGHT }}>\r\n            Refresh\r\n          </AppButton>\r\n\r\n          <Box sx={{ flex: 1 }} />\r\n\r\n          {/* How the prediction is made, in the tenant's own numbers. */}\r\n          <Tooltip arrow title={forecastTooltip(data?.settings)}>\r\n            <Box\r\n              sx={{\r\n                display: \"inline-flex\",\r\n                alignItems: \"center\",\r\n                gap: 0.4,\r\n                cursor: \"help\",\r\n                color: COLORS.grey,\r\n              }}>\r\n              <InfoOutlinedIcon sx={{ fontSize: 16 }} />\r\n              <Typography sx={{ fontFamily: FONT, fontSize: \"0.72rem\" }}>\r\n                How this is forecast\r\n              </Typography>\r\n            </Box>\r\n          </Tooltip>\r\n        </Box>\r\n\r\n        <WeekdayAndTimeFilters\r\n          weekdays={weekdays}\r\n          onWeekdaysChange={setWeekdays}\r\n          timeFrom={timeFrom}\r\n          timeTo={timeTo}\r\n          onTimeFromChange={setTimeFrom}\r\n          onTimeToChange={setTimeTo}\r\n        />\r\n\r\n        {error && (\r\n          <Alert\r\n            severity=\"error\"\r\n            sx={{ my: 1, fontFamily: FONT, fontSize: \"0.78rem\" }}\r\n            onClose={() => setError(\"\")}>\r\n            {error}\r\n          </Alert>\r\n        )}\r\n\r\n        {/* Only on an unfiltered first page: a search that matches nothing is\r\n\t\t\t\t    not the same as a tenant with no history. */}\r\n        {!error && !loading && data && data.rows.length === 0 && !search && (\r\n          <Typography\r\n            sx={{\r\n              fontFamily: FONT,\r\n              fontSize: \"0.78rem\",\r\n              color: COLORS.grey,\r\n              px: 1,\r\n              py: 3,\r\n              textAlign: \"center\",\r\n            }}>\r\n            No call history for these queues yet. The forecast needs at least\r\n            one past week of calls.\r\n          </Typography>\r\n        )}\r\n\r\n        {/* ── The roster view ──────────────────────────────────────────────\r\n\t\t\t\t    One row per queue per day. The five \"actual\" columns only exist\r\n\t\t\t\t    once the range reaches into the past. */}\r\n        <Box\r\n          sx={{\r\n            display: \"flex\",\r\n            alignItems: \"center\",\r\n            gap: 1,\r\n            flexWrap: \"wrap\",\r\n            mx: 1,\r\n            mt: 1,\r\n            mb: 0.8,\r\n          }}>\r\n          <Typography\r\n            sx={{\r\n              fontFamily: FONT,\r\n              fontSize: \"0.85rem\",\r\n              fontWeight: 700,\r\n              color: COLORS.navy,\r\n            }}>\r\n            Forecast by day\r\n          </Typography>\r\n          <Typography\r\n            sx={{ fontFamily: FONT, fontSize: \"0.72rem\", color: COLORS.grey }}>\r\n            Click a day to open its slots\r\n          </Typography>\r\n          {data?.generatedAt && (\r\n            <Typography\r\n              sx={{\r\n                fontFamily: FONT,\r\n                fontSize: \"0.72rem\",\r\n                color: COLORS.grey,\r\n              }}>\r\n              · generated {dayjs(data.generatedAt).format(\"D MMM, HH:mm\")}\r\n            </Typography>\r\n          )}\r\n        </Box>\r\n\r\n        <AppTable\r\n          // MRT keeps the column order it first saw, so adding or dropping the\r\n          // actual columns needs a remount rather than a re-render.\r\n          key={hasActuals ? \"with-actuals\" : \"forecast-only\"}\r\n          columns={dayColumns}\r\n          data={days}\r\n          layoutMode=\"semantic\"\r\n          enableTopToolbar={false}\r\n          enableColumnActions={false}\r\n          enableColumnFilters={false}\r\n          enableColumnDragging={false}\r\n          enableHiding={false}\r\n          enableDensityToggle={false}\r\n          enableFullScreenToggle={false}\r\n          enableStickyHeader\r\n          // Paged by the server: MRT is told the total and asks for a page\r\n          // rather than slicing rows it does not have.\r\n          manualPagination\r\n          rowCount={data?.total ?? 0}\r\n          onPaginationChange={setDaysPagination}\r\n          muiPaginationProps={{ rowsPerPageOptions: PAGE_SIZE_OPTIONS }}\r\n          state={{ pagination: daysPagination, isLoading: loading }}\r\n          localization={\r\n            { noRecordsToDisplay: \"No forecast for this range.\" } as any\r\n          }\r\n          muiCircularProgressProps={{ sx: { color: COLORS.blue }, size: 28 }}\r\n          muiTablePaperProps={{\r\n            elevation: 0,\r\n            sx: {\r\n              borderRadius: \"8px\",\r\n              border: \"1px solid #e0e7ef\",\r\n              overflow: \"hidden\",\r\n              boxShadow: \"none\",\r\n              // mb: 2\r\n            },\r\n          }}\r\n          muiTableProps={{\r\n            sx: {\r\n              fontFamily: FONT,\r\n              border: \"none\",\r\n              borderRadius: 0,\r\n              boxShadow: \"none\",\r\n            },\r\n          }}\r\n          // The whole tab is the day table now, so it takes the height the\r\n          // panel underneath it used to leave behind.\r\n          muiTableContainerProps={{ sx: { maxHeight: \"calc(100vh - 340px)\" } }}\r\n          muiTableHeadProps={{ sx: { boxShadow: \"none\" } }}\r\n          muiTableHeadRowProps={{ sx: { boxShadow: \"none\" } }}\r\n          // A row is a way in: clicking it opens that day's own view. No\r\n          // selected state to show — the list is gone once one is open.\r\n          muiTableBodyRowProps={({ row }) => ({\r\n            onClick: () => openDay(row.original),\r\n            sx: {\r\n              cursor: \"pointer\",\r\n              backgroundColor:\r\n                (row.original.intervalsUnderstaffed ?? 0) > 0\r\n                  ? \"#fdf4f4\"\r\n                  : \"#fff\",\r\n              \"&:hover\": {\r\n                backgroundColor:\r\n                  (row.original.intervalsUnderstaffed ?? 0) > 0\r\n                    ? \"#fbe9e9\"\r\n                    : \"#f0f7f8\",\r\n              },\r\n            },\r\n          })}\r\n          muiBottomToolbarProps={{\r\n            sx: {\r\n              borderTop: \"1px solid #eef1f6\",\r\n              boxShadow: \"none\",\r\n              fontFamily: FONT,\r\n              \"& .MuiTablePagination-displayedRows, & .MuiTablePagination-selectLabel, & .MuiInputBase-root\":\r\n                { fontFamily: FONT, fontSize: \"0.73rem\" },\r\n            },\r\n          }}\r\n        />\r\n      </Box>\r\n    </LocalizationProvider>\r\n  );\r\n}\r\n","import { useMemo } from \"react\";\r\nimport { Box, Paper, Typography } from \"@mui/material\";\r\nimport {\r\n\tCartesianGrid,\r\n\tComposedChart,\r\n\tLegend,\r\n\tLine,\r\n\tResponsiveContainer,\r\n\tTooltip,\r\n\tXAxis,\r\n\tYAxis,\r\n} from \"recharts\";\r\nimport dayjs from \"dayjs\";\r\n\r\nimport { COLORS, FONT } from \"./tableBits\";\r\nimport type { ForecastIntervalRow } from \"./forecastTypes\";\r\n\r\ninterface ChartPoint {\r\n\tt: number;\r\n\texpected: number;\r\n\tcampaign: number;\r\n\tneeded: number;\r\n\t/** Null for slots still to come — a future slot has no actual, which is not zero. */\r\n\tactual: number | null;\r\n}\r\n\r\n/** The slot's own window plus each series, rather than a bare timestamp. */\r\nfunction ChartTooltip({ active, payload, label, slotMinutes }: any) {\r\n\tif (!active || !payload?.length) return null;\r\n\tconst start = dayjs(label);\r\n\treturn (\r\n\t\t<Box sx={{ backgroundColor: \"#fff\", border: \"1px solid #e2e8f0\", borderRadius: \"8px\", px: 1.25, py: 0.75, boxShadow: \"0 6px 18px rgba(15,23,42,0.12)\" }}>\r\n\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.7rem\", color: COLORS.grey, mb: 0.3 }}>\r\n\t\t\t\t{`${start.format(\"HH:mm\")}–${start.add(slotMinutes, \"minute\").format(\"HH:mm\")}`}\r\n\t\t\t</Typography>\r\n\t\t\t{payload.map((entry: any) => (\r\n\t\t\t\t<Typography key={entry.dataKey} sx={{ fontFamily: FONT, fontSize: \"0.73rem\", fontWeight: 600, color: entry.color }}>\r\n\t\t\t\t\t{entry.name}: {typeof entry.value === \"number\" ? entry.value.toFixed(1) : \"–\"}\r\n\t\t\t\t</Typography>\r\n\t\t\t))}\r\n\t\t</Box>\r\n\t);\r\n}\r\n\r\n/**\r\n * Expected calls against what actually came, with the agents the forecast asks\r\n * for stepped over the top.\r\n *\r\n * Gaps are filled with zero rather than broken — the opposite of the staffing\r\n * chart. There, a missing slot meant the queue was closed and nothing is known;\r\n * here, a slot the forecast left out is a slot it expects no calls in, which is\r\n * a prediction worth drawing.\r\n */\r\nexport default function ForecastChart({\r\n\trows, slotMinutes, queueName, day, onSlotClick,\r\n}: {\r\n\trows: ForecastIntervalRow[];\r\n\tslotMinutes: number;\r\n\tqueueName: string;\r\n\tday: string;\r\n\t/** Clicking a point asks why that slot says what it does. */\r\n\tonSlotClick?: (timestamp: number) => void;\r\n}) {\r\n\tconst points = useMemo<ChartPoint[]>(() => {\r\n\t\tconst valid = rows.filter((r) => r.intervalStart && dayjs(r.intervalStart).isValid());\r\n\t\tif (valid.length === 0) return [];\r\n\r\n\t\tconst byTime = new Map<number, ForecastIntervalRow>();\r\n\t\tfor (const row of valid) byTime.set(dayjs(row.intervalStart).valueOf(), row);\r\n\r\n\t\tconst stamps = [...byTime.keys()].sort((a, b) => a - b);\r\n\t\tconst step = slotMinutes * 60_000;\r\n\t\tconst now = Date.now();\r\n\r\n\t\tconst out: ChartPoint[] = [];\r\n\t\tfor (let t = stamps[0]; t <= stamps[stamps.length - 1]; t += step) {\r\n\t\t\tconst row = byTime.get(t);\r\n\t\t\tif (row) {\r\n\t\t\t\tout.push({\r\n\t\t\t\t\tt,\r\n\t\t\t\t\texpected: row.forecastCalls ?? 0,\r\n\t\t\t\t\tcampaign: row.forecastOutboundCalls ?? 0,\r\n\t\t\t\t\tneeded: row.requiredTotalAgents ?? 0,\r\n\t\t\t\t\tactual: row.isPast ? (row.actualCalls ?? 0) : null,\r\n\t\t\t\t});\r\n\t\t\t} else {\r\n\t\t\t\t// Left out of the response because nothing is expected and nothing\r\n\t\t\t\t// happened — zero on both counts, and still null ahead of now.\r\n\t\t\t\tout.push({ t, expected: 0, campaign: 0, needed: 0, actual: t < now ? 0 : null });\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn out;\r\n\t}, [rows, slotMinutes]);\r\n\r\n\tif (points.length === 0) return null;\r\n\r\n\tconst hasActual = points.some((p) => p.actual !== null);\r\n\r\n\treturn (\r\n\t\t<Paper\r\n\t\t\televation={0}\r\n\t\t\tsx={{ borderRadius: \"8px\", border: \"1px solid #e0e7ef\", p: { xs: 1.5, sm: 2 }, mb: 2 }}\r\n\t\t>\r\n\t\t\t<Box sx={{ display: \"flex\", alignItems: \"center\", gap: 1, flexWrap: \"wrap\", mb: 1 }}>\r\n\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.85rem\", fontWeight: 700, color: COLORS.navy }}>\r\n\t\t\t\t\tExpected calls and agents\r\n\t\t\t\t</Typography>\r\n\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.72rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t{queueName} · {dayjs(day).format(\"ddd D MMM\")} · calls per {slotMinutes} min, agents on the same scale\r\n\t\t\t\t\t{onSlotClick && \" · click a slot to see why\"}\r\n\t\t\t\t</Typography>\r\n\t\t\t</Box>\r\n\r\n\t\t\t<Box sx={{ height: 260 }}>\r\n\t\t\t\t<ResponsiveContainer width=\"100%\" height=\"100%\">\r\n\t\t\t\t\t<ComposedChart\r\n\t\t\t\t\t\tdata={points}\r\n\t\t\t\t\t\tmargin={{ top: 8, right: 12, bottom: 4, left: -18 }}\r\n\t\t\t\t\t\t// Clicking anywhere on the plot opens the workings for the slot\r\n\t\t\t\t\t\t// under the cursor — the same panel the table's ⓘ opens.\r\n\t\t\t\t\t\tonClick={(state: any) => {\r\n\t\t\t\t\t\t\tconst label = state?.activeLabel;\r\n\t\t\t\t\t\t\tif (onSlotClick && label != null) onSlotClick(Number(label));\r\n\t\t\t\t\t\t}}\r\n\t\t\t\t\t\tstyle={onSlotClick ? { cursor: \"pointer\" } : undefined}\r\n\t\t\t\t\t>\r\n\t\t\t\t\t\t<CartesianGrid stroke=\"#eef1f6\" vertical={false} />\r\n\t\t\t\t\t\t<XAxis\r\n\t\t\t\t\t\t\tdataKey=\"t\"\r\n\t\t\t\t\t\t\ttype=\"number\"\r\n\t\t\t\t\t\t\tscale=\"time\"\r\n\t\t\t\t\t\t\tdomain={[\"dataMin\", \"dataMax\"]}\r\n\t\t\t\t\t\t\ttickFormatter={(t: number) => dayjs(t).format(\"HH:mm\")}\r\n\t\t\t\t\t\t\ttick={{ fontFamily: FONT, fontSize: 10, fill: COLORS.grey }}\r\n\t\t\t\t\t\t\tstroke=\"#cfd8e3\"\r\n\t\t\t\t\t\t\tminTickGap={28}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<YAxis\r\n\t\t\t\t\t\t\tallowDecimals={false}\r\n\t\t\t\t\t\t\ttick={{ fontFamily: FONT, fontSize: 10, fill: COLORS.grey }}\r\n\t\t\t\t\t\t\tstroke=\"#cfd8e3\"\r\n\t\t\t\t\t\t\twidth={44}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<Tooltip content={<ChartTooltip slotMinutes={slotMinutes} />} />\r\n\t\t\t\t\t\t<Legend wrapperStyle={{ fontFamily: FONT, fontSize: \"0.72rem\" }} />\r\n\r\n\t\t\t\t\t\t<Line\r\n\t\t\t\t\t\t\tname=\"Expected inbound\"\r\n\t\t\t\t\t\t\ttype=\"monotone\"\r\n\t\t\t\t\t\t\tdataKey=\"expected\"\r\n\t\t\t\t\t\t\tstroke={COLORS.blue}\r\n\t\t\t\t\t\t\tstrokeWidth={2}\r\n\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t<Line\r\n\t\t\t\t\t\t\tname=\"Expected campaign\"\r\n\t\t\t\t\t\t\ttype=\"monotone\"\r\n\t\t\t\t\t\t\tdataKey=\"campaign\"\r\n\t\t\t\t\t\t\tstroke={COLORS.teal}\r\n\t\t\t\t\t\t\tstrokeWidth={1}\r\n\t\t\t\t\t\t\tstrokeDasharray=\"4 3\"\r\n\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t{hasActual && (\r\n\t\t\t\t\t\t\t<Line\r\n\t\t\t\t\t\t\t\tname=\"Actual inbound\"\r\n\t\t\t\t\t\t\t\ttype=\"monotone\"\r\n\t\t\t\t\t\t\t\tdataKey=\"actual\"\r\n\t\t\t\t\t\t\t\tstroke={COLORS.green}\r\n\t\t\t\t\t\t\t\tstrokeWidth={2}\r\n\t\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\t\t// Future slots are null and stay unplotted rather than\r\n\t\t\t\t\t\t\t\t// being joined across to the last real reading.\r\n\t\t\t\t\t\t\t\tconnectNulls={false}\r\n\t\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t{/* A requirement holds for the whole slot, so it steps. */}\r\n\t\t\t\t\t\t<Line\r\n\t\t\t\t\t\t\tname=\"Agents needed\"\r\n\t\t\t\t\t\t\ttype=\"stepAfter\"\r\n\t\t\t\t\t\t\tdataKey=\"needed\"\r\n\t\t\t\t\t\t\tstroke={COLORS.navy}\r\n\t\t\t\t\t\t\tstrokeWidth={1.5}\r\n\t\t\t\t\t\t\tstrokeOpacity={0.75}\r\n\t\t\t\t\t\t\tdot={false}\r\n\t\t\t\t\t\t\tisAnimationActive={false}\r\n\t\t\t\t\t\t/>\r\n\t\t\t\t\t</ComposedChart>\r\n\t\t\t\t</ResponsiveContainer>\r\n\t\t\t</Box>\r\n\t\t</Paper>\r\n\t);\r\n}\r\n","import { useEffect, useState } from \"react\";\r\nimport {\r\n\tAlert, Box, Chip, CircularProgress, Drawer, IconButton, Table, TableBody, TableCell,\r\n\tTableHead, TableRow, Tooltip, Typography,\r\n} from \"@mui/material\";\r\nimport CloseIcon from \"@mui/icons-material/Close\";\r\nimport dayjs from \"dayjs\";\r\n\r\nimport Markdown from \"./Markdown\";\r\n\r\nimport { COLORS, DASH, FONT, count, decimal, duration, pct0 } from \"./tableBits\";\r\nimport { STATUS_STYLE, type StaffingStatus } from \"./types\";\r\nimport {\r\n\tHANDLE_SOURCE_LABEL,\r\n\ttype ForecastExplainParams,\r\n\ttype ForecastExplainResponse,\r\n\ttype HandleSource,\r\n} from \"./forecastTypes\";\r\nimport { getForecastExplain } from \"./useForecast\";\r\nimport { staffingErrorMessage } from \"./useStaffing\";\r\n\r\n/** Sits below the app bar, like the other panels on this page. */\r\nconst APP_HEADER_H = 60;\r\n\r\nconst cellSx = {\r\n\tfontFamily: FONT,\r\n\tfontSize: \"0.74rem\",\r\n\tborderBottom: \"1px solid #eef1f6\",\r\n\tpy: 0.6,\r\n\tpx: 1,\r\n};\r\n\r\nconst headCellSx = {\r\n\t...cellSx,\r\n\tfontWeight: 700,\r\n\tcolor: \"#4a5568\",\r\n\tbackgroundColor: \"#f6f8fa\",\r\n};\r\n\r\nfunction SectionTitle({ children }: { children: React.ReactNode }) {\r\n\treturn (\r\n\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", fontWeight: 700, color: COLORS.navy, mb: 0.75 }}>\r\n\t\t\t{children}\r\n\t\t</Typography>\r\n\t);\r\n}\r\n\r\n/**\r\n * Why a forecast slot says what it says.\r\n *\r\n * The forecast is a weighted average of past weeks, and a number with no\r\n * workings behind it is a number nobody trusts enough to roster against. This\r\n * shows the weeks it used, the ones it skipped and why, the handle time and\r\n * where that came from, the campaigns adding calls, and the Erlang-C ladder\r\n * that turned all of it into a number of agents.\r\n */\r\nexport default function ForecastExplainDrawer({\r\n\tparams, queueName, onClose,\r\n}: {\r\n\tparams: ForecastExplainParams | null;\r\n\tqueueName?: string;\r\n\tonClose: () => void;\r\n}) {\r\n\tconst [data, setData] = useState<ForecastExplainResponse | null>(null);\r\n\tconst [loading, setLoading] = useState(false);\r\n\tconst [error, setError] = useState(\"\");\r\n\r\n\tuseEffect(() => {\r\n\t\tif (!params) return;\r\n\t\tlet live = true;\r\n\t\tsetLoading(true);\r\n\t\tsetError(\"\");\r\n\t\tsetData(null);\r\n\t\tgetForecastExplain(params)\r\n\t\t\t.then((res) => { if (live) setData(res); })\r\n\t\t\t.catch((err) => { if (live) setError(staffingErrorMessage(err, \"Could not explain this slot\")); })\r\n\t\t\t.finally(() => { if (live) setLoading(false); });\r\n\t\t// Closing mid-flight must not land the answer in an empty drawer.\r\n\t\treturn () => { live = false; };\r\n\t}, [params]);\r\n\r\n\tconst row = data?.row;\r\n\tconst inbound = data?.inbound;\r\n\tconst campaigns = data?.campaigns ?? [];\r\n\tconst sizing = data?.sizing;\r\n\tconst actuals = data?.actuals;\r\n\tconst status = actuals?.status as StaffingStatus | undefined;\r\n\r\n\tconst slotWindow = params\r\n\t\t? `${params.slot}–${dayjs(`${params.day} ${params.slot}`).add(params.intervalMinutes, \"minute\").format(\"HH:mm\")}`\r\n\t\t: \"\";\r\n\r\n\treturn (\r\n\t\t<Drawer\r\n\t\t\tanchor=\"right\"\r\n\t\t\topen={!!params}\r\n\t\t\tonClose={onClose}\r\n\t\t\tslotProps={{\r\n\t\t\t\tpaper: {\r\n\t\t\t\t\tsx: {\r\n\t\t\t\t\t\twidth: { xs: \"100%\", sm: 440 },\r\n\t\t\t\t\t\ttop: `${APP_HEADER_H}px`,\r\n\t\t\t\t\t\theight: `calc(100% - ${APP_HEADER_H}px)`,\r\n\t\t\t\t\t\tboxShadow: \"-4px 0 24px rgba(0,0,0,0.12)\",\r\n\t\t\t\t\t\tborderLeft: \"1px solid #e0e6ed\",\r\n\t\t\t\t\t},\r\n\t\t\t\t},\r\n\t\t\t}}\r\n\t\t>\r\n\t\t\t{params && (\r\n\t\t\t\t<Box sx={{ display: \"flex\", flexDirection: \"column\", height: \"100%\", bgcolor: \"#fafbfc\", fontFamily: FONT }}>\r\n\t\t\t\t\t{/* Header */}\r\n\t\t\t\t\t<Box sx={{ px: 2.5, pt: 2.5, pb: 2, borderBottom: \"1px solid #eee\", bgcolor: \"#fff\" }}>\r\n\t\t\t\t\t\t<Box sx={{ display: \"flex\", alignItems: \"flex-start\", justifyContent: \"space-between\", gap: 1 }}>\r\n\t\t\t\t\t\t\t<Box sx={{ minWidth: 0, flex: 1 }}>\r\n\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontWeight: 700, fontSize: \"1.05rem\", color: \"#1a1a1a\", lineHeight: 1.25 }}>\r\n\t\t\t\t\t\t\t\t\tWhy this forecast?\r\n\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.78rem\", color: COLORS.grey, mt: 0.3 }}>\r\n\t\t\t\t\t\t\t\t\t{queueName ?? row?.queueName ?? `Queue ${params.queueId}`} · {dayjs(params.day).format(\"ddd D MMM YYYY\")} · {slotWindow}\r\n\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t<Tooltip title=\"Close\">\r\n\t\t\t\t\t\t\t\t<IconButton\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tonClick={onClose}\r\n\t\t\t\t\t\t\t\t\tsx={{ color: \"#555\", bgcolor: \"#f2f4f7\", \"&:hover\": { bgcolor: \"#e4e7eb\", color: \"#111\" } }}\r\n\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t<CloseIcon fontSize=\"small\" />\r\n\t\t\t\t\t\t\t\t</IconButton>\r\n\t\t\t\t\t\t\t</Tooltip>\r\n\t\t\t\t\t\t</Box>\r\n\r\n\t\t\t\t\t\t{/* The headline numbers, from the slot the response carries — no second call. */}\r\n\t\t\t\t\t\t{row && (\r\n\t\t\t\t\t\t\t<Box sx={{ display: \"flex\", alignItems: \"center\", flexWrap: \"wrap\", gap: 0.75, mt: 1.25 }}>\r\n\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tlabel={`${decimal(row.forecastCalls)} calls expected`}\r\n\t\t\t\t\t\t\t\t\tsx={{ height: 22, fontSize: \"0.68rem\", fontWeight: 700, backgroundColor: \"#e8f0fb\", color: COLORS.blue }}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\tlabel={`${count(row.requiredTotalAgents)} agents needed`}\r\n\t\t\t\t\t\t\t\t\tsx={{ height: 22, fontSize: \"0.68rem\", fontWeight: 700, backgroundColor: \"#e6f0f2\", color: COLORS.teal }}\r\n\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\t\t\t\t\t</Box>\r\n\r\n\t\t\t\t\t{/* Body */}\r\n\t\t\t\t\t<Box sx={{ flex: 1, overflowY: \"auto\", px: 2.5, py: 2, display: \"flex\", flexDirection: \"column\", gap: 2.5 }}>\r\n\t\t\t\t\t\t{loading && (\r\n\t\t\t\t\t\t\t<Box sx={{ display: \"flex\", justifyContent: \"center\", py: 4 }}>\r\n\t\t\t\t\t\t\t\t<CircularProgress size={24} sx={{ color: COLORS.blue }} />\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{error && (\r\n\t\t\t\t\t\t\t<Alert severity=\"error\" sx={{ fontFamily: FONT, fontSize: \"0.78rem\" }}>{error}</Alert>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{/* The whole explanation, up top — everything below is the detail.\r\n\t\t\t\t\t\t    The markdown version when there is one, the plain text otherwise. */}\r\n\t\t\t\t\t\t{(data?.summaryMarkdown || data?.summary) && (\r\n\t\t\t\t\t\t\t<Box sx={{ backgroundColor: \"#fff\", border: \"1px solid #e0e7ef\", borderRadius: \"8px\", p: 1.5 }}>\r\n\t\t\t\t\t\t\t\t<Markdown text={data.summaryMarkdown || data.summary || \"\"} />\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{/* ── The weeks behind it ── */}\r\n\t\t\t\t\t\t{inbound?.weeks && inbound.weeks.length > 0 && (\r\n\t\t\t\t\t\t\t<Box>\r\n\t\t\t\t\t\t\t\t<SectionTitle>Same slot, past weeks</SectionTitle>\r\n\t\t\t\t\t\t\t\t<Table size=\"small\" sx={{ backgroundColor: \"#fff\", border: \"1px solid #e0e7ef\", borderRadius: \"8px\" }}>\r\n\t\t\t\t\t\t\t\t\t<TableHead>\r\n\t\t\t\t\t\t\t\t\t\t<TableRow>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx}>Date</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"center\">Weight</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"right\">Calls</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t</TableRow>\r\n\t\t\t\t\t\t\t\t\t</TableHead>\r\n\t\t\t\t\t\t\t\t\t<TableBody>\r\n\t\t\t\t\t\t\t\t\t\t{inbound.weeks.map((week) => {\r\n\t\t\t\t\t\t\t\t\t\t\t/* A skipped day is greyed rather than dropped: that it was\r\n\t\t\t\t\t\t\t\t\t\t\t   skipped, and why, is part of the explanation. */\r\n\t\t\t\t\t\t\t\t\t\t\tconst body = (\r\n\t\t\t\t\t\t\t\t\t\t\t\t<TableRow key={week.day} sx={{ opacity: week.used ? 1 : 0.45 }}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={cellSx}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{week.day ? dayjs(week.day).format(\"ddd D MMM\") : DASH}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontVariantNumeric: \"tabular-nums\" }} align=\"center\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t×{week.weight}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontWeight: 600, fontVariantNumeric: \"tabular-nums\" }} align=\"right\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{count(week.calls)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t</TableRow>\r\n\t\t\t\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\t\t\t\t\treturn week.note\r\n\t\t\t\t\t\t\t\t\t\t\t\t? <Tooltip key={week.day} arrow title={week.note} followCursor>{body}</Tooltip>\r\n\t\t\t\t\t\t\t\t\t\t\t\t: body;\r\n\t\t\t\t\t\t\t\t\t\t})}\r\n\t\t\t\t\t\t\t\t\t</TableBody>\r\n\t\t\t\t\t\t\t\t</Table>\r\n\t\t\t\t\t\t\t\t{inbound.formula && (\r\n\t\t\t\t\t\t\t\t\t<Typography\r\n\t\t\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\t\tfontFamily: \"ui-monospace, SFMono-Regular, Menlo, monospace\",\r\n\t\t\t\t\t\t\t\t\t\t\tfontSize: \"0.72rem\", color: \"#4a5568\", mt: 1, lineHeight: 1.6,\r\n\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: \"#f6f8fa\", border: \"1px solid #eef1f6\", borderRadius: \"6px\", px: 1, py: 0.75,\r\n\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t{inbound.formula}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{/* ── Handle time ── */}\r\n\t\t\t\t\t\t{inbound && (typeof inbound.handleSeconds === \"number\" || inbound.handleSource) && (\r\n\t\t\t\t\t\t\t<Box>\r\n\t\t\t\t\t\t\t\t<SectionTitle>Handle time</SectionTitle>\r\n\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", color: \"#334155\", lineHeight: 1.6 }}>\r\n\t\t\t\t\t\t\t\t\t<Box component=\"span\" sx={{ fontWeight: 700 }}>{duration(inbound.handleSeconds)}</Box>\r\n\t\t\t\t\t\t\t\t\t{inbound.handleSource && ` — ${HANDLE_SOURCE_LABEL[inbound.handleSource as HandleSource] ?? inbound.handleSource}`}\r\n\t\t\t\t\t\t\t\t\t{typeof inbound.handleCalls === \"number\" && `, from ${count(inbound.handleCalls)} answered calls`}\r\n\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{/* ── Campaigns ── hidden when none reach this queue. */}\r\n\t\t\t\t\t\t{campaigns.length > 0 && (\r\n\t\t\t\t\t\t\t<Box>\r\n\t\t\t\t\t\t\t\t<SectionTitle>Campaign calls</SectionTitle>\r\n\t\t\t\t\t\t\t\t<Table size=\"small\" sx={{ backgroundColor: \"#fff\", border: \"1px solid #e0e7ef\", borderRadius: \"8px\" }}>\r\n\t\t\t\t\t\t\t\t\t<TableHead>\r\n\t\t\t\t\t\t\t\t\t\t<TableRow>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx}>Campaign</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"right\">Pattern</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"right\">To this queue</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t</TableRow>\r\n\t\t\t\t\t\t\t\t\t</TableHead>\r\n\t\t\t\t\t\t\t\t\t<TableBody>\r\n\t\t\t\t\t\t\t\t\t\t{campaigns.map((campaign, i) => (\r\n\t\t\t\t\t\t\t\t\t\t\t<TableRow key={`${campaign.name}-${i}`}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={cellSx}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{campaign.name || DASH}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{campaign.note && (\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.68rem\", color: COLORS.amber, mt: 0.2 }}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t{campaign.note}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontVariantNumeric: \"tabular-nums\" }} align=\"right\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{decimal(campaign.patternCalls)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontWeight: 600, color: COLORS.teal, fontVariantNumeric: \"tabular-nums\" }} align=\"right\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t{decimal(campaign.callsToQueue)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t</TableRow>\r\n\t\t\t\t\t\t\t\t\t\t))}\r\n\t\t\t\t\t\t\t\t\t</TableBody>\r\n\t\t\t\t\t\t\t\t</Table>\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{/* ── How many agents that needs ── */}\r\n\t\t\t\t\t\t{sizing?.options && sizing.options.length > 0 && (\r\n\t\t\t\t\t\t\t<Box>\r\n\t\t\t\t\t\t\t\t<SectionTitle>Agents considered</SectionTitle>\r\n\t\t\t\t\t\t\t\t<Table size=\"small\" sx={{ backgroundColor: \"#fff\", border: \"1px solid #e0e7ef\", borderRadius: \"8px\" }}>\r\n\t\t\t\t\t\t\t\t\t<TableHead>\r\n\t\t\t\t\t\t\t\t\t\t<TableRow>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"center\">Agents</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"right\">Answered in time</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={headCellSx} align=\"right\">Busy</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t</TableRow>\r\n\t\t\t\t\t\t\t\t\t</TableHead>\r\n\t\t\t\t\t\t\t\t\t<TableBody>\r\n\t\t\t\t\t\t\t\t\t\t{sizing.options.map((option) => {\r\n\t\t\t\t\t\t\t\t\t\t\t// The rung the forecast settled on — the first that clears the target.\r\n\t\t\t\t\t\t\t\t\t\t\tconst chosen = option.agents === sizing.inboundAgents;\r\n\t\t\t\t\t\t\t\t\t\t\treturn (\r\n\t\t\t\t\t\t\t\t\t\t\t\t<TableRow\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tkey={option.agents}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tsx={{ backgroundColor: chosen ? \"#e6f0f2\" : undefined }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontWeight: chosen ? 700 : 600, fontVariantNumeric: \"tabular-nums\" }} align=\"center\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{option.agents}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{chosen && (\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tlabel=\"chosen\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tsx={{ ml: 0.75, height: 16, fontSize: \"0.6rem\", fontWeight: 700, backgroundColor: COLORS.teal, color: \"#fff\" }}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontVariantNumeric: \"tabular-nums\" }} align=\"right\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{pct0(option.serviceLevel)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t<TableCell sx={{ ...cellSx, fontVariantNumeric: \"tabular-nums\" }} align=\"right\">\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t{pct0(option.occupancy)}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t</TableCell>\r\n\t\t\t\t\t\t\t\t\t\t\t\t</TableRow>\r\n\t\t\t\t\t\t\t\t\t\t\t);\r\n\t\t\t\t\t\t\t\t\t\t})}\r\n\t\t\t\t\t\t\t\t\t</TableBody>\r\n\t\t\t\t\t\t\t\t</Table>\r\n\t\t\t\t\t\t\t\t{sizing.totalRule && (\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.74rem\", color: \"#4a5568\", mt: 1, lineHeight: 1.6 }}>\r\n\t\t\t\t\t\t\t\t\t\t{sizing.totalRule}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{/* ── What actually happened ── past slots only. */}\r\n\t\t\t\t\t\t{actuals && (\r\n\t\t\t\t\t\t\t<Box>\r\n\t\t\t\t\t\t\t\t<SectionTitle>What actually happened</SectionTitle>\r\n\t\t\t\t\t\t\t\t<Box sx={{ display: \"grid\", gridTemplateColumns: \"1fr auto\", rowGap: 0.85, columnGap: 2, backgroundColor: \"#fff\", border: \"1px solid #e0e7ef\", borderRadius: \"8px\", px: 1.5, py: 1.25 }}>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.76rem\", color: COLORS.grey }}>Inbound calls</Typography>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", fontWeight: 600, textAlign: \"right\", fontVariantNumeric: \"tabular-nums\" }}>\r\n\t\t\t\t\t\t\t\t\t\t{count(actuals.calls)}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.76rem\", color: COLORS.grey }}>Campaign calls</Typography>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", fontWeight: 600, textAlign: \"right\", fontVariantNumeric: \"tabular-nums\" }}>\r\n\t\t\t\t\t\t\t\t\t\t{decimal(actuals.campaignCalls)}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.76rem\", color: COLORS.grey }}>Staffed</Typography>\r\n\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", fontWeight: 600, textAlign: \"right\", fontVariantNumeric: \"tabular-nums\" }}>\r\n\t\t\t\t\t\t\t\t\t\t{decimal(actuals.staffedAgents)}\r\n\t\t\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t\t\t\t{status && (\r\n\t\t\t\t\t\t\t\t\t\t<>\r\n\t\t\t\t\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.76rem\", color: COLORS.grey }}>Rostered to forecast</Typography>\r\n\t\t\t\t\t\t\t\t\t\t\t<Box sx={{ textAlign: \"right\" }}>\r\n\t\t\t\t\t\t\t\t\t\t\t\t<Chip\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tsize=\"small\"\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tlabel={status}\r\n\t\t\t\t\t\t\t\t\t\t\t\t\tsx={{\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\theight: 20, fontSize: \"0.66rem\", fontWeight: 700,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tbackgroundColor: STATUS_STYLE[status]?.bg,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tcolor: STATUS_STYLE[status]?.color,\r\n\t\t\t\t\t\t\t\t\t\t\t\t\t}}\r\n\t\t\t\t\t\t\t\t\t\t\t\t/>\r\n\t\t\t\t\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t\t\t\t</>\r\n\t\t\t\t\t\t\t\t\t)}\r\n\t\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t\t</Box>\r\n\t\t\t\t\t\t)}\r\n\r\n\t\t\t\t\t\t{!loading && !error && !data?.summary && !data?.summaryMarkdown && !inbound?.weeks?.length && (\r\n\t\t\t\t\t\t\t<Typography sx={{ fontFamily: FONT, fontSize: \"0.8rem\", color: COLORS.grey }}>\r\n\t\t\t\t\t\t\t\tNo explanation available for this slot.\r\n\t\t\t\t\t\t\t</Typography>\r\n\t\t\t\t\t\t)}\r\n\t\t\t\t\t</Box>\r\n\t\t\t\t</Box>\r\n\t\t\t)}\r\n\t\t</Drawer>\r\n\t);\r\n}\r\n","import { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport axiosInstance from \"../../services/axios\";\r\nimport { END_POINT } from \"../../services/endPoint\";\r\nimport type { LiveStaffingResponse } from \"./types\";\r\n\r\n/** The server caches 15s per tenant, so many open pages cost one query. */\r\nconst POLL_MS = 30_000;\r\n\r\n/**\r\n * Live staffing for every queue, polled while the page is open.\r\n *\r\n * Polling stops while the browser tab is hidden and catches up the moment it\r\n * comes back, so a supervisor who leaves this open all day is not quietly\r\n * making a request every half minute forever.\r\n */\r\nexport function useLiveStaffing(windowMinutes: number, enabled: boolean) {\r\n\tconst [data, setData] = useState<LiveStaffingResponse | null>(null);\r\n\tconst [loading, setLoading] = useState(false);\r\n\tconst [error, setError] = useState(\"\");\r\n\r\n\t// Keyed by window, so changing it is never the request that gets skipped —\r\n\t// that would leave the previous window's figures under the new heading.\r\n\tconst inFlightFor = useRef<number | null>(null);\r\n\t/** The window being asked about now; a reply for any other one is late. */\r\n\tconst wanted = useRef(windowMinutes);\r\n\twanted.current = windowMinutes;\r\n\r\n\tconst load = useCallback(async (showSpinner: boolean) => {\r\n\t\tif (inFlightFor.current === windowMinutes) return;\r\n\t\tinFlightFor.current = windowMinutes;\r\n\t\tif (showSpinner) setLoading(true);\r\n\t\ttry {\r\n\t\t\tconst res = await axiosInstance.get<LiveStaffingResponse>(END_POINT.WFM_STAFFING_LIVE, {\r\n\t\t\t\tparams: { window_minutes: windowMinutes },\r\n\t\t\t});\r\n\t\t\tif (wanted.current !== windowMinutes) return;\r\n\t\t\tsetData(res.data ?? null);\r\n\t\t\tsetError(\"\");\r\n\t\t} catch (err: any) {\r\n\t\t\tif (wanted.current !== windowMinutes) return;\r\n\t\t\tsetData(null);\r\n\t\t\tsetError(err?.response?.data?.detail || err?.response?.data?.message || \"Could not load live staffing\");\r\n\t\t} finally {\r\n\t\t\tif (inFlightFor.current === windowMinutes) inFlightFor.current = null;\r\n\t\t\tif (showSpinner) setLoading(false);\r\n\t\t}\r\n\t}, [windowMinutes]);\r\n\r\n\tuseEffect(() => {\r\n\t\t// Cleared on every change of window, not just on disable: otherwise the\r\n\t\t// previous window's arrivals sit under the new heading until the first\r\n\t\t// response lands.\r\n\t\tsetData(null);\r\n\t\tsetError(\"\");\r\n\r\n\t\tif (!enabled) return;\r\n\r\n\t\tload(true);\r\n\t\tconst timer = window.setInterval(() => {\r\n\t\t\tif (document.visibilityState === \"visible\") load(false);\r\n\t\t}, POLL_MS);\r\n\r\n\t\t// Coming back to the tab should show current numbers, not wait for the next tick.\r\n\t\tconst onVisible = () => { if (document.visibilityState === \"visible\") load(false); };\r\n\t\tdocument.addEventListener(\"visibilitychange\", onVisible);\r\n\r\n\t\treturn () => {\r\n\t\t\twindow.clearInterval(timer);\r\n\t\t\tdocument.removeEventListener(\"visibilitychange\", onVisible);\r\n\t\t};\r\n\t}, [enabled, windowMinutes, load]);\r\n\r\n\treturn { data, loading, error, refresh: () => load(true) };\r\n}\r\n","import { useEffect, useRef } from \"react\";\r\nimport { useToast } from \"../../services/toastMessage\";\r\nimport type { LiveQueueRow } from \"./types\";\r\n\r\n/**\r\n * A queue has to report UNDERSTAFFED twice running before it is announced.\r\n * A small queue crosses the line on a single late call and crosses back on the\r\n * next poll; announcing that would train people to ignore the toast.\r\n */\r\nconst POLLS_BEFORE_ALERT = 2;\r\n\r\ninterface QueueMemory {\r\n\t/** Consecutive polls reporting UNDERSTAFFED. */\r\n\tshortPolls: number;\r\n\t/** Already toasted — so it is announced once, and its recovery is worth saying. */\r\n\tannounced: boolean;\r\n}\r\n\r\n/**\r\n * Staffing toasts for the queues on screen, while this page is open.\r\n *\r\n * Deliberately page-scoped: the hook lives with the page rather than the app\r\n * layout, so nothing polls once the supervisor navigates away. Moving it into\r\n * the host layout would make the alerts reach them anywhere, and is a decision\r\n * to take on its own.\r\n *\r\n * There is no alert feed to subscribe to — the live endpoint reports the\r\n * current state — so the change is detected here by comparing each poll with\r\n * the last.\r\n */\r\nexport function useStaffingAlerts(queues: LiveQueueRow[] | null | undefined, enabled: boolean) {\r\n\tconst { showToast } = useToast();\r\n\r\n\tconst memory = useRef(new Map<number, QueueMemory>());\r\n\t/**\r\n\t * The first response only records where things stand. A queue that was\r\n\t * already short when the page opened has not changed, and replaying it would\r\n\t * announce history as news.\r\n\t */\r\n\tconst primed = useRef(false);\r\n\r\n\tuseEffect(() => {\r\n\t\tif (!enabled) return;\r\n\t\tif (!queues) return;\r\n\r\n\t\tconst nowShort: LiveQueueRow[] = [];\r\n\t\tconst recovered: LiveQueueRow[] = [];\r\n\t\tconst seen = new Set<number>();\r\n\r\n\t\tfor (const queue of queues) {\r\n\t\t\tseen.add(queue.queueId);\r\n\t\t\tconst previous = memory.current.get(queue.queueId) ?? { shortPolls: 0, announced: false };\r\n\r\n\t\t\tif (queue.status === \"UNDERSTAFFED\") {\r\n\t\t\t\tconst shortPolls = previous.shortPolls + 1;\r\n\t\t\t\tconst announce = !previous.announced && shortPolls >= POLLS_BEFORE_ALERT;\r\n\t\t\t\t// Priming counts the poll but never speaks.\r\n\t\t\t\tif (announce && primed.current) nowShort.push(queue);\r\n\t\t\t\tmemory.current.set(queue.queueId, {\r\n\t\t\t\t\tshortPolls,\r\n\t\t\t\t\tannounced: previous.announced || announce || !primed.current,\r\n\t\t\t\t});\r\n\t\t\t} else {\r\n\t\t\t\tif (previous.announced && primed.current) recovered.push(queue);\r\n\t\t\t\tmemory.current.set(queue.queueId, { shortPolls: 0, announced: false });\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t// A queue that disappears from the response (deleted, or no longer served)\r\n\t\t// should not keep a watermark that fires if it comes back short.\r\n\t\tfor (const queueId of [...memory.current.keys()]) {\r\n\t\t\tif (!seen.has(queueId)) memory.current.delete(queueId);\r\n\t\t}\r\n\r\n\t\tif (!primed.current) {\r\n\t\t\t// A first response counts as one poll, so a queue short on arrival still\r\n\t\t\t// needs a second to be announced if it ever recovers and goes short again.\r\n\t\t\tprimed.current = true;\r\n\t\t\treturn;\r\n\t\t}\r\n\r\n\t\t/*\r\n\t\t * Several at once become one toast. The provider holds a single message,\r\n\t\t * so a loop would overwrite itself and only the last queue would be read.\r\n\t\t */\r\n\t\tif (nowShort.length === 1) {\r\n\t\t\tconst q = nowShort[0];\r\n\t\t\t// Staffing is fractional — an agent in three queues counts as a third in\r\n\t\t\t// each — so \"0.3333333 on shift\" has to be rounded before it is read out.\r\n\t\t\tconst staffed = typeof q.staffedAgents === \"number\" ? q.staffedAgents.toFixed(1) : \"0\";\r\n\t\t\tshowToast(`${q.queueName}: needs ${q.requiredTotalAgents ?? \"?\"}, ${staffed} on shift`, \"warning\");\r\n\t\t} else if (nowShort.length > 1) {\r\n\t\t\tshowToast(\r\n\t\t\t\t`${nowShort.length} queues understaffed — ${nowShort.map((q) => q.queueName).join(\", \")}`,\r\n\t\t\t\t\"warning\",\r\n\t\t\t);\r\n\t\t}\r\n\r\n\t\tif (recovered.length === 1) {\r\n\t\t\tshowToast(`${recovered[0].queueName}: staffing recovered`, \"info\");\r\n\t\t} else if (recovered.length > 1) {\r\n\t\t\tshowToast(\r\n\t\t\t\t`${recovered.length} queues recovered — ${recovered.map((q) => q.queueName).join(\", \")}`,\r\n\t\t\t\t\"info\",\r\n\t\t\t);\r\n\t\t}\r\n\t}, [queues, enabled, showToast]);\r\n\r\n\t// Starting over on a fresh visit: the page remembers nothing between mounts.\r\n\tuseEffect(() => () => {\r\n\t\tmemory.current.clear();\r\n\t\tprimed.current = false;\r\n\t}, []);\r\n}\r\n"],"mappings":"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","names":["useEffect","useState","Box","useMemo","useState","Box","CircularProgress","IconButton","Tooltip","Typography","dayjs","Box","Chip","Tooltip","Typography","Box","jsx","jsxs","Box","jsx","jsxs","_a","Box","Typography","Tooltip","Chip","jsx","jsxs","useMemo","_a","useState","Box","dayjs","Typography","Tooltip","IconButton","CircularProgress","useEffect","useMemo","useState","Alert","Box","CircularProgress","IconButton","Typography","RefreshIcon","dayjs","useMemo","Box","Typography","Tooltip","dayjs","jsx","jsxs","dayjs","Box","Typography","useMemo","Tooltip","useState","Box","Chip","Typography","jsx","jsxs","useState","Box","Typography","Chip","buildQs","Fragment","jsx","jsxs","useState","dayjs","useMemo","useEffect","Box","_a","_b","IconButton","Typography","CircularProgress","Alert","RefreshIcon","useCallback","useEffect","useMemo","useRef","useState","Alert","Box","Chip","CircularProgress","IconButton","Tooltip","Typography","InfoOutlinedIcon","ArrowBackIcon","RefreshIcon","dayjs","LocalizationProvider","AdapterDayjs","useMemo","Box","Paper","Typography","CartesianGrid","ComposedChart","Legend","Line","ResponsiveContainer","Tooltip","XAxis","YAxis","dayjs","jsx","jsxs","ChartTooltip","dayjs","Box","Typography","useMemo","Paper","ResponsiveContainer","ComposedChart","CartesianGrid","XAxis","YAxis","Tooltip","Legend","Line","useEffect","useState","Alert","Box","Chip","CircularProgress","Drawer","IconButton","Tooltip","Typography","CloseIcon","dayjs","Fragment","jsx","jsxs","APP_HEADER_H","headCellSx","Typography","useState","useEffect","dayjs","Drawer","Box","Tooltip","IconButton","CloseIcon","Chip","CircularProgress","Alert","Fragment","jsx","jsxs","PAGE_SIZE_OPTIONS","DEFAULT_PAGE_SIZE","dayjs","useState","useMemo","useCallback","useRef","useEffect","_a","_b","Box","Chip","window","Tooltip","IconButton","ArrowBackIcon","Typography","CircularProgress","Alert","LocalizationProvider","AdapterDayjs","RefreshIcon","InfoOutlinedIcon","useCallback","useEffect","useRef","useState","useState","useRef","useCallback","useEffect","useEffect","useRef","useRef","useEffect","jsx","jsxs","FONT","COLORS","useState","_a","useEffect","Box"]}