diff --git a/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsMenu.tsx b/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsMenu.tsx
index 2fac40263..01a0db69a 100644
--- a/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsMenu.tsx
+++ b/apps/edr-freight-web/backoffice/src/components/bookings/BookingActionsMenu.tsx
@@ -8,6 +8,7 @@ import { useAuth } from "@/auth/useAuth";
import {
getNextPendingApprovalStep,
isAllocateAction,
+ isClearanceNavAction,
isContractNavAction,
listRowHasActions,
type BookingActionContext,
@@ -38,6 +39,7 @@ export function BookingActionsMenu({
reference: row.reference,
approvalSteps: row.approvalSteps,
schedulingStatus: row.schedulingStatus,
+ customsClearingEnabled: row.customsClearingEnabled,
};
const flow = useBookingActionDialog(row.id, context);
@@ -46,10 +48,15 @@ export function BookingActionsMenu({
const goToContract = () =>
navigate(`/dashboard/booking-requests/${row.id}/contract`);
+ const goToClearanceTab = () =>
+ navigate(`/dashboard/booking-requests/${row.id}?tab=clearance`);
+
const handleAction = (action: (typeof actions)[number]) => {
onSuppressRowClick?.();
if (isContractNavAction(action.id)) {
goToContract();
+ } else if (isClearanceNavAction(action.id)) {
+ goToClearanceTab();
} else if (isAllocateAction(action.id)) {
onAllocateBooking?.();
} else {
diff --git a/apps/edr-freight-web/backoffice/src/components/bookings/detail/ClearanceReviewSection.tsx b/apps/edr-freight-web/backoffice/src/components/bookings/detail/ClearanceReviewSection.tsx
index 409f698d6..bb50168ad 100644
--- a/apps/edr-freight-web/backoffice/src/components/bookings/detail/ClearanceReviewSection.tsx
+++ b/apps/edr-freight-web/backoffice/src/components/bookings/detail/ClearanceReviewSection.tsx
@@ -44,9 +44,9 @@ const STATUS_META: Record<
Freight.DocumentReviewStatus,
{ label: string; color: string }
> = {
- APPROVED: { label: "Approved", color: "edr-green" },
+ APPROVED: { label: "Approved", color: "edr-blue" },
QUERIED: { label: "Queried", color: "red" },
- PENDING: { label: "Pending", color: "edr-slate" },
+ PENDING: { label: "Pending", color: "edr-accent" },
};
/**
@@ -142,7 +142,7 @@ export function ClearanceReviewSection({
if (isLoading || !clearance) {
return (
-
+
Loading clearance…
);
@@ -165,15 +165,15 @@ export function ClearanceReviewSection({
-
+
-
+
)}
@@ -219,13 +219,13 @@ export function ClearanceReviewSection({
icon={Upload}
title="Customs output documents"
subtitle="Upload the cleared/customs paperwork to hand back to the customer."
- accent="edr-blue"
+ accent="edr-accent"
>
{glDocs.map((doc) => (
-
+
{doc.label}
{doc.required ? " *" : ""}
@@ -239,7 +239,7 @@ export function ClearanceReviewSection({
href={doc.file.url}
target="_blank"
rel="noreferrer"
- c="edr-blue"
+ c="edr-accent"
style={{ display: "flex" }}
>
@@ -261,7 +261,7 @@ export function ClearanceReviewSection({
{...props}
size="compact-xs"
variant="light"
- color="edr-green"
+ color="edr-accent"
leftSection={}
>
{outputFiles[doc.fileKey] ? "Selected" : "Upload"}
@@ -275,7 +275,7 @@ export function ClearanceReviewSection({
}
disabled={Object.keys(outputFiles).length === 0}
@@ -301,7 +301,7 @@ export function ClearanceReviewSection({
@@ -314,7 +314,7 @@ export function ClearanceReviewSection({
}
disabled={!clearance.allApproved}
@@ -391,7 +391,7 @@ function DocReviewCard({
status === "QUERIED"
? "var(--mantine-color-red-2)"
: status === "APPROVED"
- ? "var(--mantine-color-edr-green-2)"
+ ? "var(--mantine-color-edr-blue-2)"
: "var(--mantine-color-edr-border-6)",
}}
>
@@ -471,7 +471,7 @@ function DocReviewCard({
}
disabled={busy}
diff --git a/apps/edr-freight-web/backoffice/src/features/bookings/booking-actions.config.ts b/apps/edr-freight-web/backoffice/src/features/bookings/booking-actions.config.ts
index 8f66cd124..df50b5ed2 100644
--- a/apps/edr-freight-web/backoffice/src/features/bookings/booking-actions.config.ts
+++ b/apps/edr-freight-web/backoffice/src/features/bookings/booking-actions.config.ts
@@ -32,6 +32,7 @@ export type BookingActionId =
| "rejectApproval"
| "viewContract"
| "signContractStaff"
+ | "reviewClearance"
| "allocateBooking"
| "startTransit"
| "complete"
@@ -69,6 +70,7 @@ export type BookingActionContext = Pick<
| "approvalSteps"
| "reference"
| "schedulingStatus"
+ | "customsClearingEnabled"
>;
const ALLOCATABLE_SCHEDULING_STATUSES = new Set([
@@ -250,6 +252,20 @@ const SIGN_CONTRACT_STAFF_ACTION: BookingActionDef = {
primary: true,
};
+// Opens the booking detail straight on the Clearance tab so Marketing can
+// review the customer's clearance documents (non-customs bookings only).
+const REVIEW_CLEARANCE_ACTION: BookingActionDef = {
+ id: "reviewClearance",
+ label: "Review clearance",
+ shortLabel: "Clearance",
+ description: "Approve or query the customer's clearance documents",
+ confirmTitle: "",
+ confirmDescription: "",
+ variant: "default",
+ icon: ShieldCheck,
+ primary: true,
+};
+
function withCancel(actions: BookingActionDef[]): BookingActionDef[] {
return [...actions, CANCEL_ACTION];
}
@@ -261,6 +277,7 @@ const ACTION_PERMISSION: Partial> = {
rejectApproval: FREIGHT_PERMS.bookings.rejectApproval,
viewContract: FREIGHT_PERMS.bookings.view,
signContractStaff: FREIGHT_PERMS.bookings.signStaff,
+ reviewClearance: FREIGHT_PERMS.bookings.reviewDocuments,
startTransit: FREIGHT_PERMS.bookings.operations,
complete: FREIGHT_PERMS.bookings.operations,
operationAccept: FREIGHT_PERMS.bookings.operations,
@@ -359,6 +376,14 @@ export function getBookingActions(
},
];
break;
+ case "AWAITING_DOCUMENTS":
+ case "DOCUMENTS_UNDER_REVIEW":
+ // Marketing reviews non-customs clearance here; customs bookings are
+ // handled in the Global Logistics clearance queue, not the booking list.
+ actions = ctx.customsClearingEnabled
+ ? [CANCEL_ACTION]
+ : withCancel([REVIEW_CLEARANCE_ACTION]);
+ break;
case "OPERATION_REQUEST_PENDING":
actions = withCancel(OPERATION_REVIEW_ACTIONS);
break;
@@ -446,11 +471,17 @@ export function isAllocateAction(id: BookingActionId): boolean {
return id === "allocateBooking";
}
+/** Opens the booking detail on the Clearance tab without a confirm dialog. */
+export function isClearanceNavAction(id: BookingActionId): boolean {
+ return id === "reviewClearance";
+}
+
export function listRowHasActions(
row: {
status: BookingStatus;
paymentCurrency: string;
approvalSteps?: BookingApprovalStep[] | null;
+ customsClearingEnabled?: boolean;
},
user?: AuthUser | null,
): boolean {
@@ -461,6 +492,7 @@ export function listRowHasActions(
reference: "",
approvalSteps: row.approvalSteps ?? undefined,
schedulingStatus: row.status,
+ customsClearingEnabled: row.customsClearingEnabled,
},
user,
);
diff --git a/apps/edr-freight-web/backoffice/src/features/bookings/mapBookingListRow.ts b/apps/edr-freight-web/backoffice/src/features/bookings/mapBookingListRow.ts
index 7f250daeb..ec4708868 100644
--- a/apps/edr-freight-web/backoffice/src/features/bookings/mapBookingListRow.ts
+++ b/apps/edr-freight-web/backoffice/src/features/bookings/mapBookingListRow.ts
@@ -44,6 +44,7 @@ export function toBookingListRow(booking: BookingDetail): BookingListRow {
governmentInstitution: booking.governmentInstitution ?? null,
consolidationPartnerId: booking.consolidationPartnerId ?? null,
consolidationPartnerReference: booking.consolidationPartner?.reference ?? null,
+ customsClearingEnabled: booking.customsClearingEnabled ?? false,
createdAt: booking.createdAt,
};
}
diff --git a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx
index c51285d66..2a7198c9b 100644
--- a/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx
+++ b/apps/edr-freight-web/backoffice/src/pages/bookings/BookingRequestDetailPage.tsx
@@ -1,11 +1,18 @@
-import { useNavigate, useParams } from "react-router-dom";
-import { ArrowLeft, FileSignature, Package } from "lucide-react";
+import { useNavigate, useParams, useSearchParams } from "react-router-dom";
+import {
+ ArrowLeft,
+ FileSignature,
+ LayoutGrid,
+ Package,
+ ShieldCheck,
+} from "lucide-react";
import {
Container,
Stack,
Grid,
Center,
Loader,
+ Tabs,
Text,
Paper,
Button,
@@ -34,6 +41,7 @@ import {
import { WarehouseInfoCard } from "@/components/warehouses";
import { getStatusMeta } from "@/features/bookings/booking-status.config";
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
+import type { BookingDetail } from "@/types/booking";
import { downloadBookingFile } from "@/services/files.service";
import {
useBookingDetail,
@@ -53,6 +61,7 @@ const SIGNATURE_FILE_CODES = new Set([
export default function BookingRequestDetailPage() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
+ const [searchParams, setSearchParams] = useSearchParams();
const {
data: booking,
isLoading,
@@ -143,6 +152,23 @@ export default function BookingRequestDetailPage() {
booking.status === "PENDING_APPROVAL" ||
booking.status === "APPROVED_PENDING_SIGNATURE";
+ // Non-customs clearance is reviewed here by Marketing in its own tab; customs
+ // bookings are handled in the Global Logistics clearance queue instead.
+ const showClearanceTab =
+ !booking.customsClearingEnabled &&
+ ["AWAITING_DOCUMENTS", "DOCUMENTS_UNDER_REVIEW", "CLEARANCE_READY"].includes(
+ booking.status,
+ );
+ const requestedTab = searchParams.get("tab");
+ const activeTab =
+ requestedTab === "clearance" && showClearanceTab ? "clearance" : "overview";
+ const setActiveTab = (tab: string | null) => {
+ const next = new URLSearchParams(searchParams);
+ if (tab && tab !== "overview") next.set("tab", tab);
+ else next.delete("tab");
+ setSearchParams(next, { replace: true });
+ };
+
return (
- {/* LEFT — primary content */}
+ {/* LEFT — primary content, split into tabs to keep each view focused */}
-
- {/* Non-customs clearance is reviewed here by Marketing; customs
- bookings are handled in the Global Logistics clearance queue. */}
- {!booking.customsClearingEnabled &&
- ["AWAITING_DOCUMENTS", "DOCUMENTS_UNDER_REVIEW"].includes(
- booking.status,
- ) ? (
- refetch()}
- />
- ) : null}
-
+
+ }
+ >
+ Overview
+
+ }
+ >
+ Customer clearance
+
+
+
+
+
+
+
+ refetch()}
+ />
+
+
+ ) : (
+
-
-
- {booking.contractSummary && (
-
- )}
- !SIGNATURE_FILE_CODES.has(f.code ?? ""),
- )}
+ row={row}
onDownload={handleDownloadFile}
/>
-
+ )}
{/* RIGHT — sticky action / summary rail */}
@@ -245,3 +286,35 @@ export default function BookingRequestDetailPage() {
);
}
+
+/** The booking's primary detail cards — route, services, cargo, contract, docs. */
+function OverviewPanel({
+ booking,
+ row,
+ onDownload,
+}: {
+ booking: BookingDetail;
+ row: ReturnType;
+ onDownload: (file: BookingFileView) => void;
+}) {
+ return (
+
+
+
+
+ {booking.contractSummary && (
+
+ )}
+ !SIGNATURE_FILE_CODES.has(f.code ?? ""),
+ )}
+ onDownload={onDownload}
+ />
+
+ );
+}
diff --git a/apps/edr-freight-web/backoffice/src/pages/bookings/DocumentClearanceDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/bookings/DocumentClearanceDetailPage.tsx
index 646678d69..fa18a440d 100644
--- a/apps/edr-freight-web/backoffice/src/pages/bookings/DocumentClearanceDetailPage.tsx
+++ b/apps/edr-freight-web/backoffice/src/pages/bookings/DocumentClearanceDetailPage.tsx
@@ -64,7 +64,7 @@ export default function DocumentClearanceDetailPage() {
return (
-
+
Loading clearance…
@@ -103,7 +103,7 @@ export default function DocumentClearanceDetailPage() {
clearance.allApproved ? (
}
>
@@ -112,7 +112,7 @@ export default function DocumentClearanceDetailPage() {
) : (
}
>
@@ -133,13 +133,13 @@ export default function DocumentClearanceDetailPage() {
{/* RIGHT — sticky progress gauge */}
-
+
@@ -153,7 +153,7 @@ export default function DocumentClearanceDetailPage() {
/>
@@ -163,7 +163,7 @@ export default function DocumentClearanceDetailPage() {
value={stats.queried}
/>
@@ -199,7 +199,7 @@ function ClearanceHero({
-
+
@@ -219,7 +219,7 @@ function ClearanceHero({
}
>
@@ -248,7 +248,7 @@ function ClearanceHero({
{stats.approved}/{stats.total}
-
+
diff --git a/apps/edr-freight-web/backoffice/src/types/booking.ts b/apps/edr-freight-web/backoffice/src/types/booking.ts
index 98b09a527..53a63b34d 100644
--- a/apps/edr-freight-web/backoffice/src/types/booking.ts
+++ b/apps/edr-freight-web/backoffice/src/types/booking.ts
@@ -221,5 +221,6 @@ export interface BookingListRow {
governmentInstitution?: string | null;
consolidationPartnerId?: string | null;
consolidationPartnerReference?: string | null;
+ customsClearingEnabled?: boolean;
createdAt: string;
}