/* ============================================================= CoreIQ Office — Head-office modules: Payment accounts · Promotions · Loyalty ============================================================= */ const { useState: hoUseState, useMemo: hoUseMemo } = React; // ----------------------------------------------------------------- // Sample data for accounts, promos, loyalty // ----------------------------------------------------------------- const HO_ACCOUNTS = [ { id: "ACC-1042", type: "business", name: "Camberwell Aged Care Facility", abn: "49 122 884 207", store: "S-001", terms: "Net 30", creditLimit: 15000, balance: 8420.65, status: "active", overdueDays: 0, monthlyAvg: 9840, contact: "Joanne Pereira", authorised: 24, lastCharge: "Today 14:12" }, { id: "ACC-1108", type: "business", name: "Whitehorse Family Medical Centre", abn: "62 008 117 003", store: "S-001", terms: "Net 14", creditLimit: 5000, balance: 1280.50, status: "active", overdueDays: 0, monthlyAvg: 2240, contact: "Dr S. Kapoor", authorised: 0, lastCharge: "Yesterday" }, { id: "ACC-1214", type: "business", name: "Bridgewater NDIS Services", abn: "98 642 119 220", store: "S-002", terms: "Net 30", creditLimit: 10000, balance: 4152.00, status: "active", overdueDays: 0, monthlyAvg: 4200, contact: "Marta Voss", authorised: 7, lastCharge: "Today 11:30" }, { id: "ACC-1018", type: "person", name: "Whitlock, Henry", abn: "—", store: "S-001", terms: "Monthly", creditLimit: 1500, balance: 890.40, status: "overdue", overdueDays: 12, monthlyAvg: 410, contact: "Henry Whitlock", authorised: 1, lastCharge: "3 days ago" }, { id: "ACC-1320", type: "person", name: "Costa, Ana", abn: "—", store: "S-003", terms: "Net 30", creditLimit: 300, balance: 145.20, status: "active", overdueDays: 0, monthlyAvg: 160, contact: "Ana Costa", authorised: 1, lastCharge: "Yesterday" }, { id: "ACC-1190", type: "business", name: "Burke Road Community House", abn: "11 220 401 558", store: "S-001", terms: "Net 30", creditLimit: 2000, balance: 1980.00, status: "over-limit", overdueDays: 0, monthlyAvg: 1840, contact: "Linh Tran", authorised: 8, lastCharge: "Today 13:02" }, { id: "ACC-1488", type: "business", name: "Yarra River Disability Support", abn: "76 119 882 044", store: "S-002", terms: "Net 30", creditLimit: 4000, balance: 0, status: "hold", overdueDays: 0, monthlyAvg: 0, contact: "Aleksei Markov", authorised: 0, lastCharge: "—" }, { id: "ACC-1611", type: "business", name: "Eastland Medical Suites", abn: "33 991 002 117", store: "S-003", terms: "Net 14", creditLimit: 7500, balance: 3210.40, status: "active", overdueDays: 0, monthlyAvg: 3680, contact: "Dr R. Patel", authorised: 12, lastCharge: "Today 10:48" }, { id: "ACC-1612", type: "business", name: "Glen Iris Day Centre", abn: "55 880 117 022", store: "S-002", terms: "Net 30", creditLimit: 6000, balance: 5418.20, status: "active", overdueDays: 0, monthlyAvg: 5240, contact: "Helen Yeo", authorised: 14, lastCharge: "Today 11:48" }, { id: "ACC-1718", type: "person", name: "Sharma, Priya", abn: "—", store: "S-001", terms: "Net 30", creditLimit: 500, balance: 88.10, status: "active", overdueDays: 0, monthlyAvg: 120, contact: "Priya Sharma", authorised: 1, lastCharge: "Yesterday" }, ]; const HO_PROMOS = [ { id: "PR-2026-001", name: "Summer Saver · Skincare", type: "percent", value: 30, target: "category", targetLabel: "Skincare", stores: ["S-001","S-002","S-003"], starts: "20/05/2026", ends: "31/05/2026", redemptions: 142, revenue: 2480.40, status: "active", priority: 1, requiresLoyalty: false }, { id: "PR-2026-002", name: "Loyalty $5 off · Spend > $30", type: "amount", value: 5, target: "minSpend", targetLabel: "Spend ≥ $30", stores: ["S-001","S-002","S-003"], starts: "01/05/2026", ends: "30/06/2026", redemptions: 280, revenue: 1400.00, status: "active", priority: 2, requiresLoyalty: true }, { id: "PR-2026-003", name: "Vitamins · Buy 2 save $4", type: "bogo", value: 4, target: "category", targetLabel: "Vitamins · 2+", stores: ["S-001","S-002","S-003"], starts: "01/05/2026", ends: "31/05/2026", redemptions: 88, revenue: 1742.30, status: "active", priority: 3, requiresLoyalty: false }, { id: "PR-2026-004", name: "Hayfever bundle", type: "amount", value: 3, target: "bundle", targetLabel: "Loratadine + Cetirizine", stores: ["S-001","S-002"], starts: "12/05/2026", ends: "10/06/2026", redemptions: 24, revenue: 412.20, status: "active", priority: 4, requiresLoyalty: false }, { id: "PR-2026-005", name: "Mother's Day skincare", type: "percent", value: 20, target: "category", targetLabel: "Skincare", stores: ["S-001","S-002","S-003"], starts: "01/05/2026", ends: "12/05/2026", redemptions: 312, revenue: 6420.80, status: "ended", priority: 1, requiresLoyalty: false }, { id: "PR-2026-006", name: "Winter Wellness · Coming", type: "percent", value: 15, target: "category", targetLabel: "Cold & flu", stores: ["S-001","S-002","S-003"], starts: "01/06/2026", ends: "31/07/2026", redemptions: 0, revenue: 0, status: "scheduled", priority: 1, requiresLoyalty: false }, { id: "PR-2026-007", name: "NDSS pen-needle saver", type: "amount", value: 1, target: "product", targetLabel: "BD Pen Needles", stores: ["S-001","S-002","S-003"], starts: "01/04/2026", ends: "31/12/2026", redemptions: 188, revenue: 188.00, status: "active", priority: 5, requiresLoyalty: false }, { id: "PR-2026-008", name: "Camberwell only · Spacers", type: "percent", value: 10, target: "product", targetLabel: "Salbutamol Spacer", stores: ["S-001"], starts: "15/05/2026", ends: "31/05/2026", redemptions: 12, revenue: 168.00, status: "active", priority: 6, requiresLoyalty: false }, ]; const HO_LOYALTY_TIERS = [ { id: "bronze", name: "Bronze", members: 1842, color: "#a0683e", spendThreshold: 0, discount: 2, perks: ["2% off non-PBS", "Birthday treat"] }, { id: "silver", name: "Silver", members: 642, color: "#7d9097", spendThreshold: 400, discount: 5, perks: ["5% off non-PBS", "Free delivery on $50+", "Birthday treat"] }, { id: "gold", name: "Gold", members: 118, color: "#b8893e", spendThreshold: 1200, discount: 5, perks: ["5% off non-PBS", "Free delivery", "Priority counsel", "Bonus 2× points"] }, ]; const HO_LOYALTY_KPI = { totalMembers: 2602, newThisMonth: 184, activeRate: 0.62, avgBasketLift: 14, monthlyRedemption: 2840, }; // ================================================================= // PAYMENT ACCOUNTS — list // ================================================================= function OfficeAccounts() { const { setScreen, setScreenState, pushToast } = window.useOffice(); const [filter, setFilter] = hoUseState("all"); const [q, setQ] = hoUseState(""); const FILTERS = [ { key: "all", label: "All", pred: () => true }, { key: "business", label: "Business", pred: a => a.type === "business" }, { key: "person", label: "Person", pred: a => a.type === "person" }, { key: "overdue", label: "Overdue", pred: a => a.status === "overdue" }, { key: "over-limit", label: "Over limit", pred: a => a.status === "over-limit" }, { key: "hold", label: "On hold", pred: a => a.status === "hold" }, ]; const list = HO_ACCOUNTS.filter(a => { if (!FILTERS.find(f => f.key === filter).pred(a)) return false; if (!q) return true; return (a.name + " " + a.id + " " + (a.abn || "")).toLowerCase().includes(q.toLowerCase()); }); const totals = { receivable: HO_ACCOUNTS.reduce((s, a) => s + a.balance, 0), overdue: HO_ACCOUNTS.filter(a => a.status === "overdue").reduce((s, a) => s + a.balance, 0), overLimit: HO_ACCOUNTS.filter(a => a.status === "over-limit").length, business: HO_ACCOUNTS.filter(a => a.type === "business").length, person: HO_ACCOUNTS.filter(a => a.type === "person").length, }; return (
Head-office function

Payment accounts

setQ(e.target.value)} style={{height:34, fontSize:13}}/>
{FILTERS.map(f => { const c = HO_ACCOUNTS.filter(f.pred).length; return ( ); })}
{list.length} of {HO_ACCOUNTS.length}
Account
Holder
Type
Home store
Terms
Available · Limit
Balance
Status
{list.map(a => { const used = a.creditLimit > 0 ? Math.min(1, a.balance / a.creditLimit) : 0; const usedPct = Math.round(used * 100); const store = findS(a.store); return (
{ setScreenState({ activeAccountId: a.id }); setScreen("accountDetail"); }}>
{a.id}
{a.name}
{a.type === "business" ? `ABN ${a.abn}` : a.contact}
{a.type === "business" ? "Business" : "Person"}
{store?.code} · {store?.name}
{a.terms}
{oMoney(Math.max(0, a.creditLimit - a.balance))} of {oMoney(a.creditLimit)}
95 ? " inv-stockbar--bad" : usedPct > 75 ? " inv-stockbar--warn" : "")}>
{oMoney(a.balance)}
{a.status === "active" && Active} {a.status === "overdue" && Overdue · {a.overdueDays}d} {a.status === "over-limit" && Over limit} {a.status === "hold" && On hold}
); })}
); } // ================================================================= // PAYMENT ACCOUNT — detail (compact admin view) // ================================================================= function OfficeAccountDetail() { const { setScreen, screenState, pushToast } = window.useOffice(); const a = HO_ACCOUNTS.find(x => x.id === screenState.activeAccountId) || HO_ACCOUNTS[0]; const usedPct = a.creditLimit > 0 ? Math.round((a.balance / a.creditLimit) * 100) : 0; return (
Account · {a.id}

{a.name}

95 ? "down" : "up", text: usedPct > 95 ? "Over limit" : "Within terms"}}/>

Recent activity

When
Type
Description
By
Amount
Balance
{[ { ts:"Today 14:12", kind:"charge", who:"SK", desc:"Metformin XR · M. Tan (room 14)", amt: 31.40, bal: a.balance }, { ts:"Today 11:48", kind:"charge", who:"RP", desc:"Dispense ×3 · Ana Costa (room 09)", amt: 92.20, bal: a.balance - 31.40 }, { ts:"Today 10:14", kind:"charge", who:"JL", desc:"DAA refill week 22 · 12 residents", amt: 144.00, bal: a.balance - 123.60 }, { ts:"Yesterday", kind:"charge", who:"SK", desc:"Webster pack · H. Whitlock (room 22)", amt: 84.00, bal: a.balance - 267.60 }, { ts:"20/05/2026", kind:"charge", who:"JL", desc:"Dispense ×6 · weekly batch", amt: 412.65, bal: a.balance - 351.60 }, { ts:"18/05/2026", kind:"credit", who:"SK", desc:"Damaged stock credit · S. Wilson", amt: -28.20, bal: a.balance - 764.25 }, { ts:"12/05/2026", kind:"payment", who:"—", desc:"Payment received · BPay", amt: -4800.00, bal: a.balance - 736.05 }, ].map((t, i) => (
{t.ts}
{t.kind === "charge" && Charge} {t.kind === "payment" && Payment} {t.kind === "credit" && Credit}
{t.desc}
{t.who}
{oMoney(t.amt)}
{oMoney(t.bal)}
))}

Account details

{a.abn !== "—" && }
{a.status === "overdue" && (

Overdue · {a.overdueDays} days

Charges blocked until payment received. SMS reminder sent — no response.
)}

Quick actions

); } function Row({ label, value, mono }) { return (
{label} {value}
); } // ================================================================= // PROMOTIONS — list + designer // ================================================================= function Promotions() { const { setScreen, setScreenState, pushToast } = window.useOffice(); const [tab, setTab] = hoUseState("active"); const FILTER = { active: p => p.status === "active", scheduled: p => p.status === "scheduled", ended: p => p.status === "ended", all: () => true, }; const list = HO_PROMOS.filter(FILTER[tab]); const totals = { redemptionsMTD: HO_PROMOS.reduce((s, p) => s + p.redemptions, 0), revenue: HO_PROMOS.reduce((s, p) => s + p.revenue, 0), active: HO_PROMOS.filter(p => p.status === "active").length, scheduled: HO_PROMOS.filter(p => p.status === "scheduled").length, }; return (
Head-office function

Promotions

Promotion
Discount
Applies to
Stores
Dates
Redeems
Revenue
Status
{list.map(p => (
{ setScreenState({ activePromoId: p.id }); setScreen("promoNew"); }}>
{p.name}
{p.id}
{p.type === "percent" ? `${p.value}%` : p.type === "amount" ? `$${p.value} off` : p.type === "bogo" ? `$${p.value} bundle` : `$${p.value}`}
{p.targetLabel}{p.requiresLoyalty && Loyalty}
{p.stores.length === 3 ? "All stores" : p.stores.map(id => findS(id)?.code).join(", ")}
{p.starts} → {p.ends.split("/").slice(0,2).join("/")}
{p.redemptions}
{p.revenue > 0 ? oMoney(p.revenue) : "—"}
{p.status === "active" && Active} {p.status === "scheduled" && Scheduled} {p.status === "ended" && Ended}
))}
); } function PromoNew() { const { setScreen, pushToast, screenState } = window.useOffice(); const editing = screenState.activePromoId ? HO_PROMOS.find(p => p.id === screenState.activePromoId) : null; const [form, setForm] = hoUseState(editing || { name: "", type: "percent", value: 10, target: "category", targetLabel: "Vitamins", stores: ["S-001","S-002","S-003"], starts: "27/05/2026", ends: "30/06/2026", requiresLoyalty: false, priority: 5, }); function set(k, v) { setForm(f => ({ ...f, [k]: v })); } function toggleStore(id) { setForm(f => ({ ...f, stores: f.stores.includes(id) ? f.stores.filter(x => x !== id) : [...f.stores, id] })); } function submit() { pushToast({ kind:'paid', icon:'check-circle', title:`Promotion ${editing ? 'updated' : 'created'} · ${form.name}`, meta: `${form.type === "percent" ? form.value + "%" : "$" + form.value + " off"} · ${form.stores.length} stores` }); setScreen("promotions"); } return (
Promotions

{editing ? `Edit · ${editing.name}` : "New promotion"}

1

Name & identification

set("name", e.target.value)} placeholder="e.g. Winter Wellness 15% off" autoFocus/>
2

Discount

set("value", parseFloat(e.target.value) || 0)} placeholder={form.type === "percent" ? "15" : "5.00"}/>
3

Applies to

{form.target === "category" ? ( ) : ( set("targetLabel", e.target.value)}/> )}
4

Stores & dates

{OD.STORES.map(s => ( ))}
set("starts", e.target.value)}/>
set("ends", e.target.value)}/>
{/* Right — preview */}
Preview · shelf flag
Special
{form.type === "percent" ? `${form.value}%` : `$${form.value}`}
{form.type === "percent" ? "off" : "off"} · {form.targetLabel}
Until {form.ends}{form.requiresLoyalty && " · Loyalty members"}
When it goes live
{form.name || "This promotion"} will activate at {form.stores.length} {form.stores.length === 1 ? "store" : "stores"} from {form.starts} to {form.ends}.

POS will auto-apply at checkout. Receipts show the line-item discount.
); } // ================================================================= // LOYALTY // ================================================================= function Loyalty() { const { pushToast } = window.useOffice(); const [tab, setTab] = hoUseState("tiers"); return (
Head-office function

Loyalty programme

{tab === "tiers" && (
{HO_LOYALTY_TIERS.map(t => (
{t.name}
{t.members.toLocaleString()} members
Spend threshold ${t.spendThreshold}/year
Standard discount {t.discount}%
Perks
    {t.perks.map((p, i) =>
  • {p}
  • )}
))}
)} {tab === "earn" && (

Points & earn rules

Earn rate
Points per dollar spent on non-PBS items.
PBS items
Whether PBS prescriptions earn points.
Points expiry
After how long unused points expire.
Birthday bonus
Double points in member's birthday month.
Sign-up bonus
Points credited to new members.
)} {tab === "rewards" && (
Reward
Points cost
Tier required
Type
Redeems
Status
{[ { name: "$5 off voucher", cost: 100, tier: "Any", type: "Voucher", redeems: 482, status: "active" }, { name: "$10 off voucher", cost: 200, tier: "Any", type: "Voucher", redeems: 312, status: "active" }, { name: "Free delivery", cost: 150, tier: "Any", type: "Service", redeems: 188, status: "active" }, { name: "Multivitamin · free", cost: 400, tier: "Silver+", type: "Product", redeems: 22, status: "active" }, { name: "MedsCheck consult", cost: 600, tier: "Gold", type: "Service", redeems: 8, status: "active" }, { name: "Sunscreen bundle", cost: 300, tier: "Any", type: "Product", redeems: 0, status: "scheduled" }, ].map((r, i) => (
{r.name}
{r.cost}
{r.tier}
{r.type}
{r.redeems}
{r.status === "active" ? Active : Scheduled}
))}
)} {tab === "members" && (
Member
Home store
Tier
Points
YTD spend
Last visit
{[ { id:"u01", first:"Mei", last:"Tan", tier:"Silver", store:"S-001", points: 432, ytd: 1240, lastVisit:"3 days ago" }, { id:"u02", first:"Jordan", last:"Riley", tier:"Bronze", store:"S-001", points: 88, ytd: 320, lastVisit:"Today" }, { id:"u03", first:"Ana", last:"Costa", tier:"Gold", store:"S-003", points: 1840,ytd: 2410, lastVisit:"Yesterday" }, { id:"u05", first:"Priya", last:"Sharma", tier:"Silver", store:"S-001", points: 318, ytd: 680, lastVisit:"5 days ago" }, { id:"u06", first:"Nadia", last:"Khoury", tier:"Bronze", store:"S-001", points: 142, ytd: 220, lastVisit:"Today" }, { id:"u09", first:"Henry", last:"Whitlock", tier:"Gold", store:"S-001", points: 2210,ytd: 3120, lastVisit:"2 days ago" }, { id:"u10", first:"Beatrix", last:"Lim", tier:"Bronze", store:"S-002", points: 64, ytd: 140, lastVisit:"Today" }, ].map(m => { const tier = HO_LOYALTY_TIERS.find(t => t.name === m.tier); return (
{(m.first[0]+m.last[0]).toUpperCase()}
{m.first} {m.last}
{findS(m.store)?.code} · {findS(m.store)?.name}
{m.tier}
{m.points.toLocaleString()}
${m.ytd.toLocaleString()}
{m.lastVisit}
); })}
)} {tab === "campaigns" && (
Campaign
Channel
Audience
Sent
Redeems
Status
{[ { name: "Mother's Day · Gold members", ch: "SMS", aud: "Gold", sent: "08/05/2026", reds: 64, status: "complete" }, { name: "Bronze re-engage · no visit 60d", ch: "Email", aud: "Bronze · inactive", sent: "15/05/2026", reds: 38, status: "complete" }, { name: "Hayfever bundle · all members", ch: "SMS", aud: "All", sent: "12/05/2026", reds: 24, status: "complete" }, { name: "Winter Wellness · launch", ch: "Email",aud: "All", sent: "Scheduled 01/06", reds: 0, status: "scheduled" }, { name: "Birthday bonus · May", ch: "SMS", aud: "Birthdays", sent: "01/05/2026", reds: 122, status: "complete" }, ].map((c, i) => (
{c.name}
{c.ch}
{c.aud}
{c.sent}
{c.reds}
{c.status === "complete" ? Complete : Scheduled}
))}
)}
); } window.OFFICE_SCREENS = Object.assign(window.OFFICE_SCREENS || {}, { accounts: OfficeAccounts, accountDetail: OfficeAccountDetail, promotions: Promotions, promoNew: PromoNew, loyalty: Loyalty, });