import { api } from '../auth/http'; import { URL_CONSTANTS } from '@/constants/URLS'; import type { FeePreview } from '@/types/warehouse'; export const LAST_MILE_STATUSES = [ 'PAYMENT_PENDING', 'READY_TO_TRANSIT', 'IN_TRANSIT', 'DELIVERED', ] as const; export type LastMileApiStatus = (typeof LAST_MILE_STATUSES)[number]; export interface LastMileBooking { id: string; reference: string; lastMileDeliveryAddress?: string | null; cargoFreeText?: string | null; cargoTotalWeightVgm: number; totalAmount: number; paymentCurrency?: string | null; scheduledDate?: string | null; company?: { id: string; name?: string; phone?: string | null; contactPersonName?: string | null; contactPersonPhone?: string | null } | null; serviceType?: { id: string; name?: string; label?: string } | null; originYard?: { id: string; name?: string; label?: string } | null; destinationYard?: { id: string; name?: string; label?: string } | null; cargoType?: { id: string; name?: string; label?: string; cargoTypeName?: string } | null; /** Container lines — total container count drives how many trucks are needed. */ bookingContainers?: Array<{ id: string; quantity: number; containerNumber?: string | null; containerSize?: string | null; containerType?: { id: string; name?: string; label?: string; code?: string } | null; /** Physical containers under this line — their real numbers (line-level * containerNumber is often a TBD placeholder). */ units?: Array<{ id: string; containerNumber?: string | null; sortOrder?: number }>; }>; } export interface LastMileVehicle { id: string; plateNumber: string; manufacturer: string; model: string; vehicleType?: string | null; code?: string | null; powerPlateNo?: string | null; trailerPlateNo?: string | null; assignedDriverId?: string | null; assignedDriverName?: string | null; pricePerKm?: number | string | null; currency?: string | null; } export interface LastMileRecord { id: string; bookingId: string; status: LastMileApiStatus; advancedPayment: number; remainingPayment: number; estimatedKm?: number | null; exactKm?: number | null; vehicleId?: string | null; booking?: LastMileBooking | null; vehicle?: LastMileVehicle | null; /** Full set of vehicles serving this delivery (multi-truck). */ vehicleAssignments?: Array<{ id: string; vehicleId: string; containerNumber?: string | null; distanceKm?: number | null; vehicle?: LastMileVehicle | null; }>; /** Present only when an invoice has actually been generated (not on distance). */ invoice?: { id: string; number: string; status: string } | null; /** Truck-detention clock: vehicle arrival + delivery/return times. */ arrivedAt?: string | null; deliveredAt?: string | null; createdAt: string; updatedAt: string; } export interface LastMileListResponse { data: LastMileRecord[]; meta: { total: number; page: number; pageSize: number; totalPages: number }; } const LM = URL_CONSTANTS.LAST_MILE; export const lastMileService = { list: (pageSize = 1000) => api.get(`${LM.BASE}?pageSize=${pageSize}`), getById: (id: string) => api.get(LM.BY_ID(id)), update: (id: string, data: { status?: LastMileApiStatus; vehicleId?: string | null; estimatedKm?: number | null; exactKm?: number | null; paid?: boolean; arrivedAt?: string | null; deliveredAt?: string | null }) => api.patch(LM.BY_ID(id), data), accept: (bookingReference: string) => api.post(LM.ACCEPT(encodeURIComponent(bookingReference))), remove: (id: string) => api.delete(LM.BY_ID(id)), setVehicles: ( id: string, vehicles: Array<{ vehicleId: string; containerNumber?: string | null }>, ) => api.post(`${LM.BASE}/${id}/vehicles`, { vehicles }), setDistances: ( id: string, distances: Array<{ vehicleId: string; distanceKm: number }>, remainingPayment?: number, ) => api.post(`${LM.BASE}/${id}/distances`, { distances, remainingPayment }), generateInvoice: (id: string) => api.post<{ id: string; invoiceNumber?: string } | null>(`${LM.BASE}/${id}/invoice`), /** Record proof of delivery (recipient signature + photos) and complete the leg. */ recordProofOfDelivery: ( id: string, payload: { recipientName: string; notes?: string; signature?: Blob | null; photos?: File[] }, ) => { const form = new FormData(); form.append("recipientName", payload.recipientName); if (payload.notes) form.append("notes", payload.notes); if (payload.signature) form.append("signature", payload.signature, "signature.png"); (payload.photos ?? []).forEach((photo) => form.append("photos", photo)); return api.post(LM.PROOF_OF_DELIVERY(id), form, { headers: { "Content-Type": "multipart/form-data" }, }); }, /** Generate a truck-detention invoice (per truck per day after the grace window). */ generateTruckDetentionInvoice: (id: string) => api.post<{ id: string; invoiceNumber?: string } | null>( `${LM.BASE}/${id}/generate-truck-detention-invoice`, ), /** Preview the truck-detention charge for a last-mile leg. */ truckDetentionPreview: (id: string) => api.get(`${LM.BASE}/${id}/truck-detention-preview`), };