/* ============================================================= CoreIQ Office — Stores expansion Performance comparison · Stock requests · Regions · Procedures ============================================================= */ const { useState: stExtUseState, useMemo: stExtUseMemo } = React; const STORE_COL = { "S-001": "var(--teal-700)", "S-002": "var(--navy-600)", "S-003": "var(--violet-600)" }; const ST_REGIONS = [ { id: "rg-east", name: "Eastern Metro", manager: "Suni Kapoor", stores: ["S-001","S-002"], color: "var(--teal-700)" }, { id: "rg-outer", name: "Outer Eastern", manager: "James Liu", stores: ["S-003"], color: "var(--violet-600)" }, { id: "rg-north", name: "Northern (proposed)", manager: "—", stores: [], color: "var(--text-subtle)" }, ]; const ST_REQUESTS = [ { id: "REQ-118", from: "S-002", to: "S-001", productId: "p008", qty: 24, reason: "Stock-out — patient waiting on script", requestedBy: "Rachel Pham", ts: "Today 14:18", priority: "urgent", status: "approved", responseTs: "Today 14:22", responder: "Suni Kapoor" }, { id: "REQ-117", from: "S-003", to: "S-001", productId: "p012", qty: 4, reason: "Cold-chain Insulin Glargine low", requestedBy: "James Liu", ts: "Today 11:48", priority: "urgent", status: "in-transit",responseTs: "Today 11:52", responder: "Suni Kapoor", linkedTransfer: "TR-2208" }, { id: "REQ-116", from: "S-001", to: "S-003", productId: "p019", qty: 6, reason: "CGM sensor stock-out — flagged patient", requestedBy: "Suni Kapoor", ts: "Today 10:14", priority: "high", status: "approved", responseTs: "Today 10:18", responder: "James Liu" }, { id: "REQ-115", from: "S-002", to: "S-003", productId: "p014", qty: 12, reason: "Vitamin run-out · promo demand", requestedBy: "Priya Sharma", ts: "Yesterday", priority: "normal", status: "fulfilled", responseTs: "Yesterday", responder: "James Liu", linkedTransfer: "TR-2205" }, { id: "REQ-114", from: "S-001", to: "S-002", productId: "p016", qty: 24, reason: "Paracetamol low", requestedBy: "Rachel Pham", ts: "20/05/2026", priority: "normal", status: "declined", responseTs: "20/05/2026", responder: "Rachel Pham", declineReason: "Glen Iris stock also low — ordered fresh PO instead" }, { id: "REQ-113", from: "S-003", to: "S-001", productId: "p002", qty: 30, reason: "Metformin spike post-clinic referral", requestedBy: "James Liu", ts: "19/05/2026", priority: "high", status: "fulfilled", responseTs: "19/05/2026", responder: "Suni Kapoor", linkedTransfer: "TR-2199" }, { id: "REQ-112", from: "S-002", to: "S-001", productId: "p025", qty: 6, reason: "Probiotic — local promo demand", requestedBy: "Priya Sharma", ts: "18/05/2026", priority: "normal", status: "fulfilled", responseTs: "18/05/2026", responder: "Suni Kapoor" }, ]; const ST_CHECKLIST_OPEN = [ { id: "o1", task: "Unlock storefront and disable alarm", category: "Security", time: "08:25" }, { id: "o2", task: "Count opening cash float ($200) — two-person sign-off", category: "Cash", time: "08:30" }, { id: "o3", task: "Open S8 safe and verify register count", category: "S8", time: "08:32" }, { id: "o4", task: "Power up registers, scanners, label printers", category: "Hardware", time: "08:35" }, { id: "o5", task: "Test Eftpos terminal (Tyro/Adyen) with $0 ping", category: "Hardware", time: "08:38" }, { id: "o6", task: "Check cold-chain fridge temperatures (2–8°C)", category: "Compliance", time: "08:40" }, { id: "o7", task: "Review overnight scripts (eScript queue)", category: "Dispensary", time: "08:42" }, { id: "o8", task: "Restock front-counter promo items", category: "Merchandising", time: "08:45" }, { id: "o9", task: "Brief team on today's pickups, counsel due, flags", category: "Team", time: "08:50" }, { id: "o10", task: "Unlock doors to public · note opening time", category: "Security", time: "08:30" }, ]; const ST_CHECKLIST_CLOSE = [ { id: "c1", task: "Lock doors to public · finalise queue", category: "Security", time: "19:00" }, { id: "c2", task: "Close active sales, void held sales", category: "POS", time: "19:02" }, { id: "c3", task: "Run End-of-day · reconcile registers", category: "Cash", time: "19:05" }, { id: "c4", task: "Count cash, lodge tomorrow's float, drop excess to safe", category: "Cash", time: "19:15" }, { id: "c5", task: "S8 register countersign — two pharmacists", category: "S8", time: "19:20" }, { id: "c6", task: "Lock S8 safe", category: "S8", time: "19:22" }, { id: "c7", task: "Confirm cold-chain temperatures logged", category: "Compliance", time: "19:25" }, { id: "c8", task: "Submit PBS Online Claiming batch", category: "Compliance", time: "19:30" }, { id: "c9", task: "Power down non-essential equipment", category: "Hardware", time: "19:35" }, { id: "c10", task: "Set alarm, lock storefront", category: "Security", time: "19:40" }, ]; // ================================================================= // STORES — expanded landing // ================================================================= function StoresExpanded() { const { setScreen, setScreenState, pushToast } = window.useOffice(); const [tab, setTab] = stExtUseState("compare"); return (
Head-office function

Stores

{tab === "list" && } {tab === "compare" && } {tab === "regions" && } {tab === "requests" && } {tab === "procedures" && }
); } function StoresListInline() { const { setScreen, setScreenState } = window.useOffice(); return (
Code
Store
Manager
Registers
Patients
Today
MTD
Stock value
Open
{OD.STORES.map(s => (
{s.code}
{s.name}
{s.address}
{s.manager}
{s.registers}
{s.patients.toLocaleString()}
{oMoney(s.takings.today)}
{oMoney(s.takings.mtd)}
{oMoney(s.stockValue)}
))}
); } // ================================================================= // PERFORMANCE COMPARISON // ================================================================= function PerformanceComparison() { const [period, setPeriod] = stExtUseState("mtd"); const [metric, setMetric] = stExtUseState("revenue"); const METRICS = [ { id: "revenue", label: "Revenue", val: s => s.takings[period === "today" ? "today" : period === "ytd" ? "ytd" : "mtd"], fmt: v => oMoney(v) }, { id: "scripts", label: "Scripts dispensed", val: s => s.scripts[period === "today" ? "today" : period === "ytd" ? "ytd" : "mtd"], fmt: v => v.toLocaleString() }, { id: "patients", label: "Patient base", val: s => s.patients, fmt: v => v.toLocaleString() }, { id: "stockValue", label: "Stock value", val: s => s.stockValue, fmt: v => oMoney(v) }, { id: "perRegister", label: "Revenue per register", val: s => s.takings.mtd / s.registers, fmt: v => oMoney(v) }, { id: "perPatient", label: "Revenue per patient", val: s => s.takings.mtd / s.patients, fmt: v => oMoney(v) }, ]; // Per-metric rows with bar visualisation const rows = METRICS.map(m => ({ metric: m, values: OD.STORES.map(s => ({ store: s, value: m.val(s) })), })); return ( <>
Period
{[["today","Today"],["mtd","Month to date"],["ytd","Year to date"]].map(([k, label]) => ( ))}
{/* Hero metric: revenue */}

Revenue · {period === "today" ? "today" : period === "ytd" ? "YTD" : "MTD"}

{OD.REVENUE_30D["S-001"].map((_, i) => (
{OD.STORES.map(s => { const v = OD.REVENUE_30D[s.id][i]; const max = 6500; return
; })}
))}
{OD.STORES.map(s => ( {s.name} · {oMoney(s.takings[period === "today" ? "today" : period === "ytd" ? "ytd" : "mtd"])} ))}
{/* Multi-metric comparison */}

Side-by-side · {period === "today" ? "today" : period === "ytd" ? "YTD" : "MTD"}

{rows.map((row, i) => { const max = Math.max(...row.values.map(v => v.value)); const leader = row.values.find(v => v.value === max); return (
{row.metric.label}
Leader · {leader.store.name}
{row.values.map(v => { const pct = max > 0 ? (v.value / max) * 100 : 0; return (
{v.store.code} {row.metric.fmt(v.value)}
); })}
{row.metric.fmt(row.values.reduce((s, v) => s + v.value, 0))}
Group
); })}
{/* Index vs group average */}

Index vs group average

100 = group avg
Store
Revenue idx
Scripts idx
$/patient idx
$/register idx
{OD.STORES.map(s => { const avgRev = OD.STORES.reduce((sum, x) => sum + x.takings.mtd, 0) / OD.STORES.length; const avgScr = OD.STORES.reduce((sum, x) => sum + x.scripts.mtd, 0) / OD.STORES.length; const avgPp = OD.STORES.reduce((sum, x) => sum + x.takings.mtd / x.patients, 0) / OD.STORES.length; const avgPr = OD.STORES.reduce((sum, x) => sum + x.takings.mtd / x.registers, 0) / OD.STORES.length; const idx = (val, avg) => Math.round((val / avg) * 100); const cell = (v) => ({color: v >= 110 ? 'var(--paid-text)' : v <= 90 ? 'var(--void-text)' : 'var(--text)'}); return (
{s.code}
{s.name}
{idx(s.takings.mtd, avgRev)}
{idx(s.scripts.mtd, avgScr)}
{idx(s.takings.mtd / s.patients, avgPp)}
{idx(s.takings.mtd / s.registers, avgPr)}
); })}
); } // ================================================================= // REGIONS // ================================================================= function Regions() { const { pushToast } = window.useOffice(); return ( <>
Group stores into regions to scope reports, manager access, and bulk operations. Useful as the network grows past a handful of stores.
{ST_REGIONS.map(r => { const stores = r.stores.map(id => OD.STORES.find(s => s.id === id)).filter(Boolean); const rev = stores.reduce((s, x) => s + x.takings.mtd, 0); const scripts = stores.reduce((s, x) => s + x.scripts.mtd, 0); const patients = stores.reduce((s, x) => s + x.patients, 0); return (
{r.name}
{stores.length === 0 ? Empty : {stores.length} {stores.length === 1 ? "store" : "stores"}}
Manager · {r.manager}
{stores.length > 0 ? ( <>
Revenue MTD
{oMoney(rev)}
Scripts MTD
{scripts.toLocaleString()}
Patient base
{patients.toLocaleString()}
Stores {stores.map(s => ( {s.code} · {s.name} ))}
) : (
No stores assigned yet — drag a store here, or assign from the store detail.
)}
); })}
); } // ================================================================= // STOCK REQUESTS // ================================================================= function StockRequests() { const { pushToast } = window.useOffice(); const [filter, setFilter] = stExtUseState("active"); const [creating, setCreating] = stExtUseState(false); const FILTERS = [ { key: "active", label: "Active", pred: r => ["approved","in-transit","pending"].includes(r.status) }, { key: "pending", label: "Awaiting response", pred: r => r.status === "pending" }, { key: "fulfilled", label: "Fulfilled", pred: r => r.status === "fulfilled" }, { key: "declined", label: "Declined", pred: r => r.status === "declined" }, { key: "all", label: "All", pred: () => true }, ]; const list = ST_REQUESTS.filter(FILTERS.find(f => f.key === filter).pred); return ( <>
Stock requests are how a store asks another for stock — they need approval from the source store before becoming a transfer. Use these for unplanned needs (stock-out, patient waiting). For routine movements, create a transfer directly.
["approved","in-transit","pending"].includes(r.status)).length} delta={{dir:"up", text:"Awaiting fulfillment"}}/> r.priority === "urgent" && r.ts.includes("Today")).length} delta={{dir:"down", text:"Inter-store stock-outs"}}/>
{FILTERS.map(f => { const c = ST_REQUESTS.filter(f.pred).length; return ( ); })}
ID
From
To
Product · reason
Qty
Priority
Requested
Response
Status
{list.map(r => { const product = findP(r.productId); const from = findS(r.from); const to = findS(r.to); return (
{r.id}
{from?.code}
{to?.code}
{product?.name} · {product?.strength}
{r.reason}
{r.declineReason &&
Declined · {r.declineReason}
} {r.linkedTransfer &&
Linked to transfer {r.linkedTransfer}
}
{r.qty}
{r.priority === "urgent" && Urgent} {r.priority === "high" && High} {r.priority === "normal" && Normal}
{r.ts}
{r.requestedBy}
{r.responseTs}
{r.responder}
{r.status === "pending" && Awaiting} {r.status === "approved" && Approved} {r.status === "in-transit" && In transit} {r.status === "fulfilled" && Fulfilled} {r.status === "declined" && Declined}
); })}
{creating && setCreating(false)} onSubmit={() => { setCreating(false); pushToast({kind:'paid', icon:'check-circle', title:'Stock request sent', meta:'Source store will respond'}); }}/>} ); } function StockRequestModal({ onClose, onSubmit }) { const [from, setFrom] = stExtUseState("S-001"); const [to, setTo] = stExtUseState("S-002"); const [productId, setProductId] = stExtUseState("p008"); const [qty, setQty] = stExtUseState(12); const [priority, setPriority] = stExtUseState("normal"); const [reason, setReason] = stExtUseState(""); return (
e.stopPropagation()}>

Request stock from another store

setQty(parseInt(e.target.value) || 0)}/>