diff --git a/apps/hq-web/src/Layout.tsx b/apps/hq-web/src/Layout.tsx index f2fec9a..436bdef 100644 --- a/apps/hq-web/src/Layout.tsx +++ b/apps/hq-web/src/Layout.tsx @@ -45,7 +45,7 @@ export function Layout() { fetch('/api/health').then((r) => setHealthy(r.ok)).catch(() => setHealthy(false)) }, []) - // Reminder-queue badge (Dashboard nav item) — refreshed on every route change. + // Reminder-queue badge (Reminders nav item) — refreshed on every route change. // Uses the honest scoped `total` (never rows.length): staff badge counts match // their own queue, and pagination cannot under-count. useEffect(() => { diff --git a/apps/hq-web/src/pages/Modules.tsx b/apps/hq-web/src/pages/Modules.tsx index f629395..154ef09 100644 --- a/apps/hq-web/src/pages/Modules.tsx +++ b/apps/hq-web/src/pages/Modules.tsx @@ -27,6 +27,20 @@ export function Modules() { const modules = useData(getModules, []) const [creating, setCreating] = useState(false) const [selected, setSelected] = useState() + // How many clients sit on each module — one lightweight roster count per module, + // best-effort so a slow/failed one never blocks the table (cell shows '…' until then). + const [clientCounts, setClientCounts] = useState>({}) + useEffect(() => { + const ms = modules.data + if (ms === undefined) return + let live = true + ms.forEach((m) => { + getModuleClients(m.id, 1) + .then((r) => { if (live) setClientCounts((c) => ({ ...c, [m.id]: r.total })) }) + .catch(() => { /* best-effort */ }) + }) + return () => { live = false } + }, [modules.data]) return (
@@ -43,12 +57,14 @@ export function Modules() { setSelected(modules.data![i])} rows={modules.data.map((m) => ({ code: m.code, name: m.name, sac: m.sac, + clients: clientCounts[m.id] !== undefined ? clientCounts[m.id] : '…', kinds: m.allowedKinds.map((k) => KIND_LABEL[k]).join(' · '), multi: m.multiSubscription ? yes : '—', active: m.active ? active : inactive,