medOS ultra

Specialty-Clinic Order System Miniapps

Per-clinic Order System surface for every OPD specialty clinic.

7 min read diagramsUpdated 2026-05-29docs/architecture/clinic-order-system-miniapps.md

Status: Phase 1–2 in progress (2026-05-29). Goal: a per-clinic “Order System” surface for every OPD specialty clinic where a user picks one of the clinic’s dayProcedures, opens an order dialog (with STAT / Urgent / Routine + clinic addon fields), submits, and watches the order land in that clinic’s procedure day-queue.

Decisions (locked 2026-05-29)

  1. Surface: sandbox targets first (this work), then Core App Dialogs integration + an upgraded Core App launcher (orthogonal filters, full-screen view, menu search) — Phase 3, next.
  2. Scope: cover all 13 clinics (15 configs) — table below is the source of truth so nothing is forgotten. The reusable component is config-driven, so each clinic is “free” once the component works.
  3. Queue behaviour: optimistic + live merge — an optimistic row appears immediately on submit (so the flow is visible offline in the sandbox) AND useProcedureDayQueue realtime rows supersede it when wired to a live Supabase backend (dedupe by procedure_request_id, live wins).

Plumbing facts (verified, do not re-derive)

  • Order dialog: web/packages/miniapps/specialty-clinics/OpdProcedureRequestDialog.tsx — already has the STAT/Urgent/Routine radio group + phase-grouped addon fields. Fires useRecordProcedureEvent.record({ eventType:'requested', departmentId: subDeptId, priority, … }).
  • Write hook: @/services/procedure-day-queue/useRecordProcedureEventsupabase.rpc('record_procedure_event'). Reads state.user.user via useSelector (needs a Redux Provider). Returns { ok }; returns {ok:false} when Supabase env is absent (sandbox) — so the dialog’s onSubmitted does NOT fire offline unless we opt in via the new demoOptimistic prop.
  • Read hook: @/services/procedure-day-queue/useProcedureDayQueue({ deptId, day }) → realtime on procedure_day_queue filtered by department_id. Returns { rows, loading, error, refetch, connected }. Row type ProcedureDayQueueRow (status: pending|accepted|waiting|operating|completed|cancelled|postponed).
  • Sandbox providers = React Query + MUI + Router only (no Redux/auth/i18n). A target must wrap its own minimal Redux store (see OpdProcedureRequestDialogTarget.tsx). Target key parsed from window.location.search (?target=) in sandbox/main.tsx; extra params like &clinic= are readable by the target.
  • Key collision: palliative-care is used by BOTH the Clinic and the Center configs; getSpecialtyClinicConfig('palliative-care') returns the Clinic (first match). Iterate SPECIALTY_CLINIC_CONFIGS directly to reach both.

Per-clinic coverage (all 15 configs)

Add = sandbox order-system reachable (?target=ClinicOrderSystem&clinic=<key>). Verify = ordered a procedure, optimistic row appeared in the day-queue panel.

# Clinic key category subDeptId preset #proc #addons Add Verify
1 GI Endoscopy gi-endoscopy clinic 0e42fb73-7112-4d09-8e6b-b18802da3bd6 full-or 5 9 [ ] [ ]
2 Hemodialysis hemodialysis clinic 91d1f806-3f36-4e54-ac27-d8a109f73710 minor-ops 4 15 [ ] [ ]
3 Psychiatry psychiatry clinic 605d9544-dc29-4c36-9c88-bbb6253b8f21 3 0 [ ] [ ]
4 Family Medicine family-medicine clinic fc1a0001-0001-4000-a000-000000000001 3 0 [ ] [ ]
5 Palliative Care (Clinic) palliative-care clinic fc1a0001-0003-4000-a000-000000000003 3 0 [ ] [ ]
6 Elderly elderly clinic fc1a0001-0005-4000-a000-000000000005 3 0 [ ] [ ]
7 Holter Monitoring holter-monitoring diagnostic_device cadba3b9-40e2-4f9e-8f2d-da3ab1704b28 custom 3 11 [ ] [ ]
8 ABP Monitoring abp diagnostic_device cadba3b9-40e2-4f9e-8f2d-da3ab1704b28 shared custom 2 13 [ ] [ ]
9 Respiratory/Thoracic respiratory-thoracic specialized_unit 88e4977c-72c5-4fcc-b227-f5e136713055 5 0 [ ] [ ]
10 Antenatal Care antenatal-care specialized_unit 041c90e4-e750-4d85-a0c1-98d40f997e9e 4 0 [ ] [ ]
11 Diabetes Registry diabetes-registry specialized_unit 8df92e78-0823-4908-b8ac-b24ccd3f3276 4 0 [ ] [ ]
12 Vaccination vaccination other_service fc1a0001-0006-4000-a000-000000000006 safety-only 3 13 [ ] [ ]
13 Palliative Care Center palliative-care collides other_service fc1a0001-0002-4000-a000-000000000002 3 0 [ ] [ ]
14 Home Isolation home-isolation other_service c979fb20-8fcd-4a11-a615-4a3a12d47fd5 2 0 [ ] [ ]
15 Home Visit home-visit other_service sc-placeholder-home-visit-0001 placeholder 3 0 [ ] [ ]

Clinics with 0 addons still order fine — the dialog submits the canonical ProcedureRequest fields (code, name, priority) and the row lands in that subDept’s queue; there are just no pre/intra/post addon fields to fill.


Phase 1 — Component + dialog (DONE 2026-05-29)

  • [x] Dialog OpdProcedureRequestDialog.tsx — additive optional props (existing callers unaffected):
    • [x] procedureChoices?: {code,nameTh,nameEn}[] → render a Select (auto-fills code+name) vs free-text
    • [x] initialProcedureCode? / initialProcedureName? → seed state (re-seeds on open via useEffect)
    • [x] demoOptimistic?: boolean → on record() not-ok, still fire onSubmitted({…, offline:true}) + close
    • [x] onSubmitted payload gains optional offline?: boolean
  • [x] Component ClinicOrderSystem.tsx (new, reusable, in the specialty-clinics package):
    • [x] Left: config.dayProcedures picker → opens the dialog with that procedure preselected
    • [x] Right: useProcedureDayQueue({deptId: config.subDeptId}) rows + optimistic rows, deduped by id
    • [x] Tabs Active / Done / Cancelled; per-row advance (pending→accepted→operating→completed) + cancel for optimistic rows
    • [x] connected/offline badge + counts; STAT rows sort to top with red tint
  • [x] Barrel index.ts exports ClinicOrderSystem

Phase 2 — Sandbox targets (all 13, DONE 2026-05-29)

  • [x] web/sandbox/targets/ClinicOrderSystemTarget.tsx — minimal Redux store; reads ?clinic=<key>; hub grid + search of all 15 configs when absent; full clinic order system when present
  • [x] Register ClinicOrderSystem in web/sandbox/registry.ts
  • [x] Per-clinic reach: the hub lists all 15 configs (verified via snapshot — counts + presets + placeholder warning all correct). One generic component covers every clinic, so Add = done for all via ?target=ClinicOrderSystem&clinic=<key>.

Verification log (2026-05-29, sandbox port 5188)

  • Hub ?target=ClinicOrderSystem — renders all 15 clinics with correct category/preset/proc/addon counts + Home Visit placeholder warning. — verified
  • GI Endoscopy ?clinic=gi-endoscopy — order system renders; clicking EGD opens the dialog with EGD preselected in the new procedure dropdown, 9 addons phase-grouped, STAT/Urgent/Routine present. Submit correctly disabled until required pre-procedure addons (scope_id/type/sedation) are filled. — verified
  • Psychiatry ?clinic=psychiatry — ordered ECT (no required addons → submit enabled): queue went 0 → 2 active across two submits, rows show HN/name/PSY-ECT/ROUTINE/Pending/time. — verified
  • Live-backend finding: this sandbox has VITE_SUPABASE_URL/ANON_KEY set → the queue badge reads live, the record_procedure_event RPC actually succeeded, and real procedure_day_queue rows arrived via realtime. The optimistic→live “live wins” dedupe was exercised for real (a locally-advanced optimistic row is superseded by the backend row — backend owns lifecycle, which is correct). Note: two demo ECT rows for HN 66000123 (สมชาย ใจดี) were written to the live procedure_day_queue under psychiatry subDept 605d9544… — harmless additive demo data; clean up if undesired.

Phase 3 — Core App Dialogs + launcher upgrade (NEXT, not this work)

Per the locked decision: after the sandbox flow is proven, surface these in the real Core App.

  • [ ] Register an Order-System module per clinic in DynamicCoreApp enum + DynamicContentRenderer (mount ``, real backend), with icon/color in web/src/utils/properties/coreApp.tsx
  • [ ] Core App launcher upgrade:
    • [ ] Orthogonal filters (e.g. by clinic category × procedure tier × dept type) over the module menu
    • [ ] Full-screen view of the launcher
    • [ ] Search across Core App menu entries
  • [ ] Surface the per-clinic order systems in web/src/containers/specialty-clinic/page.tsx hub

Key files

File Role
web/packages/miniapps/specialty-clinics/ClinicOrderSystem.tsx NEW reusable order-system (picker + optimistic-merge queue)
web/packages/miniapps/specialty-clinics/OpdProcedureRequestDialog.tsx order dialog (STAT/urgency/addons) — enhanced with preselect + demoOptimistic
web/packages/miniapps/specialty-clinics/clinic-configs.ts the 15 clinic configs (dayProcedures + addons + subDeptId)
web/sandbox/targets/ClinicOrderSystemTarget.tsx NEW sandbox wrapper (hub + per-clinic deep link)
web/sandbox/registry.ts sandbox target registry (?target=ClinicOrderSystem)
web/src/services/procedure-day-queue/useProcedureDayQueue.ts realtime day-queue read hook
web/src/services/procedure-day-queue/useRecordProcedureEvent.ts RPC write hook (record_procedure_event)
docs/architecture/specialty-clinic-procedure-entry-paths.md canonical entry-path design
docs/architecture/procedure-day-queue.md day-queue lifecycle + Supabase-primary model
Ask Anything