From d4c51ec63af471d52e8418dd9efe5eba22b587ff Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Thu, 20 Aug 2026 10:15:27 +0000 Subject: [PATCH] feat(warehouses): add import-ops stat tiles and arrival-queue filters Adds Arrived/Unloaded/Dispatch Ready/Total Bookings stat tiles above the import queue tabs, wires search+date filters and pagination into the arrival queue table (reusing useListControls/ListControls/ RuleEngineListFooter already used by Inventory Inquiry), and adds a copy-to-clipboard action on the truncated schedule ID. --- .../warehouses/ReceiveInventoryModal.tsx | 116 ++++++++++++++++-- 1 file changed, 109 insertions(+), 7 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx index 8ea4ade67..f1dad1f1e 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/ReceiveInventoryModal.tsx @@ -4,7 +4,9 @@ import { Alert, Badge, Button, + Card, Checkbox, + CopyButton, Group, Loader, Menu, @@ -12,20 +14,25 @@ import { NumberInput, ScrollArea, Select, + SimpleGrid, Stack, Table, Tabs, Text, Textarea, TextInput, + ThemeIcon, Tooltip, } from '@mantine/core'; import { ArrowRightLeft, + Calendar, Check, + CheckCheck, ChevronDown, ChevronRight, ClipboardCheck, + Copy, Eye, FileText, History, @@ -83,6 +90,9 @@ import { StoreInventoryModal } from './StoreInventoryModal'; import { WarehouseInquiryTable } from './WarehouseInquiryTable'; import { extractDownloadErrorMessage, extractErrorMessage, formatDate, formatNumber, inventoryStatusOptions, warehousesAtStation, yardsForBooking } from './options'; import { openPdfBlob } from './pdf'; +import ListControls from '@/components/common/ListControls'; +import RuleEngineListFooter from '@/components/ruleEngine/RuleEngineListFooter'; +import { useListControls } from '@/hooks/useListControls'; import '@/components/overview/overview.css'; type ImportUnloadAssignment = { bookingId: string; warehouseId: string; yardId: string; zoneId: string }; @@ -2218,6 +2228,11 @@ export function ImportArriveQueueTab({ >({}); const [readyBySchedule, setReadyBySchedule] = useState>({}); + const controls = useListControls(trains, { + searchKeys: ['trainNumber', 'route', 'origin', 'destination', 'status'], + dateKey: 'arrivalTime', + }); + const autoUnload = async (train: ImportTrain) => { const assignments = Object.entries(assignmentsBySchedule[train.scheduleId] ?? {}) .filter((entry): entry is [string, Required] => @@ -2272,10 +2287,6 @@ export function ImportArriveQueueTab({ return ( - - {trains.length} arrived import train{trains.length !== 1 ? 's' : ''} - - {isLoading ? ( @@ -2285,6 +2296,25 @@ export function ImportArriveQueueTab({ No arrived import trains. Trains appear here once their schedule status is ARRIVED. ) : ( + + + + {controls.totalCount} arrived import train{controls.totalCount !== 1 ? 's' : ''} + + + + @@ -2303,7 +2333,16 @@ export function ImportArriveQueueTab({ - {trains.map((t: ImportTrain) => { + {controls.pagedRows.length === 0 ? ( + + + + No trains match the current filters. + + + + ) : ( + controls.pagedRows.map((t: ImportTrain) => { const isOpen = openId === t.scheduleId; const fullyUnloaded = isFullyUnloaded(t); const unloadedBookings = t.unloadedBookings ?? t.totalBookings - getPendingUnloadBookings(t); @@ -2311,7 +2350,18 @@ export function ImportArriveQueueTab({ - {t.scheduleId.slice(0, 8)}… + + {t.scheduleId.slice(0, 8)}… + + {({ copied, copy }) => ( + + + {copied ? : } + + + )} + + {t.trainNumber ?? '—'} @@ -2390,10 +2440,19 @@ export function ImportArriveQueueTab({ )} ); - })} + }))}
+ + +
)} setConfirmAction(null)} />
@@ -2872,6 +2931,41 @@ interface WarehouseFlowWorkbenchProps { focusedBookingLabel?: string; } +function ImportStatCard({ + icon, + label, + value, + sub, + color, +}: { + icon: React.ReactNode; + label: string; + value: React.ReactNode; + sub: string; + color: string; +}) { + return ( + + + + {icon} + + + + {value} + + + {label} + + + {sub} + + + + + ); +} + function WarehouseQueueTabs({ value, onChange, @@ -3039,6 +3133,7 @@ function ImportWarehouseTabs({ enabled, onChanged }: { enabled: boolean; onChang const { data: arriveRows = [] } = useQuery(api.warehouses.importArriveQueue.queryOptions({ enabled })); const { data: unloadedRows = [] } = useQuery(api.warehouses.importUnloadedQueue.queryOptions({ enabled })); const { data: dispatchRows = [] } = useQuery(api.warehouses.importPickupReadyQueue.queryOptions({ enabled })); + const totalBookings = arriveRows.reduce((sum, t) => sum + t.totalBookings, 0); const tabs: WarehouseQueueTab[] = [ { value: 'arrive-queue', @@ -3067,6 +3162,13 @@ function ImportWarehouseTabs({ enabled, onChanged }: { enabled: boolean; onChang return ( + + } label="Arrived" value={arriveRows.length} sub="Import trains" color="edr-green" /> + } label="Unloaded" value={unloadedRows.length} sub="Import trains" color="blue" /> + } label="Dispatch Ready" value={dispatchRows.length} sub="Import trains" color="violet" /> + } label="Total Bookings" value={totalBookings} sub="Across arrived trains" color="orange" /> + + {activeTab === 'arrive-queue' && (