diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/DraftBookingView.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/DraftBookingView.tsx
index 03919c4b9..2c2835266 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/DraftBookingView.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/DraftBookingView.tsx
@@ -29,7 +29,11 @@ import { CardTitle, PageShell, SectionCard } from "./components/layout";
import { CountChip, DocRow, IconSquare } from "./components/Documents";
import { EstimateCard } from "./components/pricing";
import { HeaderButton, PageHeader } from "./components/PageHeader";
-import { MutationErrors, NoticeBanner } from "./components/Notices";
+import {
+ ActionRequiredBanner,
+ MutationErrors,
+ NoticeBanner,
+} from "./components/Notices";
import { ScheduleCard } from "./components/ScheduleCard";
import { ShipmentDetailsCard } from "./components/ShipmentDetailsCard";
import { StatusHero } from "./components/StatusHero";
@@ -132,30 +136,39 @@ export function DraftBookingView({
}
- label="Continue editing"
- onClick={() => navigate(`/bookings/${booking.id}/edit`)}
- />
+ <>
+ }
+ label="Documents"
+ onClick={() =>
+ documentsRef.current?.scrollIntoView({ behavior: "smooth" })
+ }
+ />
+ }
+ label="Continue editing"
+ onClick={() => navigate(`/bookings/${booking.id}/edit`)}
+ />
+ >
}
/>
- {booking.status === "CHANGES_REQUESTED" &&
- booking.latestChangeRequestNote && (
- }
- title="Changes requested by staff"
- >
- {booking.latestChangeRequestNote}
-
- )}
-
+
+ {booking.status === "CHANGES_REQUESTED" &&
+ booking.latestChangeRequestNote ? (
+ navigate(`/bookings/${booking.id}/edit`)}
+ >
+ {booking.latestChangeRequestNote}
+
+ ) : undefined}
+
- navigate(`/bookings/${booking.id}/edit`)}
- >
- Edit
-
- }
- />
+
- documentsRef.current?.scrollIntoView({
- behavior: "smooth",
- })
- }
- >
- Upload
-
- )
+ }
+ onClick={() =>
+ documentsRef.current?.scrollIntoView({
+ behavior: "smooth",
+ })
+ }
+ >
+ Add documents
+
}
/>
@@ -223,7 +222,10 @@ export function DraftBookingView({
onClick={handleSubmitRequest}
disabled={submitMutation.isPending}
loading={submitMutation.isPending}
- styles={{ root: { height: 46 }, label: { fontWeight: 700 } }}
+ styles={{
+ root: { height: 46 },
+ label: { fontSize: 14, fontWeight: 800 },
+ }}
>
{submitMutation.isPending ? "Submitting…" : "Submit for review"}
@@ -268,7 +270,7 @@ export function DraftBookingView({
? (file?.name ?? "Uploaded")
: selected
? selected.name
- : "Required · not uploaded"
+ : "Not added yet"
}
status={
isUploaded ? "verified" : selected ? "ready" : "missing"
@@ -297,22 +299,26 @@ export function DraftBookingView({
/>
}
+ leftSection={}
onClick={() =>
fileInputRefs.current[doc.key]?.click()
}
styles={{
- root: { height: 34, paddingInline: 12 },
+ root: {
+ height: 34,
+ paddingInline: 13,
+ border: "1.5px solid #CBD5E1",
+ },
label: {
fontSize: 12.5,
fontWeight: 700,
- color: "#475569",
+ color: "#334155",
},
}}
>
- {selected ? "Change" : "Choose"}
+ {selected ? "Change" : "Add"}
{selected && (
setCancelDialogOpen(true)} />
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/ReadonlyBookingView.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/ReadonlyBookingView.tsx
index 917894626..d5281b98b 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/ReadonlyBookingView.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/ReadonlyBookingView.tsx
@@ -10,12 +10,14 @@ import { ActivityCard } from "./components/ActivityCard";
import { ContractCard } from "./components/ContractCard";
import { DocRow, IconSquare } from "./components/Documents";
import { BodyGrid, CardTitle, PageShell, SectionCard } from "./components/layout";
+import { CancelledBanner } from "./components/Notices";
import { HeaderButton, PageHeader } from "./components/PageHeader";
import { PaymentCard } from "./components/pricing";
import { ScheduleCard } from "./components/ScheduleCard";
import { ShipmentDetailsCard } from "./components/ShipmentDetailsCard";
import { StatusHero } from "./components/StatusHero";
import { SupportCard } from "./components/SupportCard";
+import { fmtDate, isNegative } from "./utils";
export function ReadonlyBookingView({ booking }: { booking: Freight.IBooking }) {
const navigate = useNavigate();
@@ -49,7 +51,23 @@ export function ReadonlyBookingView({ booking }: { booking: Freight.IBooking })
}
/>
-
+ {isNegative(status) ? (
+ navigate("/bookings/new")}
+ />
+ ) : (
+
+ )}
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ActivityCard.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ActivityCard.tsx
index 851f5f92c..c08b6d5f5 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ActivityCard.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ActivityCard.tsx
@@ -1,5 +1,4 @@
import { Box, Group, Text } from "@mantine/core";
-import { Train } from "lucide-react";
import type { Freight } from "@edr/types";
@@ -59,16 +58,11 @@ export function ActivityCard({ booking }: { booking: Freight.IBooking }) {
style={{
width: 18,
height: 18,
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
borderRadius: 999,
backgroundColor: first ? "#0EA371" : "#D4E9DF",
boxShadow: first ? "0 0 0 4px #BFE8D4" : undefined,
}}
- >
- {first && }
-
+ />
{!lastItem && (
- Latest
+ Current
)}
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ContractCard.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ContractCard.tsx
index e9836703c..aa0abb384 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ContractCard.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/ContractCard.tsx
@@ -10,29 +10,27 @@ const CONTRACT_CONFIG: Record<
title: string;
description: string;
buttonLabel?: string;
- urgent?: boolean;
}
> = {
APPROVED_PENDING_SIGNATURE: {
- title: "Contract being prepared",
+ title: "Your contract is being prepared.",
description:
"Your booking has been approved. The contract will be available shortly.",
},
CONTRACT_READY: {
- title: "Action required — sign your contract",
+ title: "Review and sign your contract to proceed.",
description:
- "Your contract is ready. Review the agreement and apply your digital signature to proceed.",
- buttonLabel: "View & sign contract",
- urgent: true,
+ "Once all parties sign, we'll issue your payment reference (PNR) and schedule the cargo.",
+ buttonLabel: "Review & Sign",
},
SIGNED_CUSTOMER: {
- title: "You have signed the contract",
+ title: "You have signed the contract.",
description:
"Your signature has been submitted. Awaiting the final staff signature.",
buttonLabel: "View contract",
},
FULLY_EXECUTED: {
- title: "Contract fully executed",
+ title: "Contract fully executed — proceed to payment.",
description:
"The contract has been signed by all parties. You can now proceed to payment.",
buttonLabel: "View contract",
@@ -48,37 +46,54 @@ export function ContractCard({
}) {
const c = CONTRACT_CONFIG[booking.status as string];
if (!c) return null;
- const urgent = c.urgent;
return (
-
+
-
-
+
+
+ What's next
+
+
{c.title}
-
+
{c.description}
@@ -87,10 +102,10 @@ export function ContractCard({
-
+
{title}
{meta}
- {status === "verified" && (
-
-
- Verified
-
- )}
- {status === "ready" && (
-
- Ready
-
- )}
+
+ {pill.icon}
+ {pill.label}
+
{action}
);
@@ -147,15 +160,15 @@ export function CountChip({
style={{
display: "inline-flex",
borderRadius: 999,
- padding: "5px 11px",
+ padding: "5px 10px",
fontSize: 11.5,
fontWeight: 700,
- backgroundColor: done ? "#ECF6F1" : "#FDF3E0",
- color: done ? "#0A6F4D" : "#9A5B00",
+ backgroundColor: done ? "#ECF6F1" : "#F1F4F7",
+ color: done ? "#0A6F4D" : "#6B7C8E",
}}
>
{done && }
- {uploaded}/{total} uploaded
+ {uploaded} of {total} added
);
}
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Notices.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Notices.tsx
index 77565aed8..24f7fbc27 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Notices.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Notices.tsx
@@ -1,5 +1,5 @@
-import { Box, Group, Text } from "@mantine/core";
-import { AlertCircle } from "lucide-react";
+import { Box, Button, Group, Paper, Stack, Text } from "@mantine/core";
+import { AlertCircle, AlertTriangle, PencilLine, StickyNote, XCircle } from "lucide-react";
import type { ReactNode } from "react";
export function NoticeBanner({
@@ -51,6 +51,194 @@ export function NoticeBanner({
);
}
+export function ActionRequiredBanner({
+ title,
+ children,
+ onAction,
+}: {
+ title: string;
+ children: ReactNode;
+ onAction?: () => void;
+}) {
+ return (
+
+
+
+
+
+
+
+ Action required · You
+
+
+ {title}
+
+
+ {children}
+
+
+
+ {onAction && (
+ }
+ styles={{
+ root: { height: 42, paddingInline: 18, flexShrink: 0 },
+ label: { fontSize: 13, fontWeight: 800, color: "#3D2A05" },
+ }}
+ >
+ Review & Resubmit
+
+ )}
+
+ );
+}
+
+export function CancelledBanner({
+ pillLabel = "Cancelled",
+ title,
+ subtitle,
+ reason,
+ onRebook,
+}: {
+ pillLabel?: string;
+ title: string;
+ subtitle: string;
+ reason?: string | null;
+ onRebook?: () => void;
+}) {
+ return (
+
+
+
+
+
+
+
+
+
+ {pillLabel}
+
+
+ {title}
+
+
+ {subtitle}
+
+
+
+ {onRebook && (
+
+ )}
+
+ {reason && (
+
+
+
+
+ Reason for cancellation
+
+
+ {reason}
+
+
+
+ )}
+
+
+ );
+}
+
export function MutationErrors({
mutations,
}: {
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/PageHeader.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/PageHeader.tsx
index f326f86f0..a0c372a9e 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/PageHeader.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/PageHeader.tsx
@@ -27,7 +27,7 @@ export function PageHeader({
-
+
{booking.reference}
m.toUpperCase());
+
+ return (
+
+
+ {label}
+
+ );
+}
+
export function ScheduleCard({
booking,
title,
@@ -30,6 +68,11 @@ export function ScheduleCard({
muted: !booking.trainId,
};
+ const statusRow: Row = {
+ label: "Status",
+ value: ,
+ };
+
const rows: Row[] = consignment
? [
{ label: "Consignment ID", value: booking.reference },
@@ -40,6 +83,7 @@ export function ScheduleCard({
{ label: "Consolidation", value: consolidation },
]
: [
+ statusRow,
{ label: "Service", value: service },
{ label: "Equipment return", value: equipmentReturn },
{ label: "Proposed date", value: fmtDate(booking.scheduledDate) },
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/StatusHero.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/StatusHero.tsx
index b85bd81b1..89b3e7024 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/StatusHero.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/StatusHero.tsx
@@ -1,5 +1,5 @@
import { Box, Group, Text } from "@mantine/core";
-import { AlertTriangle, Check, Clock, FileText, History } from "lucide-react";
+import { AlertTriangle, Check, FileText, History } from "lucide-react";
import type { Freight } from "@edr/types";
@@ -7,7 +7,13 @@ import { PROGRESS_STAGES, STATUS_MAP } from "../constants";
import { fmtDate, isDraftLike, isNegative } from "../utils";
import { SectionCard } from "./layout";
-export function StatusHero({ booking }: { booking: Freight.IBooking }) {
+export function StatusHero({
+ booking,
+ children,
+}: {
+ booking: Freight.IBooking;
+ children?: React.ReactNode;
+}) {
const status = booking.status as string;
const cfg = STATUS_MAP[status] ?? STATUS_MAP.DRAFT;
const negative = isNegative(status);
@@ -34,7 +40,7 @@ export function StatusHero({ booking }: { booking: Freight.IBooking }) {
);
return (
-
+
-
+
{cfg.title}
-
+
{cfg.description}
@@ -72,25 +78,33 @@ export function StatusHero({ booking }: { booking: Freight.IBooking }) {
backgroundColor: "#F7FAFC",
}}
>
-
+
-
+
{chipLabel}
-
+
{chipValue}
-
+
-
+ {children ?? (
+
+ )}
);
}
@@ -185,7 +199,7 @@ function ProgressTracker({
@@ -193,7 +207,7 @@ function ProgressTracker({
diff --git a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Steps.tsx b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Steps.tsx
index cecc9639e..8ad1646c3 100644
--- a/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Steps.tsx
+++ b/apps/edr-freight-web/portal/src/pages/bookings/BookingDetailPage/components/Steps.tsx
@@ -2,6 +2,41 @@ import { Box, Button, Group, Text } from "@mantine/core";
import { Check } from "lucide-react";
import type { ReactNode } from "react";
+function StepCircle({
+ index,
+ done,
+ active,
+}: {
+ index: number;
+ done?: boolean;
+ active?: boolean;
+}) {
+ const style: React.CSSProperties = done
+ ? { backgroundColor: "#0EA371", color: "#fff" }
+ : active
+ ? { backgroundColor: "#0C1A2B", color: "#fff" }
+ : { backgroundColor: "#EEF2F6", color: "#9AA8B5" };
+
+ return (
+
+ {done ? : index}
+
+ );
+}
+
export function StepLine({
index,
title,
@@ -9,63 +44,49 @@ export function StepLine({
action,
done,
active,
- highlight,
}: {
index: number;
title: string;
- desc: string;
+ desc?: string;
action?: ReactNode;
done?: boolean;
active?: boolean;
- highlight?: boolean;
}) {
- const badgeStyle: React.CSSProperties = done
- ? { backgroundColor: "#0EA371", color: "#fff" }
- : active
- ? { backgroundColor: "#0C1A2B", color: "#fff" }
- : {
- backgroundColor: "#EEF2F6",
- color: "#9AA8B5",
- border: "1px solid #E1E7EE",
- };
+ if (active) {
+ return (
+
+
+
+
+
+ {title}
+
+ {desc && (
+
+ {desc}
+
+ )}
+
+
+ {action}
+
+ );
+ }
return (
-
-
- {done ? : index}
-
-
-
- {title}
-
-
- {desc}
-
-
- {action}
+
+
+
+ {title}
+
);
}
@@ -73,18 +94,26 @@ export function StepLine({
export function StepGhostButton({
children,
onClick,
+ icon,
}: {
children: ReactNode;
onClick?: () => void;
+ icon?: ReactNode;
}) {
return (
-
- {priceTotal(pricing)}
-
+
+
+ {priceTotal(pricing)}
+
+
+ est.
+
+
A firm price is confirmed after EDR reviews your booking.
@@ -101,9 +122,13 @@ export function PaymentCard({
Payment
-
+ {paid && }
{paid
? "Paid"
: (booking.paymentStatus?.replace(/_/g, " ") ?? "Pending")}
-
+