mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +00:00
Merge pull request #867 from Tria-plc/testfixes
Onsite truck Fixed The page now lists every truck assigned to a booking that hasn't departed, both haulage paths, tagged: On site — arrived, not departed (green) Inbound — assigned, not yet arrived (grey)
This commit is contained in:
@@ -417,12 +417,16 @@ export class WarehouseInventoryService {
|
|||||||
*
|
*
|
||||||
* Covers both haulage paths because the gate does: a customer's own truck and
|
* Covers both haulage paths because the gate does: a customer's own truck and
|
||||||
* an EDR last-mile truck arrive at the same barrier and need the same paper.
|
* an EDR last-mile truck arrive at the same barrier and need the same paper.
|
||||||
* "On site" means arrived and not yet departed.
|
* Includes trucks assigned but not yet arrived, flagged INBOUND, so staff see
|
||||||
|
* what is coming as well as what is here — an assigned truck only stamps
|
||||||
|
* `arrived_at` when it reaches the warehouse. A truck drops off the list once
|
||||||
|
* it departs.
|
||||||
*/
|
*/
|
||||||
async trucksOnSite(): Promise<
|
async trucksOnSite(): Promise<
|
||||||
Array<{
|
Array<{
|
||||||
source: 'CUSTOMER' | 'EDR';
|
source: 'CUSTOMER' | 'EDR';
|
||||||
assignmentId: string;
|
assignmentId: string;
|
||||||
|
status: 'INBOUND' | 'ON_SITE';
|
||||||
plateNumber: string | null;
|
plateNumber: string | null;
|
||||||
driverName: string | null;
|
driverName: string | null;
|
||||||
truckType: string | null;
|
truckType: string | null;
|
||||||
@@ -436,6 +440,7 @@ export class WarehouseInventoryService {
|
|||||||
return this.dataSource.query(
|
return this.dataSource.query(
|
||||||
`SELECT 'CUSTOMER' AS "source",
|
`SELECT 'CUSTOMER' AS "source",
|
||||||
a.id AS "assignmentId",
|
a.id AS "assignmentId",
|
||||||
|
CASE WHEN a.arrived_at IS NULL THEN 'INBOUND' ELSE 'ON_SITE' END AS "status",
|
||||||
a.plate_number AS "plateNumber",
|
a.plate_number AS "plateNumber",
|
||||||
a.driver_name AS "driverName",
|
a.driver_name AS "driverName",
|
||||||
a.truck_type AS "truckType",
|
a.truck_type AS "truckType",
|
||||||
@@ -450,13 +455,13 @@ export class WarehouseInventoryService {
|
|||||||
JOIN freight.bookings b ON b.id = a.booking_id AND b.deleted_at IS NULL
|
JOIN freight.bookings b ON b.id = a.booking_id AND b.deleted_at IS NULL
|
||||||
LEFT JOIN freight.companies company ON company.id = b.company_id
|
LEFT JOIN freight.companies company ON company.id = b.company_id
|
||||||
WHERE a.deleted_at IS NULL
|
WHERE a.deleted_at IS NULL
|
||||||
AND a.arrived_at IS NOT NULL
|
|
||||||
AND a.departed_at IS NULL
|
AND a.departed_at IS NULL
|
||||||
|
|
||||||
UNION ALL
|
UNION ALL
|
||||||
|
|
||||||
SELECT 'EDR' AS "source",
|
SELECT 'EDR' AS "source",
|
||||||
va.id AS "assignmentId",
|
va.id AS "assignmentId",
|
||||||
|
CASE WHEN va.arrived_at IS NULL THEN 'INBOUND' ELSE 'ON_SITE' END AS "status",
|
||||||
COALESCE(v.plate_number, v.power_plate_no) AS "plateNumber",
|
COALESCE(v.plate_number, v.power_plate_no) AS "plateNumber",
|
||||||
NULLIF(TRIM(CONCAT_WS(' ', d.first_name, d.last_name)), '') AS "driverName",
|
NULLIF(TRIM(CONCAT_WS(' ', d.first_name, d.last_name)), '') AS "driverName",
|
||||||
v.vehicle_type AS "truckType",
|
v.vehicle_type AS "truckType",
|
||||||
@@ -474,10 +479,11 @@ export class WarehouseInventoryService {
|
|||||||
LEFT JOIN freight.drivers d ON d.id = v.assigned_driver_id
|
LEFT JOIN freight.drivers d ON d.id = v.assigned_driver_id
|
||||||
LEFT JOIN freight.companies company ON company.id = b.company_id
|
LEFT JOIN freight.companies company ON company.id = b.company_id
|
||||||
WHERE va.deleted_at IS NULL
|
WHERE va.deleted_at IS NULL
|
||||||
AND va.arrived_at IS NOT NULL
|
|
||||||
AND va.departed_at IS NULL
|
AND va.departed_at IS NULL
|
||||||
|
|
||||||
ORDER BY "arrivedAt" ASC`,
|
-- On-site trucks first, each group oldest-arrival first; inbound trucks
|
||||||
|
-- (null arrival) sort to the end.
|
||||||
|
ORDER BY "arrivedAt" ASC NULLS LAST`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,15 +47,16 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
|||||||
if (rows.length === 0) {
|
if (rows.length === 0) {
|
||||||
return (
|
return (
|
||||||
<Alert variant="light" color="gray">
|
<Alert variant="light" color="gray">
|
||||||
No trucks on site.
|
No trucks assigned or on site.
|
||||||
</Alert>
|
</Alert>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Table.ScrollContainer minWidth={980}>
|
<Table.ScrollContainer minWidth={1040}>
|
||||||
<Table striped highlightOnHover verticalSpacing="xs">
|
<Table striped highlightOnHover verticalSpacing="xs">
|
||||||
<Table.Thead>
|
<Table.Thead>
|
||||||
<Table.Tr>
|
<Table.Tr>
|
||||||
|
<Table.Th>Status</Table.Th>
|
||||||
<Table.Th>Plate</Table.Th>
|
<Table.Th>Plate</Table.Th>
|
||||||
<Table.Th>Haulage</Table.Th>
|
<Table.Th>Haulage</Table.Th>
|
||||||
<Table.Th>Driver</Table.Th>
|
<Table.Th>Driver</Table.Th>
|
||||||
@@ -69,6 +70,16 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
|||||||
<Table.Tbody>
|
<Table.Tbody>
|
||||||
{rows.map((row) => (
|
{rows.map((row) => (
|
||||||
<Table.Tr key={`${row.source}-${row.assignmentId}`}>
|
<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>
|
<Table.Td>
|
||||||
<Text size="sm" fw={600}>
|
<Text size="sm" fw={600}>
|
||||||
{row.plateNumber ?? "—"}
|
{row.plateNumber ?? "—"}
|
||||||
@@ -101,9 +112,13 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
|||||||
<Text size="sm">{row.containers ?? "Bulk"}</Text>
|
<Text size="sm">{row.containers ?? "Bulk"}</Text>
|
||||||
</Table.Td>
|
</Table.Td>
|
||||||
<Table.Td>
|
<Table.Td>
|
||||||
{isLongDwell(row.arrivedAt) ? (
|
{row.arrivedAt == null ? (
|
||||||
|
<Text size="sm" c="dimmed">
|
||||||
|
—
|
||||||
|
</Text>
|
||||||
|
) : isLongDwell(row.arrivedAt) ? (
|
||||||
<Tooltip
|
<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
|
withArrow
|
||||||
>
|
>
|
||||||
<Text size="sm" c="red" fw={600}>
|
<Text size="sm" c="red" fw={600}>
|
||||||
@@ -124,12 +139,14 @@ function Rows({ rows }: { rows: TruckOnSite[] }) {
|
|||||||
|
|
||||||
export default function TrucksOnSitePage() {
|
export default function TrucksOnSitePage() {
|
||||||
const { data: trucks = [], isLoading } = useTrucksOnSite();
|
const { data: trucks = [], isLoading } = useTrucksOnSite();
|
||||||
|
const [scope, setScope] = useState<"ALL" | "ON_SITE" | "INBOUND">("ALL");
|
||||||
const [source, setSource] = useState<"ALL" | "CUSTOMER" | "EDR">("ALL");
|
const [source, setSource] = useState<"ALL" | "CUSTOMER" | "EDR">("ALL");
|
||||||
const [search, setSearch] = useState("");
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
const rows = useMemo(() => {
|
const rows = useMemo(() => {
|
||||||
const term = search.trim().toLowerCase();
|
const term = search.trim().toLowerCase();
|
||||||
return trucks
|
return trucks
|
||||||
|
.filter((t) => scope === "ALL" || t.status === scope)
|
||||||
.filter((t) => source === "ALL" || t.source === source)
|
.filter((t) => source === "ALL" || t.source === source)
|
||||||
.filter((t) =>
|
.filter((t) =>
|
||||||
!term
|
!term
|
||||||
@@ -137,8 +154,10 @@ export default function TrucksOnSitePage() {
|
|||||||
: [t.plateNumber, t.driverName, t.bookingReference, t.customerName, t.containers]
|
: [t.plateNumber, t.driverName, t.bookingReference, t.customerName, t.containers]
|
||||||
.some((field) => field?.toLowerCase().includes(term)),
|
.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 customerCount = trucks.filter((t) => t.source === "CUSTOMER").length;
|
||||||
const edrCount = trucks.length - customerCount;
|
const edrCount = trucks.length - customerCount;
|
||||||
|
|
||||||
@@ -146,20 +165,32 @@ export default function TrucksOnSitePage() {
|
|||||||
<PageContainer>
|
<PageContainer>
|
||||||
<PageHeader
|
<PageHeader
|
||||||
title="Trucks on site"
|
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">
|
<Group justify="space-between" mb="md" wrap="wrap" gap="sm">
|
||||||
<SegmentedControl
|
<Group gap="sm" wrap="wrap">
|
||||||
size="xs"
|
<SegmentedControl
|
||||||
value={source}
|
size="xs"
|
||||||
onChange={(v) => setSource(v as typeof source)}
|
value={scope}
|
||||||
data={[
|
onChange={(v) => setScope(v as typeof scope)}
|
||||||
{ label: `All (${trucks.length})`, value: "ALL" },
|
data={[
|
||||||
{ label: `Customer (${customerCount})`, value: "CUSTOMER" },
|
{ label: `All (${trucks.length})`, value: "ALL" },
|
||||||
{ label: `EDR (${edrCount})`, value: "EDR" },
|
{ 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
|
<TextInput
|
||||||
size="xs"
|
size="xs"
|
||||||
w={280}
|
w={280}
|
||||||
|
|||||||
@@ -1128,6 +1128,8 @@ export interface WarehouseOpsStats {
|
|||||||
export interface TruckOnSite {
|
export interface TruckOnSite {
|
||||||
source: "CUSTOMER" | "EDR";
|
source: "CUSTOMER" | "EDR";
|
||||||
assignmentId: string;
|
assignmentId: string;
|
||||||
|
/** INBOUND = assigned, not yet arrived; ON_SITE = arrived, not yet departed. */
|
||||||
|
status: "INBOUND" | "ON_SITE";
|
||||||
plateNumber: string | null;
|
plateNumber: string | null;
|
||||||
driverName: string | null;
|
driverName: string | null;
|
||||||
truckType: string | null;
|
truckType: string | null;
|
||||||
|
|||||||
Reference in New Issue
Block a user