08-portals-and-i18nwave: W5filled14 citations

Nurse Portal — Clinical Workflow and Vitals

Audiences: clinical-buyer, developer

Nurse Portal — Clinical Workflow and Vitals

Pre-clinic and intra-clinic surface for nurses: today's dashboard, waiting room triage, vitals capture, patient list, intake forms, AI/voice assists, and secure messenger — gated to NURSE_ONLY.

Business Purpose

The nurse portal is where the patient is prepared for the doctor. Before the clinician sees the patient, the nurse has called them from the waiting room, taken vitals, reviewed the intake form, and updated the chart. Each of those interactions used to be paper-and-clipboard; the nurse portal converts them into structured data that flows directly into the encounter the doctor opens five minutes later.

Without the nurse portal, that prep work either does not happen (the doctor pays the time tax) or it happens but stays on paper (the data is lost). With it, vitals are captured in the same FHIR encounter the doctor will open, waiting-room status updates move appointments forward, and the clinic's throughput grows without adding clinicians.

Audiences

  • Clinical buyer (clinic admin): nurse productivity surface — direct waiting-room control, vitals at the same record as the visit, and intake handoff that closes the loop with the receptionist (check-in) and the doctor (encounter open).
  • Doctor: the source of pre-encounter prep — vitals and intake answers are already attached to the chart by the time the doctor enters the room.
  • Developer/partner: explains the nurse routes, the NURSE_ONLY gate, and which pages are currently placeholders ("coming soon") vs. fully implemented.
  • Internal (ops/support): where vitals land, how the waiting room state machine drives status changes, and how nurse-to-doctor handoff is recorded.

Architecture

Routes

diagram
portal/nurse/
├── layout.tsx                  → createRoleLayout(NURSE_CONFIG, NurseNav)
├── page.tsx                    → root entry
├── dashboard/                  → today's KPIs + appointment count
├── waiting-room/               → triage list + status transitions
├── patients/                   → roster
├── vitals/                     → vitals capture (placeholder — "coming soon")
├── notes/                      → nurse notes
├── ai/                         → AI tooling entry (medical-tool-aware)
├── flowagent/                  → conversational intake/flow
├── forms/                      → fillable forms inbox
├── messages/, messenger/       → secure messaging
├── help/                       → help center
├── hr/                         → HR/admin
├── vivoxx/                     → voice/LiveKit surface for nurse-side dictation
├── profile/                    → identity + preferences
└── (error, loading, not-found) → standard route helpers

Layout composition

ddx-web/src/app/[locale]/portal/nurse/layout.tsx:13-17:

typescript
function NurseNav() { return <PortalNavigation roleKey="nurse" />; }
export default createRoleLayout(NURSE_CONFIG, NurseNav);

NURSE_CONFIG (ddx-web/src/lib/portal/role-configs.ts:41-51): authRequirement: 'NURSE_ONLY', roleBadge.icon: HeartbeatIcon, variant: 'success', brandNameKey: 'portals.nurse'. No taglineFormatter, no needsBackendStatus, no extraElements. The simplest of the clinical-portal configs.

Authorization shape (nuance)

  • Layout-level gate (NURSE_ONLY) runs via createRoleLayout → no non-nurse can reach any portal/nurse/* page.
  • Per-page gating varies: some pages call requireAuth(locale) only (e.g. dashboard/page.tsx:18, vitals/page.tsx:19), relying on the layout for role enforcement. The defense-in-depth pattern used in doctor + patient portals (requireRoleAccess('XYZ_ONLY', locale) at the page) is not consistently applied in the nurse portal as of the audit date. Layout enforcement is sufficient as long as every page is reached via the standard routing path — but a copy-pasted handler that uses an explicit role string would catch a misconfigured layout in audit.

Placeholder pages

Several nurse surfaces are not yet implemented and render the messages.comingSoon common key:

  • vitals/page.tsx:24-27 — renders <PageContainer> with the "coming soon" body.

These are intentional placeholders; the route exists so the navigation entry resolves and i18n keys are exercised.

Tech Stack & Choices

LayerChoiceWhy
Layout factorycreateRoleLayout(NURSE_CONFIG, NurseNav)Symmetric with other portals; one factory governs all.
NavigationPortalNavigation roleKey="nurse"Generic shared component; nurse-specific sections come from nav-configs.
Role gateNURSE_ONLY at the layoutBackstopped by backend RBAC (always authoritative).
Brand iconHeartbeatIcon (success variant)Reflects vitals + monitoring focus; distinct from doctor's HeartIcon primary.
i18n namespacenurseOne namespace per role; sub-namespaces (nurse.dashboard, nurse.waitingRoom) keep keys narrow.

Rejected: a custom nurse-specific layout (would diverge from the eleven-portal symmetry), per-page requireRoleAccess('NURSE_ONLY', locale) on every page (the layout already enforces; redundant calls add overhead).

Data Flow

  1. Nurse logs in → JWT carries role: NURSE.
  2. Browser hits /<locale>/portal/nurse/dashboard.
  3. proxy.ts:248-319 — locale prefix + session gate.
  4. portal/nurse/layout.tsx:17 runs createRoleLayout(NURSE_CONFIG, NurseNav): requireRoleAccess('NURSE_ONLY', locale) returns the nurse user, organization is fetched, <PortalShell> mounts with NurseNav sidebar.
  5. dashboard/page.tsx:16-29 awaits params, calls requireAuth(locale), renders <NurseDashboardClient locale={locale} userName={user.name} todayAppointmentsCount={0}>. The count currently displays 0 (placeholder pending live data wiring from the appointments service).
  6. Waiting room (waiting-room/page.tsx, 170 lines) — server-side fetch + client triage interactions, status transitions through the BFF.
  7. Vitals capture flows into the FHIR encounter on the same patient the doctor opens next. (Wiring covered under the W2 medical-cards topic and the doctor-portal command center.)

Implicated Code

  • ddx-web/src/app/[locale]/portal/nurse/layout.tsx:1-17createRoleLayout invocation with NurseNav wrapper around PortalNavigation.
  • ddx-web/src/lib/portal/role-configs.ts:41-51NURSE_CONFIG definition.
  • ddx-web/src/app/[locale]/portal/nurse/dashboard/page.tsx:16-29 — server-side requireAuth + delegation to NurseDashboardClient.
  • ddx-web/src/app/[locale]/portal/nurse/vitals/page.tsx:17-28 — placeholder "coming soon" page using messages.comingSoon common key.
  • ddx-web/src/lib/api/rbac-config.ts:36NURSE: NURSE_PERMISSIONS registration.
  • ddx-web/src/app/[locale]/portal/nurse/waiting-room/page.tsx (170 lines) — full waiting-room triage server component.

Operational Notes

  • Layout-level role gateNURSE_CONFIG.authRequirement = 'NURSE_ONLY' is the single point of role enforcement for most nurse pages. Removing or weakening this gate exposes every child page; never edit the auth requirement without the security review.
  • Placeholder pages render messages.comingSoon — when a stakeholder reports "vitals page is empty", confirm against vitals/page.tsx:24-27 before assuming a regression — it's intentional until vitals wiring lands.
  • Defense-in-depth audit (improvement opportunity) — adding requireRoleAccess('NURSE_ONLY', locale) per page (in addition to the layout gate) would mirror the doctor/patient pattern and is an easy hardening win.
  • PortalNavigation roleKey="nurse" — the nurse-side nav resolves through getNavConfigByRole('nurse') in lib/portal/nav-configs. Adding a new nurse surface requires both a route and a nav-config entry (with nurse.* i18n keys in all three locales).
  • No (with-nav)/ group — the nurse portal has no chrome-free siblings, so the layout is a single role factory call — simpler than the doctor portal.