diff --git a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx index 0c96311df..f30e464e7 100644 --- a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx @@ -186,6 +186,133 @@ const BookingInfo = ({ job }: { job: LastMileJob }) => ( ); +const tripSlipRows = (job: LastMileJob): [string, string][] => [ + ["Customer", job.customer], + ["Service", job.serviceType], + ["Origin yard", job.originYard], + ["Destination", job.destination], + ["Cargo", job.cargo], + ["Weight", job.weight], + ["Vehicle", job.assignedVehicle ?? "Unassigned"], + ["Contact", `${job.contactName} · ${job.contactPhone}`], + ["Requested date", job.requestedDate], + ["Delivery status", DELIVERY_STATUS_META[job.deliveryStatus].label], +]; + +const SignatureBlock = ({ title }: { title: string }) => ( + + + {title} + + + + Name: + + + + + + Signature: + + + + + + Stamp + + + +); + +const TripSlipDocument = ({ job }: { job: LastMileJob }) => ( + + + EDR Freight + + Last Mile Trip Slip + + + + + {job.bookingRef} + + + {job.requestedDate} + + + + + {tripSlipRows(job).map(([label, value]) => ( + + ))} + + + + + + + +); + +const escapeHtml = (value: string) => + value + .replace(/&/g, "&") + .replace(//g, ">"); + +const buildTripSlipHtml = (job: LastMileJob) => { + const rows = tripSlipRows(job) + .map( + ([label, value]) => + `${escapeHtml(label)}${escapeHtml(value)}`, + ) + .join(""); + const signature = (title: string) => ` +
+
${title}
+
Name:
+
Signature:
+
STAMP
+
`; + return ` + Trip Slip ${escapeHtml(job.bookingRef)} + + +

EDR Freight

Last Mile Trip Slip

+
${escapeHtml(job.bookingRef)}${escapeHtml(job.requestedDate)}
+ ${rows}
+
Acknowledgement
+
${signature("Driver")}${signature("Operator")}
+ `; +}; + const LastMilePage = () => { const { toast } = useToast(); const { pagination, setPagination } = usePagination({ pageSize: 10 }); @@ -196,6 +323,8 @@ const LastMilePage = () => { const [assignOpen, setAssignOpen] = useState(false); const [detailOpen, setDetailOpen] = useState(false); + const [tripSlipOpen, setTripSlipOpen] = useState(false); + const [tripSlipJob, setTripSlipJob] = useState(null); const [activeJobId, setActiveJobId] = useState(null); const [vehicleValue, setVehicleValue] = useState(null); @@ -315,8 +444,23 @@ const LastMilePage = () => { }; const handlePrintTripSlip = (job: LastMileJob) => { - // Placeholder — wire to the trip-slip document service when available. - toast({ title: "Printing trip slip", description: job.bookingRef }); + setTripSlipJob(job); + setTripSlipOpen(true); + }; + + const printTripSlip = () => { + if (!tripSlipJob) return; + const win = window.open("", "_blank", "width=820,height=920"); + if (!win) { + toast({ + title: "Pop-up blocked", + description: "Allow pop-ups to print the trip slip.", + variant: "destructive", + }); + return; + } + win.document.write(buildTripSlipHtml(tripSlipJob)); + win.document.close(); }; const columns = useMemo((): ColumnDef[] => { @@ -582,6 +726,28 @@ const LastMilePage = () => { + + setTripSlipOpen(false)} + title={Trip Slip} + size="lg" + radius="lg" + centered + > + + {tripSlipJob && } + + + + + + + ); };