feat(booking-trucks-tab): show truck-level detention, gate times, inspection status, and cargo costs

Booking detail now has a Trucks tab displaying every EDR or customer truck
assigned to a booking's last mile. Each row shows warehouse-gate times
(arrival/departure) and destination-detention times (arrival/return), with
detention costs and inspection status. Detention rows only for EDR trucks;
customer self-haul shows —. Reuses existing TruckDetentionModal and
FeePreviewModal for edit/view actions.

Backend: arrivalTrucksForBooking() adds lastMileId per truck (direct chain to
detention preview), containerItems() adds inspection_status column to responses.
No new SQL, no migrations, no new endpoints.

Frontend: BookingTrucksPanel.tsx self-fetches all data via existing warehouse +
last-mile services, renders adaptive table with cargo-cost strip above.
Wired into BookingRequestDetailPage.tsx tab bar.
This commit is contained in:
Hagernesh
2026-07-29 11:12:32 +00:00
parent fe79c2442c
commit 1b1b0e7ade
6 changed files with 325 additions and 2 deletions

View File

@@ -327,6 +327,8 @@ export class LastMileService {
*/
async arrivalTrucksForBooking(bookingId: string): Promise<
Array<{
/** The last-mile leg this truck belongs to — lets a caller chain straight into truck-detention-preview without a separate lookup. */
lastMileId: string;
vehicleId: string;
truckPlateNumber: string | null;
trailerPlateNumber: string | null;
@@ -359,6 +361,7 @@ export class LastMileService {
: [];
const out: Array<{
lastMileId: string;
vehicleId: string;
truckPlateNumber: string | null;
trailerPlateNumber: string | null;
@@ -386,6 +389,7 @@ export class LastMileService {
}
}
out.push({
lastMileId: lm.id,
vehicleId: vehicle.id,
truckPlateNumber: vehicle.powerPlateNo || vehicle.plateNumber || null,
trailerPlateNumber: vehicle.trailerPlateNo || null,

View File

@@ -3679,6 +3679,7 @@ export class WarehouseInventoryService {
contractId: string | null;
hasLastMile: boolean;
handoverSigned: boolean;
inspectionStatus: string | null;
}>
> {
const rows: Array<{
@@ -3696,6 +3697,7 @@ export class WarehouseInventoryService {
contractId: string | null;
hasLastMile: boolean;
delivered: boolean;
inspectionStatus: string | null;
}> = await this.dataSource.query(
`SELECT bcu.container_number AS "containerNumber",
COALESCE(ct.cargo_type_name, b.cargo_free_text) AS goods,
@@ -3710,7 +3712,8 @@ export class WarehouseInventoryService {
b.reference AS "bookingReference",
b.contract_id AS "contractId",
(b.last_mile_delivery_address IS NOT NULL) AS "hasLastMile",
COALESCE(inv.status = 'DELIVERED', false) AS delivered
COALESCE(inv.status = 'DELIVERED', false) AS delivered,
inv.inspection_status AS "inspectionStatus"
FROM freight.booking_container_units bcu
JOIN freight.booking_container bc
ON bc.id = bcu.booking_container_id AND bc.deleted_at IS NULL
@@ -3762,6 +3765,7 @@ export class WarehouseInventoryService {
bookingReference: r.bookingReference,
contractId: r.contractId,
hasLastMile: r.hasLastMile,
inspectionStatus: r.inspectionStatus,
handoverSigned,
}));
}