/* ============================================================= CoreIQ Office — Suppliers: directory + New-supplier modal Registers window.OFFICE_SCREENS.suppliers ============================================================= */ const { useState: suUseState, useMemo: suUseMemo, useEffect: suUseEffect } = React; const LOGO = { "sup-01": "GSK", "sup-02": "SIG", "sup-03": "SYM", "sup-04": "API", "sup-05": "WEB", "sup-06": "NDS" }; function supMoney(n) { return window.officeMoney(n); } function supK(n) { const a = Math.abs(n); if (a >= 1000) return (n < 0 ? "−$" : "$") + (a / 1000).toFixed(a >= 100000 ? 0 : 1) + "k"; return supMoney(n); } function perfClass(v) { return v == null ? "" : v >= 95 ? "sup-perf--good" : v >= 90 ? "sup-perf--mid" : "sup-perf--bad"; } function logoText(id) { if (LOGO[id]) return LOGO[id]; const s = window.OFFICE_DATA.SUPPLIERS.find(x => x.id === id); if (!s) return "SUP"; return s.name.replace(/[^A-Za-z ]/g, "").trim().split(/\s+/).map(w => w[0]).slice(0, 3).join("").toUpperCase(); } function logoFor(id) { return
{logoText(id)}
; } function lineBadgeClass(id) { const l = window.supLine(id); if (typeof l === "number") return "sup-line-badge--n" + (l <= 3 ? l : "x"); return "sup-line-badge--" + l; } // perf chip that tolerates new suppliers (null metrics) function PerfCell({ v }) { if (v == null) return
; return
{v}%
; } // ================================================================= // NEW SUPPLIER MODAL // ================================================================= function NewSupplierModal({ onClose, onCreated }) { const nextLine = (window.supRankedIds().length || 0) + 1; const [form, setForm] = suUseState({ name: "", type: "wholesaler", line: nextLine, account: "", contact: "", phone: "", payment: "Net 30", }); const set = (k, v) => setForm(f => ({ ...f, [k]: v })); // line options depend on type const lineOpts = form.type === "wholesaler" ? [{ v: 1, l: "1st line" }, { v: 2, l: "2nd line" }, { v: 3, l: "3rd line" }, { v: nextLine, l: "Next available" }] : form.type === "program" ? [{ v: "program", l: "Program" }] : [{ v: "direct", l: "Direct" }]; // keep line valid when type changes suUseEffect(() => { if (form.type === "program" && form.line !== "program") set("line", "program"); else if (form.type === "brand-direct" && form.line !== "direct") set("line", "direct"); else if (form.type === "wholesaler" && typeof form.line !== "number") set("line", 1); }, [form.type]); const valid = form.name.trim().length > 1; function submit() { if (!valid) return; const id = window.supAddSupplier(form); onCreated(id, form); } const TYPE_OPTS = [ { v: "wholesaler", l: "Wholesaler" }, { v: "brand-direct", l: "Brand direct" }, { v: "program", l: "Program" }, ]; return (
e.stopPropagation()}>
New supplier
Add a wholesaler, brand-direct or program account
set("name", e.target.value)}/>
{lineOpts.map(o => ( ))}
{form.type === "wholesaler" && typeof form.line === "number" && form.line <= window.supRankedIds().length && (
Inserting at {form.line === 1 ? "1st" : form.line === 2 ? "2nd" : form.line === 3 ? "3rd" : form.line + "th"} line will push lower-priority wholesalers down one.
)}
set("account", e.target.value)}/>
set("contact", e.target.value)}/>
set("phone", e.target.value)}/>
New accounts start with no order history — add SKUs and PDEs from the supplier’s Catalogue tab.
); } // ================================================================= // DIRECTORY // ================================================================= // ----------------------------------------------------------------- // SUPPLIERS — LIVE · this pharmacy // The org's 226 real suppliers (migrated from Z), with how many catalogue // products name each as primary supplier. Spend / on-time / backorders are NOT // shown — none of that was migrated, so we don't fabricate it. // ----------------------------------------------------------------- function Suppliers() { const { setScreen, setScreenState } = window.useOffice(); const [suppliers, setSuppliers] = React.useState([]); const [loading, setLoading] = React.useState(true); const [err, setErr] = React.useState(null); const [q, setQ] = React.useState(""); const [sort, setSort] = React.useState({ key: "productCount", dir: "desc" }); React.useEffect(() => { let alive = true; window.OfficeAPI.listSuppliers() .then((ss) => { if (alive) { setSuppliers(ss || []); setLoading(false); } }) .catch((e) => { if (alive) { setErr(String((e && e.message) || e)); setLoading(false); } }); return () => { alive = false; }; }, []); const withProducts = suppliers.filter((s) => s.productCount > 0).length; const withContact = suppliers.filter((s) => s.email || s.phone).length; const active = suppliers.filter((s) => s.isActive).length; const filtered = suppliers.filter((s) => { if (!q.trim()) return true; const n = q.trim().toLowerCase(); return (s.name + " " + (s.code || "") + " " + (s.email || "") + " " + (s.locality || "")).toLowerCase().includes(n); }); const dir = sort.dir === "asc" ? 1 : -1; const sorted = [...filtered].sort((a, b) => { const ka = a[sort.key], kb = b[sort.key]; if (typeof ka === "number" || typeof kb === "number") return ((ka || 0) - (kb || 0)) * dir; return String(ka || "").localeCompare(String(kb || "")) * dir; }); function sortBy(k) { setSort((s) => (s.key === k ? { key: k, dir: s.dir === "asc" ? "desc" : "asc" } : { key: k, dir: "asc" })); } function open(s) { setScreenState((st) => ({ ...st, supplier: s })); setScreen("supplierDetail"); } const COLS = [ { k: "name", label: "Supplier" }, { k: "code", label: "Code" }, { k: "supplierType", label: "Type" }, { k: "email", label: "Contact" }, { k: "productCount", label: "Products", num: true }, { k: "accountNumber", label: "Account #" }, { k: "paymentTermsDays", label: "Terms", num: true }, ]; return (
Buying · this pharmacy · live from Aurora

Suppliers

{err && (
Couldn't load suppliers: {err}
)}
setQ(e.target.value)} style={{height:34, fontSize:'var(--fs-13)'}}/>
{loading ? "loading…" : sorted.length + " of " + suppliers.length}
{COLS.map((c) => (
sortBy(c.k)} style={c.num ? {justifyContent:'flex-end'} : null}> {c.label}{sort.key === c.k && }
))} {sorted.map((s) => (
open(s)}>
{s.name}{!s.isActive && inactive}
{s.code || "—"}
{s.supplierType || "—"}
{s.email || s.phone || }
{s.productCount.toLocaleString("en-AU")}
{s.accountNumber || "—"}
{s.paymentTermsDays != null ? "Net " + s.paymentTermsDays : "—"}
))}
{!loading && sorted.length === 0 &&
No suppliers match “{q}”.
}
); } window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, { suppliers: Suppliers }); window.SupLogo = logoFor; window.supMoneyK = supK; window.supLogoText = LOGO; window.supLogoTextFor = logoText; window.supPerfClass = perfClass;