updates on the bookings page and updated ai and more thngs
This commit is contained in:
78
src/lib/routingSummary.js
Normal file
78
src/lib/routingSummary.js
Normal file
@@ -0,0 +1,78 @@
|
||||
// ==============================|| Routing decision, in words ||============================== //
|
||||
//
|
||||
// `GET /admin/bookings/:id` returns a `routing` block: the decision the backend
|
||||
// took about where a parcel goes, and the inputs it took it from. This turns
|
||||
// that into the sentences the order drawer shows.
|
||||
//
|
||||
// It exists so support can answer "why does the rider's screen say hand over at
|
||||
// a base instead of deliver to the customer?" as a lookup instead of
|
||||
// reconstructing a pincode rule by hand on the phone.
|
||||
//
|
||||
// TWO RULES THIS FILE EXISTS TO HOLD:
|
||||
//
|
||||
// 1. A PROJECTION IS NOT A DECISION. Before pickup, nothing has been decided —
|
||||
// `routing.decided` is false and what is shown is what WILL happen. Saying
|
||||
// "this went via a base" about a parcel nobody has collected yet is a
|
||||
// wrong answer delivered confidently, which is worse than no answer.
|
||||
//
|
||||
// 2. THE WIRE SAYS HUB, PEOPLE SAY BASE. The translation happens here, at the
|
||||
// edge, and never travels back up: every value sent to the backend keeps
|
||||
// its wire spelling.
|
||||
|
||||
/** What the rider does next with the parcel. Keys are the backend's wire
|
||||
* values; an unrecognised one is shown as itself rather than swallowed —
|
||||
* the backend may add actions, and a blank row is worse than a raw word. */
|
||||
export const NEXT_ACTION_LABEL = Object.freeze({
|
||||
pickup: 'Collect from the pickup point',
|
||||
inward_at_hub: 'Carry to a base and hand over',
|
||||
start_delivery: 'Start the delivery run',
|
||||
deliver: 'Deliver to the receiver',
|
||||
handed_to_hub: 'Handed over at the base',
|
||||
none: 'Nothing further for the rider'
|
||||
});
|
||||
|
||||
export const nextActionLabel = (action) => NEXT_ACTION_LABEL[action] || action || '—';
|
||||
|
||||
/** Why the parcel is going the way it is, in one sentence an operator can read
|
||||
* to a rider over the phone. */
|
||||
export const routingReason = (routing) => {
|
||||
if (!routing) return '';
|
||||
return routing.is_hyperlocal
|
||||
? 'Same postal area — the collecting rider carries it straight to the receiver.'
|
||||
: 'Different postal area — it goes through a base rather than direct to the receiver.';
|
||||
};
|
||||
|
||||
/** The from → to line, with the honesty caveat when nothing has been decided
|
||||
* yet. A booking not yet collected has no routing FACT, only a forecast. */
|
||||
export const routingRoute = (routing) => {
|
||||
if (!routing) return '';
|
||||
const from = routing.from_pincode || '—';
|
||||
const to = routing.destination_pincode || '—';
|
||||
const line = `${from} → ${to}`;
|
||||
return routing.decided ? line : `${line} · not yet collected, so this is what will happen, not what has`;
|
||||
};
|
||||
|
||||
/** The base a parcel is routed to, as one readable line. Null when no base is
|
||||
* involved — which is the correct answer for a hyperlocal parcel, not a gap. */
|
||||
export const baseLine = (base) => {
|
||||
if (!base || !base.name) return null;
|
||||
return base.pincode ? `${base.name} · ${base.pincode}` : base.name;
|
||||
};
|
||||
|
||||
/**
|
||||
* Everything the drawer renders, in one call, so the component holds no
|
||||
* decisions of its own.
|
||||
*/
|
||||
export const summariseRouting = (routing) => {
|
||||
if (!routing) return null;
|
||||
return {
|
||||
decided: Boolean(routing.decided),
|
||||
reason: routingReason(routing),
|
||||
route: routingRoute(routing),
|
||||
nextAction: nextActionLabel(routing.next_action),
|
||||
base: baseLine(routing.next_hub),
|
||||
baseAddress: routing.next_hub?.address || null,
|
||||
consignmentState: routing.consignment_state || '—',
|
||||
inwardedAt: routing.inwardedat || null
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user