mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 12:58:13 +00:00
feat(last-mile): customer-signed LM contract gates the advance invoice
Approval now snapshots the rate estimate and generates a last-mile contract instead of invoicing immediately. The customer picks a delivery date on the confirm form, then reviews and signs the contract in the portal (saved signature or drawn); the signed PDF is stored as LM_<CustomerName>.pdf and only then is the advance invoice issued. Backoffice shows signature status and the contract download.
This commit is contained in:
@@ -94,6 +94,20 @@ export function LastMileRequestsPanel() {
|
||||
const invalidate = () =>
|
||||
qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE_REQUESTS.ROOT });
|
||||
|
||||
const downloadContract = async (r: LastMileRequest) => {
|
||||
try {
|
||||
const { data: blob } = await lastMileRequestsService.contractDocument(r.id);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement("a");
|
||||
a.href = url;
|
||||
a.download = `LM_${r.booking?.company?.name?.replace(/[^A-Za-z0-9._-]+/g, "_") ?? r.id}.pdf`;
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
} catch {
|
||||
toast({ title: "Contract PDF not available", variant: "destructive" });
|
||||
}
|
||||
};
|
||||
|
||||
const approve = useMutation({
|
||||
mutationFn: () =>
|
||||
lastMileRequestsService.approve(approveTarget!.id, Number(advanceAmount)),
|
||||
@@ -141,8 +155,16 @@ export function LastMileRequestsPanel() {
|
||||
id: "containers",
|
||||
header: () => <span>Requested Containers</span>,
|
||||
cell: ({ row }) => {
|
||||
const nums = row.original.requestedContainerNumbers;
|
||||
return <Text size="sm">{nums?.length ? nums.join(", ") : "—"}</Text>;
|
||||
const r = row.original;
|
||||
const nums = r.requestedContainerNumbers;
|
||||
return (
|
||||
<Stack gap={0}>
|
||||
<Text size="sm">{nums?.length ? nums.join(", ") : "—"}</Text>
|
||||
{r.requestedDeliveryDate && (
|
||||
<Text size="xs" c="dimmed">Delivery: {r.requestedDeliveryDate}</Text>
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -162,6 +184,24 @@ export function LastMileRequestsPanel() {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "contract",
|
||||
header: () => <span>LM Contract</span>,
|
||||
cell: ({ row }) => {
|
||||
const r = row.original;
|
||||
if (r.status !== "APPROVED") return <Text size="sm" c="dimmed">—</Text>;
|
||||
return (
|
||||
<Stack gap={4} align="flex-start">
|
||||
<Badge color={r.customerSignedAt ? "green" : "yellow"} variant="light" size="sm">
|
||||
{r.customerSignedAt ? "Signed" : "Awaiting signature"}
|
||||
</Badge>
|
||||
<Button size="compact-xs" variant="subtle" onClick={() => void downloadContract(r)}>
|
||||
Download PDF
|
||||
</Button>
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
...(canApprove
|
||||
? [
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user