/* ============================================================= CoreIQ Office — Supply & shortages (LIVE · this pharmacy) The drug-shortage radar + supplier-exposure scorecard, from the wholesaler out-of-stock / backorder events (public.supply_shortage_events; migrated Z OrdersPharmXOOSLines + a future PharmX feed). Censored demand that never shows up in sales: every line a wholesaler could not fully supply. "Exposure" = counts of OOS/backorder events per supplier / per drug — NOT a true fill-rate % (we don't carry a clean total-orders denominator), so it counts what actually went short rather than overclaim a ratio. ============================================================= */ const { useState: ssState, useEffect: ssEffect } = React; const ssN = (n) => Number(n || 0).toLocaleString("en-AU"); const ssDate = (s) => String(s || "").slice(0, 10); function StatusBadge({ status }) { const cfg = status === "backordered" ? { t: "Backordered", bg: "var(--hold-bg)", fg: "var(--hold-text)" } : status === "out_of_stock" ? { t: "Out of stock", bg: "var(--void-bg)", fg: "var(--void-text)" } : { t: status || "—", bg: "var(--bg-subtle)", fg: "var(--text-subtle)" }; return {cfg.t}; } function ShortageRadar() { const { setScreen, setScreenState } = window.useOffice(); const [data, setData] = ssState(null); const [events, setEvents] = ssState([]); const [supplier, setSupplier] = ssState(""); const [status, setStatus] = ssState(""); const [search, setSearch] = ssState(""); const [err, setErr] = ssState(null); const [loading, setLoading] = ssState(true); ssEffect(() => { let alive = true; window.OfficeAPI.shortageSummary() .then((d) => { if (alive) { setData(d); setErr(null); } }) .catch((e) => { if (alive) setErr(String(e.message || e)); }); return () => { alive = false; }; }, []); ssEffect(() => { let alive = true; setLoading(true); const q = { limit: 200 }; if (supplier) q.supplier = supplier; if (status) q.status = status; if (search.trim()) q.search = search.trim(); window.OfficeAPI.supplyShortages(q) .then((ev) => { if (alive) { setEvents(ev || []); setLoading(false); } }) .catch((e) => { if (alive) { setErr(String(e.message || e)); setLoading(false); } }); return () => { alive = false; }; }, [supplier, status, search]); const openProduct = (pid) => { if (!pid) return; setScreenState((st) => ({ ...st, activeProductId: pid, productFrom: "shortageRadar" })); setScreen("productDetail"); }; const s = data && data.summary; const bySupplier = (data && data.bySupplier) || []; const topProducts = (data && data.topProducts) || []; const Kpi = window.KpiCard; const maxSup = Math.max(1, ...bySupplier.map((r) => r.events)); return (