mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-09 05:58:18 +00:00
add first /last mile summery on booking detail
This commit is contained in:
@@ -222,7 +222,7 @@ export function ReadonlyBookingView({
|
|||||||
|
|
||||||
<ActivityCard booking={booking} />
|
<ActivityCard booking={booking} />
|
||||||
|
|
||||||
<MileSummaryCard bookingId={booking.id} />
|
<MileSummaryCard booking={booking} />
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Box, Group, Stack, Text } from "@mantine/core";
|
import { Box, Group, Stack, Text } from "@mantine/core";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
|
|
||||||
|
import type { Freight } from "@edr/types";
|
||||||
|
|
||||||
import { bookingsService } from "@/services/bookings.service";
|
import { bookingsService } from "@/services/bookings.service";
|
||||||
import type {
|
import type {
|
||||||
MileLegSummary,
|
MileLegSummary,
|
||||||
@@ -81,11 +83,19 @@ function VehicleRow({ v }: { v: MileVehicleSummary }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LegBlock({ title, leg }: { title: string; leg: MileLegSummary }) {
|
function LegBlock({
|
||||||
const fmtMoney = (n: number | null) =>
|
title,
|
||||||
|
leg,
|
||||||
|
address,
|
||||||
|
}: {
|
||||||
|
title: string;
|
||||||
|
leg: MileLegSummary | null;
|
||||||
|
address?: string | null;
|
||||||
|
}) {
|
||||||
|
const fmtMoney = (n: number | null, currency: string) =>
|
||||||
n == null
|
n == null
|
||||||
? null
|
? null
|
||||||
: `${leg.currency} ${Number(n).toLocaleString(undefined, {
|
: `${currency} ${Number(n).toLocaleString(undefined, {
|
||||||
minimumFractionDigits: 2,
|
minimumFractionDigits: 2,
|
||||||
maximumFractionDigits: 2,
|
maximumFractionDigits: 2,
|
||||||
})}`;
|
})}`;
|
||||||
@@ -96,10 +106,25 @@ function LegBlock({ title, leg }: { title: string; leg: MileLegSummary }) {
|
|||||||
<Text fz="13px" fw={700} c="#10202F">
|
<Text fz="13px" fw={700} c="#10202F">
|
||||||
{title}
|
{title}
|
||||||
</Text>
|
</Text>
|
||||||
<StatusPill status={leg.status} />
|
{leg ? (
|
||||||
|
<StatusPill status={leg.status} />
|
||||||
|
) : (
|
||||||
|
<StatusPill status="AWAITING_ASSIGNMENT" />
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{leg.vehicles.length > 0 ? (
|
{address && (
|
||||||
|
<Text fz="12px" c="#6B7C8E" pb={4}>
|
||||||
|
{title.startsWith("First") ? "Pickup" : "Delivery"}:{" "}
|
||||||
|
<b style={{ color: "#10202F" }}>{address}</b>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!leg ? (
|
||||||
|
<Text fz="12px" c="#9AA8B5" fs="italic" py={6}>
|
||||||
|
Requested — a vehicle and driver will be assigned soon.
|
||||||
|
</Text>
|
||||||
|
) : leg.vehicles.length > 0 ? (
|
||||||
<Box>
|
<Box>
|
||||||
{leg.vehicles.map((v, i) => (
|
{leg.vehicles.map((v, i) => (
|
||||||
<VehicleRow key={`${v.plate ?? v.code ?? "v"}-${i}`} v={v} />
|
<VehicleRow key={`${v.plate ?? v.code ?? "v"}-${i}`} v={v} />
|
||||||
@@ -111,36 +136,57 @@ function LegBlock({ title, leg }: { title: string; leg: MileLegSummary }) {
|
|||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Group justify="space-between" align="center" pt={8} mt={4} style={{ borderTop: "1px solid #F2F5F8" }}>
|
{leg && (
|
||||||
<Group gap={16}>
|
<Group
|
||||||
{leg.exactKm != null && (
|
justify="space-between"
|
||||||
<Text fz="12px" c="#6B7C8E">
|
align="center"
|
||||||
Total distance <b style={{ color: "#10202F" }}>{leg.exactKm} km</b>
|
pt={8}
|
||||||
</Text>
|
mt={4}
|
||||||
)}
|
style={{ borderTop: "1px solid #F2F5F8" }}
|
||||||
{leg.remainingPayment != null && leg.remainingPayment > 0 && (
|
>
|
||||||
<Text fz="12px" c="#6B7C8E">
|
<Group gap={16}>
|
||||||
Balance <b style={{ color: "#10202F" }}>{fmtMoney(leg.remainingPayment)}</b>
|
{leg.exactKm != null && (
|
||||||
|
<Text fz="12px" c="#6B7C8E">
|
||||||
|
Total distance{" "}
|
||||||
|
<b style={{ color: "#10202F" }}>{leg.exactKm} km</b>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{leg.remainingPayment != null && leg.remainingPayment > 0 && (
|
||||||
|
<Text fz="12px" c="#6B7C8E">
|
||||||
|
Balance{" "}
|
||||||
|
<b style={{ color: "#10202F" }}>
|
||||||
|
{fmtMoney(leg.remainingPayment, leg.currency)}
|
||||||
|
</b>
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</Group>
|
||||||
|
{leg.invoiced && (
|
||||||
|
<Text fz="11px" fw={700} c="#0A6F4D">
|
||||||
|
Invoiced
|
||||||
</Text>
|
</Text>
|
||||||
)}
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
{leg.invoiced && (
|
)}
|
||||||
<Text fz="11px" fw={700} c="#0A6F4D">
|
|
||||||
Invoiced
|
|
||||||
</Text>
|
|
||||||
)}
|
|
||||||
</Group>
|
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MileSummaryCard({ bookingId }: { bookingId: string }) {
|
export function MileSummaryCard({ booking }: { booking: Freight.IBooking }) {
|
||||||
const { data } = useQuery({
|
const { data } = useQuery({
|
||||||
queryKey: ["booking-mile-summary", bookingId],
|
queryKey: ["booking-mile-summary", booking.id],
|
||||||
queryFn: () => bookingsService.mileSummary(bookingId),
|
queryFn: () => bookingsService.mileSummary(booking.id),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!data || (!data.firstMile && !data.lastMile)) return null;
|
const firstLeg = data?.firstMile ?? null;
|
||||||
|
const lastLeg = data?.lastMile ?? null;
|
||||||
|
|
||||||
|
// The backend doesn't persist an "enabled" flag — the presence of a
|
||||||
|
// pickup/delivery address is the request signal. Also show a leg once its
|
||||||
|
// record exists, regardless of address.
|
||||||
|
const showFirst = !!booking.firstMilePickupAddress || !!firstLeg;
|
||||||
|
const showLast = !!booking.lastMileDeliveryAddress || !!lastLeg;
|
||||||
|
|
||||||
|
if (!showFirst && !showLast) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SectionCard p={22}>
|
<SectionCard p={22}>
|
||||||
@@ -148,8 +194,20 @@ export function MileSummaryCard({ bookingId }: { bookingId: string }) {
|
|||||||
<CardTitle>First & Last Mile</CardTitle>
|
<CardTitle>First & Last Mile</CardTitle>
|
||||||
</Box>
|
</Box>
|
||||||
<Stack gap={20}>
|
<Stack gap={20}>
|
||||||
{data.firstMile && <LegBlock title="First mile" leg={data.firstMile} />}
|
{showFirst && (
|
||||||
{data.lastMile && <LegBlock title="Last mile" leg={data.lastMile} />}
|
<LegBlock
|
||||||
|
title="First mile"
|
||||||
|
leg={firstLeg}
|
||||||
|
address={booking.firstMilePickupAddress}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showLast && (
|
||||||
|
<LegBlock
|
||||||
|
title="Last mile"
|
||||||
|
leg={lastLeg}
|
||||||
|
address={booking.lastMileDeliveryAddress}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</SectionCard>
|
</SectionCard>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user