feat(warehouse): assemble dashboard cockpit + server-side throughput series

- WarehouseDashboardPage now composes the ops KPI strip, lifecycle cards, flow
  charts, zone-occupancy heatmap and demurrage/accrual exceptions into one
  control-tower view; drop the redundant lifecycle donut.
- New GET /warehouse-inventory/throughput (date_trunc time series) replaces the
  client-side buildTrend that downloaded the entire inventory list to bucket it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hagernesh
2026-07-15 11:07:45 +00:00
parent ea2d912773
commit 2e22b72e27
8 changed files with 142 additions and 119 deletions

View File

@@ -1,5 +1,5 @@
import { useNavigate } from 'react-router-dom';
import { Card, Center, Group, Loader, SimpleGrid, Text, ThemeIcon } from '@mantine/core';
import { Card, Center, Divider, Group, Loader, SimpleGrid, Stack, Text, ThemeIcon } from '@mantine/core';
import {
ClipboardCheck,
ClipboardList,
@@ -16,10 +16,23 @@ import {
} from 'lucide-react';
import { PageContainer, PageHeader } from '@/components/page';
import { WarehouseDashboardCharts } from '@/components/warehouses';
import {
AccrualDashboard,
WarehouseDashboardCharts,
WarehouseOpsKpiStrip,
ZoneOccupancyHeatmap,
} from '@/components/warehouses';
import { useWarehouseDashboard } from '@/hooks/useWarehouses';
import type { WarehouseDashboard } from '@/types/warehouse';
function SectionTitle({ children }: { children: React.ReactNode }) {
return (
<Text fw={700} fz="sm" tt="uppercase" c="edr-muted" style={{ letterSpacing: 0.4 }}>
{children}
</Text>
);
}
interface Metric {
key: keyof WarehouseDashboard;
label: string;
@@ -67,7 +80,16 @@ export default function WarehouseDashboardPage() {
<Text c="red">Failed to load warehouse dashboard.</Text>
</Center>
) : (
<>
<Stack gap="xl">
{/* Needs attention — live ops counters (received today, pending
inspection, trucks on-site, items aging > 7 days). */}
<Stack gap="sm">
<SectionTitle>Needs attention</SectionTitle>
<WarehouseOpsKpiStrip />
</Stack>
<Divider />
<SimpleGrid cols={{ base: 1, xs: 2, md: 4 }} spacing="md">
{METRICS.map((metric) => (
<Card
@@ -98,8 +120,21 @@ export default function WarehouseDashboardPage() {
))}
</SimpleGrid>
<WarehouseDashboardCharts data={data} />
</>
<Stack gap="sm">
<SectionTitle>Flow</SectionTitle>
<WarehouseDashboardCharts data={data} />
</Stack>
<Stack gap="sm">
<SectionTitle>Zone capacity</SectionTitle>
<ZoneOccupancyHeatmap />
</Stack>
<Stack gap="sm">
<SectionTitle>Demurrage &amp; storage exceptions</SectionTitle>
<AccrualDashboard />
</Stack>
</Stack>
)}
</PageContainer>
);