medOS ultra

Pathology Request Flow

Pathology request flow plan and execution summary.

10 min read diagramsUpdated 2026-05-13docs/architecture/pathology-request-flow.md

Branch: claude/investigate-pathology-request-YL7KV Commits: bbd885e5 … 231e472f (6 commits) Status: All on branch, pushed; ready for review.


Context

The clinic ops team needed end-to-end coverage of the anatomical-pathology tissue request workflow on the new HIS, matching their Thai-HIS spec (form layout, ANATOMICAL PATHOLOGY confirmation, lab worklist columns, ประเภทงาน categories, 7-state colored flow). The pre-existing path was a generic order shell (OrderApril2025 filtered to the pathology tab) — usable, but it didn’t match the pathology-specific form fields, didn’t issue an ANATOMICAL PATHOLOGY confirmation, didn’t route nurse acknowledgement, and left the pathologist’s sign-out surface dormant.

This series of changes ships:

  1. A dedicated pathology request form — doctor side (PathologyRequest miniapp)
  2. A post-submit confirmation — ANATOMICAL PATHOLOGY layout with auto-print default + footer toggle, opt-out persisted to localStorage
  3. A nurse acknowledgement inbox with specimen barcode capture + ใบนำทาง / cover sheet printable
  4. Lab worklist parity with the spec (columns, per-day count chip strip, work-category filter)
  5. A central frontend flow contract — 7 user states, hospital-overridable status map, soft-gate transition table
  6. Work-category catalog ((S) Surgical Pathology(PM-I) Immunohistochemistry) with accession-prefix routing
  7. Pathologist sign-out surfaceApproveResult tab re-enabled with View / Send back / Reject / Approve, all gated by the flow contract

No backend code was changed. The frontend remains a soft gate; backend is still the write source of truth.


High-level flow

                          ┌────────────────────────────────────┐
                          │  PathologyRequest (miniapp, doctor) │
                          │  • Header strip: Patho No / work    │
                          │    type / surgery date / container  │
                          │  • Routing strip: units / doctor /  │
                          │    pathologist / payor              │
                          │  • Tabs: General / Exam / History   │
                          │  POST /v2/medication/orderRequests  │
                          │  status='sent-specimens'            │
                          └─────────────┬──────────────────────┘
                                        │ (auto-opens)
                                        ▼
                          ┌────────────────────────────────────┐
                          │  PathologyRequestConfirmation       │
                          │  ANATOMICAL PATHOLOGY layout        │
                          │  Auto-print toggle (default ON)     │
                          │  ปิด / พิมพ์ / Export to PDF        │
                          └────────────────────────────────────┘
                                        │
                                        ▼
                          ┌────────────────────────────────────┐
                          │  PathologyRequestNurseAck (miniapp) │
                          │  • Lists pending pathology requests │
                          │  • Specimen barcode capture         │
                          │  • พิมพ์ใบนำทาง (popup print)        │
                          │  • รับทราบสิ่งส่งตรวจ                │
                          │  POST .../updateAcknowledge/:id     │
                          └─────────────┬──────────────────────┘
                                        ▼
        ┌────────────────────────────────────────────────────────────────┐
        │  /pathology/laboratory  (existing 6-tab Worklist.tsx)          │
        │                                                                │
        │  Register   → SlidePrep   → ReportIssue                        │
        │  (sent-specimens) (accepted)   (accepted)                      │
        │                                                                │
        │       ↓ pathologist action                                     │
        │                                                                │
        │  ApproveResult  → ViewResult        CancelResult               │
        │  (resulted)        (completed)      (cancelled / rejected)     │
        │                                                                │
        │  Pathologist row actions on ApproveResult:                     │
        │    • ดูผล                                                       │
        │    • ตีกลับ Lab    → SendBackToLabDialog → REGISTERED          │
        │    • ปฏิเสธ        → RejectionFormDialog → SPECIMEN_REJECTED   │
        │    • อนุมัติ       → REPORTABLE                                 │
        └────────────────────────────────────────────────────────────────┘
                                        │
                                        ▼
                          ┌────────────────────────────────────┐
                          │  Frontend flow contract             │
                          │  (single source of truth — colours, │
                          │   labels, soft-gate, hospital map)  │
                          └────────────────────────────────────┘

Files created

New miniapp — web/packages/miniapps/pathology-request/

File Purpose
PathologyRequest.tsx Doctor-side form. Header strip, routing strip, 3 tabs of free-text. Catalog picker over getLabItem. Submits via createOrderRequestApi (POST {VITE_APP_API}v2/medication/orderRequests) with status='sent-specimens'.
PathologyRequestConfirmation.tsx Anatomical Pathology confirmation dialog opened on success. Auto-print toggle persisted to localStorage['pathology-request:auto-print'] (default ON). Buttons: ปิด / พิมพ์ / Export to PDF (html2canvas + jsPDF).
PathologyRequestNurseAck.tsx Nurse inbox. Lists pending requests for the current patient via usePathologyRequest. Specimen-barcode capture, cover-sheet print, รับทราบสิ่งส่งตรวจ → POST .../orderRequests/updateAcknowledge/:id (existing endpoint).
PathologyRequestCoverSheet.tsx Printable ใบนำทาง / A5 routing slip. Builds self-contained HTML and opens in a popup window with window.print(). Falls back to blob URL if popup blocked.
index.tsx Barrel.

Flow contract — web/packages/diagnostics-kit/src/pathology/flow-contract/

File Purpose
states.ts PathologyUserState enum (7 states from spec) + PATHOLOGY_USER_STATE_META (Thai/EN labels, hex colours matching spec, MUI chip colour hint, terminal flag, order).
status-map.ts DEFAULT_STATUS_TO_STATE + registerHospitalStatusMap() for tenant overrides + getBackendStatusesForState() reverse-lookup + resolveStateFromStatus().
transitions.ts PathologyAction enum (10 actions including SEND_BACK_TO_LAB) + TRANSITIONS table + canPerformAction() soft gate.
resolve.ts resolvePathologyState(row) reads from multiple plausible status field locations; usePathologyFlowState() hook + canRowPerformAction() row-level helper.
work-categories.ts 8-entry catalog (S, R, ST, PM-S, PM-C, PM-F, PM-R, PM-I) with numericId, accessionPrefix, worklistSection, defaultSlaHours, isPremium. findWorkCategory() accepts code / numericId / prefix. Hospital-overridable.
index.ts Barrel — single import point for consumers.

Pathologist sign-out

File Purpose
web/packages/diagnostics-kit/src/pathology/laboratory/dialogs/SendBackToLabDialog.tsx ~140-line MUI dialog. Captures reason + optional follow-up tag (Restain / Recut / IHC / Other), serialises to a remark string. Mirrors RejectionFormDialog’s style.

Files modified

File Change
web/src/setup/dynamic/DynamicCoreApp.ts Two new enum values: PATHOLOGY_REQUEST, PATHOLOGY_REQUEST_NURSE_ACK.
web/src/common/components/shared-engine/dynamic-core/engine/DynamicContentRenderer.tsx Imports + two new case branches rendering the new miniapps. Existing ORDER_PATHOLOGY case left untouched.
web/src/locales/en/coreApps.json + web/src/locales/th/coreApps.json Labels for both new modules.
web/packages/diagnostics-kit/src/pathology/laboratory/utils.tsx Register-tab columns: rename วันที่-เวลาออเดอร์วัน-เวลาส่งตรวจ, added อายุ (ปี), หน่วยงานที่ส่งตรวจ, หน่วยงานที่รับผล (with renderers reading from listitems[0].addons.*). All 6 tabs: เวลาที่ผ่าตัดวัน-เวลาผ่าตัด (date+time). Re-enabled ApproveResult row-action block with 4 buttons. Added getUserStateFromTab() returning the flow-contract user state for each tab.
web/packages/diagnostics-kit/src/pathology/laboratory/Worklist.tsx Added work-category Select filter (“ประเภทงาน”) on Register tab. Added filteredRows memo (client-side filter). Added dailyCounts memo + per-day chip strip under date pickers. Added handleSendBackToLab + submit handler, gated existing handleApproveResult via canRowPerformAction, added SEND_BACK_TO_LAB action route, un-commented bilingual snackbar copy. Mounted SendBackToLabDialog.

API surface (unchanged, just consumed differently)

Verb + path Used by Notes
POST {VITE_APP_API}v2/medication/orderRequests PathologyRequest form Submits with status='sent-specimens' → row appears in Register tab. patient + encounter from usePatientProfileMongoIds (MongoDB ObjectIds) to avoid the Supabase-UUID 422.
POST .../orderRequests/updateAcknowledge/:id PathologyRequestNurseAck updateAcknowledgeByOrderId — existing helper.
GET .../medication/labItems Form catalog picker getLabItem({ search, type:'pathology', isShowOrder:true }).
mutateAsyncUpdatePathologyById All worklist mutations Existing engine. Pathologist actions only change the status + remark payload values.

Plan files

Plan file Scope
/root/.claude/plans/ok-but-what-about-buzzing-quilt.md Pathologist sign-out plan (the final increment). Wrote in plan mode; user approved via ExitPlanMode.

Earlier increments were proposed inline (with AskUserQuestion for scoping) and shipped immediately, not via a plan file.


Commits (oldest first)

SHA Title
bbd885e5 feat(pathology-request): standalone doctor+nurse pathology tissue request
ef5d740a feat(pathology-request): post-submit Anatomical Pathology confirmation + auto-print toggle
dd20e6de feat(pathology-lab-worklist): wire PathologyRequest into Register tab + spec'd columns
65e0af8e feat(pathology): central frontend flow contract (soft gate) for the 7-state lab workflow
5c186404 feat(pathology): work-category catalog (ประเภทงาน) + Register-tab filter
231e472f feat(pathology): pathologist sign-out surface in ApproveResult tab

The flow contract — quick reference

7 user states

State Thai Colour Terminal
AWAITING_SPECIMEN รอสิ่งส่งตรวจ #E11D48 red no
REGISTERED ลงทะเบียนรับแล้ว #2563EB blue no
ANALYZING กำลังตรวจวิเคราะห์ #16A34A green no
SENT_TO_PATHOLOGIST นำส่งพยาธิแพทย์ #C026D3 magenta no
REPORTABLE ตรวจสอบแล้วรายงานผลได้ #EA580C orange no
RECEPTION_CANCELLED ยกเลิกการรับสิ่งส่งตรวจ #92400E brown yes
SPECIMEN_REJECTED ปฏิเสธสิ่งส่งตรวจ #991B1B dark red yes

Soft-gate transition table

AWAITING_SPECIMEN    → REGISTER_SPECIMEN   → REGISTERED
                    → REJECT_SPECIMEN     → SPECIMEN_REJECTED  (terminal)
                    → CANCEL_RECEPTION    → RECEPTION_CANCELLED (terminal)
                    → PRINT_COVER_SHEET   (inert)
REGISTERED           → START_ANALYSIS      → ANALYZING
                    → REJECT_SPECIMEN     → SPECIMEN_REJECTED  (terminal)
                    → PRINT_COVER_SHEET   (inert)
ANALYZING            → SEND_TO_PATHOLOGIST → SENT_TO_PATHOLOGIST
                    → PRINT_COVER_SHEET   (inert)
SENT_TO_PATHOLOGIST  → APPROVE_RESULT      → REPORTABLE
                    → REJECT_SPECIMEN     → SPECIMEN_REJECTED  (terminal)
                    → SEND_BACK_TO_LAB    → REGISTERED
                    → PRINT_RESULT        (inert)
REPORTABLE           → PRINT_RESULT        (inert)

Default backend status → state mapping (medOS canonical)

sent-specimens / pending / draft        → AWAITING_SPECIMEN
accepted / accepted-specimens /
  registered / received / collected     → REGISTERED
slide-prepared / slide-prep / analyzing /
  in-process / gross-done / micro-done  → ANALYZING
sent-pathologist / pathologist-review /
  pending-signout                       → SENT_TO_PATHOLOGIST
resulted / completed / co-signed /
  ready-to-report                       → REPORTABLE
cancelled / canceled /
  cancel-reception                      → RECEPTION_CANCELLED
reject-specimens / rejected / reject    → SPECIMEN_REJECTED

Hospitals override via registerHospitalStatusMap(key, overrides) at app boot.

8-entry work-category catalog

Code numericId Section SLA(h) Premium
S 1 surgical 72 no
R 4 review 120 no
ST 14 special-tissue 168 no
PM-S 9 surgical 24 yes
PM-C 10 cytology 24 yes
PM-F 11 frozen 1 yes
PM-R 12 review 48 yes
PM-I 18 immuno 48 yes

accessionPrefix drives global-sequence numbering (S25-…, PMI25-…).


Verification

Type-check

  • pnpm tsc --noEmit -p tsconfig.json filtered to the touched files: zero new errors (pre-existing nuclear-medicine / workflow-editor errors are unrelated and predate this branch).

Manual UAT (sandbox route — see web/CLAUDE.md)

  1. Submit a request via PathologyRequest from the /sandbox workspace browser (modules.PathologyRequest) → confirms ANATOMICAL PATHOLOGY dialog opens, auto-print window pops, row gets status='sent-specimens' in Mongo.
  2. Open modules.PathologyRequestNurseAck for the same patient → row appears, enter barcode → “พิมพ์ใบนำทาง” opens the A5 routing slip + window.print() → “รับทราบสิ่งส่งตรวจ” flips status to accepted.
  3. Open /pathology/laboratory → Register tab shows row with new columns (วัน-เวลาส่งตรวจ / อายุ / หน่วยงานที่ส่งตรวจ / หน่วยงานที่รับผล). Work-category filter (“ประเภทงาน”) narrows rows; per-day chips reflect filtered counts.
  4. Walk row through SlidePreparation / ReportIssue / ApproveResult.
  5. On ApproveResult tab: ดูผล opens DialogViewResult; ตีกลับ Lab opens SendBackToLabDialog → row hops back to SlidePreparation with status accepted + the new remark; ปฏิเสธ reuses RejectionFormDialog; อนุมัติ writes status='completed' and the row moves to ViewResult.

Programmatic

import {
  canRowPerformAction,
  PathologyAction,
} from '@diagnostics-kit/pathology/flow-contract';

const ok = canRowPerformAction(row, PathologyAction.APPROVE_RESULT);
// { allowed: true, to: 'reportable' } for a 'resulted' row
// { allowed: false, reason: '...' } for a 'cancelled' row

Intentional non-goals

  • No backend changes. All mutations reuse existing endpoints (createOrderRequestApi, updateAcknowledgeByOrderId, mutateAsyncUpdatePathologyById).
  • No role-based gating. Pathology has zero RBAC today; adding it is a separate concern that should ride on the broader auth/role refactor.
  • No standalone pathologist miniapp. Per the planning decision, the pathologist works inside the existing /pathology/laboratory lab worklist on the ApproveResult tab. Reduces UI duplication.
  • No new DynamicCoreApp entries for the lab worklist. It’s already mounted at /pathology/laboratory.
  • Client-side work-category filter on the worklist. The data endpoint accepts a single status= param today; swap to a server-side filter when the API gains a workTypeCode / pathoCategory query param.
  • Per-day count chip strip is current-page only. Backend count endpoint groups by status, not date. Tooltip explicitly flags this as a lower-bound count.

Where to look next

  • web/packages/diagnostics-kit/src/pathology/flow-contract/index.ts — barrel; every UI surface that renders a pathology row should import from here.
  • web/packages/miniapps/pathology-request/index.tsx — exports for the new doctor + nurse surfaces.
  • web/packages/diagnostics-kit/src/pathology/laboratory/Worklist.tsx — the lab tech + pathologist UI lives here; tabs are switched via the existing selectedMainTab.

Recommended follow-ups (out of scope for this branch):

  • Wire usePathologyFlowState into the existing ORDER_PATHOLOGY_STATUS badge renderer in dynamic-table/utils/badgeStatus.ts so the contract owns chip colours everywhere.
  • Add the per-day count to the backend count endpoint so the chip strip is full-range, not page-limited.
  • Add RBAC at the route level so non-pathologists can’t open the ApproveResult tab.
  • Stamp the resolved accessionPrefix onto a real global-sequence call (S25-00001, PMI25-00001) when the pathology counter is added to the global-sequence service.
Ask Anything