medOS ultra

OR Body-Site Field Map

Inventory of every OR case-modal field recording an anatomical location - surgical site, position, device/line/drain sites.

18 min read diagramsUpdated 2026-05-25docs/architecture/or-body-site-fields-map.md

Inventory of every field in the operating-room case modal that records where on the body something happens — surgical site, patient position, device placement, line/drain insertion sites, specimen anatomy, laser sites, and retained-material locations.

The modal is DialogSignIn.tsx (the OR case dialog), which has five phases:

# Phase Thai WHO bucket
1 Waiting Room ห้องรอ pre-op (staging)
2 Sign In ลงชื่อเข้า pre-op (pre-induction)
3 Time Out หยุดตรวจสอบ intra-op (pre-incision + intra-op nursing)
4 Sign Out ลงชื่อออก post-op (before leaving OR)
5 Room Out ออกจากห้อง post-op (after)

PHASE_STEPS is defined in web/packages/periops-kit/src/operating-room/components/list-or-patient/table-data/dialog/DialogSignIn.tsx:1523.


Pre-op tabs

Waiting Room

Field Component Stored at Purpose
Mark Site checkbox + photo upload OperatingMarkSite.tsx markSite (bool) + uploaded files Surgeon photographs the marked anatomy (WHO Universal Protocol). Pure free-text/photo evidence, no anatomy picker.

Mount points (rendered twice for two layout branches, same store key …:waiting:mark-site):

  • list-or-patient/table-data/dialog/WaitingRoom.tsx:622-628 (split-column branch, gated state?.page === 'waiting-room')
  • list-or-patient/table-data/dialog/WaitingRoom.tsx:753-759 (full layout branch)
  • Source: list-or-wait-requested/dialog/OperatingMarkSite.tsx

Sign In

Field Component / Line Stored at Notes
SITE per-procedure dropdown (ข้างซ้าย / ข้างขวา / ทั้ง2ข้าง / ไม่ระบุ) miniapps/perform-surgery/DiagnosisIcd9Description.tsx:94-134 Operation.procedure[i].site.value Per-row (fixed by 50a5196b2 — used to be shared across all rows)
เพิ่มเติม / note per-procedure same file :70-92 Operation.procedure[i].site.note free-text
เวลา / minute per-procedure same file :136-159 Operation.procedure[i].site.minute
Marked site radio (unnecessary / already_done / to_do) sign-in/SurgicalSafetyChecklist.tsx:355-382 signIn.safetyChecklist.markedSite WHO verification
Site verified checkbox + detail sign-in/PreoperativeChecklistReview.tsx:336-358 verifySited + sitedDetails Yup-required (DialogSignIn.tsx:479). Inlines the photo from markSite (read-only) when present.
verifyMarksite (declared, not rendered in OR) signIn.safetyChecklist.verifyMarksite Dead in OR. Only rendered in labour-room (labour-room/.../SurgicalSafetyChecklist.tsx:200-202). Initialised + autofilled in DialogSignIn.tsx:661, 1565 but no OR UI consumes it.

Intra-op tab — Time Out

Mounted under time-out/IntraoperativeNursingData.tsx, section order: ProcedureTeam → Implant → Antibiotics → Urinary Catheter → Drain → Packing → Hypothermia → Electrosurgical Risk → Injury Risk.

Surgical Safety Checklist

Field Line Stored at
ตำแหน่งที่จะลงมีด / Incision Site checkbox + note time-out/SurgicalSafetyChecklist.tsx:859-868 timeOut.safetyChecklist.surgeon.siteLocation + noteSiteLocation

InjuryRisk card (time-out/InjuryRisk.tsx)

Section Options Stored at
จัดท่า / Position for surgery Supine, Prone, Lt. Lateral, Rt. Lateral, Lithotomy, Other (+ free-text) injuryRisk.surg.*
อุปกรณ์จัดท่า / Position Devices No, Shoulder Roll, Axillary Roll, Chest Roll, Pillow/wedge, Leg Holder injuryRisk.devices.*
Skin / Safety strap — “Apply safety strap to” + “Position by” signature text + signature safetyStrap.{apply,note,positionBySignature}
Tourniquet body-region table Left Arm / Right Arm / Left Thigh / Right Thigh / Body (per-region pressure + duration + signature) injuryRisk.tourniquet.{leftArm,rightArm,leftThigh,rightThigh,body}

ElectrosurgicalRisk card (time-out/ElectrosurgicalRisk.tsx)

Section Options Stored at
Ground Pad / Return Electrode Calf, Thigh, Arm, Back, Buttock, Megadyne, Other — each with left / right checkboxes (groundPadOptions, lines 31-38) injuryRisk.groundPad.<region>.{isValue,left,right}
Laser sites (multi-select Autocomplete) ข้างซ้าย / ข้างขวา / ทั้งสองข้าง / ไม่ระบุ (siteOptions, lines 24-29) injuryRisk.laser.sites[]

CatheterDrainPacking card (time-out/CatheterDrainPacking.tsx)

Section Body-site fields Stored at
Urinary Catheter (:14-82) catheterInsert (insertion location selector) + catheterSize urinaryCatheter, catheterInsert, catheterSize
Drain (:94-208) — multi-row Per drain: drainType (Radivac / Jacobson / Lumbar / …), drainSite (text “ระบุ Site”), drainSize, drainNote columns[].{drainType,drainSite,drainSize,drainNote}
Packing (:211-233) Button-launches DialogOperatingRoomAlert (see below)

Implant card (time-out/Implant.tsx)

Field Line Stored at
C/S Specimen type + note (Pediatrician handoff) :192-216 csSpecimen.value, csSpecimen.note

Intra-op — three categories called out

1. Pathology / Specimens — time-out/Antibiotics.tsx:272-440

Nested inside the Antibiotics SignoffWrapper rather than its own card. Lives entirely under values.specimen.*.

  • Top-level Specimen Yes/Nospecimen.value
  • Four specimen-type rows, each with a per-piece list of free-text notes (one note per “ชิ้น”):
Specimen type Checkbox flag Pieces array Per-piece field
C/S (culture) specimen.cs specimen.csPieces[] .note (“ระบุ…”)
Frozen Section specimen.frozen specimen.frozenPieces[] .note
Gram Stain specimen.gramStain specimen.gramStainPieces[] .note
Other specimen.other specimen.otherPieces[] .note
  • Pathology Orders sub-section (:393-440): specimen.pathologyOrders[].note — free-text rows.
  • Running total รวมชิ้นส่วน: N ชิ้น chip across all four piece arrays.
  • Mirror in Sign Out (sign-out/SurgicalSafetyChecklist.tsx:477-549): re-reads timeOut.intraoperative.specimen.*, tags each piece with แล็บใน (in-house: Frozen, Gram Stain) or แล็บนอก (send-out: C/S, Other).
  • Separate csSpecimen in time-out/Implant.tsx:192-216 for C/S obstetric specimens — independent state from specimen.cs above.

⚠️ Every “site” here is free-text. No anatomic picker, no laterality, no SNOMED body-structure code. Pathology cannot auto-fill the specimen-source field from this data.

2. Laser — time-out/ElectrosurgicalRisk.tsx:231-319

Inside the “ความเสี่ยงบาดเจ็บจากเครื่องจี้ไฟฟ้า / Electrosurgical Injury Risk” card.

Field Stored at Notes
Yes/No radio injuryRisk.laser.no / injuryRisk.laser.yes
Type (text) injuryRisk.laser.type free-text — no CO₂/Nd:YAG/KTP/Holmium picker
Setting (watts) injuryRisk.laser.setting endAdornment: watt.
Site (multi-select) injuryRisk.laser.sites[] siteOptions = ข้างซ้าย / ข้างขวา / ทั้งสองข้าง / ไม่ระบุ
Start / End time injuryRisk.laser.startTime / endTime
Position by (signature) injuryRisk.laser.positionBySignature declared + reset on No, but no visible form field for it in ElectrosurgicalRisk.tsx

⚠️ “Site” is laterality only (4 options). Captures neither body region (eye, larynx, skin, prostate, cervix, etc.) nor anatomic detail required for a real laser register / radiation-safety log.

3. Gauzes / Packing — time-out/CatheterDrainPacking.tsx:211-233DialogOperatingRoomAlert

The Packing card itself is just a label + [Management] button that opens @ui-kit/components/dialog-operating-room-alert (mounted from time-out/TimeOut.tsx:656-665).

Dialog title: “เฝ้าระวังวัสดุคงค้างในบาดแผล / แผลผ่าตัด” (surveillance of retained materials in surgical wound).

Each row = one packed item left in the patient. Columns (packages/ui-kit/src/components/dialog-operating-room-alert/config.tsx):

# Column Field Type
1 วันที่/เวลาที่ บันทึก recordDateTime timestamp
2 รายการวัสดุตกค้าง pendingMaterialsList free-text (e.g. Gauze 4x4, Laparotomy sponge, Raytec sponge)
3 ตำแหน่งที่ PACK locationPack free-text body location (e.g. RLQ abdomen, Pelvic cavity, Peritoneal)
4 จำนวน PACK numPack number
5 ผู้ปฏิบัติ (Packing) recorder user autocomplete
6 จำนวนที่ Remove quantityToRemove number
7 ผู้ Remove removedBy user
8 วันที่/เวลา Remove removalDateTime timestamp
9 คงเหลือ remaining numPack - quantityToRemove, computed
10 Acknowledged acknowledged checkbox

Dev autofill mocks (DialogOperatingRoomAlert.tsx:284-288):

  • Gauze 4x4 → RLQ abdomen × 5
  • Laparotomy sponge → Pelvic cavity × 3
  • Raytec sponge → Peritoneal × 2

Distinct from TableProceder.tsx which counts gauzes/needles/instruments on the field (in vs out reconciliation):

  • TableProceder = “did we lose anything?” (count audit)
  • DialogOperatingRoomAlert = “what did we intentionally leave behind?” (retained-material tracking)

⚠️ locationPack is free-text. No anatomic picker, no link back to the procedure’s site / laterality, no timer or ward handoff for the implied “must remove before discharge” alert.


Post-op tabs

Sign Out

Field Source Notes
detail[].site.{value,note,minute} chips (“Site: ”) sign-out/SurgicalSafetyChecklist.tsx:228-235, 395-405 Read-only mirror of the Sign-In / Time-Out site data
Specimen summary sign-out/SurgicalSafetyChecklist.tsx:477-549 (reads timeOut.intraoperative.specimen) Read-only

Room Out

Field
None — status + discharge timestamps only. No body-position fields.

Summary by intent

Intent Phase Where
Surgical site / laterality (left vs right vs bilateral) Sign In + Time Out + Sign Out mirror per-procedure SITE dropdown + Incision Site checkbox
Marked-site evidence (photo of marking) Waiting Room OperatingMarkSite
Patient body position on table Time Out InjuryRisk → Position for surgery
Positioning devices (rolls, pillows, leg holders) Time Out InjuryRisk → Position Devices
Tourniquet body regions Time Out InjuryRisk → Tourniquet (4 limbs + body)
Grounding-pad anatomic placement (L/R per region) Time Out ElectrosurgicalRisk → Ground Pad
Laser energy body sites Time Out ElectrosurgicalRisk → Laser sites (laterality only)
Catheter insertion site Time Out CatheterDrainPacking → Urinary Catheter
Drain insertion sites (per drain row) Time Out CatheterDrainPacking → Drain table drainSite
Packing / retained gauze locations Time Out Packing → DialogOperatingRoomAlert locationPack
Specimen anatomy (notes per piece) Time Out Antibiotics → Specimen pieces .note
C/S Specimen + Pediatrician handoff Time Out Implant → csSpecimen
Read-only confirmation of all the above Sign Out SSC mirror chips + Specimen rows

Gaps worth flagging

  1. Every body-site field is free-text. No anatomic picker, no SNOMED body-structure code, no diagram pin. Impact:

    • Pathology cannot auto-fill the pathology request form’s specimen-source field — lab re-keys
    • Laser register cannot produce a body-region utilization report — only laterality
    • Retained-material tracking cannot drive a ward alert (“Patient X has Raytec sponge in peritoneal cavity, due for removal in OR before discharge”)
  2. No dedicated “Lines / Vascular Access” section anywhere in the modal. IV / arterial / central / epidural / spinal lines are only referenced as pre-prep prompts ("พิจารณาเตรียม IV 2 เส้น / Central line" in sign-in/SurgicalSafetyChecklist.tsx:610; "IV line patent" in the PreferenceCardChecklist mock list). Never recorded with body site, gauge, depth, or insertion-by signature the way drains or ground pads are. If WHO / TJC line-time-out documentation is needed, this is a missing section — most naturally a new “Lines / Access” card in Time Out next to CatheterDrainPacking.

  3. verifyMarksite is declared in OR state but never rendered in the OR Sign-In UI. Only labour-room renders it as a checkbox (labour-room/.../SurgicalSafetyChecklist.tsx:200-202). In OR it’s initialised (DialogSignIn.tsx:661), reset, set by autofill (:1565), and typed (orRecord.types.ts:63) — but no SSC component reads or writes it via UI. Either restore the checkbox or drop the field from OR state shape.

  4. Lowest-friction unification: a shared AnatomicSiteAutocomplete (SNOMED body-structure-backed) swapped in for the free-text inputs across Pathology pieces, Laser sites, Packing locationPack, Drain drainSite, and Catheter catheterInsert. The data shape stays a string; entry just becomes constrained, codeable, and queryable.


File index

File Role
web/packages/periops-kit/src/operating-room/components/list-or-patient/table-data/dialog/DialogSignIn.tsx Modal shell + PHASE_STEPS + initial values + autofill + Yup
…/dialog/WaitingRoom.tsx Waiting Room tab; mounts OperatingMarkSite twice
…/dialog/sign-in/SurgicalSafetyChecklist.tsx Sign In SSC card (markedSite radio, per-procedure SITE)
…/dialog/sign-in/PreoperativeChecklistReview.tsx Sign In Preop Review (verifySited + sitedDetails, inlines mark-site photo)
…/dialog/time-out/IntraoperativeNursingData.tsx Time Out intra-op container; section ordering
…/dialog/time-out/SurgicalSafetyChecklist.tsx Time Out SSC (Incision Site)
…/dialog/time-out/InjuryRisk.tsx Position + Position Devices + Skin/Strap + Tourniquet
…/dialog/time-out/ElectrosurgicalRisk.tsx Ground Pad + Laser
…/dialog/time-out/CatheterDrainPacking.tsx Urinary Catheter + Drain (table) + Packing button
…/dialog/time-out/Antibiotics.tsx Specimen (4 types × N pieces) + Pathology orders
…/dialog/time-out/Implant.tsx csSpecimen (C/S obstetric)
…/dialog/sign-out/SurgicalSafetyChecklist.tsx Read-only mirror of site + specimen
…/list-or-wait-requested/dialog/OperatingMarkSite.tsx markSite checkbox + image upload
web/packages/miniapps/perform-surgery/DiagnosisIcd9Description.tsx Per-procedure SITE / note / minute table cells
web/packages/ui-kit/src/components/dialog-operating-room-alert/DialogOperatingRoomAlert.tsx Retained-material surveillance dialog
web/packages/ui-kit/src/components/dialog-operating-room-alert/config.tsx Column definitions for the dialog

Connecting to the 3D Anesthesia Body Mapper

AnesthesiaBodyMapper (web/packages/miniapps/3d-health-anesthesia/AnesthesiaBodyMapper.tsx) is already wired through DynamicContentRenderer (web/src/common/components/shared-engine/dynamic-core/engine/DynamicContentRenderer.tsx:3568-3581) and exposed via DynamicCoreApp.ANESTHESIA_BODY_MAPPER = 'modules.AnesthesiaBodyMapper'. It is not yet 3D — currently a 100×100 SVG with CSS rotation simulating positions — but the semantic vocabulary is already correct, and DynamicCoreApp.ts:482 documents intent to evolve into a true 3D viewer. A 3DPainMapper.tsx sibling and a 3d-Health-Model/ subdir (DentalCharting, CameraControl, Annotation, Model, encounter-detail, hooks) suggest the Three.js work is underway.

The mapper’s onSave callback in DynamicContentRenderer currently just console.logs — nothing persists yet.

What the mapper already provides

Construct Coverage
11 MarkerTypes injection_site, nerve_block, epidural, spinal, airway, iv_access, arterial_line, monitoring, incision, position_marker, annotation
9 PATIENT_POSITIONS Supine / Prone / Left Lateral / Right Lateral / Lithotomy / Trendelenburg / Reverse Tren / Sitting / Beach Chair
32 named BODY_PARTS_3D head, neck, throat, chest L/C/R, shoulder L/R, arm/forearm/hand L/R, abdomen, back upper/lower, lumbar spine, hip/groin L/R, thigh/knee/leg/ankle/foot L/R
10 NERVE_BLOCK_SITES Interscalene, Supraclavicular, Infraclavicular, Axillary, Femoral, Sciatic, Popliteal, Ankle, TAP, Erector Spinae
AnesthesiaMarker shape {id, type, position{x,y,z}, bodyPart, label, labelTh, details: Record<string,any>, color, timestamp, createdBy}
Output onSave({markers, position})

OR-modal fields mapped to mapper markers

OR-modal field Mapper marker Notes
signIn.safetyChecklist.markedSite + Waiting-Room photo incision exact
surgeon.siteLocation (Time-Out incision) incision exact
Per-procedure Operation.procedure[i].site.value incision × N exact
injuryRisk.surg.{supine,prone,LtLateral,RtLateral,lithotomy} position_marker + the modal’s position prop identical enum already
injuryRisk.tourniquet.{leftArm,rightArm,leftThigh,rightThigh,body} new type tourniquet arm_left/right, thigh_left/right already exist
injuryRisk.groundPad.{calf,thigh,arm,back,buttock}.{left,right} new type ground_pad all body parts already named
injuryRisk.laser.sites (4-option laterality) new type laser upgrades from 4 → 32 body parts
urinaryCatheter + catheterInsert new type urinary_catheter groin_left/right
columns[].drainSite (multi-drain) new type drain any named part
csSpecimen + specimen.csPieces[i].note etc. new type specimen per-piece body part
DialogOperatingRoomAlert.locationPack (gauze packing) new type retained_material any named part
Missing entirely in OR modal iv_access, arterial_line, airway already supported by mapper

The mapper is therefore a superset of the modal’s vocabulary — it already covers vascular lines and airway, which the modal does not capture at all.

Three connection patterns (layered)

Pattern A — Read-only summary view (zero-risk, ~1 day)

Mount `` at the bottom of Sign Out and Room Out tabs (and on the printable OR record). Build a pure-function deriveMarkersFromOrRequest(orRequest) that walks every form path catalogued above and emits markers. Nothing else changes; you get one visual summary at end-of-case that previously required reading 8 separate cards.

Also expose via DynamicContentRenderer as a new case 'OrBodyMapSummary' so the same widget appears in patient profile, OR landing, PACU handoff, and the print report — single source of truth, no fork.

Pattern B — Inline picker upgrade (medium effort)

Every free-text site field in the modal gains a [📍 Pin on body] button. Click opens the mapper as a Dialog in single-marker-pick mode, returns {bodyPart, position{x,y,z}, label, labelTh} and writes into the existing field path PLUS into a parallel bodyMarker field. Old free-text still works; structured data accumulates alongside.

Six target fields ordered by ROI:

  1. DialogOperatingRoomAlert.locationPack — biggest payoff (retained-material safety)
  2. specimen.csPieces[i].note (and frozen/gramStain/other Pieces) — feeds pathology requisition
  3. columns[].drainSite (drain rows)
  4. injuryRisk.laser.sites — produces a real laser register
  5. catheterInsert
  6. signIn.preoperativeChecklist.sitedDetails

Pattern C — Mapper as source of truth (the actual unification)

A useOrBodyMarkers(orRequestId) store (same useSyncExternalStore + localStorage pattern as useOrTimesStore introduced on the claude/find-or-list-route-KxRbe branch). The mapper writes; every section form reads. Section forms become editors for marker.details — drain size, catheter size, lot/serial, pressure, duration, signatures all go into details: Record<string, any>.

This gives architectural cleanup:

  • One canonical array orRequest.bodyMarkers[] replaces ~15 scattered field paths
  • The marker IS the site markingverifyMarksite becomes dead state to remove
  • Per-phase visibility filter: Sign-In shows incision + airway + iv_access; Time-Out shows everything; Sign-Out shows everything read-only; Room-Out filters to retained_material + drain + iv_access still in patient
  • Sign-Out specimen mirror becomes a marker filter, not a hand-coded read of timeOut.intraoperative.specimen

Four payoffs that justify the work

  1. Retained-material safety alert. Today: locationPack is free-text, no link to discharge. After: the retained_material marker carries to PACU/ward handoff, gates discharge if unresolved, fires AcknowledgementInbox task per the acknowledgement system contract.
  2. Pathology requisition autopopulation. Today: specimen.csPieces[i].note is free-text the lab re-keys. After: each specimen marker has bodyPart (SNOMED-mappable) → pathology request gets the source anatomy automatically. Plugs directly into the pathology module work on the pathology-lab-work branch.
  3. Real laser/radiation register. Today: injuryRisk.laser.sites is 4-option laterality. After: 32 body parts + duration + watts + type → device utilisation reports, radiation-safety logs, NM module integration (per nuclear-medicine-ris-pacs-integration.md).
  4. The missing Lines/Vascular-Access section appears for free. The mapper already has iv_access / arterial_line / airway types. Adding a “Lines & Access” card in Time Out that is just a filtered marker list closes the gap flagged earlier — no new form components needed.

One caveat (forward-compatible)

The mapper today is SVG with CSS rotation. Calling it “3D” is aspirational (position.z exists in the schema but is unused). This does not block the connection above — the schema is forward-compatible. When the 3d-Health-Model/ Three.js work lands and the renderer swaps, the marker store, the OR modal wiring, the Supabase persistence, and the summary view all keep working unchanged. Do not wait for true 3D to start connecting; the data layer is the lasting contract, the renderer is replaceable.


Dual-View + BodyMapperSection (shipped)

The first piece of the integration above is now live. Two layers:

1. AnesthesiaBodyMapper — view-mode + face-aware markers

New props on the existing mapper (web/packages/miniapps/3d-health-anesthesia/AnesthesiaBodyMapper.tsx):

Prop Type Notes
viewMode? 'single' | 'front' | 'back' | 'dual' Controlled mode. If omitted, mapper manages its own state.
defaultViewMode? same union Initial mode for uncontrolled usage. Defaults to 'single' so existing consumers (DynamicContentRenderer, AnesthesiaLandingPage, OpdSystemLanding) keep current behaviour.
onViewModeChange? (mode: ViewMode) => void Fires whenever the user toggles the in-mapper view-mode group.

New type fields:

  • AnesthesiaMarker.face?: 'anterior' \| 'posterior' \| 'lateral_left' \| 'lateral_right' — optional. Old markers without it are resolved by inferFace() (exported helper) using the body-part name.
  • BODY_PARTS_3D[part].faces: Face[] — every body part declares which faces it is clickable from. New posterior-only parts: buttock_left/right, calf_left/right.

BodySVG now accepts a face? prop. When set, the panel is locked to that face (no rotation), markers are filtered to that face, and the click handler restricts body-part matching to parts whose faces include the current face. When omitted (legacy / single mode), behaviour is unchanged — the body rotates with the patient position.

The header has a 4-button toggle group: Default / Front / Back / Front+Back (TH tooltips). Center panel switches between 1 and 2 SVG panels via CSS grid based on viewMode.

2. BodyMapperSection — drop-in wrapper

web/packages/miniapps/3d-health-anesthesia/BodyMapperSection.tsx. Context-agnostic. Owns:

  • Collapsible card chrome (per-context state in localStorage[bodyMapper:collapsed:<type>:<id>])
  • Marker persistence (per-context, localStorage[bodyMapper:markers:<type>:<id>])
  • View-mode preference (per-user, localStorage[bodyMapper:viewMode])
  • Header summary chips (marker count + active view-mode)

Props:

interface BodyMapperSectionProps {
  contextId: string;
  contextType: 'OR' | 'LR' | 'ER' | 'NURSING_HOME' | 'IPD' | 'ANESTHESIA_PRE' | 'CUSTOM';
  patientId?: string;
  patientName?: string;
  gender?: 'male' | 'female';
  initialMarkers?: AnesthesiaMarker[];
  defaultCollapsed?: boolean;
  defaultViewMode?: ViewMode;       // wrapper default: 'dual'
  readOnly?: boolean;
  title?: string;                    // default: 'Body Map / แผนภาพร่างกาย'
  onMarkersChange?: (markers: AnesthesiaMarker[]) => void;
}

Drop-in usage (one-liner per modal)

import { BodyMapperSection } from '@miniapps/3d-health-anesthesia';

<BodyMapperSection
  contextId={record._id}
  contextType="LR"                    // or "ER" / "NURSING_HOME" / "IPD" / "ANESTHESIA_PRE"
  patientId={record.patientRef?._id}
  patientName={record.patientRef?.fullName}
  gender={record.patientRef?.gender === 'F' ? 'female' : 'male'}
  defaultViewMode="dual"               // optional; per-user pref overrides once set
/>

Currently mounted in

Surface File Position
OR Time-Out (intra-op nursing) web/packages/periops-kit/src/operating-room/.../time-out/IntraoperativeNursingData.tsx Between InjuryRisk and FocusList
LR Time-Out (intra-op nursing) web/packages/periops-kit/src/labour-room/.../time-out/IntraoperativeNursingData.tsx Above FocusList
LR Time-Out (duplicated path) web/packages/periops-kit/src/labour-room/labour-room/.../time-out/IntraoperativeNursingData.tsx Same

Not yet wired (intentional follow-ups)

  1. ER encounter modal — same one-liner, contextType="ER". Pending identification of the right mount file in the ER module.
  2. Nursing home roundscontextType="NURSING_HOME". Natural home is the rounds note dialog.
  3. Backend persistence — markers cache to localStorage today. When bodyMarkers[] lands on Mongo + Supabase, swap the storage layer in BodyMapperSection.tsx without touching any mount site.
  4. Pattern B/C field-level integration — the OR-modal free-text site fields (drainSite, locationPack, specimen.csPieces[i].note, etc.) do not yet pre-populate the mapper or vice versa. The onMarkersChange callback is the integration seam when this work lands.
  5. face field for NERVE_BLOCK_SITES — quick-add nerve blocks currently land on the inferred face. Adding an explicit face to each preset will auto-pin Sciatic on posterior, Femoral on anterior, etc.
  6. Lateral profiles (quad mode) — the ViewMode union is deliberately capped at 4 modes (single / front / back / dual) to avoid shipping a broken quad toggle. Lateral SVG outlines are a separate piece of work.
Ask Anything