feat: add hazardous goods declaration feature

- Introduced HazardDeclarationPanel component to display dangerous goods declaration details.
- Updated URL constants to include CLEARANCE_PROCEED endpoint for re-requesting operations.
- Enhanced permissions to include hazardous approval roles for contract approvals.
- Integrated HazardDeclarationPanel into ContractRequestDetailPage and ContractClearanceDetailPage.
- Added proceedToOperation method in bookings service for handling operation re-requests.
- Updated contract forms and schemas to include hazard class and UN number fields.
- Implemented validation for hazardous contracts in the contract creation flow.
- Added expiry notice functionality for contracts nearing validity end.
- Created tests for expiry notice calculations and labels.
- Updated UI components to reflect hazardous cargo information and validation errors.
This commit is contained in:
Marshal
2026-07-25 21:10:09 +00:00
parent fde5e6de4b
commit 9a1c8e5603
41 changed files with 1663 additions and 99 deletions

View File

@@ -20,6 +20,38 @@ export enum ContractFreightType {
Bulk = "BULK",
}
/**
* The nine UN/ADR dangerous-goods classes. A hazardous contract must declare
* exactly one class plus the shipment's UN number — both are captured with the
* hazard documents in the portal and reviewed by the two hazardous approvers in
* the backoffice.
*/
export const HAZARD_CLASSES = [
{ value: "CLASS_1", label: "Class 1: Explosives" },
{ value: "CLASS_2", label: "Class 2: Gases" },
{ value: "CLASS_3", label: "Class 3: Flammable Liquids" },
{ value: "CLASS_4", label: "Class 4: Flammable Solids" },
{ value: "CLASS_5", label: "Class 5: Oxidizers and Organic Peroxides" },
{ value: "CLASS_6", label: "Class 6: Toxic and Infectious Substances" },
{ value: "CLASS_7", label: "Class 7: Radioactive Materials" },
{ value: "CLASS_8", label: "Class 8: Corrosive Substances" },
{ value: "CLASS_9", label: "Class 9: Miscellaneous Dangerous Goods" },
] as const;
export type HazardClass = (typeof HAZARD_CLASSES)[number]["value"];
export const HAZARD_CLASS_VALUES: readonly string[] = HAZARD_CLASSES.map(
(c) => c.value,
);
/** Human label for a stored hazard class code; falls back to the raw code. */
export function hazardClassLabel(
value: string | null | undefined,
): string | null {
if (!value) return null;
return HAZARD_CLASSES.find((c) => c.value === value)?.label ?? value;
}
/** Who created a shipment booking under a contract. */
export type BookingCreatedByRole = "CUSTOMER" | "GL_ET" | "STAFF";
@@ -428,6 +460,10 @@ export interface ContractClearanceView {
/** Reference + status of the GL-created shipment booking, once it exists. */
linkedBookingReference?: string | null;
linkedBookingStatus?: string | null;
/** Operations' latest "needs changes" note on that booking — GL acts on it. */
linkedBookingReviewNote?: string | null;
/** Shipment day the booking holds; the default when GL resubmits it. */
linkedBookingScheduledDate?: string | null;
dutyAdvice?: {
amount: number;
currency: string;
@@ -661,6 +697,10 @@ export interface IContract extends BaseEntity {
lastMileDeliveryLng?: number | null;
isHazardous: boolean;
/** UN/ADR dangerous-goods class — set only when `isHazardous`. */
hazardClass?: HazardClass | string | null;
/** UN number of the dangerous good — set only when `isHazardous`. */
unNumber?: string | null;
isReefer: boolean;
estimatedShipmentDate?: string | null;
@@ -788,6 +828,10 @@ export interface CreateContractDto {
lastMileDeliveryLng?: number;
isHazardous?: boolean;
/** Required when `isHazardous` — one of HAZARD_CLASSES. */
hazardClass?: string;
/** Required when `isHazardous` — the shipment's UN number. */
unNumber?: string;
isReefer?: boolean;
estimatedShipmentDate?: string;