From fc03e27bdc13570d1e0e72acd0869993893663ca Mon Sep 17 00:00:00 2001 From: natib21 Date: Mon, 29 Jun 2026 09:43:31 +0000 Subject: [PATCH] fix --- .../src/pages/operations/LastMilePage.tsx | 230 ++++++++++++++++++ 1 file changed, 230 insertions(+) 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 5535809a2..894a5a091 100644 --- a/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/operations/LastMilePage.tsx @@ -5,6 +5,7 @@ import { MoreHorizontal, Printer, RefreshCw, + Ruler, Truck, } from "lucide-react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; @@ -21,12 +22,14 @@ import { Group, Menu, Modal, + NumberInput, ScrollArea, Select, SimpleGrid, Stack, Text, TextInput, + UnstyledButton, } from "@mantine/core"; import type { ArrivalQueueItem } from "@/types/warehouse"; import { warehouseService } from "@/services/warehouse.service"; @@ -41,6 +44,7 @@ import { lastMileService, } from "@/services/last-mile.service"; import { vehiclesService } from "@/services/vehicles.service"; +import { ratesService } from "@/services/rates.service"; const formatPrice = (amount: number) => `ETB ${amount.toLocaleString("en-US", { @@ -311,6 +315,11 @@ const LastMilePage = () => { const [acceptVehicleValue, setAcceptVehicleValue] = useState(null); const [arrivalSearch, setArrivalSearch] = useState(""); + const [distanceOpen, setDistanceOpen] = useState(false); + const [distanceValue, setDistanceValue] = useState(""); + const [invoiceOpen, setInvoiceOpen] = useState(false); + const [invoiceRecord, setInvoiceRecord] = useState(null); + const { data: listData, isLoading } = useQuery({ queryKey: QUERY_KEYS.LAST_MILE.list(), queryFn: async () => { @@ -327,6 +336,14 @@ const LastMilePage = () => { }, }); + const { data: ratesData } = useQuery({ + queryKey: ["rates", "LAST_MILE"], + queryFn: async () => { + const res = await ratesService.getByType("LAST_MILE"); + return res.data; + }, + }); + const records = listData?.data ?? []; const vehicleOptions = useMemo( @@ -352,6 +369,21 @@ const LastMilePage = () => { }, }); + const updateDistanceMutation = useMutation({ + mutationFn: ({ id, exactKm, remainingPayment }: { id: string; exactKm: number; remainingPayment?: number }) => + lastMileService.update(id, { exactKm, ...(remainingPayment != null && { remainingPayment }) }), + onSuccess: () => { + void qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE.list() }); + if (activeRecord) { + toast({ title: "Distance updated", description: `${bookingRef(activeRecord)} → ${distanceValue} km` }); + } + closeDistance(); + }, + onError: () => { + toast({ title: "Update failed", variant: "destructive" }); + }, + }); + const { data: arrivalQueueData, isLoading: arrivalLoading } = useQuery({ queryKey: ["warehouse-inventory", "arrival-queue"], queryFn: () => warehouseService.arrivalQueue().then((r) => r.data), @@ -422,6 +454,49 @@ const LastMilePage = () => { acceptMutation.mutate({ items: selectedArrivalItems, vehicleId: acceptVehicleValue }); }; + const openDistance = (id: string) => { + setActiveId(id); + setDistanceValue(""); + setDistanceOpen(true); + }; + + const closeDistance = () => { + setDistanceOpen(false); + setActiveId(null); + setDistanceValue(""); + }; + + const openInvoice = (record: LastMileRecord) => { + setInvoiceRecord(record); + setInvoiceOpen(true); + }; + + const closeInvoice = () => { + setInvoiceOpen(false); + setInvoiceRecord(null); + }; + + const handleSaveDistance = () => { + const distance = parseFloat(distanceValue); + if (!activeId || isNaN(distance) || distance < 0) { + toast({ title: "Invalid distance", description: "Enter a valid distance value.", variant: "destructive" }); + return; + } + + let remainingPayment: number | undefined; + if (ratesData?.data) { + const lastMileRate = ratesData.data.find( + (r) => r.rateType === "LAST_MILE" && (r.status === "LIVE" || r.status === "DRAFT") + ); + if (lastMileRate) { + const rateValue = parseFloat(lastMileRate.rateValue); + remainingPayment = distance * rateValue; + } + } + + updateDistanceMutation.mutate({ id: activeId, exactKm: distance, remainingPayment }); + }; + const activeRecord = useMemo( () => records.find((r) => r.id === activeId) ?? null, [records, activeId], @@ -639,6 +714,27 @@ const LastMilePage = () => { meta: { headerClassName, cellClassName }, cell: ({ row }) => row.original.exactKm != null ? row.original.exactKm : , }, + { + id: "invoice", + header: "Invoice", + meta: { headerClassName, cellClassName }, + cell: ({ row }) => { + const hasDistance = row.original.exactKm != null && row.original.exactKm > 0; + if (!hasDistance) { + return ; + } + return ( + openInvoice(row.original)} + c="blue" + fw={500} + style={{ textDecoration: "underline", cursor: "pointer" }} + > + #345 + + ); + }, + }, { id: "status", header: "Status", @@ -704,6 +800,12 @@ const LastMilePage = () => { > View detail + } + onClick={() => openDistance(row.original.id)} + > + Add distance + {canPrint && ( } @@ -1000,6 +1102,134 @@ const LastMilePage = () => { + + {/* Add Actual Distance modal */} + Add Actual Distance} + size="md" + radius="lg" + centered + > + + {activeRecord && ( + + + {bookingRef(activeRecord)} + + Customer + {customerName(activeRecord)} + + + Est. Distance (KM) + {activeRecord.estimatedKm ?? "—"} + + + + )} + setDistanceValue(String(v ?? ""))} + min={0} + step={0.1} + decimalScale={2} + /> + + + + + + + + {/* Invoice modal */} + Invoice #345} + size="lg" + radius="lg" + centered + > + + {invoiceRecord && ( + <> + + + + EDR Freight + Invoice #345 + + + + + + + + + + + + + + + + + Post Payment + {formatPrice(invoiceRecord.remainingPayment)} + + + Advanced Payment + {formatPrice(invoiceRecord.advancedPayment)} + + + {(() => { + const postPayment = parseFloat(String(invoiceRecord.remainingPayment)); + const advancedPayment = parseFloat(String(invoiceRecord.advancedPayment)); + const difference = postPayment - advancedPayment; + + if (difference > 0) { + return ( + + Remaining to Pay + {formatPrice(difference)} + + ); + } else if (difference < 0) { + return ( + + Refund + {formatPrice(Math.abs(difference))} + + ); + } else { + return ( + + Status + Settled + + ); + } + })()} + + + + + + )} + + + + + ); };