mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 06:00:55 +00:00
- Added parameter to and for server-side free-text search on contract reference, company name, and booking details. - Introduced new validation errors in for container clashes and space issues when creating bookings. - Implemented paginated dropdown settings retrieval in . - Updated to fetch active yards using a new method that handles pagination. - Enhanced with a method to fetch all records by walking through pages. - Refactored to support filtering and pagination in schedule listings. - Improved to return a paginated list of facilities. - Updated UI components in and to utilize debounced search inputs for better performance. - Added alerts in to inform users about booking constraints related to splits and capacity. - Enhanced to display notifications for split bookings and capacity usage.
98 lines
3.5 KiB
TypeScript
98 lines
3.5 KiB
TypeScript
import type { Freight } from "@edr/types";
|
|
|
|
/**
|
|
* Booking statuses that no longer occupy the single active-booking slot of a
|
|
* ONE_TIME contract. Mirrors the API source of truth in
|
|
* `contract-booking.service.ts`.
|
|
*/
|
|
export const TERMINAL_BOOKING_STATUSES = [
|
|
"EXPIRED",
|
|
"CANCELLED",
|
|
"COMPLETED",
|
|
"REJECTED",
|
|
];
|
|
|
|
/** Path A statuses where a customer (no customs) may book against the contract. */
|
|
const PATH_A_BOOKABLE = ["FULLY_EXECUTED", "CONTRACT_ACTIVE"];
|
|
|
|
/** Split bookings in these statuses released their quantity — no remainder to book. */
|
|
const RELEASING_BOOKING_STATUSES = ["CANCELLED", "REJECTED", "EXPIRED"];
|
|
|
|
export type ContractBookingActionKind =
|
|
| "book"
|
|
| "rebook"
|
|
| "request"
|
|
| "initiate"
|
|
| "none";
|
|
|
|
export interface ContractBookingAction {
|
|
kind: ContractBookingActionKind;
|
|
to: string;
|
|
/**
|
|
* ONE_TIME contract whose booking was split (partial batch payment): the
|
|
* customer may book again, but ONLY the whole outstanding remainder — the
|
|
* exact quantities come from the contract capacity endpoint.
|
|
*/
|
|
remainderOnly?: boolean;
|
|
}
|
|
|
|
/**
|
|
* The single source of truth for whether a contract row should show a
|
|
* Book / Re-book shipment button, and where it should navigate.
|
|
*
|
|
* - ONE_TIME: book only while there is no active booking. If the previous
|
|
* booking expired, offer Re-book. Once a live booking exists, no button.
|
|
* - GENERAL: bookable while CONTRACT_ACTIVE (CONTRACT_CLOSED / EXPIRED are
|
|
* already excluded by the PATH_A_BOOKABLE gate).
|
|
*/
|
|
export function getContractBookingAction(
|
|
contract: Freight.IContract,
|
|
bookings: Freight.IBooking[],
|
|
): ContractBookingAction {
|
|
// GENERAL + customs: customer submits a shipment request; GL creates the booking.
|
|
if (
|
|
contract.customsClearingEnabled &&
|
|
contract.contractKind === "GENERAL" &&
|
|
contract.status === "CONTRACT_ACTIVE"
|
|
) {
|
|
return {
|
|
kind: "request",
|
|
to: `/contracts/${contract.id}/shipment-requests/new`,
|
|
};
|
|
}
|
|
|
|
// Other customs (ONE_TIME): booked by GL — no customer action.
|
|
if (contract.customsClearingEnabled) return { kind: "none", to: "" };
|
|
if (!PATH_A_BOOKABLE.includes(contract.status)) return { kind: "none", to: "" };
|
|
|
|
const to = `/contracts/${contract.id}/bookings/new`;
|
|
|
|
if (contract.contractKind === "ONE_TIME") {
|
|
const mine = bookings.filter((b) => b.contractId === contract.id);
|
|
// Split chain: a paid partial split frees the single-booking slot for the
|
|
// remainder. The API enforces that the next booking takes the whole
|
|
// leftover; once it is booked the contract closes (CONTRACT_CLOSED), which
|
|
// the PATH_A_BOOKABLE gate above already excludes.
|
|
const hasLiveSplit = mine.some(
|
|
(b) => b.isSplit && !RELEASING_BOOKING_STATUSES.includes(b.status),
|
|
);
|
|
if (hasLiveSplit) return { kind: "book", to, remainderOnly: true };
|
|
const hasActive = mine.some(
|
|
(b) => !TERMINAL_BOOKING_STATUSES.includes(b.status),
|
|
);
|
|
if (hasActive) return { kind: "none", to: "" };
|
|
const hasExpired = mine.some((b) => b.status === "EXPIRED");
|
|
return { kind: hasExpired ? "rebook" : "book", to };
|
|
}
|
|
|
|
// GENERAL non-customs import/export: one-click bare booking instance — the
|
|
// per-booking clearance runs first, cargo + shipment day come at completion.
|
|
// No window gate here; the window is checked when the booking is completed.
|
|
// DOMESTIC (intercity) keeps the direct booking form.
|
|
if (contract.tradeDirection !== "DOMESTIC") {
|
|
return { kind: "initiate", to: `/contracts/${contract.id}` };
|
|
}
|
|
|
|
return { kind: "book", to };
|
|
}
|