ui fix
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
import type { ReactNode } from 'react';
|
||||
import { ArrowDown, Bike, Check, MapPin, Phone, X } from 'lucide-react';
|
||||
import type { DeliveryRow, OrderRow } from '@/api/types';
|
||||
import type { DeliveryRow, OrderItem, OrderRow } from '@/api/types';
|
||||
import { orderStage, type Stage } from './orderProgress';
|
||||
import { useDeliveryMoves } from './DeliveryProgress';
|
||||
import { Drawer } from './Drawer';
|
||||
import { Badge, DrawerButton, DrawerCard, Metric, Metrics, Note, Row, Section } from './drawerKit';
|
||||
import { branchLabel, moneyExact } from './format';
|
||||
import { lineTotal, qtyLabel, summarise } from './orderItems';
|
||||
import { useOrderItems } from '@/queries/hooks';
|
||||
import {
|
||||
DELIVERY_STATUS,
|
||||
ORDER_STATUS,
|
||||
@@ -135,6 +137,30 @@ function Sheet({
|
||||
const value = job ? (job.deliveryamt ?? 0) : orderValue(row);
|
||||
const cancelled = (status ?? '').toLowerCase().includes('cancel');
|
||||
|
||||
/*
|
||||
* What is in the order, read once here and used twice.
|
||||
*
|
||||
* The three figures at the top of this sheet — value, cash to collect, items
|
||||
* — came only from the row the list handed over, and the list does not carry
|
||||
* them on every order: order 1151-1 opened with "Value —", "Cash to collect
|
||||
* —", "Items —" while the section at the bottom of the same sheet correctly
|
||||
* reported one item. Two blocks describing the same order, disagreeing,
|
||||
* because one of them had asked Fiesta and the other had not.
|
||||
*
|
||||
* So the detail read fills the gaps. The row wins when it has a figure — it
|
||||
* is the number the table behind this sheet is showing, and the two must not
|
||||
* differ — and this answers only where the row was silent.
|
||||
*
|
||||
* One fetch: the same query key, passed down rather than called again.
|
||||
*/
|
||||
const detail = useOrderItems(row.orderheaderid);
|
||||
const items = detail.data?.items ?? [];
|
||||
const detailAmount = detail.data?.amount ?? 0;
|
||||
// Units, not lines, because this metric sits beside a money figure and reads
|
||||
// as "how much is in it". The Items section below names both.
|
||||
const itemCount =
|
||||
(job ? job.itemcount : orderQuantity(row)) || Math.round(summarise(items).units);
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
title={title}
|
||||
@@ -152,7 +178,13 @@ function Sheet({
|
||||
Three equal columns, the shape the spec asks for and the one that
|
||||
answers "what is this worth, who pays what, how much of it". */}
|
||||
<Metrics>
|
||||
<Metric label="Value" value={value > 0 ? moneyExact(value) : '—'} />
|
||||
{/* The row's figure where it has one, Fiesta's where it does not. An
|
||||
order the list reported as worth nothing is usually an order the
|
||||
list was not told about, not a free one. */}
|
||||
<Metric
|
||||
label="Value"
|
||||
value={value > 0 ? moneyExact(value) : detailAmount > 0 ? moneyExact(detailAmount) : '—'}
|
||||
/>
|
||||
{job ? (
|
||||
<Metric
|
||||
label="Rider charge"
|
||||
@@ -166,11 +198,10 @@ function Sheet({
|
||||
isSmall
|
||||
/>
|
||||
)}
|
||||
<Metric
|
||||
label="Items"
|
||||
value={job ? String(job.itemcount ?? '—') : String(orderQuantity(row) || '—')}
|
||||
isSmall
|
||||
/>
|
||||
{/* Counted from the lines when the row carries no count. `itemcount`
|
||||
and `quantity` are both routinely absent on the list read, which is
|
||||
what made this a dash on an order that plainly had something in it. */}
|
||||
<Metric label="Items" value={itemCount > 0 ? String(itemCount) : '—'} isSmall />
|
||||
</Metrics>
|
||||
|
||||
<Section title="Order journey">
|
||||
@@ -262,10 +293,7 @@ function Sheet({
|
||||
</Section>
|
||||
) : null}
|
||||
|
||||
<Note>
|
||||
Line items are not in the list read — Fiesta returns them from the order-detail endpoint,
|
||||
which this sheet does not call yet.
|
||||
</Note>
|
||||
<OrderItems items={items} isLoading={detail.isLoading} isError={detail.isError} />
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
@@ -447,3 +475,95 @@ const km = (value: string | undefined): string => {
|
||||
const n = Number(value);
|
||||
return Number.isFinite(n) && n > 0 ? `${n.toFixed(1)} km` : '—';
|
||||
};
|
||||
/* ── What is actually in the order ───────────────────────────────────────── */
|
||||
|
||||
/**
|
||||
* The products on the order, line by line.
|
||||
*
|
||||
* This sheet has always been able to say what an order was WORTH and never what
|
||||
* it was. The note that used to sit here said as much: Fiesta returns the
|
||||
* contents from `orders/getorderdetails` and nothing called it. So an operator
|
||||
* chasing a stalled delivery could see ₹840 and had to open another system to
|
||||
* find out whether that was rice or ice cream — which decides whether it can
|
||||
* wait an hour.
|
||||
*
|
||||
* Handed its data rather than fetching its own. The drawer above needs the same
|
||||
* read to fill the three figures in its header, and two components asking
|
||||
* separately is how the header came to say "Items —" on an order this section
|
||||
* was, at that moment, listing.
|
||||
*/
|
||||
function OrderItems({
|
||||
items,
|
||||
isLoading,
|
||||
isError,
|
||||
}: {
|
||||
items: readonly OrderItem[];
|
||||
isLoading: boolean;
|
||||
isError: boolean;
|
||||
}) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Section title="Items">
|
||||
<Note>Reading what is in this order…</Note>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
// Not a blank section. The rest of the sheet is sound and this one read
|
||||
// failed; saying so beats an empty heading that reads like an empty order.
|
||||
return (
|
||||
<Section title="Items">
|
||||
<Note>Could not read the items on this order.</Note>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
if (items.length === 0) {
|
||||
return (
|
||||
<Section title="Items">
|
||||
<DrawerCard tone="subtle">
|
||||
<Row label="Contents" value="Fiesta returned no line items for this order." isStacked />
|
||||
</DrawerCard>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
const { lines, units, short } = summarise(items);
|
||||
|
||||
return (
|
||||
<Section
|
||||
title={
|
||||
// Both numbers when they differ, because they answer different
|
||||
// questions: four products, twelve units. One labelled as the other is
|
||||
// how a picker packs the wrong trolley.
|
||||
units !== lines ? `Items · ${lines} products · ${qtyLabel(units)} units` : `Items · ${lines}`
|
||||
}
|
||||
>
|
||||
{short > 0 ? (
|
||||
<Note>
|
||||
{short === 1 ? 'One line was' : `${short} lines were`} short-supplied — the shop could not
|
||||
give the full quantity ordered.
|
||||
</Note>
|
||||
) : null}
|
||||
|
||||
<DrawerCard>
|
||||
{items.map((item) => (
|
||||
<Row
|
||||
key={item.orderdetailid}
|
||||
label={item.productname?.trim() || `Product ${item.productid}`}
|
||||
value={
|
||||
<span style={{ display: 'inline-flex', alignItems: 'baseline', gap: 10 }}>
|
||||
{/* Quantity first: a picker reads this column, not the price. */}
|
||||
<span style={{ color: 'var(--color-ink-2)' }}>
|
||||
{qtyLabel(item.orderqty, item.unitname)}
|
||||
</span>
|
||||
<span style={{ fontWeight: 600 }}>{moneyExact(lineTotal(item))}</span>
|
||||
</span>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</DrawerCard>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user