contrat,booking,global logestic

This commit is contained in:
Marshal
2026-06-26 23:24:48 +00:00
parent f931342f31
commit 01d53c218c
105 changed files with 19573 additions and 909 deletions

View File

@@ -0,0 +1,86 @@
import type { Freight } from "@edr/types";
export interface ApprovalProgressSummary {
label: string;
detail: string;
complete: boolean;
}
function nextPending(
steps: Freight.IContractApprovalStep[],
): Freight.IContractApprovalStep | undefined {
return steps.find((s) => s.status === "PENDING");
}
/** Compact approval-chain summary for contract list rows (mirrors bookings). */
export function formatContractApprovalProgress(
status: string,
steps?: Freight.IContractApprovalStep[] | null,
): ApprovalProgressSummary {
const sorted = [...(steps ?? [])].sort((a, b) => a.stepOrder - b.stepOrder);
if (sorted.length === 0) {
if (status === "SUBMITTED") {
return {
label: "Awaiting accept",
detail: "Staff must accept intake",
complete: false,
};
}
if (
status === "PENDING_APPROVAL" ||
status === "APPROVED_PENDING_SIGNATURE"
) {
return {
label: "No steps",
detail: "Approval chain not started",
complete: false,
};
}
if (
[
"APPROVED",
"CONTRACT_READY",
"SIGNED_CUSTOMER",
"FULLY_EXECUTED",
"CONTRACT_ACTIVE",
"CLEARANCE_READY_FOR_BOOKING",
"ACTIVE_SHIPMENT_IN_PROGRESS",
"CONTRACT_CLOSED",
].includes(status)
) {
return {
label: "Approved",
detail: "Internal approval complete",
complete: true,
};
}
return { label: "—", detail: "", complete: false };
}
const approved = sorted.filter((s) => s.status === "APPROVED").length;
const total = sorted.length;
const next = nextPending(sorted);
if (!next && approved === total) {
return {
label: `${approved}/${total} done`,
detail: sorted.map((s) => `${s.requiredRole}`).join(" · "),
complete: true,
};
}
if (next) {
return {
label: `${approved}/${total}`,
detail: `Next: ${next.requiredRole} (step ${next.stepOrder})`,
complete: false,
};
}
return {
label: `${approved}/${total}`,
detail: sorted.map((s) => `${s.requiredRole}: ${s.status}`).join(" · "),
complete: approved === total,
};
}

View File

@@ -0,0 +1,355 @@
import type { ContractStatus } from "@edr/types";
export interface StatusStyle {
label: string;
color: string;
}
/** Tailwind chip styling per contract status (mirrors booking-status.config). */
export const CONTRACT_STATUS_STYLES: Record<string, StatusStyle> = {
DRAFT: {
label: "Draft",
color: "bg-slate-100 text-slate-700 border-slate-300",
},
SUBMITTED: {
label: "Submitted",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
PRICE_CHANGED_PENDING_CONFIRM: {
label: "Price Confirm",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
CHANGES_REQUESTED: {
label: "Changes Requested",
color: "bg-orange-50 text-orange-700 border-orange-200",
},
PENDING_APPROVAL: {
label: "Pending Approval",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
APPROVED: {
label: "Approved",
color: "bg-[color:var(--freight-brand-muted)] text-[color:var(--freight-brand)] border-[color:var(--freight-brand-border)]",
},
APPROVED_PENDING_SIGNATURE: {
label: "Pending Signature",
color: "bg-sky-50 text-sky-700 border-sky-200",
},
CONTRACT_READY: {
label: "Contract Ready",
color: "bg-indigo-50 text-indigo-700 border-indigo-200",
},
SIGNED_CUSTOMER: {
label: "Customer Signed",
color: "bg-sky-50 text-sky-700 border-sky-200",
},
FULLY_EXECUTED: {
label: "Fully Executed",
color: "bg-indigo-50 text-indigo-700 border-indigo-200",
},
CONTRACT_ACTIVE: {
label: "Active",
color: "bg-[color:var(--freight-brand-muted)] text-[color:var(--freight-brand)] border-[color:var(--freight-brand-border)]",
},
AWAITING_CLEARANCE_DOCUMENTS: {
label: "Awaiting Documents",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
CLEARANCE_UNDER_REVIEW: {
label: "Clearance Review",
color: "bg-amber-50 text-amber-800 border-amber-200",
},
CLEARANCE_READY_FOR_BOOKING: {
label: "Ready for Booking",
color: "bg-[color:var(--freight-brand-muted)] text-[color:var(--freight-brand)] border-[color:var(--freight-brand-border)]",
},
ACTIVE_SHIPMENT_IN_PROGRESS: {
label: "Shipment in Progress",
color: "bg-sky-50 text-sky-700 border-sky-200",
},
CONTRACT_CLOSED: {
label: "Closed",
color: "bg-slate-100 text-slate-700 border-slate-300",
},
EXPIRED: {
label: "Expired",
color: "bg-red-50 text-red-700 border-red-200",
},
REJECTED: {
label: "Rejected",
color: "bg-red-50 text-red-700 border-red-200",
},
CANCELLED: {
label: "Cancelled",
color: "bg-red-50 text-red-700 border-red-200",
},
RENEWAL_DRAFT: {
label: "Renewal Draft",
color: "bg-slate-100 text-slate-700 border-slate-300",
},
RENEWAL_SUBMITTED: {
label: "Renewal Submitted",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
RENEWAL_PENDING_APPROVAL: {
label: "Renewal Approval",
color: "bg-amber-50 text-amber-700 border-amber-200",
},
AMENDMENTS_PROPOSED: {
label: "Amendments Proposed",
color: "bg-orange-50 text-orange-700 border-orange-200",
},
ARCHIVED: {
label: "Archived",
color: "bg-slate-100 text-slate-700 border-slate-300",
},
};
/** Mantine palette colour per contract status (mirrors BookingStatusBadge map). */
export const CONTRACT_STATUS_COLOR: Record<string, string> = {
DRAFT: "gray",
SUBMITTED: "yellow",
PRICE_CHANGED_PENDING_CONFIRM: "yellow",
CHANGES_REQUESTED: "orange",
PENDING_APPROVAL: "yellow",
APPROVED: "edr-green",
APPROVED_PENDING_SIGNATURE: "cyan",
CONTRACT_READY: "indigo",
SIGNED_CUSTOMER: "cyan",
FULLY_EXECUTED: "indigo",
CONTRACT_ACTIVE: "edr-green",
AWAITING_CLEARANCE_DOCUMENTS: "yellow",
CLEARANCE_UNDER_REVIEW: "yellow",
CLEARANCE_READY_FOR_BOOKING: "edr-green",
ACTIVE_SHIPMENT_IN_PROGRESS: "cyan",
CONTRACT_CLOSED: "gray",
EXPIRED: "red",
REJECTED: "red",
CANCELLED: "red",
RENEWAL_DRAFT: "gray",
RENEWAL_SUBMITTED: "yellow",
RENEWAL_PENDING_APPROVAL: "yellow",
AMENDMENTS_PROPOSED: "orange",
ARCHIVED: "gray",
};
export interface StatusMeta {
title: string;
description: string;
color: string;
stage: number;
}
export const CONTRACT_STATUS_META: Record<string, StatusMeta> = {
DRAFT: {
title: "Draft",
description: "Contract is being prepared by the customer.",
color: "text-slate-500",
stage: 0,
},
SUBMITTED: {
title: "Submitted",
description: "Awaiting staff review.",
color: "text-amber-600",
stage: 0,
},
PRICE_CHANGED_PENDING_CONFIRM: {
title: "Price Confirm",
description: "Awaiting customer confirmation of revised unit rates.",
color: "text-amber-600",
stage: 0,
},
CHANGES_REQUESTED: {
title: "Changes Requested",
description: "Returned to customer for updates.",
color: "text-orange-600",
stage: 0,
},
PENDING_APPROVAL: {
title: "Pending Approval",
description: "Moving through the internal approval chain.",
color: "text-amber-600",
stage: 1,
},
APPROVED: {
title: "Approved",
description: "Approved; contract document can be generated.",
color: "text-[color:var(--freight-brand)]",
stage: 1,
},
APPROVED_PENDING_SIGNATURE: {
title: "Pending Signature",
description: "Awaiting director or CEO signature steps.",
color: "text-sky-600",
stage: 1,
},
CONTRACT_READY: {
title: "Contract Ready",
description: "Contract generated; awaiting customer signature.",
color: "text-indigo-600",
stage: 2,
},
SIGNED_CUSTOMER: {
title: "Customer Signed",
description: "Awaiting contract execution.",
color: "text-sky-600",
stage: 2,
},
FULLY_EXECUTED: {
title: "Fully Executed",
description: "One-time contract executed; transport-only path.",
color: "text-indigo-600",
stage: 3,
},
CONTRACT_ACTIVE: {
title: "Active",
description: "General contract active over its validity window.",
color: "text-[color:var(--freight-brand)]",
stage: 3,
},
AWAITING_CLEARANCE_DOCUMENTS: {
title: "Awaiting Documents",
description: "Customer is uploading pre-booking clearance documents.",
color: "text-amber-600",
stage: 3,
},
CLEARANCE_UNDER_REVIEW: {
title: "Clearance Review",
description: "Global Logistics ET is reviewing clearance documents.",
color: "text-amber-700",
stage: 3,
},
CLEARANCE_READY_FOR_BOOKING: {
title: "Ready for Booking",
description: "Clearance complete — GL can create the shipment booking.",
color: "text-[color:var(--freight-brand)]",
stage: 4,
},
ACTIVE_SHIPMENT_IN_PROGRESS: {
title: "Shipment in Progress",
description: "A shipment booking is active under this contract.",
color: "text-sky-600",
stage: 4,
},
CONTRACT_CLOSED: {
title: "Closed",
description: "Contract fulfilled and closed.",
color: "text-slate-500",
stage: 5,
},
EXPIRED: {
title: "Expired",
description: "Validity window elapsed.",
color: "text-red-600",
stage: -1,
},
REJECTED: {
title: "Rejected",
description: "Contract was rejected.",
color: "text-red-600",
stage: -1,
},
CANCELLED: {
title: "Cancelled",
description: "Contract was cancelled.",
color: "text-red-600",
stage: -1,
},
};
export const CONTRACT_LIST_TABS = [
{ key: "all", label: "All contracts", statuses: null as string[] | null },
{
key: "intake",
label: "Submitted",
statuses: ["SUBMITTED", "PRICE_CHANGED_PENDING_CONFIRM", "CHANGES_REQUESTED"],
},
{
key: "in_approval",
label: "In approval",
statuses: ["PENDING_APPROVAL", "APPROVED", "APPROVED_PENDING_SIGNATURE"],
},
{
key: "approved_contract",
label: "Contract & signature",
statuses: ["CONTRACT_READY", "SIGNED_CUSTOMER"],
},
{
key: "clearance",
label: "Clearance",
statuses: [
"AWAITING_CLEARANCE_DOCUMENTS",
"CLEARANCE_UNDER_REVIEW",
"CLEARANCE_READY_FOR_BOOKING",
],
},
{
key: "active",
label: "Active",
statuses: [
"FULLY_EXECUTED",
"CONTRACT_ACTIVE",
"ACTIVE_SHIPMENT_IN_PROGRESS",
],
},
{
key: "closed",
label: "Closed",
statuses: ["CONTRACT_CLOSED", "EXPIRED", "REJECTED", "CANCELLED"],
},
] as const;
export type ContractStatusTabKey = (typeof CONTRACT_LIST_TABS)[number]["key"];
export const CONTRACT_WORKFLOW_STAGES = [
{
label: "Submission",
statuses: [
"DRAFT",
"SUBMITTED",
"PRICE_CHANGED_PENDING_CONFIRM",
"CHANGES_REQUESTED",
],
},
{
label: "Approval",
statuses: ["PENDING_APPROVAL", "APPROVED", "APPROVED_PENDING_SIGNATURE"],
},
{
label: "Signature",
statuses: ["CONTRACT_READY", "SIGNED_CUSTOMER"],
},
{
label: "Clearance",
statuses: [
"FULLY_EXECUTED",
"CONTRACT_ACTIVE",
"AWAITING_CLEARANCE_DOCUMENTS",
"CLEARANCE_UNDER_REVIEW",
],
},
{
label: "Shipment",
statuses: ["CLEARANCE_READY_FOR_BOOKING", "ACTIVE_SHIPMENT_IN_PROGRESS"],
},
{ label: "Done", statuses: ["CONTRACT_CLOSED"] },
] as const;
export function getContractStatusMeta(status: ContractStatus | string): StatusMeta {
return (
CONTRACT_STATUS_META[status] ?? {
title: status,
description: "",
color: "text-muted-foreground",
stage: 0,
}
);
}
export function getContractWorkflowStageIndex(
status: ContractStatus | string,
): number {
const meta = getContractStatusMeta(status);
if (meta.stage < 0) return -1;
return meta.stage;
}

View File

@@ -0,0 +1,55 @@
import type { Freight } from "@edr/types";
export interface ContractListRow {
id: string;
reference: string;
approvalSteps?: Freight.IContractApprovalStep[];
customerLabel: string;
status: string;
contractKind: Freight.ContractKind;
tradeDirection: string;
freightType: string;
originLabel: string;
destinationLabel: string;
validFrom?: string | null;
validUntil?: string | null;
validityDays?: number | null;
isRenewal: boolean;
createdAt: string;
}
function yardLabel(
yard?: { label?: string; code?: string; name?: string } | null,
fallback = "—",
): string {
if (!yard) return fallback;
return yard.label ?? yard.name ?? yard.code ?? fallback;
}
export function toContractListRow(contract: Freight.IContract): ContractListRow {
const routes = [...(contract.routes ?? [])].sort(
(a, b) => a.sortOrder - b.sortOrder,
);
const first = routes[0];
const last = routes[routes.length - 1] ?? first;
return {
id: contract.id,
reference: contract.reference,
approvalSteps: contract.approvalSteps,
customerLabel: contract.isGovernment
? (contract.governmentInstitution ?? "Government")
: (contract.companyId ?? "—"),
status: contract.status,
contractKind: contract.contractKind,
tradeDirection: contract.tradeDirection,
freightType: contract.freightType,
originLabel: yardLabel(first?.originYard),
destinationLabel: yardLabel(last?.destinationYard),
validFrom: contract.contractValidFrom,
validUntil: contract.contractValidUntil,
validityDays: contract.contractValidityDays,
isRenewal: Boolean(contract.renewalOfId),
createdAt: contract.createdAt,
};
}