diff --git a/README.md b/README.md index b76de4e..2fa787a 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ A production-quality clinical backend built with ASP.NET Core 8, PostgreSQL, Apache Kafka, RabbitMQ, Elasticsearch, Redis, and MinIO. The domain models the observe-alert-acknowledge lifecycle at the center of any clinical monitoring system: patient encounters, continuous vital sign and lab result ingest, real-time sepsis and NEWS2 scoring, and clinician notification with automatic escalation. -**Implementation status:** Twenty-nine planned phases are complete through Phase 31 (plus Phases 20–22) — from schema and CRUD through Kafka, Elasticsearch CQRS, sepsis detection, RabbitMQ paging with DLQ escalation, reconciliation jobs, Prometheus/Grafana observability, the MinIO Parquet data lake, clinical data model expansion, warning alerts and orders, the NEWS2 composite scoring engine, trend detection with alert suppression, qSOFA bedside screening, medication administration with alert correlation annotations, the console replay simulator, the **Vue 3 ward dashboard**, clinician feedback mode, **Glasgow Coma Scale (GCS) scoring**, **SOFA organ-dysfunction scoring with baseline tracking and delta sepsis alerts**, the **Sepsis-3 clinical refactor** (SIRS removed, qSOFA repositioned as screening, SOFA delta ≥ 2 triggers bundles), **frontend GCS entry and SOFA display**, **expanded simulator scenarios with clinical validation**, the **Site & Gateway Registry** with dual authentication, shared clinical sync contracts, and fleet health Prometheus gauges, the **Ward Gateway Service** (local-first clinical path with offline buffering and central sync), the **FHIR R4 Inbound Facade** for EHR integration, **Role-Based Access Control (RBAC) with clinical audit logging**, and the **Dashboard Gap Analysis Fixes** (SOFA/GCS/qSOFA history charts, patient banner, encounter timeline, medication markers on vital charts). Post-phase hardening includes health check endpoints, Kafka poison pill protection, outbox dead-letter with retry tracking, data lake partial-commit safety, MRN sequence-based generation, FHIR bundle transaction rollback, **FHIR R4 read/search endpoints** (Patient and Encounter), **alert threshold deletion with audit trail**, **FHIR API key rotation** (constant-time multi-key validation), **authorization failure logging** with Prometheus metrics, **JWT signing key validation** at startup, and **concurrency hardening** (transactional sepsis bundle creation, unique active encounter constraint). See [Implemented Phases](#implemented-phases) for the full breakdown. Guides: [dashboard-guide.md](docs/dashboard-guide.md) (technical), [clinical-testing-guide.md](docs/clinical-testing-guide.md) (doctors & nurses). +**Implementation status:** Twenty-nine planned phases are complete through Phase 31 (plus Phases 20–22) — from schema and CRUD through Kafka, Elasticsearch CQRS, sepsis detection, RabbitMQ paging with DLQ escalation, reconciliation jobs, Prometheus/Grafana observability, the MinIO Parquet data lake, clinical data model expansion, warning alerts and orders, the NEWS2 composite scoring engine, trend detection with alert suppression, qSOFA bedside screening, medication administration with alert correlation annotations, the console replay simulator, the **Vue 3 ward dashboard**, clinician feedback mode, **Glasgow Coma Scale (GCS) scoring**, **SOFA organ-dysfunction scoring with baseline tracking and delta sepsis alerts**, the **Sepsis-3 clinical refactor** (SIRS removed, qSOFA repositioned as screening, SOFA delta ≥ 2 triggers bundles), **frontend GCS entry and SOFA display**, **expanded simulator scenarios with clinical validation**, the **Site & Gateway Registry** with dual authentication, shared clinical sync contracts, and fleet health Prometheus gauges, the **Ward Gateway Service** (local-first clinical path with offline buffering and central sync), the **FHIR R4 Inbound Facade** for EHR integration, **Role-Based Access Control (RBAC) with clinical audit logging**, the **Dashboard Gap Analysis Fixes** (SOFA/GCS/qSOFA history charts, patient banner, encounter timeline, medication markers on vital charts), and the **Enhanced Dashboard** (department overview, sepsis bundle board, critical alert notifications, shift handoff reports, vitals entry form, sortable/filterable ward table). Post-phase hardening includes health check endpoints, Kafka poison pill protection, outbox dead-letter with retry tracking, data lake partial-commit safety, MRN sequence-based generation, FHIR bundle transaction rollback, **FHIR R4 read/search endpoints** (Patient and Encounter), **alert threshold deletion with audit trail**, **FHIR API key rotation** (constant-time multi-key validation), **authorization failure logging** with Prometheus metrics, **JWT signing key validation** at startup, and **concurrency hardening** (transactional sepsis bundle creation, unique active encounter constraint). See [Implemented Phases](#implemented-phases) for the full breakdown. Guides: [dashboard-guide.md](docs/dashboard-guide.md) (technical), [clinical-testing-guide.md](docs/clinical-testing-guide.md) (doctors & nurses). ## Domain Model — How It Maps to a Real Clinical System @@ -71,8 +71,8 @@ An `OutboxEvent` is written in the same transaction as any observation or alert, - **Alert Suppression Windows** — acknowledging a suppressible alert (`WARNING_*`, `NEWS2_WARNING`) sets a Redis key `suppress:{encounterId}:{alertType}` with a configurable TTL (default 30 min from `AlertSuppression` config; optional per-code override via `alert_thresholds.suppression_window_minutes`); `WarningEvaluator` and `News2Detector` check suppression before creating new warning alerts; critical alerts (`CRITICAL_*`, `NEWS2_EMERGENCY`, `SEPSIS_WARNING`, `RAPID_DETERIORATION`) are never suppressed; observations and NEWS2 scores continue to persist during suppression; Prometheus `alert_suppressions_total` - **Medication Administration** — `POST /encounters/:id/medications` records drug administrations (name, dose, route, timestamp, administered-by); `GET /encounters/:id/medications` lists with optional `since` filter; `GET /medications/:id` detail; active-encounter guard; FluentValidation on request DTOs - **Medication Correlation Annotations** — `MedicationCorrelationHelper` appends medication context to warning and NEWS2 alert details when a mapped drug was administered within the correlation window (default 90 min); drug-to-vital mappings in `MedicationCorrelation` config (`appsettings.json`); annotates rather than suppresses — alerts still fire; sepsis, trend, and critical sync-path alerts are never annotated; design rationale in `docs/decisions/medication-correlation-design.md` -- **Ward Dashboard APIs** — `GET /encounters` returns paginated `WardEncounterSummary` rows (patient name/MRN, room/bed, department, status, latest NEWS2 score, live qSOFA criteria count from Redis, sepsis bundle status, open alert count); filterable by `status` and `department`; `GET /encounters/:id/qsofa/current` exposes Redis-backed qSOFA state; CORS policy `Dashboard` allows configured origins (default `http://localhost:5173`) -- **Ward Dashboard Frontend** — Vue 3 SPA (`vigilcare-dashboard/`) with virtual ward table (NEWS2-sorted), patient detail (vitals, scores, alerts, orders, sepsis bundle, GCS entry form, SOFA score panel, patient banner with demographics/allergies/emergency contact, encounter timeline), alert center (global acknowledge/resolve), vital sign trend charts with medication administration markers and local replay scrubbing, NEWS2 history chart, SOFA history chart with organ-system breakdown, GCS history chart with component tracking, qSOFA evaluation history, alert reasoning with optional medication context, and clinician feedback on every alert; polls API every 5–10 s; guides in `docs/dashboard-guide.md` and `docs/clinical-testing-guide.md` +- **Ward Dashboard APIs** — `GET /encounters` returns paginated `WardEncounterSummary` rows (patient name/MRN, room/bed, department, status, latest NEWS2 score, live qSOFA criteria count from Redis, sepsis bundle status, open alert count, SOFA score/delta, GCS score/classification, attending physician, admitted-at, last observation time); filterable by `status` and `department`; `GET /encounters/:id/qsofa/current` exposes Redis-backed qSOFA state; `GET /sepsis-bundles` lists bundles hospital-wide with optional `status` filter (returns `SepsisBundleSummary` with patient demographics, elements, and deadlines); CORS policy `Dashboard` allows configured origins (default `http://localhost:5173`) +- **Ward Dashboard Frontend** — Vue 3 SPA (`vigilcare-dashboard/`) with virtual ward table (multi-column sortable, patient search, quick-filters for critical/alerts/sepsis), patient detail (vitals, scores, alerts, orders, sepsis bundle, GCS entry form, SOFA score panel, patient banner with demographics/allergies/emergency contact, encounter timeline, vitals entry form for manual observation recording), alert center (global acknowledge/resolve with role-aware modal and acknowledgment note preview), department overview (unit-level snapshot cards with acuity bars, patient/alert/bundle counts per department), sepsis bundle board (real-time countdown timers, on-track/at-risk/overdue urgency sorting), critical alert banner with browser notifications and audible tone, shift handoff report generator (SBAR format with ward summary, exportable via print/PDF), vital sign trend charts with medication administration markers and local replay scrubbing, NEWS2 history chart, SOFA history chart with organ-system breakdown, GCS history chart with component tracking, qSOFA evaluation history, alert reasoning with optional medication context, and clinician feedback on every alert; polls API every 5–10 s; guides in `docs/dashboard-guide.md` and `docs/clinical-testing-guide.md` - **FHIR R4 Inbound Facade** — `POST /fhir/R4/{Patient,Encounter,Observation,MedicationAdministration}` accepts FHIR R4 JSON resources (`application/fhir+json`); `POST /fhir/R4` processes transaction Bundles (Patient → Encounter → Observation in dependency order); `GET /fhir/R4/metadata` returns a CapabilityStatement; LOINC-to-internal code mapping (19 observation codes + SNOMED CT fallbacks); Fahrenheit-to-Celsius unit conversion; `ExternalResourceIdentifier` table links hospital MRNs and visit numbers to internal UUIDs for idempotent upserts; `FhirApiKeyOrJwtMiddleware` authenticates via JWT bearer or `X-Api-Key` header (supports multiple keys via `Fhir:ApiKeys` array for zero-downtime rotation; constant-time comparison via `CryptographicOperations.FixedTimeEquals`); `FhirExceptionFilter` returns FHIR `OperationOutcome` on errors; configurable identifier systems, department codes, and encounter class mappings via `Fhir` config section; Prometheus `fhir_ingest_total` and `fhir_mapping_errors_total`; integration guide for Mirth Connect HL7v2→FHIR channels in `docs/integration/mirth-fhir-channels.md` - **FHIR R4 Read/Search** — `GET /fhir/R4/Patient/{id}` reads a Patient by internal ID; `GET /fhir/R4/Patient` searches by `identifier` (system|value) or lists all patients; `GET /fhir/R4/Encounter/{id}` reads an Encounter by internal ID; `GET /fhir/R4/Encounter` searches by `patient` (UUID) and/or `status` (`in-progress`, `finished`, `cancelled`); all return FHIR R4 JSON (`application/fhir+json`); search endpoints return `Bundle.type=searchset`; requires `fhir:read` permission (Admin and Integration roles); internal resources mapped back to FHIR via `PatientFhirMapper.ToFhirResponse` / `EncounterFhirMapper.ToFhirResponse` with hospital identifier resolution; Prometheus `fhir_read_total` counter with `resource_type`, `interaction`, `outcome` labels - **Role-Based Access Control (RBAC)** — JWT bearer authentication (`POST /auth/login`); four clinical roles (`Nurse`, `Physician`, `Admin`, `Integration`) with 17 granular permissions (`patients:read`, `alerts:acknowledge`, `thresholds:write`, `fhir:ingest`, `fhir:read`, `audit:read`, etc.); `AuthorizePermission` attribute on every controller action; `PermissionAuthorizationHandler` resolves role → permission at runtime from `ClinicalRolePermissionMap` and logs authorization failures with structured details (user, role, permission, endpoint) plus `authorization_failures_total` Prometheus counter; `CurrentUserService` exposes authenticated identity (user ID, display name, role, IP address) to services; nurses and physicians get clinical read/write permissions; admins additionally get `thresholds:write`, `fhir:read`, `audit:read`, and `users:admin`; integration accounts get FHIR ingest and read access; FHIR endpoints accept both JWT and `X-Api-Key` authentication via `FhirApiKeyOrJwtMiddleware`; alert `acknowledgedBy` is set from the authenticated user identity, not the request body; startup validates JWT signing key is at least 256 bits (HMAC-SHA256 minimum); four seeded demo users (`nurse.demo`, `physician.demo`, `admin.demo`, `integration.mirth`); frontend login page with `localStorage` token persistence and automatic `Authorization: Bearer` header injection @@ -551,13 +551,23 @@ VigilCare.Simulator/ # Phase 16 — console replay vigilcare-dashboard/ # Phases 17–19, 22, 27–28, 31 — Vue 3 ward dashboard SPA ├── src/ -│ ├── api/ # HTTP client (auto Bearer header), encounters, clinical (GCS, SOFA, qSOFA history), alerts, normalize -│ ├── components/ # charts (SofaHistory, GcsHistory, QsofaHistory, VitalChart with medication markers), replay, alerts, feedback, patient (GcsEntryForm, SofaScorePanel, PatientBanner, EncounterTimeline), ward, layout, ui -│ ├── composables/ # useChartData, useReplayControls, usePolling, useFeedback, useGcs, useSofa, chartFormat, patientFormat, timelineFormat, chartMedications +│ ├── api/ # HTTP client (auto Bearer header), encounters, clinical (GCS, SOFA, qSOFA history), alerts, analytics, sepsis, normalize +│ ├── components/ +│ │ ├── alerts/ # AlertCard, AcknowledgeModal (role-aware), CriticalAlertBanner (browser notifications + audible tone) +│ │ ├── charts/ # SofaHistory, GcsHistory, QsofaHistory, VitalChart with medication markers +│ │ ├── departments/ # DepartmentCard, AcuityBar (unit-level snapshot) +│ │ ├── feedback/ # FeedbackButtons, FeedbackSummary +│ │ ├── layout/ # AppShell, AppHeader, AppSidebar +│ │ ├── patient/ # GcsEntryForm, SofaScorePanel, PatientBanner, EncounterTimeline, VitalsEntryForm, VitalsPanel, AlertsList +│ │ ├── replay/ # ReplayControls +│ │ ├── sepsis/ # SepsisBundleTable, SepsisBundleRow, SepsisBundleCard (countdown timer) +│ │ ├── ward/ # WardTable (sortable headers), PatientRow, PatientCard, WardToolbar, SortableHeader, HandoffReport (SBAR + print) +│ │ └── ui/ # Button, Card, Badge, Skeleton, EmptyState +│ ├── composables/ # useChartData, useReplayControls, usePolling, useFeedback, useGcs, useSofa, chartFormat, patientFormat, timelineFormat, chartMedications, wardSort, wardFilter, departmentFormat, sepsisFormat, alertAcknowledge, criticalAlertDetect, useAlertNotification, useCriticalAlertPolling, handoffReport, vitalsForm │ ├── plugins/ # medicationMarkerPlugin (Chart.js plugin for medication administration markers on vital charts) -│ ├── stores/ # Pinia — ward, alerts, settings, feedback, scoring, auth (localStorage token + user) -│ ├── views/ # LoginView, WardDashboard, PatientDetail, AlertCenter, FeedbackSummary -│ └── __tests__/ # Vitest — tests (store, feedback, replay, charts, alerts, ward, GCS, SOFA, qSOFA, PatientBanner, EncounterTimeline, patientFormat, timelineFormat, chartMedications) +│ ├── stores/ # Pinia — ward (sort + filter + search), alerts (banner + polling), settings (sort prefs + sound mute), feedback, scoring, auth, departments, sepsis +│ ├── views/ # LoginView, WardDashboard, PatientDetail, AlertCenter, FeedbackSummary, DepartmentOverviewView, SepsisBoardView +│ └── __tests__/ # Vitest — tests (store, feedback, replay, charts, alerts, ward, GCS, SOFA, qSOFA, PatientBanner, EncounterTimeline, patientFormat, timelineFormat, chartMedications, wardSort, wardFilter, departmentFormat, sepsisFormat, alertAcknowledge, criticalAlertDetect, HandoffReport, handoffReport, VitalsEntryForm, vitalsForm, useAlertStore, useWardStore, DepartmentOverviewView, SepsisBoardView, AcknowledgeModal, CriticalAlertBanner) ├── vite.config.js └── README.md # Dev quick start → docs/dashboard-guide.md @@ -1036,7 +1046,7 @@ Error response: **GET `/encounters` query params:** `status` (DB literal, e.g. `ACTIVE`), `department` (DB literal, e.g. `ICU`), `page`, `pageSize` -**Ward summary fields:** `encounterId`, `patientId`, `mrn`, `firstName`, `lastName`, `roomBed`, `department`, `status`, `news2Score`, `news2RiskLevel`, `qsofaScore`, `sepsisActive`, `sepsisBundleStatus`, `openAlertCount` +**Ward summary fields:** `encounterId`, `patientId`, `mrn`, `firstName`, `lastName`, `roomBed`, `department`, `status`, `news2Score`, `news2RiskLevel`, `qsofaScore`, `sepsisActive`, `sepsisBundleStatus`, `openAlertCount`, `sofaScore`, `sofaDelta`, `gcsScore`, `gcsClassification`, `lastObservationAt`, `attendingPhysician`, `admittedAt` **Encounter status machine:** @@ -1273,6 +1283,7 @@ SOFA scores are computed asynchronously by `SofaScoringService` from SOFA-relate | Method | Path | Description | |---|---|---| +| GET | `/sepsis-bundles` | Paginated list of bundles hospital-wide; optional `status` filter (`IN_PROGRESS`, `COMPLIANT`, `NON_COMPLIANT`); returns `SepsisBundleSummary` with patient demographics, elements, and deadlines | | GET | `/encounters/{id}/sepsis-bundle/current` | Current (most recent) sepsis bundle for an encounter (404 if none) | | GET | `/sepsis-bundles/{id}` | Bundle detail with all elements and linked orders | @@ -2034,7 +2045,7 @@ Observation history uses cursor pagination on `(recorded_at DESC, id DESC)`. Off ## Implemented Phases -Twenty-nine phases from the project roadmap are implemented and verified, including the **Site & Gateway Registry** (Phase 20), the **Ward Gateway Service** (Phase 21), the **Dashboard Gap Analysis Fixes** (Phase 22), the **Sepsis-3 clinical refactor** (Phases 27–29), the **FHIR R4 Inbound Facade** (Phase 30), and **RBAC with clinical audit logging** (Phase 31). Integration tests (`dotnet test`) and per-phase verification scripts cover Phases 8–15, 20–22, 25–31. Phases 17–19 add the Vue dashboard and clinician feedback (Vitest in `vigilcare-dashboard/`). +Twenty-nine phases from the project roadmap are implemented and verified, including the **Site & Gateway Registry** (Phase 20), the **Ward Gateway Service** (Phase 21), the **Dashboard Gap Analysis Fixes** (Phase 22), the **Sepsis-3 clinical refactor** (Phases 27–29), the **FHIR R4 Inbound Facade** (Phase 30), **RBAC with clinical audit logging** (Phase 31), and the **Enhanced Dashboard** (department overview, sepsis bundle board, critical alert notifications, shift handoff reports, vitals entry, sortable/filterable ward table). Integration tests (`dotnet test`) and per-phase verification scripts cover Phases 8–15, 20–22, 25–31. Phases 17–19 add the Vue dashboard and clinician feedback (Vitest in `vigilcare-dashboard/`). | Phase | Feature | Status | |---|---|---| @@ -2068,7 +2079,9 @@ Twenty-nine phases from the project roadmap are implemented and verified, includ | 30 | **FHIR R4 Inbound Facade** — `FhirIngestController` (`POST /fhir/R4/{Patient,Encounter,Observation,MedicationAdministration}`); `FhirMetadataController` (CapabilityStatement); `FhirBundleProcessor` (transaction Bundles in dependency order); `LoincCodeMapper` (19 LOINC + 3 SNOMED CT → internal codes); `FhirUnitConverter` (°F→°C); `ExternalResourceIdentifier` table + `ExternalIdentifierService` for hospital MRN/visit number ↔ internal UUID linking; `FhirApiKeyMiddleware` (`X-Api-Key` auth); `FhirExceptionFilter` (→ OperationOutcome); `PatientFhirMapper`, `EncounterFhirMapper`, `ObservationFhirMapper`, `MedicationAdministrationFhirMapper`, `FhirReferenceResolver`; idempotent patient/encounter upserts (`RegisterOrUpdateByIdentifierAsync`, `OpenOrUpdateByIdentifierAsync`); configurable identifier systems, department codes, encounter class maps (`FhirOptions`); Prometheus `fhir_ingest_total`, `fhir_mapping_errors_total`; Mirth Connect integration guide; `FhirIngestTests`; `run-phase30-verification.sh` | Done | | 31 | **RBAC + Clinical Audit Logging** — JWT bearer authentication (`AuthService`, `AuthController`); four clinical roles (`Nurse`, `Physician`, `Admin`, `Integration`) with 17 granular permissions; `AuthorizePermission` attribute on all controller actions; `PermissionAuthorizationHandler` + `PermissionPolicyProvider` resolve `perm:*` policies; `CurrentUserService` extracts identity from JWT claims; `ClinicalUser` entity with BCrypt password hashing; `ClinicalAuditLog` append-only table with before/after JSONB, user identity, IP, and correlation ID; `AuditService` writes log entries on clinical write actions (10 audit actions); `AuditLogsController` admin-only query with filters; `FhirApiKeyOrJwtMiddleware` dual auth for FHIR routes (JWT or X-Api-Key with multi-key rotation); alert `acknowledgedBy` set from authenticated user, not request body; four seeded demo users; frontend `LoginView` + `auth` Pinia store with `localStorage` token persistence; Vue router auth guard; `RbacTests`; `run-phase31-verification.sh` | Done | -**Ward dashboard:** backend APIs (`GET /encounters` ward list, `GET /qsofa/current`, `GET /qsofa/history`, `GET /gcs/history`, CORS) and frontend SPA — `EncountersListTests`, `QsofaCurrentTests`, `GapAnalysisFixTests`, `vigilcare-dashboard` Vitest suite (replay scrubbing, feedback store, FeedbackButtons, FeedbackSummary, alert components, charts, ward table, GCS entry/history, SOFA panel/history, qSOFA history, scores panel, alert labels, PatientBanner, EncounterTimeline, medication chart markers). +**Ward dashboard:** backend APIs (`GET /encounters` ward list with extended summary fields including SOFA/GCS/attending/admitted-at, `GET /qsofa/current`, `GET /qsofa/history`, `GET /gcs/history`, `GET /sepsis-bundles` hospital-wide list, CORS) and frontend SPA — `EncountersListTests`, `QsofaCurrentTests`, `GapAnalysisFixTests`, `vigilcare-dashboard` Vitest suite (replay scrubbing, feedback store, FeedbackButtons, FeedbackSummary, alert components, charts, ward table, ward sort, ward filter, department format, sepsis format, alert acknowledge, critical alert detect, handoff report, vitals form, GCS entry/history, SOFA panel/history, qSOFA history, scores panel, alert labels, PatientBanner, EncounterTimeline, medication chart markers, AcknowledgeModal, CriticalAlertBanner, DepartmentOverviewView, SepsisBoardView, VitalsEntryForm, useAlertStore, useWardStore). + +**Enhanced Dashboard (post-Phase 22):** Major dashboard feature expansion addressing clinical workflow gaps. **Department Overview** (`/departments`) — unit-level snapshot cards showing patient count, critical/alert/bundle totals per department with acuity distribution bars; click-through to ward filtered by department. **Sepsis Bundle Board** (`/sepsis`) — real-time bundle compliance tracking with countdown timers to 1-hour deadline, urgency-sorted (overdue → at-risk → on-track), live 1-second tick updates. **Critical Alert Notifications** — `CriticalAlertBanner` surfaces new critical alerts from polling cycle with audible 880Hz two-tone alert, browser title flash, and native `Notification` API integration; mute toggle persisted in settings. **Shift Handoff Report** — `HandoffReport.vue` generates SBAR-format (Situation, Background, Assessment, Recommendation) structured reports for all ward patients, enriched with latest vitals, open alerts, pending orders, and sepsis bundle status; ward summary with department stats; print/PDF export. **Vitals Entry Form** — `VitalsEntryForm.vue` on patient detail page enables manual observation recording (7 vital parameters with AVPU dropdown) with client-side plausibility validation matching server-side ranges. **Ward Table Enhancements** — multi-column sorting (room, patient, department, NEWS2, qSOFA, sepsis, alerts) with sortable column headers, debounced patient search (name/MRN), quick-filter toggles (critical, has alerts, active sepsis), clear-all filters. **Acknowledge Modal** — role-aware acknowledgment with clinician identity pre-populated from JWT, role-specific guidance text, and acknowledgment note preview. Backend additions: `GET /sepsis-bundles` paginated hospital-wide list with `SepsisBundleSummary` (patient demographics, elements, deadlines); `WardEncounterSummary` extended with `sofaScore`, `sofaDelta`, `gcsScore`, `gcsClassification`, `lastObservationAt`, `attendingPhysician`, `admittedAt`. **Site & Gateway Registry (Phase 20):** Central API manages clinical sites and ward edge nodes (gateways). Gateways authenticate via API key for heartbeat and sync upload. Shared `VigilCare.ClinicalContracts` class library defines sync DTOs consumed by both central API and ward gateway projects. Prometheus fleet health gauges track offline gateways and buffer depth per site. diff --git a/docs/clinical-testing-guide.md b/docs/clinical-testing-guide.md index c73408a..baffcd0 100644 --- a/docs/clinical-testing-guide.md +++ b/docs/clinical-testing-guide.md @@ -21,6 +21,7 @@ No programming knowledge is required. A facilitator will start the backend servi 9. [Session checklist](#9-session-checklist) 10. [Submitting your feedback](#10-submitting-your-feedback) 11. [Frequently asked questions](#11-frequently-asked-questions) +12. [New dashboard features](#12-new-dashboard-features) --- @@ -34,7 +35,7 @@ VigilCare is a **clinical decision support prototype**. It watches vital signs a - **Rapid deterioration** — a vital changing quickly even if still “in range” - **Sepsis bundle** tracking — whether time-critical treatments were ordered and completed -The **dashboard** is where you, as a clinician, would see those alerts and decide what to do. This testing round adds **structured feedback**: after reviewing each alert, you tell us whether it would help or hinder real clinical work. +The **dashboard** is where you, as a clinician, would see those alerts and decide what to do. This testing round adds **structured feedback**: after reviewing each alert, you tell us whether it would help or hinder real clinical work. The dashboard also includes a **department overview** for unit-level situational awareness, a **sepsis bundle board** tracking compliance deadlines in real time, **critical alert notifications** (audible + browser), **shift handoff reports** in SBAR format, and a **vitals entry form** for recording observations at the bedside. Your ratings are the primary output of this study. There are no right or wrong answers — we want your honest clinical judgment on simulated cases. @@ -81,18 +82,21 @@ If the ward list is empty, ask the facilitator to start or replay a scenario (se ## 4. Tour of the application -Use the sidebar (desktop) or bottom navigation (mobile) to move between screens. +Use the sidebar (desktop) or bottom navigation (mobile) to move between screens. The sidebar includes links to **Virtual Ward**, **Department Overview**, **Sepsis Bundle Board**, **Alert Center**, and **Feedback Summary**. ### Virtual Ward **Purpose:** See who is on the floor and who needs attention first. -- Patients sorted by **NEWS2 score** (higher = higher concern on this board). +- Patients sorted by **NEWS2 score** by default — click any column header (Room, Patient, NEWS2, qSOFA, Sepsis, Alerts) to re-sort. - Badge shows count of patients with NEWS2 ≥ 7. +- **Search bar** — type a patient name or MRN to find someone quickly. +- **Quick-filter toggles** — narrow the list to Critical (NEWS2 ≥ 7), Has Alerts, or Active Sepsis. - Filter by department if asked (ICU, General Medicine, Surgery). - **Click a patient row** to open their detail page. +- **Handoff Report** button in the toolbar generates a structured shift handoff for all visible patients (see [§12](#12-new-dashboard-features)). -**What to notice:** Does the sort order match how you would prioritize a real ward round? +**What to notice:** Does the sort order match how you would prioritize a real ward round? Are the search and filters useful for finding patients quickly? --- @@ -102,17 +106,21 @@ Use the sidebar (desktop) or bottom navigation (mobile) to move between screens. | Section | What it shows | |---|---| -| **Scores** | Current NEWS2 total and risk level | +| **Scores** | Current NEWS2 total and risk level, SOFA score with organ-system breakdown, GCS total and component scores | | **Latest Vitals** | Most recent heart rate, RR, BP, SpO₂, temperature, etc. | +| **Vitals Entry** | Record new vital signs manually (7 parameters with AVPU dropdown) — plausibility-validated before submission | | **Active Alerts** | Open alerts for this patient — **click a row** to see reasoning | | **Alert reasoning** | Plain-language explanation of why the alert fired; may show recent medications | | **Orders** | Clinical orders (labs, antibiotics, fluids, etc.) | | **Sepsis bundle** | If sepsis was suspected — four time-critical elements and compliance status | -| **Vital sign charts** | Trends for HR, RR, systolic BP, SpO₂, temperature | +| **Vital sign charts** | Trends for HR, RR, systolic BP, SpO₂, temperature — with medication administration markers | | **NEWS2 history** | How the early warning score changed over time | +| **SOFA / GCS / qSOFA history** | Organ-system breakdown over time, component tracking, evaluation history | +| **Patient banner** | Demographics, age, blood type, allergies, emergency contact | +| **Encounter timeline** | Merged chronological view of status changes, observation summaries, and alerts | | **Replay controls** | Local timeline bar (pause, speed, **Next Alert →**) — scrubs charts/vitals to a point in time; does not control the simulator | -**What to notice:** Would you trust these charts and explanations during a real handoff? Is anything missing? +**What to notice:** Would you trust these charts and explanations during a real handoff? Is the vitals entry form intuitive? Is anything missing? --- @@ -122,10 +130,38 @@ Use the sidebar (desktop) or bottom navigation (mobile) to move between screens. - Tabs: **Open**, **Acknowledged**, **Resolved**, **Escalated**. - Each card shows severity, type, details, and time. -- **Ack** / **Resolve** for workflow practice. +- **Ack** opens a role-aware acknowledgment modal — your name and role are pre-filled from your login, with a preview of the acknowledgment note. Add optional clinical notes before confirming. +- **Resolve** (must be acknowledged first). +- **Critical alert banner** — when new critical alerts arrive (detected during polling), a banner appears at the top of the page with an audible tone and browser notification. You can dismiss individual alerts or all at once. - **Feedback buttons** on every card (see [§5](#5-your-core-task--review-and-rate-alerts)). -**What to notice:** Is it easy to triage multiple patients from one screen? Would you use this during a shift? +**What to notice:** Is it easy to triage multiple patients from one screen? Is the critical alert notification helpful or distracting? Would you use this during a shift? + +--- + +### Department Overview + +**Purpose:** Bird's-eye view of all departments — which units are busiest and which need attention. + +- Summary cards show **total active patients**, **critical count** (NEWS2 ≥ 7), **open alerts**, and **active sepsis bundles** across the hospital. +- One card per department with patient count, acuity distribution bar, alert count, and bundle count. +- **Click a department card** to jump to the Virtual Ward filtered to that unit. +- Polls every 10 seconds for live updates. + +**What to notice:** Would this view help a charge nurse or bed manager allocate resources? Does the acuity breakdown feel clinically useful? + +--- + +### Sepsis Bundle Board + +**Purpose:** Track all active sepsis bundles hospital-wide with countdown timers to the 1-hour compliance deadline. + +- Each bundle row shows patient name/MRN, room, department, recognition time, countdown timer, compliance status, and element progress (how many of the four treatment elements are complete). +- Bundles are sorted by urgency: **overdue** (past deadline, red) → **at risk** (< 15 minutes remaining, amber) → **on track** (green). +- Countdown updates every second. +- Summary line shows total bundles and breakdown by urgency category. + +**What to notice:** Is the urgency sorting intuitive? Would real-time countdown timers help your team meet sepsis compliance targets? --- @@ -221,8 +257,25 @@ Short phrases are enough. Examples: 1. Facilitator runs `replay-all` on the scenario folder (or 2–3 scenarios back-to-back). 2. You: Stay on Virtual Ward — note sort order as new patients appear. -3. Round on each high-NEWS2 patient — detail page only, no Alert Center until end. -4. Document: *Would this order match your morning ward round?* +3. Try sorting by different columns (qSOFA, Alerts, Sepsis) — which view is most useful? +4. Use the search bar and quick filters to narrow the list. +5. Round on each high-NEWS2 patient — detail page only, no Alert Center until end. +6. Generate a **Handoff Report** and review the SBAR content for each patient. +7. Document: *Would this order match your morning ward round? Is the handoff report format useful?* + +### Session D — New features deep dive (30–45 minutes) + +**Goal:** Evaluate the new dashboard features for clinical usefulness. + +1. Facilitator replays 3–4 scenarios (including `uti-sepsis-elderly-01` for sepsis bundles). +2. Open **Department Overview** — review the unit-level cards and summary stats. +3. Open **Sepsis Bundle Board** — watch countdown timers and urgency sorting. +4. Return to **Virtual Ward** — test the search bar, quick filters, and column sorting. +5. Open a patient → use the **Vitals Entry form** to record manual observations. +6. Wait for a critical alert → note the **critical alert banner**, sound, and browser notification. +7. Acknowledge an alert using the new **role-aware modal** — review the note preview. +8. Generate a **Handoff Report** and review the SBAR format. +9. Document: *Which new features would you use on a real ward? Which need improvement?* --- @@ -260,24 +313,46 @@ Use this during or after your session. ### Virtual Ward - [ ] Patient list loads and refreshes - [ ] NEWS2 sort order feels clinically sensible +- [ ] Clicking column headers (Room, Patient, NEWS2, qSOFA, Sepsis, Alerts) changes sort order +- [ ] Patient search by name or MRN works +- [ ] Quick filters (Critical, Has Alerts, Active Sepsis) narrow the list correctly - [ ] Department filter works (if tested) - [ ] Patient detail opens from row click +- [ ] Handoff Report generates and displays correctly (test print/PDF if possible) ### Patient Detail - [ ] Latest vitals match what you expect for the scenario - [ ] NEWS2 score and risk level display correctly +- [ ] SOFA score with organ-system breakdown visible +- [ ] GCS score with component breakdown visible +- [ ] Vitals Entry form validates values and submits successfully - [ ] Clicking an alert shows reasoning panel - [ ] Medication context appears when relevant (beta-blocker scenario) -- [ ] Vital sign charts show sensible trends +- [ ] Vital sign charts show sensible trends (with medication markers where applicable) - [ ] NEWS2 history chart updates over time +- [ ] SOFA / GCS / qSOFA history charts update over time +- [ ] Patient banner shows demographics, allergies, emergency contact +- [ ] Encounter timeline shows chronological events - [ ] Sepsis bundle panel appears when sepsis alerts fire - [ ] **Next Alert →** selects each open alert, scrubs charts to that time, and scrolls the review section into view ### Alert Center - [ ] Open / Acknowledged / Resolved tabs filter correctly +- [ ] Acknowledge modal shows your name and role, note preview works - [ ] Acknowledge and resolve workflow understandable +- [ ] Critical alert banner appears for new critical alerts (with sound if not muted) - [ ] Feedback buttons visible on every card +### Department Overview +- [ ] Summary cards show total patients, critical count, alerts, bundles +- [ ] Department cards load with acuity bars +- [ ] Clicking a department navigates to ward filtered by that unit + +### Sepsis Bundle Board +- [ ] Active bundles listed with countdown timers +- [ ] Urgency sorting (overdue → at risk → on track) feels correct +- [ ] Countdown timer updates in real time + ### Feedback (Phase 19) - [ ] Rated at least **5 alerts** (more is better) - [ ] Used more than one rating category (not all “Useful”) @@ -290,6 +365,10 @@ Use this during or after your session. - [ ] Which alert types were most trustworthy? - [ ] Which caused unnecessary noise? - [ ] What information was missing from alert text or reasoning? +- [ ] Was the department overview useful for situational awareness? +- [ ] Was the sepsis bundle board useful for compliance tracking? +- [ ] Was the handoff report format (SBAR) clinically appropriate? +- [ ] Were critical alert notifications helpful or disruptive? - [ ] Would you want this on a real ward? Why or why not? --- @@ -332,6 +411,18 @@ No. Rate any alert in any status. **The replay bar doesn’t pause the simulator.** Correct — replay controls scrub observation/chart data already loaded in the browser (pause, speed, and **Next Alert →** move a local timeline). The facilitator controls simulator speed separately. +**The alert sound is annoying — can I turn it off?** +Yes. Click the sound toggle icon in the header bar to mute critical alert tones. The visual banner and browser notifications still appear. + +**How do I print the handoff report?** +Open the Handoff Report from the Virtual Ward toolbar, then click **Print / Save PDF**. The report is formatted for printing — the overlay and controls are hidden in print view. + +**Can I record vitals from the dashboard?** +Yes. On any Patient Detail page, use the **Vitals Entry** section to enter vital signs. Values are validated before submission and processed by the same scoring engines as device data. + +**What is the Sepsis Bundle Board?** +A hospital-wide view of all active sepsis bundles with live countdown timers. It helps track whether the four required treatment elements (blood cultures, lactate, antibiotics, fluids) are completed within the 1-hour compliance window. + **Dark mode?** Toggle in the header if your eyes prefer it; all screens support dark mode. @@ -345,13 +436,80 @@ Speak to your session facilitator. Technical issues (blank screen, errors) may n | I want to… | Go to… | |---|---| | See all patients by acuity | Virtual Ward | +| Find a specific patient | Virtual Ward → search bar (name or MRN) | +| See only critical patients | Virtual Ward → Critical quick-filter toggle | | Deep-dive one patient | Click patient → Patient Detail | +| Record vital signs manually | Patient Detail → Vitals Entry form | +| See all departments at a glance | Department Overview (sidebar) | +| Track sepsis bundle compliance | Sepsis Bundle Board (sidebar) | | Triage all hospital alerts | Alert Center | | Understand why an alert fired | Patient Detail → click alert → Reasoning | +| Acknowledge an alert | Alert Center or Patient Detail → Ack button → modal | | Rate an alert | Feedback buttons under alert card or reasoning | | See my ratings aggregate | Feedback Summary | +| Generate a shift handoff | Virtual Ward → Handoff Report button | | Submit results | Feedback Summary → Export CSV | --- +## 12. New dashboard features + +This section summarizes features added after the initial dashboard release. If you are already familiar with the basic workflow (ward → patient detail → alerts → feedback), read this section for what is new. + +### Sortable ward table + +The ward table now supports **column sorting**. Click any column header (Room, Patient, Department, NEWS2, qSOFA, Sepsis, Alerts) to sort by that column. Click again to reverse direction. The default remains NEWS2 descending (most acute first). A secondary sort by NEWS2 breaks ties when sorting by other columns. + +### Patient search and quick filters + +A **search bar** at the top of the ward lets you type a patient name or MRN to find someone instantly. Three **quick-filter toggles** narrow the list: +- **Critical** — only patients with NEWS2 ≥ 7 +- **Has Alerts** — only patients with open alerts +- **Active Sepsis** — only patients with an active sepsis bundle or SOFA_SEPSIS alert + +A "Clear filters" button resets all filters and search. + +### Department Overview (`/departments`) + +A new **Department Overview** page (accessible from the sidebar) provides a bird's-eye view of the hospital. Summary cards at the top show total active patients, critical count, open alerts, and active sepsis bundles. Below, one card per department shows its patient count, acuity distribution (a visual bar), and alert/bundle counts. Click a department card to navigate to the ward filtered to that unit. + +### Sepsis Bundle Board (`/sepsis`) + +A new **Sepsis Bundle Board** page tracks all active sepsis bundles hospital-wide. Each bundle row shows the patient, room, department, recognition time, a **live countdown timer** to the 1-hour compliance deadline, and element completion progress. Bundles are color-coded and sorted by urgency: **overdue** (past deadline) → **at risk** (< 15 minutes remaining) → **on track**. The countdown updates every second. + +### Critical alert notifications + +When the dashboard detects new critical alerts during its polling cycle, a **critical alert banner** appears at the top of the page. It includes: +- An **audible two-tone alert** (can be muted via the sound toggle in the header) +- A **flashing browser title** (`⚠ CRITICAL ALERT — VigilCare`) +- A **browser notification** (if you grant notification permission when prompted) + +Dismiss individual alerts or all at once. The banner does not replace the Alert Center — it highlights new critical arrivals so you do not miss them. + +### Shift handoff report + +The **Handoff Report** button on the Virtual Ward toolbar generates a structured shift handoff document for all visible patients (respecting any active filters). The report includes: + +- **Ward summary** — department, patient count, critical count, open alerts, active bundles +- **Per-patient SBAR** — Situation (admission reason), Background (allergies), Assessment (scores + latest vitals + open alerts), Recommendation (pending orders + sepsis bundle status) +- **Scores overview** — NEWS2, SOFA (with delta), GCS, qSOFA per patient + +Use **Print / Save PDF** to export the report for paper handoff or archive. + +### Vitals entry form + +On the **Patient Detail** page, a **Vitals Entry** section lets you manually record observations for seven vital parameters: Heart Rate, Respiratory Rate, Systolic BP, Diastolic BP, SpO₂, Temperature, and AVPU (as a dropdown). Values are validated against plausibility ranges before submission. At least one vital sign must be entered. This simulates bedside manual charting — observations posted through this form are processed by the same scoring engines as device-generated data. + +### Role-aware alert acknowledgment + +The **Ack** button now opens a modal that shows: +- Your name and role (from your login session) +- Role-specific guidance (e.g. "You are acknowledging as a nurse — documenting awareness of this alert") +- A preview of the acknowledgment note that will be recorded +- An optional text field for additional clinical notes + +This makes the acknowledgment workflow more realistic and tracks who acknowledged what. + +--- + *Thank you for participating. Your clinical feedback directly shapes whether VigilCare alerts help or harm real ward workflows.*