Skip to main content

MasterAdmin Enterprise Console — Design Spec

Date: 2026-06-15 Status: Design (approved in brainstorm) Supersedes/extends: 2026-06-12-network-operations-console-design.md (this is the full module-by-module superset; the prior spec’s Financial slice is already partly built and folds in unchanged). First test surface: ssh & Associates (shared app) as HQ/main branch + seeded sub-branches. Pattern also applies to isolated enterprise instances (DentoCorrect).

1. Problem

A multi-branch owner/Head of Operations cannot run a network from what we have. Today the Network Hub /network is a vanity rollup (today’s appointments, revenue, no-show rate, branch list) and the org-insights API exposes only /financial. The metrics engine has only financial.ts. Everything else still requires logging into each branch one at a time and hand-gathering reports — exactly the pain to kill. The Master Admin needs, across all branches at once: where the money is (billed vs collected vs outstanding), where it’s leaking (no-shows, overdue AR, low collection, expense bloat, lab delays, absenteeism), and which branch/doctor is underperforming — with one-click drill-down into the why and into the branch itself to act, plus targets they’re measured against and a Ruby layer that reads the network for them. The bar is “better than the generic CRM they’re using” — and the CRM knows nothing about a chair, a no-show, a treatment plan, or a dentist’s daily production.

2. Core insight — mostly reuse

Per-clinic analytics already compute the hard parts; they’re just filtered to a single clinicId. The console is the same queries run with clinicId IN (branchIds) and GROUP BY clinic (or doctor), plus presentation. ~90% reuse. The genuinely net-new pieces are: the Ruby network layer, predictive KPIs, and cross-branch suggestions (e.g. inventory rebalance).

3. Locked decisions (from brainstorm)

  1. Role — reuse existing org_admin, branded “Master Admin” (Head of Operations) in the UI. Sits above clinic admin, separate from platform superadmin — exactly the missing tier. branch_manager = same console scoped to one branch.
  2. Navigation — console at HQ altitude; branch rows are click-through into that branch’s full dashboard with a persistent ← Back to Network. The clinic switcher is kept as plumbing but demoted: it becomes the in-console scope selector (Viewing: Whole Network ▾ / <branch>). The standalone top-bar switcher stays only for non-enterprise multi-clinic owners (unchanged).
  3. Landing default — Master Admin lands in their clinic as today, and opts up to the Network console via the scope selector. Login experience does not change. (Contrast DentoCorrect, where HQ is admin-only and lands on the network hub.)
  4. Ruby — all four roles, phased: Digest + Branch-benchmarking in P1; Per-module strips + Predictive in P3. DeepSeek only, cost-disciplined (digest = one cached call/day per org).
  5. Demo seed — ssh & Associates = HQ; seed 3 DEMO--prefixed sub-branches with deliberately differentiated profiles; one-command teardown. Creating org tables + branch rows on shared prod is a live-tenant action → explicit per-action confirmation required at execution (no-live-tenant-execution rule).

4. What stays unchanged (de-risking the test tenant)

  • The ssh & Associates clinic and every module inside it — untouched. It remains a normal operating clinic.
  • Every other user in the clinic (doctors, reception, other admins) sees zero change; they never get the org layer.
  • The org layer is additive + reversible: a mapping (organization_clinics) + a role assignment (user_organization_assignments). Remove the assignment → plain clinic user again.
  • The shared per-clinic analytics (routes/analytics.ts, routes/reports.ts) are never touched — they also ship to the main app (prime directive: never destabilize the main app). The engine is NEW code, additive-only, parity-tested against those routes.

5. Approach (chosen: A)

  • A — Extend the metrics-engine pattern already started (server/src/lib/metrics/financial.ts + routes/org-insights.ts). One pure function per domain, each taking MetricScope { clinicIds[], from, to, groupBy? }, doing WHERE clinicId IN (...) GROUP BY clinic in SQL against the read replica (getReadDb() / ANALYTICS_DATABASE_URL). Additive-only; parity-tested so single-branch console == that branch’s own dashboard. Chosen — lowest risk, egress-disciplined, already in flight.
  • B — Materialized rollup tables fed by cron. Faster at scale but new infra + sync lag + drift. Overkill at 4–40 branches. Rejected.
  • C — Client-side fan-out to each branch’s per-clinic endpoints. N× egress, slow, violates egress rule. Rejected.

6. Architecture

6.1 Metrics engine (server/src/lib/metrics/)

Pure functions reading from the read replica, common options object:
interface MetricScope {
  clinicIds: string[];        // 1 = a branch; N = whole network
  from: string; to: string;   // PKT 'YYYY-MM-DD'
  groupBy?: 'clinic' | 'doctor';
}
One file per domain, each returns { totals, groups }:
  • financial.ts (exists) — billed, collected, outstanding (AR), AR aging 0–30/31–60/61–90/90+, collection rate, gross profit, margin, avg ticket, payment-method mix, status counts, trend.
  • appointments.ts (new) — booked/arrived/completed/cancelled/no_show/missed + rates; no-show PKR bleed = no_show × avg completed ticket; new vs returning; chair & doctor utilization = booked minutes ÷ capacity (doctor_schedules × working days, rooms for chairs).
  • treatments.ts (new) — plans created, pipeline value, acceptance/conversion %, avg plan value, completion rate, top procedures by volume & revenue, per-doctor acceptance.
  • patients.ts (new) — active patients, new (period), returning %, recall-due/overdue, churn risk (no visit 9mo+).
  • accounting.ts (new) — cash-basis net profit (receipts − direct cost − opex − payroll), opex by category, payroll total, expense/revenue ratio.
  • staff.ts (new) — headcount by role, attendance/absenteeism rate, present-today, on-leave, late arrivals, payroll cost, revenue-per-staff. Reads the new attendance/day-summary tables (kiosk punch system, feat/staff-hub-hr).
  • inventory.ts (new) — stock value on hand, low/out-of-stock SKU count, items expiring, consumption rate, reorder-needed; cross-branch rebalance candidates.
  • lab.ts (new) — open lab cases, avg turnaround (TAT), overdue, lab cost, by vendor.
All time filtering uses AT TIME ZONE 'Asia/Karachi' (PKT convention). Each engine ships with a parity test: engine(single branch) == existing per-clinic route output.

6.2 API surface (server/src/routes/org-insights.ts, mounted /api/v1/org/insights)

Middleware requireInsightsScope resolves c.get('insightsScope') = { clinicIds, level }:
  • org_admin → all organization_clinics for their org → level:'network'.
  • branch_manager → [assigned clinicId]level:'branch'.
Endpoints (all accept ?from&to, optional ?branch=<clinicId> to focus one branch within scope; out-of-scope branch= → 403):
  • GET /financial (exists), GET /appointments, GET /clinical, GET /patients, GET /accounting, GET /staff, GET /inventory-lab — each → { totals, perBranch[] }.
  • GET /command → KPI rollup + branch leaderboard + alerts (targets evaluated) + Ruby digest reference + live feed.
  • GET /ruby/digest → cached daily network digest (§7).
  • GET /ruby/branch/:clinicId → on-demand benchmarking commentary (§7).
  • GET /targets, PUT /targets (exist) — per-branch / network targets (org_admin only).
Scope is server-resolved, never trusted from client. Branch managers cannot widen scope by any parameter.

6.3 Targets + alerts

Existing app.ops_targets (organization_id, clinic_id nullable, metric, value, period). metric ∈ revenue_monthly | noshow_rate_max | collection_rate_min | utilization_min (extend as engines land). Alerts evaluator compares current-period actuals (engine output) to targets → { metric, clinicId, actual, target, severity }[], surfaced in Command Center + per-branch badges.

7. Module KPI catalog (the deliverable)

Console tabs map to engine domains. Each tab: network hero cards (KPIs), per-branch drill (leaderboard/table → click into branch dashboard), data source, Ruby hook. PKR currency, Banknote icon (never DollarSign), no raw clinic IDs surfaced.
TabNetwork hero cards / KPIsPer-branch drillData sourceRuby
Command CenterRuby network digest · Revenue MTD vs target · Collection % · No-show bleed PKR · Net profit · Alert strip (targets breached) · Branch leaderboard · Network live feedRank branches; flag worst no-show & ARall engines rolled upDigest (daily)
FinancialBilled · Collected · Outstanding AR · Collection rate % · Gross profit + margin · Avg ticket · AR aging 0–30/31–60/61–90/90+ · Payment-method mixPer-branch table + trend; click → branch Financial Hubinvoices, payments, receipts, quotationsBenchmark + digest
OperationsBooked→arrived→completed funnel · No-show / cancel rate · No-show PKR bleed · Chair & doctor utilization %Per-branch rates + bleed + utilization; per-doctorappointments, doctor_schedules, roomsPredict no-show risk
ClinicalPlans created · Pipeline value (proposed PKR) · Acceptance / conversion % · Avg plan value · Completion rate · Top procedures by vol & revenuePer-branch + per-doctor acceptancetreatment_plans, treatment_plan_procedures, quotationsBenchmark (“replicate top-acceptance branch”)
PatientsActive patients · New (period) · Returning % · Recall-due / overdue · Churn risk (no visit 9mo+)Per-branch growth + retentionpatients, appointmentsPredict churn list
AccountingCash-basis Net profit · Opex by category · Payroll total · Expense / revenue ratioPer-branch P&L comparisonexpenses, payroll, receiptsBenchmark expense bloat
StaffHeadcount by role · Attendance / absenteeism · Present today · On leave · Late arrivals · Payroll cost · Revenue-per-staffPer-branch roster + attendance + productivityattendance/day-summary (kiosk), leave, payroll, usersBenchmark
Inventory & LabStock value on hand · Low/out-of-stock SKUs · Items expiring · Open lab cases · Avg lab TAT · Overdue lab · Lab costPer-branch stock + low-stock; lab TAT by vendor; rebalance (X overstocked, Y short)inventory, lab_trackingBenchmark + transfer suggestion
Settings → TargetsPer-branch + network target editorops_targets
Lower-value-for-rollup modules (clinical notes, dental chart, prescriptions, patient files, medicine library) are not their own tabs; they may surface later as “documentation completeness” signals — not v1.

8. Ruby layer (all four, phased)

  • Network digest (P1): one DeepSeek call/day per org, cached; reads rolled-up engine output → narrative + anomalies + 3 ranked actions on Command Center. Highest signal, bounded cost.
  • Branch benchmarking (P1): on-demand when a branch is opened → ranks that branch’s drivers vs network medians (gaps computed in SQL; Ruby narrates).
  • Per-module insight strips (P3): one short read per tab, cached per range.
  • Predictive KPIs (P3): month-end revenue projection, no-show risk, churn risk. Net-new modeling; validated before surfacing as numbers.
Ruby surfaces are branded “Ruby” with the Ruby logo (never “AI”/“Action Items”). Prompts versioned in Langfuse; tracing on HIPAA cloud.

9. UI / navigation model

  • One app, two altitudes governed by the scope selector (the demoted switcher): Viewing: <branch> ▾ default (normal clinic UI) → toggle ▲ Whole Network = console tabs.
  • Console tabs render premium KPI cards (sparklines + vs-target deltas), branch leaderboard, charts — not plain shadcn wraps. Visual pass required before “done” (reload + screenshot / Playwright).
  • Every branch row → that branch’s existing dashboard (clinic-context + hard reload, reusing switcher plumbing) with persistent ← Back to Network.
  • Branch managers see the same tabs scoped to their branch (leaderboard collapses to “your branch vs network average”).

10. Demo seed plan (removable)

ssh & Associates = HQ/main branch. Seed 3 sub-branches, deliberately differentiated so the leaderboard + benchmarking visibly earn their keep:
  • DEMO-Gulberg — underperformer: no-show ~19%, heavy 60d+ AR, expense bloat, low utilization.
  • DEMO-Johar-Town — strong: on-target revenue, high treatment acceptance, low no-show.
  • DEMO-Karachi — mid: insurance-claim AR drag.
Each branch seeded across patients, appointments, invoices/payments, expenses, lab cases, staff attendance — enough for every P1/P2 KPI to compute non-trivially. All rows DEMO- prefixed → one-command teardown. Prerequisite: org tables (organizations, organization_clinics, user_organization_assignments, ops_targets) and any drifted columns must exist on the shared prod DB — ensured via idempotent CREATE TABLE IF NOT EXISTS ensure-steps (sidebar-order / ensureOpsTargets precedent). Touching shared prod = explicit per-action confirmation at execution.

11. Error handling

  • Empty branch set → zeroed totals + empty table (never throw), mirrors current /org/overview.
  • Out-of-scope branch= → 403; branch managers cannot widen scope.
  • Missing reused table/column (pre-sync) → endpoint returns typed degraded: true + which module is unavailable → UI shows “needs setup” instead of 500.
  • Ruby call failure → console renders fully without the digest/strip (Ruby is enhancement, never a hard dependency).
  • All queries wrapped in handleError per route convention.

12. Testing

  • Unit: each engine with a fixture of 2 clinics × known data → assert totals + per-branch split + boundary math (AR aging buckets, no-show bleed, utilization, TAT, attendance rate).
  • Scope: branch_manager cannot read another branch (branch= 403; absent → own clinic only).
  • Parity: per-clinic dashboard numbers == console single-branch numbers (proves the shared engine).
  • Smoke: the seeded ssh & Associates network is the live smoke target; eyeball each tab (visual pass) before calling UI done.

13. Phasing

  • P1 (spine — money & leaks): Command Center + Ruby digest + Branch benchmarking + Financial + Operations + Accounting. (Financial half-built.)
  • P2: Clinical + Patients + Staff (leans on the just-shipped attendance kiosk).
  • P3: Inventory & Lab + per-module insight strips + Predictive + Communication/WA automation-gap signals.

14. Out of scope (this spec)

  • Doctor commissions (net-new finance subsystem) — later.
  • Cross-branch patient transfer (patients stay branch-local).
  • Self-serve enterprise signup (manual provisioning only).
  • Converging the legacy per-clinic routes onto the shared engine (separate, separately-verified step — not part of this build).