Merge branch 'freight_feature/profile' of github.com:Tria-plc/edr-platform into freight_feature/profile

This commit is contained in:
marshal
2026-06-25 08:56:13 +03:00
8 changed files with 361 additions and 6 deletions

View File

@@ -203,6 +203,19 @@ export const STATUS_CONFIG: Record<string, StageConfig> = {
badgeDot: "edr-green.5",
action: { label: "Schedule & proceed", kind: "amber", icon: ArrowRight },
},
OPERATION_REQUEST_PENDING: {
stage: 3,
icon: FileCheck2,
iconColor: "edr-blue",
tile: "edr-blue-soft",
hint: "Operation request under review by operations",
step: "edr-blue-dot",
badgeLabel: "Op. review",
badgeBg: "edr-blue-soft",
badgeText: "edr-blue",
badgeDot: "edr-blue-dot",
action: { label: "View", kind: "outline" },
},
OPERATION_REQUESTED: {
stage: 3,
icon: CheckCircle2,
@@ -216,6 +229,45 @@ export const STATUS_CONFIG: Record<string, StageConfig> = {
badgeDot: "edr-green.5",
action: { label: "View", kind: "outline" },
},
OPERATION_CHANGES_REQUESTED: {
stage: 3,
icon: FilePen,
iconColor: "edr-amber-text",
tile: "edr-amber-soft",
hint: "Operations requested changes · please review",
step: "edr-accent",
badgeLabel: "Revise",
badgeBg: "edr-amber-soft",
badgeText: "edr-amber-text",
badgeDot: "edr-accent",
action: { label: "Review", kind: "dark" },
},
OPERATION_PRICE_PENDING_CONFIRM: {
stage: 3,
icon: Wallet,
iconColor: "edr-amber-text",
tile: "edr-amber-soft",
hint: "Price adjusted · confirm to proceed",
step: "edr-accent",
badgeLabel: "Confirm price",
badgeBg: "edr-amber-soft",
badgeText: "edr-amber-text",
badgeDot: "edr-accent",
action: { label: "Confirm", kind: "amber", icon: ArrowRight },
},
ROAD_DISPATCH_PENDING: {
stage: 3,
icon: Truck,
iconColor: "edr-blue",
tile: "edr-blue-soft",
hint: "Accepted · awaiting truck dispatch",
step: "edr-blue-dot",
badgeLabel: "Awaiting dispatch",
badgeBg: "edr-blue-soft",
badgeText: "edr-blue",
badgeDot: "edr-blue-dot",
action: { label: "View", kind: "outline" },
},
PNR_GENERATED: {
stage: 3,
icon: FileCheck2,
@@ -372,6 +424,84 @@ export const STATUS_CONFIG: Record<string, StageConfig> = {
badgeDot: "edr-green.5",
action: { label: "View", kind: "outline" },
},
PRICE_CHANGED_PENDING_CONFIRM: {
stage: 1,
icon: Wallet,
iconColor: "edr-amber-text",
tile: "edr-amber-soft",
hint: "Price changed · confirm to continue",
step: "edr-accent",
badgeLabel: "Confirm price",
badgeBg: "edr-amber-soft",
badgeText: "edr-amber-text",
badgeDot: "edr-accent",
action: { label: "Confirm", kind: "amber", icon: ArrowRight },
},
READY_FOR_ASSIGNMENT: {
stage: 2,
icon: FileCheck2,
iconColor: "edr-blue",
tile: "edr-blue-soft",
hint: "Approved · awaiting wagon assignment",
step: "edr-blue-dot",
badgeLabel: "Assigning",
badgeBg: "edr-blue-soft",
badgeText: "edr-blue",
badgeDot: "edr-blue-dot",
action: { label: "View", kind: "outline" },
},
WAGON_ASSIGNED: {
stage: 3,
icon: CheckCircle2,
iconColor: "edr-green.7",
tile: "edr-soft",
hint: "Wagon assigned · preparing for loading",
step: "edr-green.5",
badgeLabel: "Wagon assigned",
badgeBg: "edr-soft",
badgeText: "edr-green.7",
badgeDot: "edr-green.5",
action: { label: "View", kind: "outline" },
},
INVOICED: {
stage: 3,
icon: Wallet,
iconColor: "edr-blue",
tile: "edr-blue-soft",
hint: "Invoice issued · awaiting payment",
step: "edr-blue-dot",
badgeLabel: "Invoiced",
badgeBg: "edr-blue-soft",
badgeText: "edr-blue",
badgeDot: "edr-blue-dot",
action: { label: "View", kind: "outline" },
},
CONTRACT_ACTIVE: {
stage: 3,
icon: CheckCircle2,
iconColor: "edr-green.7",
tile: "edr-soft",
hint: "Contract active · accepting orders",
step: "edr-green.5",
badgeLabel: "Active",
badgeBg: "edr-soft",
badgeText: "edr-green.7",
badgeDot: "edr-green.5",
action: { label: "View", kind: "outline" },
},
CONTRACT_CLOSED: {
stage: 4,
icon: CheckCircle2,
iconColor: "edr-slate",
tile: "edr-slate-soft2",
hint: "Contract closed · quantity used or window elapsed",
step: "edr-step",
badgeLabel: "Closed",
badgeBg: "edr-slate-soft2",
badgeText: "edr-slate",
badgeDot: "edr-step",
action: { label: "View", kind: "outline" },
},
};
export const ACTION_PROPS: Record<

View File

@@ -59,7 +59,7 @@ export function ClearanceCard({ booking }: { booking: Freight.IBooking }) {
leftSection={<Upload size={16} />}
onClick={() => flow.submitDocuments()}
loading={flow.uploadMutation.isPending}
disabled={!flow.hasStagedFiles}
disabled={!flow.canSubmit}
>
Submit documents
</Button>

View File

@@ -177,6 +177,61 @@ export const STATUS_MAP: Record<
description: "Cargo has been consolidated with a partner shipment.",
stage: 5,
},
AWAITING_DOCUMENTS: {
title: "Clearance documents needed",
description:
"Upload the required clearance documents so your shipment can be reviewed.",
stage: 5,
},
DOCUMENTS_UNDER_REVIEW: {
title: "Documents under review",
description:
"Your clearance documents are being reviewed. Re-upload any queried documents to proceed.",
stage: 5,
},
CLEARANCE_READY: {
title: "Cleared — choose a shipment day",
description:
"Clearance is complete. Pick a shipment day and proceed to operation.",
stage: 5,
},
OPERATION_REQUESTED: {
title: "Operation requested",
description: "Operation requested. An operator will take your shipment forward.",
stage: 5,
},
CONTRACT_ACTIVE: {
title: "Contract active",
description: "This general contract is active and accepting drawdown orders.",
stage: 5,
},
CONTRACT_CLOSED: {
title: "Contract closed",
description:
"This general contract is closed — its reserved quantity has been used or its window has elapsed.",
stage: 7,
},
PRICE_CHANGED_PENDING_CONFIRM: {
title: "Price changed — confirm to proceed",
description:
"The price for this booking changed. Confirm the new price to continue.",
stage: 1,
},
READY_FOR_ASSIGNMENT: {
title: "Awaiting wagon assignment",
description: "Approved and queued for wagon assignment.",
stage: 2,
},
WAGON_ASSIGNED: {
title: "Wagon assigned",
description: "A wagon has been assigned and your cargo is being prepared for loading.",
stage: 5,
},
INVOICED: {
title: "Invoice issued",
description: "An invoice has been issued for this booking.",
stage: 5,
},
COMPLETED: {
title: "Service complete",
description: "Cargo delivered and service successfully terminated.",

View File

@@ -86,7 +86,7 @@ function BookingActionModalBody({
leftSection={<Upload size={16} />}
onClick={handleSubmit}
loading={flow.uploadMutation.isPending}
disabled={!flow.hasStagedFiles}
disabled={!flow.canSubmit}
>
Submit documents
</Button>

View File

@@ -90,9 +90,11 @@ export function ClearanceFlow({ booking, flow, footer }: ClearanceFlowProps) {
glDocs,
isReady,
canUpload,
isInitialUpload,
status,
pending,
adHoc,
missingRequired,
stagePending,
addAdHocRow,
setAdHocName,
@@ -116,14 +118,23 @@ export function ClearanceFlow({ booking, flow, footer }: ClearanceFlowProps) {
) : status === "DOCUMENTS_UNDER_REVIEW" ? (
<Alert color="blue" radius="md" icon={<Clock size={18} />} mb="md">
{clearance.includesCustoms
? "Global Logistics is reviewing your documents and will clear your shipment. Queried documents below need to be re-uploaded."
: "Our team is reviewing your documents. Queried documents below need to be re-uploaded."}
? "Global Logistics is reviewing your documents and will clear your shipment. Only re-upload the documents flagged with a query below — approved documents stay as they are."
: "Our team is reviewing your documents. Only re-upload the documents flagged with a query below — approved documents stay as they are."}
</Alert>
) : (
<Alert color="yellow" radius="md" icon={<AlertCircle size={18} />} mb="md">
{clearance.includesCustoms
? "Upload the documents customs needs Global Logistics will clear your shipment and return the cleared documents here."
: "Upload all the required clearance documents below to start the review."}
? "Upload every required document customs needs (marked *) to start the review. Global Logistics will clear your shipment and return the cleared documents here."
: "Upload every required clearance document (marked *) below to start the review."}
</Alert>
)}
{isInitialUpload && missingRequired.length > 0 && (
<Alert color="yellow" variant="light" radius="md" mb="md" p="xs">
<Text fz="12px" c="#9A5B00">
Still required:{" "}
{missingRequired.map((d) => d.label).join(", ")}
</Text>
</Alert>
)}

View File

@@ -69,10 +69,32 @@ export function useClearanceFlow(booking: Freight.IBooking) {
const isReady = status === "CLEARANCE_READY";
const canUpload =
status === "AWAITING_DOCUMENTS" || status === "DOCUMENTS_UNDER_REVIEW";
// The very first upload (nothing in review yet). Here every required document
// must be provided. Once GL has started reviewing (DOCUMENTS_UNDER_REVIEW) the
// customer is only re-uploading queried/pending docs, so we don't re-gate on
// the full required set.
const isInitialUpload = status === "AWAITING_DOCUMENTS";
const hasStagedFiles =
Object.keys(pending).length > 0 || adHoc.some((r) => r.file);
// A required customer document is satisfied when it already has an uploaded
// file or the customer has just staged one for this submission.
const missingRequired = useMemo(
() =>
customerDocs.filter(
(d) => d.required && !d.file && !pending[d.fileKey],
),
[customerDocs, pending],
);
// Initial upload: block submit until every required field has a file (and at
// least one file is actually staged to send). Re-upload rounds only need at
// least one staged file — the customer fixes the specific queried documents.
const canSubmit = isInitialUpload
? hasStagedFiles && missingRequired.length === 0
: hasStagedFiles;
// --- staged-upload mutators ----------------------------------------------
const stagePending = (fileKey: string, file: File) =>
@@ -117,10 +139,13 @@ export function useClearanceFlow(booking: Freight.IBooking) {
glDocs,
isReady,
canUpload,
isInitialUpload,
// staged upload state
pending,
adHoc,
hasStagedFiles,
missingRequired,
canSubmit,
stagePending,
addAdHocRow,
setAdHocName,