diff --git a/apps/edr-freight-api/src/modules/contracts/contract-clearance.service.ts b/apps/edr-freight-api/src/modules/contracts/contract-clearance.service.ts index 964e9f817..71cfa0580 100644 --- a/apps/edr-freight-api/src/modules/contracts/contract-clearance.service.ts +++ b/apps/edr-freight-api/src/modules/contracts/contract-clearance.service.ts @@ -479,13 +479,19 @@ export class ContractClearanceService { } /** - * GL ET queue: customs (Path B) contracts awaiting pre-booking document review. + * GL ET clearance hub: every customs (Path B) contract that still needs + * customs clearance — awaiting the customer's documents, under GL review, or + * finalized and waiting for the customer to create the booking in the portal. */ async queue(filter: FilterContractDto): Promise { return this.contractsRepository.findAllPaginated({ page: filter.page ?? 1, pageSize: filter.pageSize ?? 100, - statuses: ['CLEARANCE_UNDER_REVIEW'], + statuses: [ + 'AWAITING_CLEARANCE_DOCUMENTS', + 'CLEARANCE_UNDER_REVIEW', + 'CLEARANCE_READY_FOR_BOOKING', + ], customsClearingEnabled: true, sortBy: filter.sortBy, sortOrder: filter.sortOrder, diff --git a/apps/edr-freight-web/backoffice/src/App.tsx b/apps/edr-freight-web/backoffice/src/App.tsx index 01b4ff98b..6d2e723ec 100644 --- a/apps/edr-freight-web/backoffice/src/App.tsx +++ b/apps/edr-freight-web/backoffice/src/App.tsx @@ -29,12 +29,12 @@ import LoginPage from "./pages/auth/LoginPage"; import BookingContractPage from "./pages/bookings/BookingContractPage"; import BookingRequestDetailPage from "./pages/bookings/BookingRequestDetailPage"; import BookingRequestsPage from "./pages/bookings/BookingRequestsPage"; -import DocumentClearanceDetailPage from "./pages/bookings/DocumentClearanceDetailPage"; -import DocumentClearanceListPage from "./pages/bookings/DocumentClearanceListPage"; import NewBookingPage from "./pages/bookings/NewBookingPage"; import ContractRequestsPage from "./pages/contracts/ContractRequestsPage"; import ContractRequestDetailPage from "./pages/contracts/ContractRequestDetailPage"; import ContractViewPage from "./pages/contracts/ContractViewPage"; +import ContractClearanceListPage from "./pages/contracts/ContractClearanceListPage"; +import ContractClearanceDetailPage from "./pages/contracts/ContractClearanceDetailPage"; import GlCreateBookingForm from "./components/contracts/GlCreateBookingForm"; import BookingMilestonesPage from "./pages/contracts/BookingMilestonesPage"; import CustomerDetailPage from "./pages/customers/CustomerDetailPage"; @@ -128,9 +128,9 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [ items: [ { label: "Document Clearance", - href: "/dashboard/clearance", + href: "/dashboard/contracts/clearance", icon: , - permission: FREIGHT_PERMS.bookings.reviewDocuments, + permission: FREIGHT_PERMS.contracts.clearanceReview, }, { label: "Train Schedules", @@ -409,21 +409,14 @@ const App = () => { path="booking-requests/:id/contract" element={} /> + {/* Legacy booking-based clearance URLs → the contract clearance hub. */} - - - } + element={} /> - - - } + element={} /> {/* Contracts (Path A/B) */} @@ -451,17 +444,27 @@ const App = () => { } /> + {/* GL (Path B) contract clearance review hub */} } - /> - } + element={ + + + + } /> } + element={ + + + + } + /> + {/* Path A ops queue out of scope for now → fold into the GL hub. */} + } /> - (clearance?.documents ?? []).filter((d) => d.uploadedBy === "gl"), + (clearance?.documents ?? []).filter( + (d) => d.uploadedBy === "gl_et" || d.uploadedBy === "gl_dj", + ), [clearance], ); diff --git a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx index ff2aaeca7..287b5e1f3 100644 --- a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx +++ b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx @@ -90,6 +90,20 @@ export default function GlCreateBookingForm() { return [...sizes]; }, [contract?.cargoScope]); + // Bulk cargo types declared on the contract scope (prefill, no free-text). + const bulkCargoOptions = useMemo(() => { + const seen = new Map(); + (contract?.cargoScope ?? []).forEach((s) => { + if (s.containerSize || !s.cargoTypeId) return; + if (!seen.has(s.cargoTypeId)) { + seen.set(s.cargoTypeId, s.cargoFreeText?.trim() || s.cargoTypeId); + } + }); + return [...seen.entries()].map(([value, label]) => ({ value, label })); + }, [contract?.cargoScope]); + + const defaultBulkCargoTypeId = bulkCargoOptions[0]?.value ?? ""; + if (isLoading) { return ( @@ -105,7 +119,7 @@ export default function GlCreateBookingForm() { ); @@ -151,7 +165,12 @@ export default function GlCreateBookingForm() { const addBulkLine = () => setBulkLines((prev) => [ ...prev, - { cargoTypeId: "", cargoWeightTons: "", itemCount: "", hazardousQuantity: "" }, + { + cargoTypeId: defaultBulkCargoTypeId, + cargoWeightTons: "", + itemCount: "", + hazardousQuantity: "", + }, ]); const removeBulkLine = (idx: number) => setBulkLines((prev) => prev.filter((_, i) => i !== idx)); @@ -214,12 +233,12 @@ export default function GlCreateBookingForm() { - - patchBulk(idx, { cargoTypeId: e.currentTarget.value }) - } - /> + {bulkCargoOptions.length > 0 ? ( +