/* =============================================================
CoreIQ Office — Pricing review (LIVE · this pharmacy)
The lines that need a price decision, and the working behind each
recommendation. Ported from the design's Pricing shell + recommendation
detail, wired to /pricing/queue and /pricing/recommendation/:id.
ADVISORY ONLY. Nothing on these two screens changes a shelf price. The
queue is DERIVED on every read from facts already on file — a cost that
moved on a real invoice, a line received with no price, a competitor
whose price fell out of its observed range — so there is nothing stored
to go stale and nothing to "dismiss". Prices are changed in Manage stock.
Registers window.OFFICE_SCREENS.pricing and .priceDetail
============================================================= */
const { useState: prState, useEffect: prEffect, useMemo: prMemo } = React;
const prM = (c) => (c == null ? "—" : "$" + (Number(c) / 100).toLocaleString("en-AU", { minimumFractionDigits: 2, maximumFractionDigits: 2 }));
const prSigned = (c) => (c == null ? "—" : (c > 0 ? "+" : c < 0 ? "−" : "") + prM(Math.abs(c)));
const prPct = (v) => (v == null ? "—" : Number(v).toFixed(1) + "%");
const prDate = (iso) => (iso ? String(iso).slice(0, 10) : "—");
// Why a line is in the queue. Order here is priority order — it matches the API.
const PR_REASONS = {
new: { label: "No price", bg: "var(--void-bg)", fg: "var(--void-text)", bd: "var(--void-border)" },
cost: { label: "Cost moved", bg: "var(--hold-bg)", fg: "var(--hold-text)", bd: "var(--hold)" },
floor: { label: "Under floor", bg: "var(--void-bg)", fg: "var(--void-text)", bd: "var(--void-border)" },
sale: { label: "Competitor sale", bg: "var(--hold-bg)", fg: "var(--hold-text)", bd: "var(--hold)" },
kvi: { label: "Price-sensitive", bg: "var(--navy-100)", fg: "var(--navy-800)", bd: "var(--navy-200)" },
move: { label: "Room to move", bg: "var(--bg-subtle)", fg: "var(--text-subtle)", bd: "var(--line)" },
};
function PrReasonTag({ reason }) {
const r = PR_REASONS[reason] || PR_REASONS.move;
return (
{r.label}
);
}
function PrAdvisoryNote() {
return (
Recommendations only — nothing here changes a shelf price. The queue is worked out fresh from the stock card, the latest invoice cost and observed competitor prices each time you open it. Change a price in Manage stock.
{data ? "Recomputed " + new Date(data.generatedAt).toLocaleTimeString("en-AU", { hour: "2-digit", minute: "2-digit" }) + " · sorted by why, then by size of move" : ""}
{!loading && rows.length === 0 && (
{total === 0
? Nothing needs a price decision right now. A line appears here when its cost moves on an invoice, it arrives with no price, it is selling under its cost floor, or an observed competitor price changes enough to matter.
: No lines match this filter.}
{rec.kvi.reason} Priced against the cheapest of {rec.kvi.set.map((o) => o.retailerShort).join(", ")} — {rec.kvi.cheapest.retailerShort} at {prM(rec.kvi.cheapest.priceCents)}, floor {prM(rec.kvi.floorCents)}.
{rec.kvi.missing.length > 0 &&
Not seen on: {rec.kvi.missing.join(", ")} — the evidence is incomplete.
}
)}
{/* the working */}
The working
Every number above, with the step behind it
{/* observations */}
What we observed
{d.observations.length ? "Latest price per retailer, last 90 days" : "No observations in the last 90 days"}