mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 21:08:12 +00:00
feat(rates): last-mile rate rules with bulk per-ton-km and container distance bands
- rates: min_km/max_km columns, PER_TON_KM unit, ETB|USD currency for last mile - extend UQ_rates_pattern with band start; overlap + shape validation - shared last-mile charge resolver; approve-dialog price estimate endpoint - delivery-fee invoice prices via rules, falls back to vehicle price/km - backoffice: last-mile rate form (mode, container type, band, currency)
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
@@ -76,6 +76,21 @@ export function LastMileRequestsPanel() {
|
||||
queryFn: async () => (await lastMileRequestsService.freeTruckCount()).data,
|
||||
});
|
||||
|
||||
// Rule-based estimate for the approve dialog (estimated km × live last-mile
|
||||
// rates). Prefills the advance once, without clobbering a typed value.
|
||||
const { data: estimate } = useQuery({
|
||||
queryKey: QUERY_KEYS.LAST_MILE_REQUESTS.priceEstimate(approveTarget?.id ?? ""),
|
||||
queryFn: async () =>
|
||||
(await lastMileRequestsService.priceEstimate(approveTarget!.id)).data,
|
||||
enabled: Boolean(approveTarget),
|
||||
});
|
||||
useEffect(() => {
|
||||
if (approveTarget && estimate?.total != null && advanceAmount === "") {
|
||||
setAdvanceAmount(estimate.total);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [estimate, approveTarget]);
|
||||
|
||||
const invalidate = () =>
|
||||
qc.invalidateQueries({ queryKey: QUERY_KEYS.LAST_MILE_REQUESTS.ROOT });
|
||||
|
||||
@@ -227,11 +242,29 @@ export function LastMileRequestsPanel() {
|
||||
|
||||
<Modal
|
||||
opened={Boolean(approveTarget)}
|
||||
onClose={() => setApproveTarget(null)}
|
||||
onClose={() => {
|
||||
setApproveTarget(null);
|
||||
setAdvanceAmount("");
|
||||
}}
|
||||
title={<Text fw={700}>Approve request{approveTarget?.booking?.reference ? ` · ${approveTarget.booking.reference}` : ""}</Text>}
|
||||
centered
|
||||
>
|
||||
<Stack gap="md">
|
||||
{estimate?.total != null && (
|
||||
<Stack gap={4}>
|
||||
{estimate.lines.map((line) => (
|
||||
<Text key={line.description} size="xs" c="dimmed">
|
||||
{line.description} — {line.amount.toLocaleString()}
|
||||
</Text>
|
||||
))}
|
||||
<Text size="sm" fw={600}>
|
||||
Estimated total: {estimate.total.toLocaleString()} {estimate.currency}
|
||||
{estimate.estimatedKm != null
|
||||
? ` · ${estimate.estimatedKm} km (straight-line estimate)`
|
||||
: ""}
|
||||
</Text>
|
||||
</Stack>
|
||||
)}
|
||||
<NumberInput
|
||||
label="Advance amount"
|
||||
placeholder="0.00"
|
||||
@@ -241,7 +274,13 @@ export function LastMileRequestsPanel() {
|
||||
onChange={setAdvanceAmount}
|
||||
/>
|
||||
<Group justify="flex-end">
|
||||
<Button variant="default" onClick={() => setApproveTarget(null)}>
|
||||
<Button
|
||||
variant="default"
|
||||
onClick={() => {
|
||||
setApproveTarget(null);
|
||||
setAdvanceAmount("");
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
|
||||
Reference in New Issue
Block a user