rider status

This commit is contained in:
2026-09-22 11:13:15 +05:30
parent 19ed3585ff
commit cca3c50a89
21 changed files with 1389 additions and 41 deletions

View File

@@ -36,7 +36,8 @@ import {
import { OrderDetailDrawer, type RowKind } from '../OrderDetailDrawer';
import { shortAge } from '../posStatus';
import { AssignBar } from '../AssignBar';
import { assignability, assignedFrom } from '../assignDelivery';
import { assignability, assignedFrom, releasedFrom } from '../assignDelivery';
import { deliveryStageFrom, orderStage } from '../orderProgress';
import { useSelection } from '@/components/useSelection';
import { Panel } from '@/components/Panel';
import { TablePager } from '@/components/TablePager';
@@ -140,6 +141,16 @@ export function SalesPage() {
const importedBills = useMemo(() => everyOrder.filter(isCounterSale), [everyOrder]);
const allDeliveries = deliveries.data ?? [];
/*
* Filtered on the ORDER ladder, deliberately, even though the chip now shows
* the delivery stage.
*
* `ORDER_STATUS_TABS` has no Picked or Arrived tab — an order never reaches
* those words — so filtering on the stage would drop every order in progress
* out of every tab except All. Under Pending the rows are the outstanding
* orders, which is what Pending means here, and the chip says how far along
* each one is. The tab counts stay on the same footing for the same reason.
*/
const orderRows = useMemo(
() => allOrders.filter((row) => matchesStatus(status, row.orderstatus)),
[allOrders, status],
@@ -270,6 +281,15 @@ export function SalesPage() {
* of production's delivery history a second time.
*/
const assigned = useMemo(() => assignedFrom(allDeliveries), [allDeliveries]);
/* Orders whose only delivery was rejected or called off. They are waiting
for a rider again, and without this the delivery row left behind keeps
them out of every assign surface for good — see `releasedFrom`. */
const released = useMemo(() => releasedFrom(allDeliveries), [allDeliveries]);
/* How far each order has actually got. The order row is only ever told
pending/delivered/cancelled, so the six stages in between are read off the
deliveries list — same window, same query, no extra fetch. See gap D in
`orderProgress.ts`. */
const stages = useMemo(() => deliveryStageFrom(allDeliveries), [allDeliveries]);
const branchOf = useMemo(
() => (row: OrderRow) => branches.find((branch) => branch.locationid === row.locationid),
@@ -288,7 +308,7 @@ export function SalesPage() {
const selectable = useMemo(
() =>
orderRows
.filter((row) => assignability(row, branchOf(row), assigned).canAssign)
.filter((row) => assignability(row, branchOf(row), assigned, released).canAssign)
.map((row) => row.orderheaderid),
[orderRows, branchOf, assigned],
);
@@ -410,6 +430,7 @@ export function SalesPage() {
orders={pickedOrders}
branchOf={branchOf}
assigned={assigned}
released={released}
onClear={picked.clear}
onDone={picked.clear}
/>
@@ -422,6 +443,8 @@ export function SalesPage() {
selection={picked}
branchOf={branchOf}
assigned={assigned}
released={released}
stages={stages}
onOpen={(row) => setDetail({ row, kind: 'order' })}
/>
{orderRows.length > 0 ? (
@@ -564,6 +587,7 @@ export function SalesPage() {
<OrderDetailDrawer
row={freshRow(detail, allOrders, allDeliveries)}
kind={detail.kind}
stages={stages}
onClose={() => setDetail(null)}
/>
) : null}
@@ -656,6 +680,8 @@ function OrdersTable({
selection,
branchOf,
assigned,
released,
stages,
onOpen,
queryKey,
}: {
@@ -667,6 +693,10 @@ function OrdersTable({
selection: ReturnType<typeof useSelection>;
branchOf: (row: OrderRow) => TenantLocation | undefined;
assigned: ReadonlySet<number>;
/** Orders whose delivery is dead — see `releasedFrom`. */
released?: ReadonlySet<number>;
/** Where each order's delivery has got to — see `orderProgress.ts`. */
stages: ReadonlyMap<number, string>;
onOpen: (row: OrderRow) => void;
}) {
// Above the early returns — a hook cannot be called conditionally.
@@ -723,7 +753,8 @@ function OrdersTable({
{paged.rows.map((row, index) => {
const cod = row.collectionamt ?? 0;
const amount = orderValue(row);
const verdict = assignability(row, branchOf(row), assigned);
const verdict = assignability(row, branchOf(row), assigned, released);
const stage = orderStage(row, stages);
const isPicked = selection.has(row.orderheaderid);
return (
<Row key={row.orderheaderid} onClick={() => onOpen(row)} isPicked={isPicked}>
@@ -802,7 +833,14 @@ function OrdersTable({
<Mono>{amount > 0 ? moneyExact(amount) : '—'}</Mono>
</Td>
<Td>
<Chip label={row.orderstatus} color={statusColor(ORDER_STATUS, row.orderstatus)} />
{/* The stage, not the order's own word — which sits on `pending`
from assignment until delivery. The ladder has to follow
it: colouring `picked` with the ORDER map paints it grey,
in the middle of the journey where it matters most. */}
<Chip
label={stage.status}
color={statusColor(stage.isDelivery ? DELIVERY_STATUS : ORDER_STATUS, stage.status)}
/>
</Td>
<Td>
<ViewHint />