From 46971fd5c57f07fec7b3b49f46fb934f6c425c9f Mon Sep 17 00:00:00 2001 From: Hagernesh Date: Fri, 3 Jul 2026 07:30:12 +0000 Subject: [PATCH 1/3] Export Djbouti unloading queue --- .../ExportDjiboutiUnloadingQueuePage.tsx | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/apps/edr-freight-web/backoffice/src/pages/warehouses/ExportDjiboutiUnloadingQueuePage.tsx b/apps/edr-freight-web/backoffice/src/pages/warehouses/ExportDjiboutiUnloadingQueuePage.tsx index 31ee5ea36..609995e3e 100644 --- a/apps/edr-freight-web/backoffice/src/pages/warehouses/ExportDjiboutiUnloadingQueuePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/warehouses/ExportDjiboutiUnloadingQueuePage.tsx @@ -14,7 +14,17 @@ import { Text, } from '@mantine/core'; import { useNavigate } from 'react-router-dom'; -import { ChevronDown, ChevronRight, Eye, FileText, History, PackageOpen, Truck } from 'lucide-react'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { + ChevronDown, + ChevronRight, + Eye, + FileText, + History, + PackageOpen, + ShieldCheck, + Truck, +} from 'lucide-react'; import { PageHeader } from '@/components/page'; import Breadcrumbs from '@/components/ui/Breadcrumbs'; @@ -36,6 +46,7 @@ import { useInterchangeDocuments, } from '@/hooks/useInterchangeDocuments'; import { useToast } from '@/hooks/use-toast'; +import { trainSchedulingService } from '@/services/trainScheduling.service'; import type { AutoUnloadExportDjiboutiResult, ExportTrain, @@ -179,6 +190,14 @@ export default function ExportDjiboutiUnloadingQueuePage() { const { data: interchangeDocuments = [] } = useInterchangeDocuments({ direction: 'EXPORT' }); const autoUnload = useAutoUnloadExportAtDjibouti(); const generateInterchange = useGenerateInterchangeDocument(); + const qc = useQueryClient(); + const secureGatePass = useMutation({ + mutationFn: (scheduleId: string) => trainSchedulingService.grantImportDjiboutiGatepass(scheduleId), + onSuccess: () => + qc.invalidateQueries({ + queryKey: ['warehouse-inventory', 'export-djibouti-arrival-queue'], + }), + }); const [openScheduleId, setOpenScheduleId] = useState(null); const [busyScheduleId, setBusyScheduleId] = useState(null); const [historyInventoryId, setHistoryInventoryId] = useState(null); @@ -189,6 +208,25 @@ export default function ExportDjiboutiUnloadingQueuePage() { .map((doc) => [doc.scheduleId as string, doc]), ); + const secureGate = async (train: ExportTrain) => { + setBusyScheduleId(train.scheduleId); + try { + await secureGatePass.mutateAsync(train.scheduleId); + toast({ + title: 'Gate pass secured', + description: `Djibouti Port entry allowed for ${train.trainNumber ?? 'the train'}. You can now auto unload.`, + }); + } catch (error) { + toast({ + variant: 'destructive', + title: 'Could not secure gate pass', + description: getErrorMessage(error), + }); + } finally { + setBusyScheduleId(null); + } + }; + const unloadTrain = async (train: ExportTrain) => { setBusyScheduleId(train.scheduleId); try { @@ -346,6 +384,16 @@ export default function ExportDjiboutiUnloadingQueuePage() { > Open + + + + + + + {items.map((booking) => ( + toggle(booking.id)} + /> + ))} + + + + + + + + ); +} diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx index 9319e6a01..c02eb2c39 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx @@ -42,6 +42,7 @@ import { } from "@/components/trainScheduling/containerPlacement.util"; import { ContainerPlacementGrid } from "@/components/trainScheduling/ContainerPlacementGrid"; import { FleetAvailabilitySummary } from "@/components/trainScheduling/FleetAvailabilitySummary"; +import { ImportLoadingConfirmationPanel } from "@/components/trainScheduling/ImportLoadingConfirmationPanel"; import { RescheduleTrainDialog } from "@/components/trainScheduling/RescheduleTrainDialog"; import { ScheduleBatchPanel } from "@/components/trainScheduling/ScheduleBatchPanel"; import { ScheduleBookingsStep } from "@/components/trainScheduling/ScheduleBookingsStep"; @@ -141,6 +142,13 @@ export default function TrainScheduleV2DetailPage() { }, }); + const importLoadingQuery = useQuery( + api.trainScheduling.importLoadingBookings.queryOptions({ + input: { id: scheduleId ?? "" }, + enabled: Boolean(scheduleId && schedule?.direction === "IMPORT"), + }), + ); + const eligibleFilters = useMemo( () => schedule @@ -951,6 +959,23 @@ export default function TrainScheduleV2DetailPage() { ]} /> + {schedule?.direction === "IMPORT" ? ( + + + Import loading confirmation + + Paid import bookings with a wagon allocated on this schedule. Marking loaded/unloaded + is tracking only — it does not block dispatch. + + + + + ) : null} + {gatepassApplies ? (