mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 10:40:58 +00:00
feat(warehouse): P1 dashboard analytics — dwell/aging, cycle-time + on-time, live deltas
Adds the operational-performance layer to the warehouse cockpit:
- Dwell time & aging: dwellStats() (avg + 0-3/4-7/8-14/15+ buckets over
in-warehouse items) → DwellAgingCard histogram.
- Cycle time & on-time: cycleStats() (arrived→ready→loaded→dispatched stage
averages + dock-to-dispatch) and onTimeDispatchStats() (share of items that
left before their storage free-days expired, resolved via the fee engine's
own rule matching) → CycleTimeCard.
- Live tiles: opsStats() now returns receivedYesterday; KpiStrip renders an
optional ▲/▼ delta, and the ops strip shows received-today vs yesterday.
New endpoints: GET /warehouse-inventory/{dwell-stats,cycle-stats} and
/warehouse-fees/on-time-dispatch (all guarded). New "Performance" section on
WarehouseDashboardPage. On-time reads N/A when there is no sample / no active
storage rule.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,11 @@ export interface KpiItem {
|
||||
* into semantic tints.
|
||||
*/
|
||||
color?: string;
|
||||
/**
|
||||
* Optional change vs a prior period, rendered as a ▲/▼ chip next to the value
|
||||
* (green up, red down, muted zero). E.g. today's count minus yesterday's.
|
||||
*/
|
||||
delta?: number;
|
||||
}
|
||||
|
||||
export interface KpiStripProps {
|
||||
@@ -67,16 +72,30 @@ export function KpiStrip({ items, loading = false }: KpiStripProps) {
|
||||
{loading ? (
|
||||
<Skeleton height={26} width={72} radius="sm" my={2} />
|
||||
) : (
|
||||
<Text
|
||||
fw={800}
|
||||
fz={24}
|
||||
lh={1.05}
|
||||
c="edr-text"
|
||||
style={{ letterSpacing: "-0.02em" }}
|
||||
truncate
|
||||
>
|
||||
{item.value}
|
||||
</Text>
|
||||
<div className="flex items-baseline gap-2">
|
||||
<Text
|
||||
fw={800}
|
||||
fz={24}
|
||||
lh={1.05}
|
||||
c="edr-text"
|
||||
style={{ letterSpacing: "-0.02em" }}
|
||||
truncate
|
||||
>
|
||||
{item.value}
|
||||
</Text>
|
||||
{item.delta != null && item.delta !== 0 ? (
|
||||
<Text
|
||||
component="span"
|
||||
fz="xs"
|
||||
fw={700}
|
||||
c={item.delta > 0 ? "edr-green" : "red"}
|
||||
style={{ whiteSpace: "nowrap" }}
|
||||
>
|
||||
{item.delta > 0 ? "▲" : "▼"}
|
||||
{Math.abs(item.delta)}
|
||||
</Text>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
<Text size="xs" fw={600} c="edr-muted" truncate>
|
||||
{item.label}
|
||||
|
||||
Reference in New Issue
Block a user