Nexus Docs Documentation for the Nexus hospital management system

Guides

ICU

The first of the four specialty boards — critical-care patient chart, vitals, and orders.

Two parts:

  • Part A — Business level. What ICU staff are responsible for, what the screen looks like when they sign in, and how the day flows.
  • Part B — Technical level. What the routes, components, RBAC, and APIs do under the hood.

This guide complements docs/WORKFLOWS.md (cross-department flows) and docs/ADMISSIONS_OFFICER_GUIDE.md (reception side).


Part A — Business level

A.1 What an ICU doctor / nurse owns

ResponsibilityDetail
ICU boardThe single screen they sign in to. One row per ICU patient, colour-coded by severity (critical / serious / stable).
Patient chartA popup that opens when they click a patient name. Holds vitals, allergies, conditions, nursing assessments, current medications, lab/imaging orders, results, care plans, consents.
Recording observationsThey can add new vitals, allergies, conditions, and nursing assessments directly from the chart popup — no page hop.
Placing ordersThe + Order button on each row opens Quick Orders (Prescription / Lab / Imaging). The order is routed to Pharmacy / Laboratory / Radiology, but those receiving departments are invisible to the ICU clinician.
Transfer to wardWhen the patient stabilises, the row’s Transfer to Ward button moves them out of ICU into a regular wing.
DischargeWhen the patient is going home from ICU directly (rare), the Discharge button finalises the encounter.

A.2 What they cannot do

The ICU shell is intentionally narrow — it’s a kiosk, not a full clinical workstation:

  • No sidebar, no global navigation, no dashboard.
  • No access to lab, pharmacy, radiology, billing, HR, or admissions screens.
  • No access to other wards (Emergency, Maternity, Operations).
  • No access to patients who are not in ICU.

If they try to type a forbidden URL into the address bar, the system responds with the standard Forbidden page.

A.3 What the screen looks like

When an ICU doctor or nurse logs in:

┌─────────────────────────────────────────────────────────────────────────────┐
│ M  Mando · ICU      [ Intensive Care ]                  🔔   Dr. Layla H ┄  │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│   Intensive Care Unit                                  + ICU Admission     │
│   3 patients in ICU • 1 critical                                            │
│                                                                             │
│   ┌─ Critical (1) ─────────────────────────────────────────────────────┐   │
│   │  John Smith    Dr. Karim N • Room 401                  [critical]  │   │
│   │  Ventilator: BiPAP   O2 Flow: 6 L/min   Vitals: 92/58, 110, 39°C   │   │
│   │  ────────────────────────────────────────  [+ Order] [Transfer]    │   │
│   └────────────────────────────────────────────────────────────────────┘   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘

Top bar: brand → single static tab “Intensive Care” → notification bell → user pill → Sign out.

The bell still fires for admission alerts, deposit-collected callbacks, lab/imaging acknowledgements that target the doctor.

A.4 The day, end to end

  1. Sign in. The system bounces them straight to /icu. There is no dashboard intermediate step.
  2. Read the board. A coloured strip on each card says critical / serious / stable. The header counts all three.
  3. Click the patient name. A popup opens with ten clinical category cards (Vitals, Allergies, Conditions, Nursing Assess., Medications, Lab Orders, Imaging Orders, Results, Care Plans, Consents). Each card shows a count.
  4. Click a category. The popup swaps content to that category’s list. If the category is editable (Vitals / Allergies / Conditions / Nursing Assess.), an + Add button appears top-right of the list. Read-only categories (Medications, Orders, Results, Care Plans, Consents) just show the existing items.
  5. Add a vital (or allergy / condition / nursing score). The form opens inline below the list header. Quick chips at the top of the vitals form (BP, HR, Temp, SpO2, RR, Wt, Pain) prefill the LOINC code, display name, and unit so the user only has to type the value.
  6. Save. The list refreshes; the count badge on the category card updates.
  7. Place an order. Back on the board, click + Order on the row. Three tabs: Prescription, Lab, Imaging. Submit. Pharmacy / Lab / Radiology gets the alert; the ICU clinician sees nothing about who’s working on it until results show up under Results.
  8. Transfer or discharge when ready.

A.5 Notifications

Inbound (the bell flashes):

  • New ICU admission landed on the wing — fired by the Reception/Admissions Officer at admit time.
  • A stat lab result was finalised on one of their patients (planned — see WORKFLOWS.md §14).
  • A prior-auth or eligibility check came back relevant to one of their patients.

Outbound (they create them implicitly):

  • Placing a Lab order notifies Laboratory.
  • Placing an Imaging order notifies Radiology.
  • A new prescription enters the MAR / pharmacy queue.

A.6 Common pitfalls

  • “I don’t see a sidebar.” Correct — the ICU role is kiosk-style. Click the patient name on the board to drill in.
  • “I tried to open the lab page and got Forbidden.” Correct — ICU staff can place orders but never browse lab/pharmacy/radiology. Use + Order on the patient row instead.
  • “The patient I’m looking for isn’t in the list.” They aren’t admitted to ICU. Reception admits patients into ICU using admission_type = icu — until that happens, the patient won’t appear on this board.
  • “Why is the count badge 0 on Allergies but the patient clearly has allergies?” The patient may have allergies recorded against a prior chart that wasn’t migrated. Click the card → + Add to record the current set.

Part B — Technical level

B.1 Identity & ward shell

A user is treated as an “ICU ward user” when all of these are true:

  • users.role is doctor or nurse.
  • users.department (case-insensitive, trimmed) is one of: icu, critical care, intensive care, cardiology, neurology.

This list is the same as WARD_NAV in frontend/src/services/role-permissions.js. The frontend check lives in AppRoot.isICUWardUser.

When isICUWardUser(user) returns true, app-root.js:

  1. Sets the post-login home to #/icu (skips /dashboard).
  2. Bounces any #/dashboard hit back to #/icu.
  3. Renders renderICUWardShell() instead of the sidebar shell — top bar with brand, a single static “Intensive Care” tab, the notification bell, the user pill, and Sign out. No <sidebar-nav>, no <patient-search>, no breadcrumb.
  4. The simple-content slot just hosts <icu-page> directly. Hash navigation still works for the forbidden-page fallback.

B.2 Seed data

Four ward-doctor logins are created idempotently on every boot by ensureWardDoctors. For ICU specifically:

  • email: icu.doctor@hospital.com
  • password: admin123
  • name: Dr. Layla Haddad
  • department: ICU
  • role: doctor

Idempotency:

  • users.email is UNIQUE → INSERT uses ON CONFLICT (email) DO NOTHING.
  • doctors row is added via INSERT … WHERE NOT EXISTS (SELECT 1 FROM doctors WHERE email = ?).

Nurses are seeded the same way once a user record with role='nurse' and department='ICU' is created (no nurse seed today — add via /hr or /users).

B.3 RBAC

Backend (path-based middleware): the doctor and nurse role permission tables in backend/internal/middleware/roles.go explicitly omit lab, pharmacy, pharmacy-dispense, radiology, general-inventory. They allow prescriptions, service-requests, diagnostic-reports, imaging-studies, observations, allergies, conditions, nursing-assessments, care-plans, consents, plus the ward routes (icu, emergency, maternity, operations) and the utility routes (oncall, beds, wings:GET).

Frontend: getNavigationForUser(user) in frontend/src/services/role-permissions.js narrows frontDesk to ['icu'] for any doctor/nurse whose department resolves to ICU. Other wards are not in their nav.

B.4 ICU board

frontend/src/pages/icu-page.js is unchanged in shape:

  • loadData() calls GET /api/icu (cases), GET /api/admissions?status=admitted, GET /api/wings — in parallel.
  • cases[] is the source of truth for the row list.
  • _isWardUser getter (new) hides the Quick Navigation chip-bar for doctors and nurses, since those chips routed to pages outside their permission set.
  • Patient name click now calls openChart(c)<icu-patient-chart>.show(patient_id, patient_name, admission_id) instead of the older <patient-encounter-popup>. The popup is still imported and stays available, but the default click target is the chart.

B.5 ICU patient chart popup

frontend/src/components/icu-patient-chart.js is the new kiosk-friendly drill-down. Two views inside one panel:

Grid view (default after show() is called):

  • Header: name • MRN • gender • DOB. “All categories” button hidden.
  • Info bar: gender, DOB, blood type, phone, admission #.
  • Body: 10 category cards in a CSS-grid. Each card has icon + label + hint + count badge.
  • Counts are loaded by loadCounts() — fires nine list endpoints in parallel and bins service-requests into lab/imaging by category substring.

Detail view (after a card click):

  • Header gains an “All categories” button that calls backToGrid() (which also re-fetches counts).
  • Body shows <h3> + count pill + (if editable) + Add button + the list + (if showAdd) the inline add form.
  • Each list item is rendered by renderItem(catId, x) — one switch arm per category, with severity / criticality / priority badges where they apply.

B.6 Categories — list and create endpoints

CardList APIAdd APIEditable
VitalsGET /api/observations/patient/:id/vitalsPOST /api/observations category: vital-signsyes
AllergiesGET /api/allergies/patient/:idPOST /api/allergiesyes
ConditionsGET /api/conditions/patient/:idPOST /api/conditionsyes
Nursing Assess.GET /api/nursing-assessments/patient/:idPOST /api/nursing-assessmentsyes
MedicationsGET /api/prescriptions?patient_id=(use Quick Orders → Prescription)no
Lab OrdersGET /api/service-requests?patient_id= filter category ~ “lab”(use Quick Orders → Lab)no
Imaging Orderssame, filter category ~ “rad|imag”(use Quick Orders → Imaging)no
ResultsGET /api/diagnostic-reports?patient_id=n/ano
Care PlansGET /api/care-plans?patient_id=(use the care-plan workflow)no
ConsentsGET /api/consents?patient_id=(use the consents workflow)no

For each editable category the chart’s submitAdd(e) method assembles the FHIR-shaped payload and POSTs it. On success it re-fetches the active category and re-runs loadCounts() so the badge on the card matches.

B.7 Vitals quick chips

The vitals add form has seven preset chips above the inputs. Clicking one calls applyVitalPreset(preset), which fills code, display, and unit for that vital (LOINC codes):

Chipcodedisplayunit
BP85354-9Blood PressuremmHg
HR8867-4Heart Ratebpm
Temp8310-5Body Temperature°C
SpO259408-5Oxygen Saturation%
RR9279-1Respiratory Rate/min
Wt29463-7Body Weightkg
Pain38208-5Pain Severity0-10

The user only types the value, then Save. BP values can be entered as 120/80submitAdd parses via parseFloat and falls back to value_string when not numeric.

B.8 Quick Orders (placed from the board, not the chart)

The + Order button on each ICU row opens frontend/src/components/quick-orders.js. Submitting routes to:

TabEndpointReceiving alert
PrescriptionPOST /api/prescriptions source=icuPharmacy queue
LabPOST /api/service-requests category=laboratoryLaboratory department
ImagingPOST /api/service-requests category=radiologyRadiology department

The receiving department’s notification is fired in backend/internal/handlers/service-requests.go by notifyOrderRouted(...) immediately after the row is inserted. ICU staff never see the receiving department’s worklist.

B.9 Build / verification

  • Backend: go build ./... — clean.
  • Frontend: npx vite build — 1.34 MB / 205 kB gzip in ~3s.

B.10 Known follow-ups (not done yet)

  • Per-patient row-level filter on reads — RBAC today is path-based; an ICU doctor with the URL of a non-ICU patient could fetch that patient’s data via /api/patients/:id. Tighten by joining on the active encounter’s department.
  • Critical-result back-alert when a stat lab is finalised — currently the doctor sees it only if they re-open the chart. Plan: notify the ordering doctor on diagnostic-report finalisation.
  • “All my orders” widget — a single feed showing every order this doctor has placed across all their patients.
  • Apply the same kiosk shell + chart popup pattern to Emergency / Maternity / Operations once the ICU rollout settles.

Last updated: 2026-04-26