mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-06 21:45:03 +00:00
fix(warehouse): show assigned trucks on the Trucks on Site page, not only arrived ones
The page filtered on arrived_at IS NOT NULL, so a truck appeared only once the warehouse receive flow stamped its arrival. Assigned trucks that had not yet reached the yard were invisible, which left the page empty whenever nothing had been received — every assigned truck was missing. It now lists every truck assigned to a booking that has not departed, from both haulage paths, tagged INBOUND (assigned, not yet arrived) or ON_SITE (arrived). A scope toggle filters between them, dwell time shows only once a truck has actually arrived, and the KPI count on the dashboard stays strict (arrived only). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -47,15 +47,16 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
||||
if (rows.length === 0) {
|
||||
return (
|
||||
<Alert variant="light" color="gray">
|
||||
No trucks on site.
|
||||
No trucks assigned or on site.
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Table.ScrollContainer minWidth={980}>
|
||||
<Table.ScrollContainer minWidth={1040}>
|
||||
<Table striped highlightOnHover verticalSpacing="xs">
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>Status</Table.Th>
|
||||
<Table.Th>Plate</Table.Th>
|
||||
<Table.Th>Haulage</Table.Th>
|
||||
<Table.Th>Driver</Table.Th>
|
||||
@@ -69,6 +70,16 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
||||
<Table.Tbody>
|
||||
{rows.map((row) => (
|
||||
<Table.Tr key={`${row.source}-${row.assignmentId}`}>
|
||||
<Table.Td>
|
||||
<Badge
|
||||
size="sm"
|
||||
radius="sm"
|
||||
variant={row.status === "ON_SITE" ? "filled" : "light"}
|
||||
color={row.status === "ON_SITE" ? "edr-green" : "gray"}
|
||||
>
|
||||
{row.status === "ON_SITE" ? "On site" : "Inbound"}
|
||||
</Badge>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
<Text size="sm" fw={600}>
|
||||
{row.plateNumber ?? "—"}
|
||||
@@ -101,9 +112,13 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
||||
<Text size="sm">{row.containers ?? "Bulk"}</Text>
|
||||
</Table.Td>
|
||||
<Table.Td>
|
||||
{isLongDwell(row.arrivedAt) ? (
|
||||
{row.arrivedAt == null ? (
|
||||
<Text size="sm" c="dimmed">
|
||||
—
|
||||
</Text>
|
||||
) : isLongDwell(row.arrivedAt) ? (
|
||||
<Tooltip
|
||||
label={`On site over ${LONG_DWELL_HOURS}h — arrived ${new Date(row.arrivedAt as string).toLocaleString()}`}
|
||||
label={`On site over ${LONG_DWELL_HOURS}h — arrived ${new Date(row.arrivedAt).toLocaleString()}`}
|
||||
withArrow
|
||||
>
|
||||
<Text size="sm" c="red" fw={600}>
|
||||
@@ -124,12 +139,14 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
||||
|
||||
export default function TrucksOnSitePage() {
|
||||
const { data: trucks = [], isLoading } = useTrucksOnSite();
|
||||
const [scope, setScope] = useState<"ALL" | "ON_SITE" | "INBOUND">("ALL");
|
||||
const [source, setSource] = useState<"ALL" | "CUSTOMER" | "EDR">("ALL");
|
||||
const [search, setSearch] = useState("");
|
||||
|
||||
const rows = useMemo(() => {
|
||||
const term = search.trim().toLowerCase();
|
||||
return trucks
|
||||
.filter((t) => scope === "ALL" || t.status === scope)
|
||||
.filter((t) => source === "ALL" || t.source === source)
|
||||
.filter((t) =>
|
||||
!term
|
||||
@@ -137,8 +154,10 @@ export default function TrucksOnSitePage() {
|
||||
: [t.plateNumber, t.driverName, t.bookingReference, t.customerName, t.containers]
|
||||
.some((field) => field?.toLowerCase().includes(term)),
|
||||
);
|
||||
}, [trucks, source, search]);
|
||||
}, [trucks, scope, source, search]);
|
||||
|
||||
const onSiteCount = trucks.filter((t) => t.status === "ON_SITE").length;
|
||||
const inboundCount = trucks.length - onSiteCount;
|
||||
const customerCount = trucks.filter((t) => t.source === "CUSTOMER").length;
|
||||
const edrCount = trucks.length - customerCount;
|
||||
|
||||
@@ -146,20 +165,32 @@ export default function TrucksOnSitePage() {
|
||||
<PageContainer>
|
||||
<PageHeader
|
||||
title="Trucks on site"
|
||||
subtitle="Arrived at the yard and not yet left — customer self-haul and EDR last-mile."
|
||||
subtitle="Customer self-haul and EDR last-mile trucks — assigned (inbound) or arrived, until they leave the yard."
|
||||
/>
|
||||
|
||||
<Group justify="space-between" mb="md" wrap="wrap" gap="sm">
|
||||
<SegmentedControl
|
||||
size="xs"
|
||||
value={source}
|
||||
onChange={(v) => setSource(v as typeof source)}
|
||||
data={[
|
||||
{ label: `All (${trucks.length})`, value: "ALL" },
|
||||
{ label: `Customer (${customerCount})`, value: "CUSTOMER" },
|
||||
{ label: `EDR (${edrCount})`, value: "EDR" },
|
||||
]}
|
||||
/>
|
||||
<Group gap="sm" wrap="wrap">
|
||||
<SegmentedControl
|
||||
size="xs"
|
||||
value={scope}
|
||||
onChange={(v) => setScope(v as typeof scope)}
|
||||
data={[
|
||||
{ label: `All (${trucks.length})`, value: "ALL" },
|
||||
{ label: `On site (${onSiteCount})`, value: "ON_SITE" },
|
||||
{ label: `Inbound (${inboundCount})`, value: "INBOUND" },
|
||||
]}
|
||||
/>
|
||||
<SegmentedControl
|
||||
size="xs"
|
||||
value={source}
|
||||
onChange={(v) => setSource(v as typeof source)}
|
||||
data={[
|
||||
{ label: "All", value: "ALL" },
|
||||
{ label: `Customer (${customerCount})`, value: "CUSTOMER" },
|
||||
{ label: `EDR (${edrCount})`, value: "EDR" },
|
||||
]}
|
||||
/>
|
||||
</Group>
|
||||
<TextInput
|
||||
size="xs"
|
||||
w={280}
|
||||
|
||||
@@ -1128,6 +1128,8 @@ export interface WarehouseOpsStats {
|
||||
export interface TruckOnSite {
|
||||
source: "CUSTOMER" | "EDR";
|
||||
assignmentId: string;
|
||||
/** INBOUND = assigned, not yet arrived; ON_SITE = arrived, not yet departed. */
|
||||
status: "INBOUND" | "ON_SITE";
|
||||
plateNumber: string | null;
|
||||
driverName: string | null;
|
||||
truckType: string | null;
|
||||
|
||||
Reference in New Issue
Block a user