# 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 alert’s `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 alert’s 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).
