From ef88240365636f46ce104a72fb2fa34ba3b24040 Mon Sep 17 00:00:00 2001 From: natib21 Date: Fri, 17 Jul 2026 10:37:20 +0000 Subject: [PATCH] fix train build --- .../trainBuilder/AvailableWagonsPanel.tsx | 58 +++++++++++++++++-- .../trainBuilder/TrainBuilderDetailPage.tsx | 2 + .../backoffice/src/services/wagon.service.ts | 4 ++ 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/components/trainBuilder/AvailableWagonsPanel.tsx b/apps/edr-freight-web/backoffice/src/components/trainBuilder/AvailableWagonsPanel.tsx index c121acd36..fc33ae45a 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainBuilder/AvailableWagonsPanel.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainBuilder/AvailableWagonsPanel.tsx @@ -1,5 +1,6 @@ import { Freight } from "@edr/types"; import { + Badge, Button, Checkbox, Group, @@ -24,11 +25,19 @@ export default function AvailableWagonsPanel({ yardLabel, onAssign, assigning, + exportTrainNumber, + importTrainNumber, }: AvailableWagonsPanelProps) { const [search, setSearch] = useState(""); const [typeFilter, setTypeFilter] = useState("ALL"); + const [runOnly, setRunOnly] = useState(false); const [selected, setSelected] = useState([]); + // The train's own run, e.g. "8001-8002" — only offered when the train has one. + const runLabel = exportTrainNumber + ? `${exportTrainNumber}${importTrainNumber ? `-${importTrainNumber}` : ""}` + : null; + const wagonsQuery = useQuery( api.wagons.list.queryOptions({ input: { @@ -42,10 +51,23 @@ export default function AvailableWagonsPanel({ const q = search.trim().toLowerCase(); return (wagonsQuery.data ?? []).filter((wagon) => { if (typeFilter !== "ALL" && wagon.wagonTypeId !== typeFilter) return false; + // Rostered to this train's run — match on the export run, which fixes the + // import run anyway. + if (runOnly && wagon.exportTrainNumber !== exportTrainNumber) return false; if (q && !wagon.wagonNumber.toLowerCase().includes(q)) return false; return true; }); - }, [wagonsQuery.data, search, typeFilter]); + }, [wagonsQuery.data, search, typeFilter, runOnly, exportTrainNumber]); + + const runMatchCount = useMemo( + () => + exportTrainNumber + ? (wagonsQuery.data ?? []).filter( + (w) => w.exportTrainNumber === exportTrainNumber, + ).length + : 0, + [wagonsQuery.data, exportTrainNumber], + ); const typeOptions = useMemo(() => { const byId = new Map(); @@ -112,6 +134,15 @@ export default function AvailableWagonsPanel({ /> + {runLabel ? ( + setRunOnly(e.currentTarget.checked)} + /> + ) : null} + {wagons.length ? ( - - {wagon.wagonNumber} - + + + {wagon.wagonNumber} + + {wagon.exportTrainNumber ? ( + + {wagon.exportTrainNumber} + {wagon.importTrainNumber ? `-${wagon.importTrainNumber}` : ""} + + ) : null} + {wagon.wagonType ? `${wagon.wagonType.name} · ${wagon.wagonType.capacityTons ?? "—"}T cap` @@ -183,4 +229,8 @@ export interface AvailableWagonsPanelProps { yardLabel?: string | null; onAssign: (wagonIds: string[]) => void; assigning: boolean; + /** This train's odd EXPORT run — drives the "only this run" filter. */ + exportTrainNumber?: string | null; + /** This train's even IMPORT run — label only; the export run does the matching. */ + importTrainNumber?: string | null; } diff --git a/apps/edr-freight-web/backoffice/src/pages/trainBuilder/TrainBuilderDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainBuilder/TrainBuilderDetailPage.tsx index f4e65266f..378bb5aad 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainBuilder/TrainBuilderDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainBuilder/TrainBuilderDetailPage.tsx @@ -255,6 +255,8 @@ export default function TrainBuilderDetailPage() { void withToast( diff --git a/apps/edr-freight-web/backoffice/src/services/wagon.service.ts b/apps/edr-freight-web/backoffice/src/services/wagon.service.ts index c7e1bf142..524b2f9f6 100644 --- a/apps/edr-freight-web/backoffice/src/services/wagon.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/wagon.service.ts @@ -28,6 +28,10 @@ export interface Wagon { status: Freight.WagonStatus; currentYardId: string | null; currentYard?: { id: string; label?: string; code?: string } | null; + /** Odd EXPORT run (Ethiopia → Djibouti); null when the wagon is not on a run. */ + exportTrainNumber?: string | null; + /** Even IMPORT run (Djibouti → Ethiopia); always the export run + 1. */ + importTrainNumber?: string | null; notes?: string; }