Files
vigilcare-clinical/docs/dashboard-guide.md
T
2026-06-20 14:41:11 +08:00

319 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VigilCare Dashboard — User Guide
The **VigilCare Dashboard** (`vigilcare-dashboard/`) is a Vue 3 single-page application that gives clinicians a live view of the virtual ward: who is on the floor, which patients are deteriorating, what alerts need action, and — on the patient detail screen — vital sign trends, NEWS2 history, and plain-language alert reasoning.
It reads from the VigilCare Clinical API over HTTP. It does **not** connect to Kafka, RabbitMQ, or PostgreSQL directly. Polling keeps the UI fresh while the API and background consumers do the clinical work.
---
## Table of Contents
1. [What the dashboard does](#1-what-the-dashboard-does)
2. [Prerequisites](#2-prerequisites)
3. [Quick start](#3-quick-start)
4. [Screens](#4-screens)
5. [Patient detail — clinical review mode](#5-patient-detail--clinical-review-mode)
6. [Working with the simulator](#6-working-with-the-simulator)
7. [Configuration](#7-configuration)
8. [Development](#8-development)
9. [Troubleshooting](#9-troubleshooting)
---
## 1. What the dashboard does
In a real hospital, nurses and physicians need a ward board: acuity at a glance, drill-down into one patient, and a way to act on alerts. This frontend implements that workflow against the VigilCare API.
| Capability | Where in the app | API backing |
|---|---|---|
| Ward overview — active patients sorted by NEWS2 risk | **Virtual Ward** (`/ward`) | `GET /encounters?status=ACTIVE` |
| Department filter | Virtual Ward toolbar | Same list endpoint with `department` |
| Patient drill-down | **Patient Detail** (`/patients/:encounterId`) | `GET /encounters/{id}`, observations, scores, orders |
| Latest vitals snapshot | Patient Detail — Vitals panel | `GET /encounters/{id}/observations` |
| NEWS2 current score | Patient Detail — Scores panel | `GET /encounters/{id}/news2/current` |
| Open alerts per patient | Patient Detail — Active Alerts | `GET /encounters/{id}/alerts` |
| Acknowledge / resolve alerts | Patient Detail or Alert Center | `PATCH /alerts/{id}/acknowledge`, `/resolve` |
| Sepsis bundle status | Patient Detail — Sepsis Bundle panel | `GET /encounters/{id}/sepsis-bundle/current` |
| Clinical orders | Patient Detail — Orders panel | `GET /encounters/{id}/orders` |
| Vital sign trend charts (HR, RR, BP, SpO₂, temp) | Patient Detail — Clinical review section | Observations (Chart.js) |
| NEWS2 score over time | Patient Detail — NEWS2 history chart | `GET /encounters/{id}/news2/history` |
| Alert reasoning (“why did this fire?”) | Patient Detail — click an alert row | Client-side rules + alert `details` |
| Medication context on alerts | Alert reasoning panel | `GET /encounters/{id}/medications` |
| Local replay controls | Patient Detail — replay bar | Scrubs fetched data (not live simulator control) |
| Global alert inbox | **Alert Center** (`/alerts`) | `GET /alerts` with status filter |
| Clinician feedback on alerts | Alert Center, Patient Detail reasoning | Pinia + `localStorage` (client-side) |
| Feedback summary & export | **Feedback Summary** (`/feedback`) | Aggregate stats; export JSON/CSV |
**What it is not:** a full EHR, authentication server, or simulator remote control. Replay controls scrub through data already loaded from the API; they do not pause or speed up the .NET simulator process. Alert feedback is stored in the browser until exported — see [clinical-testing-guide.md](clinical-testing-guide.md) for the clinician workflow.
---
## 2. Prerequisites
- **Node.js 20+** and npm
- **VigilCare Clinical API** running at `http://localhost:5270` (see root [README](../README.md) — Getting Started)
- **CORS:** API `Dashboard` policy must allow the dev origin (`http://localhost:5173` by default)
Optional but recommended for demo data:
- **VigilCare Simulator** — replay a scenario so observations and alerts populate while you watch the dashboard update. See [simulator-guide.md](simulator-guide.md).
---
## 3. Quick start
From the repository root:
```bash
# Terminal 1 — infrastructure + API
docker compose up -d
dotnet run --project VigilCareClinicalAPI
# Terminal 2 — dashboard
cd vigilcare-dashboard
npm install
npm run dev
```
Open **http://localhost:5173**. The app redirects to **Virtual Ward**.
To populate live data, run a simulator scenario in a third terminal:
```bash
dotnet run --project VigilCare.Simulator -- replay \
VigilCare.Simulator/Scenarios/List/uti-sepsis-elderly-01.json \
--speed 60
```
Return to the dashboard, click a patient row, and watch vitals, charts, and alerts update every few seconds.
---
## 4. Screens
### Virtual Ward (`/ward`)
The home screen lists **active encounters** as a responsive table (desktop) or card list (mobile).
- Patients are sorted by **NEWS2 score** (highest risk first).
- A badge shows how many patients have NEWS2 ≥ 7.
- Filter by department: All, ICU, General Medicine, Surgery.
- Click a row to open **Patient Detail**.
Data refreshes every **10 seconds** via polling.
### Alert Center (`/alerts`)
A hospital-wide alert inbox independent of any single patient.
- Filter tabs: **Open**, **Acknowledged**, **Resolved**, **Escalated**.
- Each card shows severity, alert type, details, and triggered time.
- **Ack** opens a confirmation modal (uses clinician ID from settings — default `DR-DEMO`).
- **Resolve** is available after acknowledge.
Use this when you need to triage alerts across the ward without opening each patient first.
### Feedback Summary (`/feedback`)
**Purpose:** Research and evaluation view for structured clinician ratings on alerts.
- Aggregate stats: total ratings, % useful/would act, % false positive, timing issues.
- Breakdown by alert type (sepsis, NEWS2, heart rate warning, etc.).
- Recent feedback entries with notes.
- **Export JSON** / **Export CSV** for study analysis.
Ratings are collected via six buttons on each alert (see [clinical-testing-guide.md](clinical-testing-guide.md) for definitions aimed at doctors and nurses).
### Patient Detail (`/patients/:encounterId`)
The clinical review screen. See [§5](#5-patient-detail--clinical-review-mode) for the full breakdown.
---
## 5. Patient detail — clinical review mode
Layout top to bottom:
```
┌─────────────────────────────────────────────────────────┐
│ ← Ward Patient Name │
├─────────────────────────────────────────────────────────┤
│ Scores (NEWS2) │ Latest Vitals │ Active Alerts │
├─────────────────────────────────────────────────────────┤
│ Alert reasoning panel (after clicking an alert) │
├─────────────────────────────────────────────────────────┤
│ Orders │ Sepsis Bundle (if present) │
├─────────────────────────────────────────────────────────┤
│ Vital sign trend charts (5) │
│ NEWS2 score history chart │
│ Replay controls │
└─────────────────────────────────────────────────────────┘
```
#### Scores panel
Shows the current **NEWS2** total, risk level, and encounter metadata (room/bed, department). If no score has been computed yet, displays `—`.
#### Vitals panel
Latest value per observation code (heart rate, respiratory rate, blood pressure, SpO₂, temperature, etc.), deduplicated to the most recent reading per code.
#### Active alerts
Open alerts for this encounter. Click a row to open the **reasoning panel** below the grid.
- **Ack** / **Resolve** buttons use `@click.stop` so they do not trigger row selection.
- Selected row is highlighted.
#### Alert reasoning
Explains **why** an alert fired in plain language:
| Alert type | Explanation shown |
|---|---|
| `WarningHeartRate`, `WarningSystolicBp`, `WarningTempC` | Threshold range + extracted value from `details` |
| `SepsisWarning` | SIRS criteria summary |
| `QsofaWarning` | qSOFA criteria summary |
| `News2Warning` / `News2Emergency` | NEWS2 risk tier |
| `RapidDeterioration` | Trajectory / rate-of-change |
| Unknown types | Falls back to raw `details` string |
If medications were administered within **90 minutes** before the alert, a **Recent medications** block appears (supports reviewing `medication-false-alarm-01` and similar scenarios).
#### Orders and sepsis bundle
- **Orders** — pending and resulted clinical orders for the encounter.
- **Sepsis bundle** — only rendered when a bundle exists; shows compliance status, deadline, and four treatment elements.
#### Clinical review charts
Five **vital sign trend** line charts (Chart.js via `vue-chartjs`):
- Heart rate, respiratory rate, systolic BP, SpO₂, temperature
- Responsive grid: 1 column (mobile) → 2 (`sm`) → 3 (`xl`)
- Respects `prefers-reduced-motion` (animations disabled when OS setting is on)
**NEWS2 history** chart plots `totalScore` over time with per-point fill colors for risk bands (low / medium / high).
#### Replay controls
Local playback UI for reviewing scenario timelines against data already loaded for this encounter:
- **Pause / resume** — starts paused at the end of the timeline (all data visible). Resume rewinds to the start and plays forward.
- **Speed presets** (1×, 60×, 360×, Instant) — advance the replay clock through observation and NEWS2 timestamps; **Instant** jumps to the end.
- **Progress bar and elapsed time** — derived from the earliest to latest timestamp across observations, NEWS2 history, and alerts.
- **Chart scrubbing** — vitals panel, vital trend charts, and NEWS2 history show only data at or before the replay clock.
- **Next Alert →** — cycles open/escalated alerts chronologically, selects each for the reasoning panel, scrubs the replay clock to that alerts `triggeredAt`, scrolls the alert row into view, and scrolls to the chart section.
Selecting an alert row in **Active Alerts** also scrubs the replay clock to that alerts time.
> Replay state is **client-side only**. It does not send commands to the simulator. Patient detail loads encounter-scoped alerts on every poll so the alert list and **Next Alert →** are not affected by a prior visit to Alert Center.
Patient detail data polls every **5 seconds**.
#### Clinician feedback (on alerts)
Below each alert in **Alert Center** and below the **reasoning panel** on Patient Detail:
| Rating | Meaning |
|---|---|
| Useful | Clinically appropriate alert |
| Would act | Would change management based on this alert |
| Too early | Right direction, wrong timing (too soon) |
| Too late | Problem existed; alert came too late |
| False positive | Should not have fired for this context |
| Missing context | Valid concern but insufficient information |
Tap **+ Note** for optional free text. Ratings save automatically in the browser. Export from **Feedback Summary** when the session ends.
---
## 6. Working with the simulator
Typical demo flow:
1. Start API + dashboard (§3).
2. Replay a scenario at moderate speed (`--speed 60`).
3. Open **Virtual Ward** — new or updated encounters appear as the simulator creates them.
4. Click the patient — watch vitals and charts fill in as observations ingest.
5. When alerts fire, click an alert row to read reasoning (replay scrubs to that time); try **Next Alert →** on the replay bar.
6. Use **Alert Center** to acknowledge alerts globally.
**Suggested scenarios:**
| Scenario file | What to observe in the dashboard |
|---|---|
| `uti-sepsis-elderly-01.json` | SIRS/qSOFA alerts, sepsis bundle panel, rising acuity |
| `medication-false-alarm-01.json` | HR warnings with medication context in reasoning |
| `news2-deterioration-01.json` (if present) | NEWS2 history chart and score panel |
---
## 7. Configuration
| Setting | Location | Default |
|---|---|---|
| API base URL | `vigilcare-dashboard/.env``VITE_API_URL` | `http://localhost:5270` |
| Clinician ID (acknowledge) | Pinia `settings` store / `localStorage` key `clinicianId` | `DR-DEMO` |
| Dark mode | Header toggle / `localStorage` key `darkMode` | System preference |
| Ward poll interval | `usePolling` in `WardDashboard.vue` | 10 s |
| Patient detail poll | `usePolling` in `PatientDetail.vue` | 5 s |
Example `.env`:
```env
VITE_API_URL=http://localhost:5270
```
---
## 8. Development
```bash
cd vigilcare-dashboard
npm install
npm run dev # http://localhost:5173
npm test # Vitest — composables + component tests
npm run build # production bundle
npm run preview # preview production build
```
**Stack:** Vue 3 (`<script setup>`), Vue Router, Pinia, Tailwind CSS v4, Chart.js + vue-chartjs.
**Key directories:**
```
vigilcare-dashboard/src/
├── api/ # HTTP client + encounter/clinical/alert fetchers
├── components/
│ ├── charts/ # VitalChart, TrendsGrid, News2History
│ ├── replay/ # ReplayControls
│ ├── alerts/ # AlertReasoning, AlertCard, …
│ ├── patient/ # VitalsPanel, ScoresPanel, AlertsList, …
│ └── ward/ # WardTable, PatientRow, PatientCard
├── composables/ # useChartData, useReplayControls, usePolling, chartFormat
├── stores/ # ward, alerts, settings (Pinia)
└── views/ # WardDashboard, PatientDetail, AlertCenter
```
Implementation plans: `docs/plans/phase-17-plan.md` (ward shell + detail), `docs/plans/phase-18-plan.md` (charts, replay, reasoning), `docs/plans/phase-19-plan.md` (clinician feedback).
**Clinician testing:** [clinical-testing-guide.md](clinical-testing-guide.md) — step-by-step guide for doctors and nurses participating in evaluation sessions.
---
## 9. Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Blank ward / network errors in console | API not running or wrong URL | Start API; check `VITE_API_URL` |
| CORS error in browser | Dashboard origin not allowed | Add origin to API `DashboardOptions` in `appsettings.json` |
| Charts crash on mount | Chart data not unwrapped | Ensure `VitalTrendChart` wrapper is used (see `TrendsGrid.vue`) |
| Replay bar stuck at 0:00 | No timestamped data yet | Wait for observations/alerts to load; bounds sync after first fetch |
| Next Alert shows wrong patient | Stale global alert store | Fixed in Patient Detail — encounter alerts reload on mount/poll |
| No patients listed | No active encounters | Run simulator or use seeded API data |
| NEWS2 chart empty | Scores not computed yet | Wait for Kafka `news2-scoring` consumer after observations ingest |
| Acknowledge fails 400 | Invalid clinician ID | Set `clinicianId` in localStorage or settings store |
For API and infrastructure issues, see the root [README](../README.md) and [docker-compose-usage-and-troubleshooting.md](docker-compose-usage-and-troubleshooting.md).