OR Count Sheet Spec
Component spec for the OR surgical-count sheet.
File:
web/sandbox/targets/OrCountSectionTarget.tsx(938 lines) Sandbox URL:http://localhost:5179/?target=OrCountSectionRegistry key:OrCountSectioninweb/sandbox/registry.tsBased on: Real productionCount.tsxatweb/packages/periops-kit/src/operating-room/components/list-or-patient/table-data/dialog/time-out/Count.tsx
Purpose
Standalone sandbox prototype of the OR Instrument Count Sheet used by Thai hospital scrub and circulating nurses during surgery. Models the AORN (Association of periOperative Registered Nurses) dual-nurse counting standard where both the scrub nurse and circulating nurse independently count surgical items at each phase, then reconcile.
Production data path: orRequest.timeOut.intraoperative.count
Architecture
State Management
Uses useReducer with a single State → Action → reducer pattern. No external state (Redux/Zustand/Supabase).
State
├── operations: Operation[] // Multi-operation support (tabs)
└── activeOpIdx: number // Currently selected tab
14 Reducer Actions
| Action | Purpose |
|---|---|
SET_COUNT |
Set a nurse’s count for a specific item/phase/role |
SET_TOTAL |
Set the expected total for an item |
SET_ACTIVE_PHASE |
Switch between the 4 counting phases |
ATTEST |
Record nurse name (from dropdown) as attestation for a phase |
SET_ACTIVE_OP |
Switch between operation tabs |
ADD_OPERATION |
Add a new operation (via dialog) |
REMOVE_OPERATION |
Remove an operation tab (minimum 1 enforced) |
ADD_ITEM |
Add a custom free-text item to current operation |
REMOVE_ITEM |
Remove a custom item (only items with id.startsWith('custom-')) |
SET_VERIFICATION |
Set N/A / Corrected / Uncorrected for sponge/needles/instruments |
TOGGLE_SURGEON_FIELD |
Toggle surgeon notified/unnotified checkbox |
SET_SURGEON_SIGNATURE |
Set signature text for surgeon notification |
SEED_DEMO |
Populate 2 demo operations with realistic data |
RESET |
Clear to single empty operation |
Data Model
Operation
type Operation = {
id: string; // 'op-{timestamp}-{random}'
name: string; // Thai name (e.g. 'ผ่าตัดถุงน้ำดี')
name_en: string; // English name (e.g. 'Laparoscopic Cholecystectomy')
surgeon: string; // Surgeon name (e.g. 'นพ. พัฒน์ / Dr. Phat')
items: CountItem[]; // 12 standard + N custom items
activePhase: CountPhase; // Currently selected phase
attestations: Record<CountPhase, Record<Role, string>>;
// '' = not attested, nurse Thai name = attested
verification: Record<VerificationSectionKey, VerificationSection>;
};
CountItem
type CountItem = {
id: string; // Standard: 'gauze', 'needle', etc. Custom: 'custom-{ts}-{rand}'
name: string; // Thai name
name_en: string; // English name
total: number; // Expected total count
counts: Record<CountPhase, Record<Role, number | null>>;
// null = not yet counted, number = counted
};
VerificationSection
type VerificationSection = {
sponge: CountVerificationStatus; // '' | 'n/a' | 'corrected' | 'uncorrected'
needles: CountVerificationStatus;
instruments: CountVerificationStatus;
surgeonNotified: boolean; // Checkbox
surgeonNotifiedSignature: string; // Sign field (appears when checkbox checked)
surgeonUnnotified: boolean; // Checkbox
surgeonUnnotifiedSignature: string; // bySign field (appears when checkbox checked)
};
Enums
type Role = 'scrub' | 'circulator';
type CountPhase = 'initial' | 'first' | 'closing' | 'final';
type VerificationSectionKey = 'firstProcedure' | 'preImplantClosing';
type CountVerificationStatus = 'n/a' | 'corrected' | 'uncorrected' | '';
4 Counting Phases
| # | Key | EN Label | TH Label | Color |
|---|---|---|---|---|
| 1 | initial |
Initial | นับเริ่มต้น | #8b5cf6 (purple) |
| 2 | first |
1st Count | นับครั้งที่ 1 | #3b82f6 (blue) |
| 3 | closing |
Closing | ก่อนปิดแผล | #f59e0b (amber) |
| 4 | final |
Final | สุดท้าย | #22c55e (green) |
Each phase has its own count values per item per role, and its own attestation state.
2 Roles (No Surgeon Column)
| Key | EN Label | TH Label |
|---|---|---|
scrub |
Scrub Nurse | พยาบาลส่งเครื่องมือ |
circulator |
Circulating Nurse | พยาบาลรอบนอก |
Important: The Thai label for circulating nurse is พยาบาลรอบนอก (NOT พยาบาลหมุนเวียน).
12 Standard Items
| # | ID | EN Name | TH Name |
|---|---|---|---|
| 1 | gauze |
Gauze | ผ้าก๊อซ |
| 2 | swab-large |
Swab (Large) | ผ้าซับเลือดใหญ่ |
| 3 | swab-small |
Swab (Small) | ผ้าซับเลือดเล็ก |
| 4 | blood-gauze |
Blood Gauze | ก๊อซชุบเลือด |
| 5 | needle |
Needle | เข็ม |
| 6 | blade |
Blade | ใบมีด |
| 7 | instrument |
Instrument | เครื่องมือ |
| 8 | peanut |
Peanut | ถั่ว (Peanut) |
| 9 | drain |
Drain | สายระบาย |
| 10 | cottonoid |
Cottonoid / Patty | สำลีแผ่น |
| 11 | vascular |
Vascular | หนีบหลอดเลือด |
| 12 | sling |
Sling | สลิง |
Custom items can be added per-operation via the inline AddItemRow at the bottom of the table (EN + TH text fields, + Add button). Custom items get id: 'custom-{timestamp}-{random}' and show a red delete icon in the Match column.
2 Verification Sections
| Key | EN Label | TH Label | Color |
|---|---|---|---|
firstProcedure |
The First Procedure | นับครั้งแรก | #3b82f6 (blue) |
preImplantClosing |
Pre-Implant — Closing Count | นับก่อนปิดแผล | #f59e0b (amber) |
Each section has 3 categories with radio-style selection (N/A / Corrected / Uncorrected):
- Sponge
- Needles
- Instruments
Conditional: “If count uncorrected” Block
When any category in a section is set to Uncorrected, a red-bordered panel appears:
If count uncorrected / กรณีนับไม่ตรง
┌──────────────────────────────────────────────────────────┐
│ ☐ Surgeon notified taken x-ray │
│ แจ้งศัลยแพทย์แล้ว ถ่ายภาพรังสี [Sign / ลงชื่อ] │
│ │
│ ☐ Surgeon unnotified │
│ ไม่ได้แจ้งศัลยแพทย์ [bySign / ลงชื่อ] │
└──────────────────────────────────────────────────────────┘
Behavior:
- Checkboxes toggle independently per section
- Signature text field appears only when the corresponding checkbox is checked
surgeonNotifiedcheckbox color:#3b82f6(blue)surgeonUnnotifiedcheckbox color:#f59e0b(amber)- The block auto-hides when all categories return to N/A or Corrected
Production data path:
orRequest.timeOut.intraoperative.count.uncorrected[]
├── surgeonNotified + surgeonNotifiedSignature
└── surgeonUnnotified + surgeonUnnotifiedSignature
Attestation (Nurse Dropdown)
Each phase has attestation for both roles. The attestation uses a Select dropdown populated with 8 sample Thai nurses:
| ID | Thai Name | EN Name |
|---|---|---|
| n1 | พย. สุภาพร วงศ์สว่าง | Supaporn W. |
| n2 | พย. นิตยา ศรีสุข | Nittaya S. |
| n3 | พย. อรุณี แก้วประเสริฐ | Arunee K. |
| n4 | พย. พรทิพย์ ชัยมงคล | Porntip C. |
| n5 | พย. ดวงใจ สุขสมบูรณ์ | Duangjai S. |
| n6 | พย. จารุวรรณ พงศ์พิพัฒน์ | Jaruwan P. |
| n7 | พย. วราภรณ์ ทองคำ | Waraporn T. |
| n8 | พย. กัลยา เจริญสุข | Kanlaya C. |
Attestation state: string per role per phase — empty string = not attested, nurse Thai name = attested.
UI layout:
ลงชื่อรับรอง / Attestation — {Phase} ({PhaseTH})
Scrub Nurse [พย. สุภาพร วงศ์สว่าง ▼] ✓
พยาบาลส่งเครื่องมือ
Circulating Nurse [เลือกพยาบาล / Select nurse ▼] ○
พยาบาลรอบนอก
When both nurses are selected → green “count verified” alert appears. Phase tab gets a green checkmark.
Match / Discrepancy Logic
Per item, per active phase:
| Condition | Match Column | Row Style |
|---|---|---|
total === 0 |
— (dash) |
Normal |
Both counts null or only one entered |
Empty circle ○ |
Normal |
| Both counts match total | Green checkmark ✓ |
Normal |
| Any count differs from total, or scrub ≠ circulator | Red warning ⚠ |
Light red background |
When discrepancies exist, a yellow Alert banner appears above the table:
N discrepanc(y/ies) — Item1, Item2, …
Multi-Operation Support
- Tab bar at top with scrollable operation tabs
- Add:
+icon button →AddOperationDialog(TH name, EN name, surgeon fields) - Remove:
✕icon inside each tab (only visible when >1 operations) - Minimum 1 operation enforced (
REMOVE_OPERATIONis no-op whenlength <= 1) - Summary chip bar appears when >1 operations showing
{name}: N/4 phasescompletion
Component Tree
OrCountSectionTarget (main, default export)
├── Header (title + Reset/Seed Demo buttons)
├── Operation Tabs (MUI Tabs + Add button)
│ └── OperationCountTable (per active operation)
│ ├── Operation Header (name, Thai name, surgeon chip)
│ ├── Phase Tabs (4 clickable phase boxes)
│ ├── Discrepancy Alert (conditional)
│ ├── Count Table (MUI Table)
│ │ ├── Header Row (# | Item | Total | Scrub | Circulator | Match)
│ │ ├── Item Rows × 12+ (number inputs per cell)
│ │ └── AddItemRow (inline EN/TH text fields + Add button)
│ ├── Verification Sections × 2
│ │ ├── Category Radios (Sponge/Needles/Instruments × N/A/Corrected/Uncorrected)
│ │ └── Surgeon Notification Block (conditional on Uncorrected)
│ │ ├── surgeonNotified checkbox + signature
│ │ └── surgeonUnnotified checkbox + signature
│ └── Attestation Section
│ ├── Scrub Nurse Select dropdown
│ ├── Circulating Nurse Select dropdown
│ └── Verified Alert (conditional on both selected)
├── All Operations Summary (conditional on >1 ops)
└── AddOperationDialog
Seed Demo Data
SEED_DEMO creates 2 operations:
Operation 1: Laparoscopic Cholecystectomy
- TH: ผ่าตัดถุงน้ำดี
- Surgeon: นพ. พัฒน์ / Dr. Phat
- Pre-filled totals: Gauze=20, Swab(L)=5, Swab(S)=10, Needle=6, Blade=2, Instrument=15, Peanut=5, Drain=1, Vascular=2
- Phases completed: Initial + 1st Count (both with matching scrub/circulator counts)
- Attested by: พย. สุภาพร วงศ์สว่าง (scrub) + พย. พรทิพย์ ชัยมงคล (circulator)
- Closing + Final: Empty (not yet counted)
Operation 2: Appendectomy
- TH: ผ่าตัดไส้ติ่ง
- Surgeon: นพ. สมปอง / Dr. Sompong
- Pre-filled totals: Gauze=10, Swab(L)=5, Swab(S)=5, Needle=4, Blade=1, Instrument=12, Drain=1
- No phases completed, no attestations
UI Tech Stack
- Framework: React 19 + TypeScript
- Components: MUI v7 (Table, Paper, Stack, Tabs, Select, MenuItem, FormControl, TextField, Alert, Dialog, Chip, IconButton, Tooltip, Typography)
- Icons: @mui/icons-material (ContentCut, CheckCircle, RadioButtonUnchecked, WarningAmber, Add, RestartAlt, DeleteOutline, Edit, CheckBoxOutlineBlank, CheckBox, Draw)
- Color system: MUI
alpha()utility for transparency overlays - State:
useReducer(no external store) - Bilingual: All labels in Thai + English throughout
Verification Checklist (for Claude reviewer)
Structural
- [ ] File renders without runtime errors at
?target=OrCountSection - [ ] 12 standard items displayed in table
- [ ] 4 phase tabs (Initial/1st Count/Closing/Final) clickable and switch content
- [ ] 2 verification sections (The First Procedure / Pre-Implant — Closing Count)
- [ ] Attestation section with 2 nurse dropdown selects
Functional
- [ ] Total, Scrub, and Circulator number inputs are editable
- [ ] Match column shows
✓when all 3 values agree,⚠on mismatch,—when total=0 - [ ] Discrepancy alert appears when any item has mismatched counts
- [ ] Clicking N/A/Corrected/Uncorrected toggles correctly (click again to deselect)
- [ ] Setting any item to “Uncorrected” reveals surgeon notification block
- [ ] Surgeon notification checkboxes toggle independently
- [ ] Signature field appears only when checkbox is checked
- [ ] Surgeon notification block hides when all items return to N/A/Corrected
- [ ] Nurse dropdown selection stores Thai nurse name as attestation
- [ ] Both nurses selected → “count verified” success alert
- [ ] Phase tab shows green checkmark when both nurses attested
Multi-Operation
- [ ] Seed Demo creates 2 tabs (Lap Chole + Appendectomy)
- [ ] Tabs switch operations correctly
- [ ] Add Operation dialog creates new tab
- [ ] Remove operation (X button) removes tab (minimum 1 enforced)
- [ ] Summary chips appear when >1 operations
Custom Items
- [ ] AddItemRow (EN + TH fields) visible at bottom of table
- [ ] Typing name and clicking “+ Add” adds row to table
- [ ] Enter key also triggers add
- [ ] Custom items show red delete icon in Match column
- [ ] Standard items do NOT show delete icon
- [ ] Reset clears custom items
Data Integrity
- [ ] Each operation maintains independent state (items, phases, attestations, verification)
- [ ] Verification state is per-section per-operation (not shared)
- [ ] Attestation is per-phase per-role per-operation
- [ ] Switching phases preserves count data in other phases