mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-27 00:52:50 +00:00
mile
This commit is contained in:
@@ -57,10 +57,10 @@ export class LastMileService {
|
||||
'last_mile',
|
||||
records.map((r) => r.id),
|
||||
);
|
||||
const byId = new Map<string, { number: string; status: string }>();
|
||||
const byId = new Map<string, { id: string; number: string; status: string }>();
|
||||
for (const inv of invoices) {
|
||||
if (!byId.has(inv.sourceId)) {
|
||||
byId.set(inv.sourceId, { number: inv.invoiceNumber, status: String(inv.status) });
|
||||
byId.set(inv.sourceId, { id: inv.id, number: inv.invoiceNumber, status: String(inv.status) });
|
||||
}
|
||||
}
|
||||
for (const r of records) {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
X,
|
||||
} from "lucide-react";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import type { ColumnDef } from "@edr/ui-common";
|
||||
import { DataTable, DataTableFooter, usePagination } from "@edr/ui-common";
|
||||
import {
|
||||
@@ -467,6 +468,7 @@ const buildTripSlipHtml = (record: LastMileRecord) => {
|
||||
const LastMilePage = () => {
|
||||
const { toast } = useToast();
|
||||
const qc = useQueryClient();
|
||||
const navigate = useNavigate();
|
||||
const { pagination, setPagination } = usePagination({ pageSize: 10 });
|
||||
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -495,8 +497,6 @@ const LastMilePage = () => {
|
||||
const [distanceOpen, setDistanceOpen] = useState(false);
|
||||
// Per-vehicle actual distance, keyed by vehicleId.
|
||||
const [distanceRows, setDistanceRows] = useState<Record<string, string>>({});
|
||||
const [invoiceOpen, setInvoiceOpen] = useState(false);
|
||||
const [invoiceRecord, setInvoiceRecord] = useState<LastMileRecord | null>(null);
|
||||
// Record pending invoice-generation confirmation (shows a summary first).
|
||||
const [invoiceConfirm, setInvoiceConfirm] = useState<LastMileRecord | null>(null);
|
||||
|
||||
@@ -743,15 +743,6 @@ const LastMilePage = () => {
|
||||
setDistanceRows({});
|
||||
};
|
||||
|
||||
const openInvoice = (record: LastMileRecord) => {
|
||||
setInvoiceRecord(record);
|
||||
setInvoiceOpen(true);
|
||||
};
|
||||
|
||||
const closeInvoice = () => {
|
||||
setInvoiceOpen(false);
|
||||
setInvoiceRecord(null);
|
||||
};
|
||||
|
||||
const openAllocation = (id: string, containers?: LastMileContainerRow[]) => {
|
||||
setActiveId(id);
|
||||
@@ -1098,7 +1089,7 @@ const LastMilePage = () => {
|
||||
return (
|
||||
<Group gap="xs" wrap="nowrap">
|
||||
<UnstyledButton
|
||||
onClick={() => openInvoice(row.original)}
|
||||
onClick={() => navigate(`/dashboard/invoices/${invoice.id}`)}
|
||||
c="blue"
|
||||
fw={500}
|
||||
style={{ textDecoration: "underline", cursor: "pointer" }}
|
||||
@@ -1775,87 +1766,6 @@ const LastMilePage = () => {
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
||||
{/* Invoice modal */}
|
||||
<Modal
|
||||
opened={invoiceOpen}
|
||||
onClose={closeInvoice}
|
||||
title={<Text fw={600}>Invoice #345</Text>}
|
||||
size="lg"
|
||||
radius="lg"
|
||||
centered
|
||||
>
|
||||
<Stack gap="md">
|
||||
{invoiceRecord && (
|
||||
<>
|
||||
<Card withBorder padding="md" radius="md" bg="var(--mantine-color-gray-0)">
|
||||
<Stack gap="sm">
|
||||
<Group justify="space-between">
|
||||
<Text fw={700}>EDR Freight</Text>
|
||||
<Text fw={600} size="sm">Invoice #345</Text>
|
||||
</Group>
|
||||
<Divider />
|
||||
<SimpleGrid cols={2} spacing="sm">
|
||||
<InfoRow label="Booking" value={bookingRef(invoiceRecord)} />
|
||||
<InfoRow label="Customer" value={customerName(invoiceRecord)} />
|
||||
<InfoRow label="Pickup" value={originYardName(invoiceRecord)} />
|
||||
<InfoRow label="Destination" value={deliveryLocation(invoiceRecord)} />
|
||||
<InfoRow label="Est. Distance" value={invoiceRecord.estimatedKm ? `${invoiceRecord.estimatedKm} km` : "—"} />
|
||||
<InfoRow label="Actual Distance" value={invoiceRecord.exactKm ? `${invoiceRecord.exactKm} km` : "—"} />
|
||||
<InfoRow label="Advanced Payment" value={formatPrice(invoiceRecord.advancedPayment)} />
|
||||
<InfoRow label="Post Payment" value={formatPrice(invoiceRecord.remainingPayment)} />
|
||||
</SimpleGrid>
|
||||
<Divider />
|
||||
<Group justify="flex-end">
|
||||
<Stack gap={0} align="flex-end" style={{ minWidth: 200 }}>
|
||||
<Group justify="space-between" w="100%">
|
||||
<Text size="sm" c="dimmed">Post Payment</Text>
|
||||
<Text size="sm">{formatPrice(invoiceRecord.remainingPayment)}</Text>
|
||||
</Group>
|
||||
<Group justify="space-between" w="100%">
|
||||
<Text size="sm" c="dimmed">Advanced Payment</Text>
|
||||
<Text size="sm">{formatPrice(invoiceRecord.advancedPayment)}</Text>
|
||||
</Group>
|
||||
<Divider my="xs" />
|
||||
{(() => {
|
||||
const postPayment = parseFloat(String(invoiceRecord.remainingPayment));
|
||||
const advancedPayment = parseFloat(String(invoiceRecord.advancedPayment));
|
||||
const difference = postPayment - advancedPayment;
|
||||
|
||||
if (difference > 0) {
|
||||
return (
|
||||
<Group justify="space-between" w="100%">
|
||||
<Text size="sm" fw={600}>Remaining to Pay</Text>
|
||||
<Text fw={700} c="orange">{formatPrice(difference)}</Text>
|
||||
</Group>
|
||||
);
|
||||
} else if (difference < 0) {
|
||||
return (
|
||||
<Group justify="space-between" w="100%">
|
||||
<Text size="sm" fw={600}>Refund</Text>
|
||||
<Text fw={700} c="green">{formatPrice(Math.abs(difference))}</Text>
|
||||
</Group>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Group justify="space-between" w="100%">
|
||||
<Text size="sm" fw={600}>Status</Text>
|
||||
<Text fw={700} c="blue">Settled</Text>
|
||||
</Group>
|
||||
);
|
||||
}
|
||||
})()}
|
||||
</Stack>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Card>
|
||||
</>
|
||||
)}
|
||||
<Group justify="flex-end" gap="sm">
|
||||
<Button variant="default" onClick={closeInvoice}>Close</Button>
|
||||
</Group>
|
||||
</Stack>
|
||||
</Modal>
|
||||
|
||||
{/* Generate Invoice — confirmation summary */}
|
||||
<Modal
|
||||
opened={Boolean(invoiceConfirm)}
|
||||
@@ -1904,10 +1814,10 @@ const LastMilePage = () => {
|
||||
loading={generateInvoiceMutation.isPending}
|
||||
onClick={() =>
|
||||
generateInvoiceMutation.mutate(invoiceConfirm.id, {
|
||||
onSuccess: () => {
|
||||
const rec = invoiceConfirm;
|
||||
onSuccess: (res) => {
|
||||
setInvoiceConfirm(null);
|
||||
openInvoice(rec);
|
||||
const invoiceId = res?.data?.id;
|
||||
if (invoiceId) navigate(`/dashboard/invoices/${invoiceId}`);
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export interface LastMileRecord {
|
||||
vehicle?: LastMileVehicle | null;
|
||||
}>;
|
||||
/** Present only when an invoice has actually been generated (not on distance). */
|
||||
invoice?: { number: string; status: string } | null;
|
||||
invoice?: { id: string; number: string; status: string } | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
@@ -97,5 +97,5 @@ export const lastMileService = {
|
||||
remainingPayment?: number,
|
||||
) => api.post<LastMileRecord>(`${LM.BASE}/${id}/distances`, { distances, remainingPayment }),
|
||||
generateInvoice: (id: string) =>
|
||||
api.post<unknown>(`${LM.BASE}/${id}/invoice`),
|
||||
api.post<{ id: string; invoiceNumber?: string } | null>(`${LM.BASE}/${id}/invoice`),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user