/* ============================================================= CoreIQ Office — National catalogue (Ironbark reference) Live search over the shared Ironbark reference catalogue served by the Office API (/reference/*). This screen reads REAL data — no sample data. It is the source-of-truth product browser a pharmacy uses before adding a line to its own catalogue. ============================================================= */ const { useState: catUseState, useEffect: catUseEffect, useRef: catUseRef } = React; function NationalCatalogue() { const [q, setQ] = catUseState(""); const [rows, setRows] = catUseState([]); const [stats, setStats] = catUseState(null); const [loading, setLoading] = catUseState(true); const [err, setErr] = catUseState(null); const reqSeq = catUseRef(0); const [sort, setSort] = catUseState({ col: "description", dir: "asc" }); // How much reference data is loaded — proves the catalogue is fully populated. catUseEffect(() => { window.OfficeAPI.referenceStats() .then(setStats) .catch((e) => setErr(String(e.message || e))); }, []); // Debounced live search (empty query = alphabetical browse). catUseEffect(() => { const seq = ++reqSeq.current; setLoading(true); const t = setTimeout(() => { window.OfficeAPI.referenceSearch(q.trim(), 60, 0, sort.col, sort.dir) .then((ps) => { if (seq !== reqSeq.current) return; // a newer query won setRows(ps); setErr(null); setLoading(false); }) .catch((e) => { if (seq !== reqSeq.current) return; setErr(String(e.message || e)); setLoading(false); }); }, 220); return () => clearTimeout(t); }, [q, sort.col, sort.dir]); const price = (cents) => cents == null ? "—" : window.officeMoney(cents / 100); const COLS = [ { k: "description", label: "Product", num: false }, { k: "barcode", label: "Barcode", num: false }, { k: "schedule", label: "Sched", num: false }, { k: "generic", label: "Generic / active", num: false }, { k: "pack", label: "Pack", num: false }, { k: "price", label: "Trade ex-GST", num: true }, { k: "provider", label: "Source", num: false }, ]; const toggleSort = (k) => setSort((s) => (s.col === k ? { col: k, dir: s.dir === "asc" ? "desc" : "asc" } : { col: k, dir: k === "price" ? "desc" : "asc" })); const arrow = (k) => (sort.col === k ? (sort.dir === "asc" ? " ↑" : " ↓") : ""); return (
Catalogue · Ironbark national reference

Product catalogue

{stats && ( {stats.wholesalerProducts.toLocaleString()} products · {" "}{stats.drugs.toLocaleString()} drugs · {" "}{stats.pbsItems.toLocaleString()} PBS · {" "}{stats.artgProducts.toLocaleString()} ARTG · export v{stats.exportVersion} )}
setQ(e.target.value)} autoFocus />
{loading ? "Searching…" : `${rows.length} shown`}
{err && (
Couldn’t reach the catalogue API — {err}
)}
{COLS.map((c) => (
toggleSort(c.k)} title="Sort" style={Object.assign({ cursor: "pointer", userSelect: "none" }, c.num ? { justifyContent: "flex-end" } : {})} > {c.label}{arrow(c.k)}
))} {rows.map((r) => (
{r.description || "—"}
{r.barcode || "—"}
{r.drugSchedule ? : }
{r.genericName || "—"}
{r.packQuantity ? `${r.packQuantity} ${r.packUom || ""}`.trim() : "—"}
{price(r.tradePriceExGstCents)}
{r.provider || "—"}
))}
{!loading && rows.length === 0 && !err && (
No products match “{q}”.
)}
); } window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, { catalogue: NationalCatalogue, });