Merge branch 'dev' into freight/feat/fixes-v1

This commit is contained in:
Nathnael
2026-07-20 12:12:30 +00:00
29 changed files with 853 additions and 211 deletions

View File

@@ -135,12 +135,14 @@ export function ClearanceReviewSection({
const finalizeMutation = useMutation({
mutationFn: () => bookingsService.finalizeClearance(bookingId),
onSuccess: () => {
toast.success("Clearance finalized");
toast.success("Document approval finalized");
refresh();
},
onError: (e) =>
toast.error(
e instanceof Error ? e.message : "Could not finalize clearance",
e instanceof Error
? e.message
: "Could not finalize document approval",
),
});
@@ -366,7 +368,7 @@ export function ClearanceReviewSection({
<Alert color="red" radius="md" icon={<AlertCircle size={16} />}>
{finalizeMutation.error instanceof Error
? finalizeMutation.error.message
: "Could not finalize clearance."}
: "Could not finalize document approval."}
</Alert>
)}
@@ -445,7 +447,7 @@ export function ClearanceReviewSection({
loading={finalizeMutation.isPending}
onClick={() => finalizeMutation.mutate()}
>
Finalize clearance
Finalize document approval
</Button>
</Group>
</Paper>

View File

@@ -68,7 +68,7 @@ export interface ContractClearanceReviewSectionProps {
queriesLocked?: boolean;
/**
* ONE_TIME customs contracts use the phased milestone workflow. Hides the
* legacy "Finalize clearance" shortcut; booking readiness follows delivery
* legacy "Finalize document approval" shortcut; booking readiness follows delivery
* order (import) or export release.
*/
phasedCustoms?: boolean;
@@ -393,7 +393,7 @@ export function ContractClearanceReviewSection({
<Alert color="red" radius="md" icon={<AlertCircle size={16} />}>
{finalizeClearance.error instanceof Error
? finalizeClearance.error.message
: "Could not finalize clearance."}
: "Could not finalize document approval."}
</Alert>
)}
@@ -482,7 +482,7 @@ export function ContractClearanceReviewSection({
})
}
>
Finalize clearance
Finalize document approval
</Button>
</Group>
</Paper>

View File

@@ -160,7 +160,8 @@ export default function BuildTrainModal({ opened, onClose, onBuilt }: BuildTrain
onChange={(value) => setImportTrainNumber(value ?? "")}
searchable
clearable
nothingFoundMessage="No free run numbers — add more in Dropdown Settings"
nothingFoundMessage={importNumbers.emptyMessage}
error={importNumbers.settingMissing ? importNumbers.emptyMessage : undefined}
/>
</Group>
<Select

View File

@@ -105,7 +105,8 @@ const EditTrainDetailsModal = ({ train, onClose }: EditTrainDetailsModalProps) =
}}
searchable
clearable
nothingFoundMessage="No free run numbers — add more in Dropdown Settings"
nothingFoundMessage={importNumbers.emptyMessage}
error={importNumbers.settingMissing ? importNumbers.emptyMessage : undefined}
radius="md"
/>
<TextInput

View File

@@ -202,6 +202,24 @@ export function ReleaseOrderModal({ opened, onClose, item, truckPrefill }: Relea
);
}, [opened, truckPrefill, isExitStep, lastMileTrucks]);
// The same for a customer self-haul truck. The prefill above reads the
// booking.customer_truck_* columns, but multi-truck self-haul writes the plate
// and driver to customer_truck_assignments and leaves those columns null — so
// a booking with a truck on file still opened this form blank. Only auto-fills
// a single truck: with several, the operator picks which one is at the gate.
useEffect(() => {
if (!opened || truckPrefill || isExitStep) return;
if (customerTrucks.length !== 1) return;
const [truck] = customerTrucks;
setTruckPlateNumber((p) => p || truck.plateNumber || '');
setDriverName((p) => p || truck.driverName || '');
setTruckType((p) => p || truck.truckType || '');
setContainerNumbers((prev) => {
const loaded = (truck.containers ?? []).map((c) => c.containerNumber).filter(Boolean);
return prev.every((n) => !n) && loaded.length ? loaded : prev;
});
}, [opened, truckPrefill, isExitStep, customerTrucks]);
// Registered trucks for THIS booking, from both sources: EDR last-mile
// (truckPrefill) and the customer portal (customer_truck_assignments).
const assignedTruckOptions = [