# VigilCare Dashboard — Gap Analysis

Comprehensive gap analysis of the VigilCare clinical dashboard system, covering the Vue 3 frontend (`vigilcare-dashboard/`), the API endpoints that feed it, and the Grafana infrastructure dashboard (`infra/grafana/`). Items are assessed from the perspective of **clinical usefulness** — what a nurse, physician, charge nurse, or administrator needs at the bedside, ward board, or admin console that the current dashboard does not provide.

Each item includes **who needs it** (clinical role), **why** it matters for patient care or operational efficiency, and **how** to implement it at a component and API level.

---

## Priority legend

| Tier | Meaning |
|------|---------|
| **P0** | Missing capability that directly affects patient safety or clinical decision-making at the bedside |
| **P1** | Missing workflow that forces clinicians to leave the dashboard and use another tool (paper, EHR, phone) |
| **P2** | Missing view that degrades situational awareness for charge nurses, supervisors, or administrators |
| **P3** | UX/usability gap that slows clinical workflows or increases cognitive load |
| **P4** | Missing operational/admin dashboard capability for IT ops or compliance teams |
| **P5** | Polish, accessibility, and developer experience improvements |

---

# Part A — Patient Safety & Bedside Decision Support

---

## P0 — No SOFA trend chart or organ-system timeline

### Who needs it

Physicians (ICU attendings, hospitalists), advanced practice providers.

### Problem

The Patient Detail view shows the current SOFA score in `SofaScorePanel.vue` with organ-system breakdowns and staleness flags, but there is **no SOFA history chart**. The backend exposes `GET /api/v1/encounters/{id}/sofa/history` with full component breakdowns, but the dashboard does not render it. NEWS2 has a dedicated `News2History.vue` chart — SOFA does not.

### Why fix

SOFA delta (change from baseline) is the primary trigger for sepsis bundle initiation. A physician reviewing a sepsis case needs to see **when** organ dysfunction began, **which organs** are driving the score, and whether the trajectory is improving or worsening. Without this, they must mentally reconstruct the trajectory from individual vital signs and lab values — exactly the cognitive burden an early warning system should eliminate.

### How to fix

1. Create `SofaHistory.vue` chart component showing SOFA total score over time (line chart, matching `News2History.vue` pattern).
2. Add optional stacked area or grouped bar overlay showing per-organ contributions (respiratory, coagulation, liver, cardiovascular, CNS, renal).
3. Fetch `GET /encounters/{id}/sofa/history` in `PatientDetail.vue` (already cursor-paginated).
4. Add a `fetchSofaHistory(encounterId, { limit })` function to `clinical.js`.
5. Place below `SofaScorePanel` in the patient detail layout.
6. Color-code risk bands: 0-5 (green), 6-9 (amber), 10+ (red).

**Frontend files:** New `components/charts/SofaHistory.vue`, update `views/PatientDetail.vue`, update `api/clinical.js`.

**Backend:** Already complete — `SofaController.History` exists.

**Dependency:** None.

---

## P0 — No GCS trend chart or component history

### Who needs it

Nurses (neuro checks), physicians (neurology, trauma, ICU).

### Problem

GCS is displayed as a single current value in `ScoresPanel.vue` with an inline entry form (`GcsEntryForm.vue`), but there is no history view. For patients with traumatic brain injury, stroke, or post-operative neurosurgery, GCS trend is a primary indicator of neurological deterioration. The backend has no `GET /encounters/{id}/gcs/history` endpoint — only `GET /encounters/{id}/gcs` (latest).

### Why fix

A GCS drop from 14 to 10 over 4 hours is a neurosurgical emergency. Without a trend view, nurses performing hourly neuro checks cannot see the trajectory — they only see the current score and must remember or write down previous values. This defeats the purpose of electronic GCS tracking.

### How to fix

1. **Backend:** Add `GET /api/v1/encounters/{encounterId}/gcs/history` endpoint to `GcsController` with cursor pagination (mirror NEWS2/SOFA pattern). Query `GcsScores` table ordered by `CalculatedAt DESC`.
2. Create `GcsHistory.vue` chart component showing total GCS and optionally eye/verbal/motor component lines.
3. Add `fetchGcsHistory(encounterId, { limit })` to `clinical.js`.
4. Render below or alongside `ScoresPanel` in `PatientDetail.vue`.
5. Highlight score boundaries: mild (13-15), moderate (9-12), severe (3-8).

**Frontend files:** New `components/charts/GcsHistory.vue`, update `views/PatientDetail.vue`, update `api/clinical.js`.

**Backend files:** `GcsController.cs`, `GcsService.cs` (new `GetHistoryAsync`).

**Dependency:** None.

---

## P0 — No qSOFA history view

### Who needs it

Physicians, nurse practitioners (sepsis screening).

### Problem

`QsofaController` only exposes `GET /encounters/{id}/qsofa/current` (Redis-backed real-time state). There is no qSOFA history endpoint and no dashboard view of qSOFA criteria over time. The score panel shows the current `ActiveCriteria` count and individual criterion states — but a clinician cannot see when criteria first appeared or how the screen has evolved.

### Why fix

qSOFA is a **screening tool** — it triggers the recommendation to order SOFA labs. Seeing when criteria first became positive (and whether they have resolved) is critical for assessing whether sepsis workup is still indicated or whether a false alarm resolved.

### How to fix

1. **Backend:** Add `GET /api/v1/encounters/{encounterId}/qsofa/history` with cursor pagination. Store qSOFA evaluations in the database (currently Redis-only with 30-min TTL) or derive from observation history.
2. Create `QsofaHistory.vue` timeline or chart showing criteria count over time with markers for when alerts fired.
3. Add `fetchQsofaHistory(encounterId, { limit })` to `clinical.js`.
4. Render in `PatientDetail.vue` clinical review section.

**Frontend files:** New `components/charts/QsofaHistory.vue`, update `views/PatientDetail.vue`, update `api/clinical.js`.

**Backend files:** `QsofaController.cs`, `QsofaService.cs`, potentially new `QsofaEvaluation` entity and migration.

**Dependency:** Backend persistence decision (Redis-only vs. database-backed history).

---

## P0 — No medication timeline integrated with vital sign charts

### Who needs it

Physicians, pharmacists, nurses.

### Problem

Medications are fetched in `PatientDetail.vue` but only used in the `AlertReasoning.vue` panel (to show "recent medications" within 90 minutes of an alert). They are **not displayed on vital sign trend charts** as event markers. A nurse looking at a heart rate spike cannot see that a beta-blocker was administered 30 minutes prior.

### Why fix

Medication-vital correlation is fundamental to clinical assessment. Without overlaying medication administration times on vital trends, clinicians cannot distinguish pharmacological effects from pathological deterioration. The `medication-false-alarm-01` simulator scenario was specifically designed to test this, but the visual integration is incomplete.

### How to fix

1. Add vertical annotation lines or markers on `VitalTrendChart.vue` for medication administration timestamps.
2. Filter medications to the same time window as the observation data.
3. Show medication name, dose, and route on hover/tooltip.
4. Use a distinct visual style (dashed vertical line with label) to avoid confusion with observation data points.

**Frontend files:** Update `components/charts/VitalTrendChart.vue` and `TrendsGrid.vue` to accept a `medications` prop. Update `PatientDetail.vue` to pass medications to chart grid.

**Backend:** Already complete — `MedicationsController.List` returns timestamped administrations.

**Dependency:** None.

---

## P1 — No encounter timeline view in the dashboard

### Who needs it

All clinicians (handoff, shift change, case review).

### Problem

The backend has `GET /api/v1/encounters/{id}/timeline` which returns a merged chronological timeline of observations, alerts, orders, medications, and status changes. The dashboard does **not** render this view. Currently, observations, alerts, orders, and sepsis bundles are displayed in separate panels with no unified chronological view.

### Why fix

During shift handoff, the outgoing clinician needs to tell a story: "at 14:00 the patient spiked a fever, at 14:15 qSOFA fired, at 14:30 we ordered labs, at 15:00 SOFA delta triggered a bundle, antibiotics were given at 15:20." The separate panels force the receiving clinician to mentally merge 4+ data streams. A timeline view is the standard handoff tool in clinical settings.

### How to fix

1. Create `EncounterTimeline.vue` component rendering a vertical chronological timeline.
2. Each event type gets a distinct icon/color: observation (blue), alert (red/amber), order (purple), medication (green), status change (gray).
3. Fetch `GET /encounters/{id}/timeline` — add `fetchTimeline(encounterId)` to `encounters.js`.
4. Add as a tab or expandable section in `PatientDetail.vue`.
5. Support time-range filter and event-type toggle.

**Frontend files:** New `components/patient/EncounterTimeline.vue`, update `views/PatientDetail.vue`, update `api/encounters.js`.

**Backend:** Already complete — `EncountersController.Timeline` exists.

**Dependency:** None.

---

## P1 — No patient demographics or allergy display

### Who needs it

All clinicians, especially nurses (medication administration safety check).

### Problem

`PatientDetail.vue` shows `encounter.patient?.firstName` and `encounter.patient?.lastName` in the header, but does not display: date of birth, age, gender, blood type, allergies, emergency contact, MRN (except in the ward table), or admission reason. The `Patient` entity contains all of these fields. The `Encounter` entity contains room/bed, department, attending physician, and admission reason.

### Why fix

Allergies are a **patient safety** field — a nurse confirming medication administration needs allergies visible. Age affects clinical scoring interpretation (NEWS2 and SOFA have different clinical implications for elderly patients). Blood type is critical during transfusion decisions. Emergency contact is needed when a patient cannot advocate for themselves.

### How to fix

1. Create `PatientBanner.vue` — a persistent header strip showing: name, MRN, DOB/age, gender, allergies (highlighted), blood type, room/bed, department, attending physician, admission reason.
2. Use standard clinical banner pattern: red background for known allergies, "NKDA" (no known drug allergies) if allergies field is empty.
3. Replace the simple name display in `PatientDetail.vue` header with `PatientBanner`.
4. Data already available from `GET /encounters/{id}` response (includes nested patient).

**Frontend files:** New `components/patient/PatientBanner.vue`, update `views/PatientDetail.vue`.

**Backend:** Already complete — encounter detail includes patient demographics.

**Dependency:** None.

---

# Part B — Ward Management & Situational Awareness

---

## P1 — Ward table missing critical clinical columns

### Who needs it

Charge nurses, nursing supervisors.

### Problem

`WardEncounterSummary` provides: MRN, name, room/bed, department, status, NEWS2 score/risk, qSOFA score, sepsis active flag, sepsis bundle status, and open alert count. `WardTable.vue` and `PatientRow.vue` render these fields. **Missing from the ward view:**
- SOFA score and delta
- GCS score (critical for neuro patients)
- Last observation time (how stale is the data?)
- Attending physician name
- Length of stay (days since admission)

### Why fix

A charge nurse scanning the ward board needs to identify: (1) patients with active organ dysfunction (SOFA), (2) patients with neurological decline (GCS), (3) patients who haven't had vitals taken recently (stale data = missed deterioration), and (4) who is responsible for each patient. The current ward table is blind to SOFA, GCS, data freshness, and physician assignment.

### How to fix

1. **Backend:** Extend `WardEncounterSummary` record to include: `int? SofaScore`, `int? SofaDelta`, `int? GcsScore`, `string? GcsClassification`, `DateTimeOffset? LastObservationAt`, `string? AttendingPhysician`, `DateTimeOffset OpenedAt`.
2. Update `EncounterService.ListAsync` to join/subquery for SOFA, GCS, and latest observation timestamp.
3. **Frontend:** Add columns to `WardTable.vue` and data to `PatientRow.vue` / `PatientCard.vue`.
4. Add "stale" visual indicator (amber/red text or icon) when `LastObservationAt` is > 2 hours old for active inpatients.
5. Compute and display length of stay from `OpenedAt`.

**Frontend files:** Update `components/ward/WardTable.vue`, `PatientRow.vue`, `PatientCard.vue`, `stores/ward.js`.

**Backend files:** `WardEncounterSummary.cs`, `EncounterService.cs`.

**Dependency:** None.

---

## P2 — No hospital-wide sepsis bundle compliance board

### Who needs it

Charge nurses, sepsis coordinators, quality improvement teams.

### Problem

Sepsis bundle status is only visible per-patient in `SepsisBundlePanel.vue`. There is no hospital-wide view showing all active bundles, their compliance status, time remaining, and which treatment elements are outstanding. The backend has `GET /api/v1/sepsis-bundles?status=IN_PROGRESS` which lists bundles across all encounters — the dashboard does not consume it.

### Why fix

Sepsis bundle compliance (CMS SEP-1) is a hospital quality metric reported to regulatory bodies. A charge nurse or sepsis coordinator needs a single screen showing: "3 active bundles — 1 on track, 1 at risk (45 min remaining, antibiotics pending), 1 overdue." Without this, bundle compliance tracking requires opening each patient individually or running database queries.

### How to fix

1. Create `SepsisBoardView.vue` — new route at `/sepsis`.
2. Display a table/card list of active bundles with: patient name/MRN, department, bundle creation time, deadline, time remaining (countdown), compliance status, completed elements, outstanding elements.
3. Sort by urgency: overdue first, then by time remaining ascending.
4. Add severity coloring: green (> 30 min remaining), amber (< 30 min), red (overdue or non-compliant).
5. Click-through to `PatientDetail` for the encounter.
6. Add `fetchSepsisBundles({ status, page, pageSize })` to a new `api/sepsis.js` module.
7. Add navigation link in `AppSidebar.vue`.

**Frontend files:** New `views/SepsisBoardView.vue`, new `api/sepsis.js`, update `router/index.js`, update `components/layout/AppSidebar.vue`.

**Backend:** Already complete — `SepsisBundlesController.List` exists.

**Dependency:** None.

---

## P2 — No department/unit view with aggregate metrics

### Who needs it

Charge nurses, nursing managers, hospital administrators.

### Problem

The ward dashboard filters by department but shows a flat patient list. There is no department-level summary showing: total patients, acuity distribution (how many high/medium/low NEWS2), active alerts, active sepsis bundles, staffing context, or bed occupancy. The `AnalyticsController` has `GET /analytics/alerts/summary` (by department) and `GET /analytics/population` (by observation threshold) — neither is surfaced in the dashboard.

### Why fix

A nursing manager responsible for 4 units needs a summary view, not 80 individual patient rows. "ICU: 12 patients, 4 critical (NEWS2 ≥7), 2 active sepsis bundles, 15 unacknowledged alerts" is actionable. Scrolling through 12 patient rows to derive the same information is not.

### How to fix

1. Create `DepartmentOverviewView.vue` — new route at `/departments` or modify `/ward` to show summary cards above the patient list.
2. Show per-department cards with: patient count, acuity distribution (pie/bar), active bundle count, unacknowledged alert count, average NEWS2.
3. Click a department card to filter the ward table to that department.
4. Consume `GET /analytics/alerts/summary` and derive patient counts from `GET /encounters?department=X&status=ACTIVE`.
5. Optionally add `GET /api/v1/encounters/summary` backend endpoint to return pre-aggregated counts by department.

**Frontend files:** New `views/DepartmentOverviewView.vue` or enhanced `WardDashboard.vue`, new API calls in `api/encounters.js`.

**Backend files:** Optionally new summary endpoint in `EncountersController`.

**Dependency:** None.

---

## P2 — No sort controls on the ward table

### Who needs it

All clinicians using the ward view.

### Problem

`WardTable.vue` sorts exclusively by NEWS2 risk (highest first) via `sortedByRisk` computed property in the ward store. There is no UI to sort by: patient name, room/bed, department, alert count, qSOFA score, or sepsis bundle status. The table is virtual-scrolled (50-row window) which is good for performance but has no column header click-to-sort.

### Why fix

A nurse doing room rounds wants to sort by room/bed. A physician looking for a specific patient wants to sort by name. A charge nurse wants to sort by alert count to prioritize triage. Fixed single-sort works for the "acuity first" workflow but blocks all others.

### How to fix

1. Add sortable column headers to `WardTable.vue` with visual sort indicators (arrow up/down).
2. Extend `ward.js` store to support `sortField` and `sortDirection` state.
3. Compute sorted list based on selected sort field.
4. Default to NEWS2 risk (preserve current behavior).
5. Persist sort preference to `localStorage` via settings store.

**Frontend files:** Update `components/ward/WardTable.vue`, update `stores/ward.js`.

**Backend:** No change needed — sorting is client-side on the full result set.

**Dependency:** None.

---

## P2 — No search/filter on the ward table

### Who needs it

All clinicians.

### Problem

The ward table has department filter only (4 options: All, ICU, General Medicine, Surgery). There is no text search for patient name or MRN, no filter by acuity level, no filter by "has active alerts", no filter by "has active sepsis bundle." The backend `AnalyticsController.PatientSearch` provides full-text search via Elasticsearch — it is not used by the dashboard.

### Why fix

On a 40-patient ward, finding a specific patient by name or MRN requires visual scanning. Filtering to "only patients with unacknowledged alerts" is the most common charge nurse workflow. Both are O(n) eye-scanning today vs. instant with a search box.

### How to fix

1. Add a search input to the ward toolbar (debounced, 300ms).
2. Client-side filter for name/MRN matching (data already loaded).
3. Add filter chips or toggles: "Has alerts", "Sepsis active", "Critical (NEWS2 ≥7)".
4. Optionally integrate `GET /analytics/patients` for server-side full-text search when local filtering is insufficient.

**Frontend files:** Update `views/WardDashboard.vue`, update `stores/ward.js`.

**Backend:** No change for client-side filtering. Optional integration with existing `AnalyticsController`.

**Dependency:** None.

---

# Part C — Clinical Workflow Gaps

---

## P1 — No notification/sound for new critical alerts

### Who needs it

Nurses at nurse stations, charge nurses with dashboard on wall display.

### Problem

The dashboard polls every 5-10 seconds and silently updates. When a new CRITICAL alert fires (SOFA_SEPSIS, NEWS2_EMERGENCY, GCS_CRITICAL, CRITICAL_HEART_RATE), there is no audible alert, no browser notification, no visual flash. A nurse glancing at a wall-mounted dashboard will not notice a new critical alert unless they happen to look at the right moment.

### Why fix

Clinical alerting is safety-critical. Wall-mounted dashboards are common in nurse stations. Without attention-grabbing notification on critical alerts, the dashboard provides less situational awareness than a traditional alarm bell. The entire alert pipeline (Kafka → alert creation → RabbitMQ paging) exists but the "last mile" to the dashboard screen is passive polling.

### How to fix

1. Track `lastSeenAlertIds` in the alert store.
2. On each poll, detect new critical alerts by comparing IDs.
3. Play an audible tone (configurable, with mute toggle) for new CRITICAL alerts.
4. Show a browser notification (requires permission request) with patient name and alert type.
5. Flash the browser tab title: `"⚠ CRITICAL ALERT — VigilCare"`.
6. Add a visual banner at the top of the page that persists until dismissed.
7. Long-term: Replace polling with WebSocket/SSE for real-time push (see P4-07).

**Frontend files:** Update `stores/alerts.js`, new `composables/useAlertNotification.js`, update `App.vue` for banner.

**Backend:** No change for polling approach. Future: add SignalR hub or SSE endpoint.

**Dependency:** None for polling-based. SSE/WebSocket requires backend work.

---

## P1 — No handoff report or shift summary

### Who needs it

Nurses, physicians (shift change).

### Problem

There is no printable or exportable shift summary. During handoff, a clinician needs a snapshot: "these are my patients, here are their current scores, here are the active alerts and pending actions." The dashboard is live and interactive — there is no "print this shift's state" or "generate a summary" feature.

### Why fix

Shift handoff is the highest-risk moment for information loss. SBAR (Situation, Background, Assessment, Recommendation) handoff is standard practice. A one-page summary per patient (or one page for the entire ward) eliminates reliance on memory and verbal communication.

### How to fix

1. Add "Export Handoff Report" button to the ward toolbar.
2. Generate a printable HTML view (or PDF via browser print) with:
   - Ward summary: department, patient count, critical count, active bundles.
   - Per-patient row: name, MRN, room, NEWS2, SOFA, GCS, active alerts summary, pending orders, key vitals (latest HR, RR, BP, SpO₂, temp).
3. Use `@media print` CSS for clean print layout.
4. Optionally add per-patient SBAR summary (Situation = chief complaint + admission reason, Background = allergies + history, Assessment = scores + trends, Recommendation = pending orders + bundle status).

**Frontend files:** New `components/ward/HandoffReport.vue`, print-specific CSS, button in `WardDashboard.vue`.

**Backend:** No change — all data already available from existing endpoints.

**Dependency:** P1 (patient demographics) for allergy display.

---

## P2 — No discharge summary view

### Who needs it

Physicians, case managers.

### Problem

`DischargeSummaryWorkerService` generates PDF discharge summaries and stores them in MinIO, but there is no dashboard UI to view or download them. `EncountersController.TransitionStatus` handles discharge transitions, but the resulting PDF is only accessible via MinIO's S3 API.

### Why fix

Discharge summaries are legally required documents. Physicians need to review the auto-generated summary, and case managers need to verify completeness before patient leaves. Without dashboard access, these documents require S3 bucket browsing or CLI tools.

### How to fix

1. **Backend:** Add `GET /api/v1/encounters/{id}/discharge-summary` endpoint that returns a pre-signed MinIO URL or streams the PDF.
2. **Frontend:** Add "Discharge Summary" section to `PatientDetail.vue` (visible only for DISCHARGED encounters).
3. Embed PDF viewer or download link.
4. Show generation status if the async worker hasn't completed yet.

**Frontend files:** New `components/patient/DischargeSummaryPanel.vue`, update `PatientDetail.vue`.

**Backend files:** New endpoint in `EncountersController`, pre-signed URL generation for MinIO.

**Dependency:** None.

---

## P3 — Alert acknowledgment lacks role context

### Who needs it

All clinicians, audit/compliance teams.

### Problem

`AcknowledgeModal.vue` uses the clinician ID from settings (default `DR-DEMO`) as a free-text string. There is no role context — the system does not distinguish between a nurse acknowledging an alert (documenting awareness) and a physician acknowledging it (confirming clinical assessment). The `POST /alerts/{id}/acknowledge` endpoint accepts `acknowledgedByClinicianId` as a string.

### Why fix

Clinical audit trails require knowing **who** acknowledged an alert and in **what capacity**. A nurse acknowledging a critical alert means "I've seen this and will escalate to a physician." A physician acknowledging means "I've assessed this and am taking action." These are distinct clinical events.

### How to fix

1. Replace free-text clinician ID with the authenticated user's identity from the JWT token.
2. Display the user's name and role in the acknowledgment confirmation modal.
3. Include role in the acknowledgment note automatically.
4. Update `AlertCard.vue` to show who acknowledged (name + role) instead of raw clinician ID.

**Frontend files:** Update `components/alerts/AcknowledgeModal.vue`, `AlertCard.vue`, `stores/auth.js`.

**Backend:** Minor — already receives user identity via JWT. Ensure `AcknowledgedBy` stores user ID, not free-text string.

**Dependency:** None.

---

## P3 — No observation entry form (nurse charting)

### Who needs it

Nurses (primary vital sign recorders).

### Problem

The dashboard has `GcsEntryForm.vue` for GCS component entry but no form for standard vital signs (HR, RR, BP, SpO₂, temperature, AVPU). Nurses must enter vitals through another system or direct API calls. The `POST /encounters/{id}/observations` endpoint exists and accepts single or batch observations.

### Why fix

If nurses cannot chart vitals in the dashboard, they need a separate system for data entry and only use the dashboard for viewing. This dual-system workflow increases error risk and reduces adoption. A single system for both entry and monitoring is the clinical standard.

### How to fix

1. Create `VitalsEntryForm.vue` — a structured form with fields for all standard vital signs.
2. Pre-populate observation codes: HEART_RATE, RESP_RATE, SYSTOLIC_BP, DIASTOLIC_BP, SPO2, TEMP_C, AVPU.
3. Add plausibility validation client-side (e.g., HR 20-300, temp 30-45°C) matching backend `PlausibilityValidator`.
4. Submit as batch observation via `POST /encounters/{id}/observations`.
5. Add to `PatientDetail.vue` as a collapsible form or modal triggered by a "Record Vitals" button.

**Frontend files:** New `components/patient/VitalsEntryForm.vue`, update `views/PatientDetail.vue`, update `api/encounters.js`.

**Backend:** Already complete — observation ingest endpoint exists with batch support.

**Dependency:** None.

---

# Part D — Admin & Operational Dashboards

---

## P2 — No alert threshold management UI

### Who needs it

Clinical administrators, biomedical engineering.

### Problem

Alert thresholds are managed via API only (`AlertThresholdsController` — full CRUD with audit trail and Redis cache invalidation). There is no dashboard UI for viewing or editing thresholds. A clinical administrator who needs to adjust the heart rate warning range from 50-100 to 50-110 must use curl or Postman.

### Why fix

Threshold tuning is the primary mechanism for reducing alert fatigue — the #1 clinician complaint about clinical alerting systems. Without a UI, threshold adjustments require developer involvement, creating a bottleneck that prevents clinical teams from optimizing their own alert configuration.

### How to fix

1. Create `ThresholdManagementView.vue` — new route at `/admin/thresholds`.
2. Display a table of all thresholds: observation code, warning low/high, critical low/high, unit.
3. Inline editing or modal edit form with validation.
4. Create/delete threshold support.
5. Add to sidebar under an "Admin" section (visible only to Admin role).
6. Consume `GET /alert-thresholds`, `POST`, `PUT /alert-thresholds/{id}`, `DELETE /alert-thresholds/{id}`.

**Frontend files:** New `views/ThresholdManagementView.vue`, new `api/thresholds.js`, update `router/index.js`, update `AppSidebar.vue`.

**Backend:** Already complete — full CRUD exists with `DELETE` support.

**Dependency:** Role-based navigation (P3-02).

---

## P2 — No user management UI

### Who needs it

Hospital IT administrators.

### Problem

User creation and role assignment happen via API only (`AuthController.Login` issues JWT tokens, but user registration is seed-data or direct DB). There is no dashboard for managing clinical users, assigning roles, or deactivating accounts.

### Why fix

A hospital with 200 clinicians cannot manage user accounts via database inserts. New staff onboarding, role changes (nurse → charge nurse), and account deactivation (termination, transfer) are daily IT operations that need a UI.

### How to fix

1. **Backend first:** Add `UsersController` with `GET /api/v1/users` (list), `POST` (create), `PATCH /{id}` (update role, deactivate), protected by `UsersAdmin` permission.
2. Create `UserManagementView.vue` — new route at `/admin/users`.
3. Table of users: username, role, email, last login, active status.
4. Create/edit form: username, password (create only), role selection, active toggle.
5. Restrict route to Admin role.

**Frontend files:** New `views/UserManagementView.vue`, new `api/users.js`, update `router/index.js`, update `AppSidebar.vue`.

**Backend files:** New `UsersController.cs`, new `UserService.cs` CRUD methods.

**Dependency:** Backend user management endpoints.

---

## P2 — No gateway/site status dashboard

### Who needs it

IT operations, hospital administrators.

### Problem

The Ward Gateway system tracks site registration, gateway heartbeats (ONLINE/DEGRADED/OFFLINE), and buffer depth via `GatewaysController` and `SitesController`. There is no dashboard for monitoring gateway health. An IT operator who needs to see which ward gateways are offline must query the API directly.

### Why fix

Gateway outages directly affect clinical data flow. If a ward gateway goes OFFLINE, observations from that ward stop syncing to the central system — scoring, alerting, and sepsis detection all stop for those patients. This is a patient safety concern that needs a visible status board.

### How to fix

1. Create `GatewayStatusView.vue` — new route at `/admin/gateways`.
2. Display sites as collapsible sections, each containing its gateways.
3. Per gateway: status badge (green/amber/red), last heartbeat time, buffer depth, department, last sync time.
4. Auto-refresh every 30 seconds.
5. Highlight gateways with buffer depth > 0 (data waiting to sync) or heartbeat older than 2 minutes.
6. Consume `GET /sites`, `GET /sites/{id}/gateways`.

**Frontend files:** New `views/GatewayStatusView.vue`, new `api/gateways.js`, update `router/index.js`, update `AppSidebar.vue`.

**Backend:** Already complete — site and gateway endpoints exist.

**Dependency:** None.

---

## P4 — No audit log viewer

### Who needs it

Compliance officers, hospital administrators.

### Problem

`AuditLogsController` and `PhiAccessLogsController` expose paginated, filterable audit trails — who did what, when, to which entity. The dashboard has no UI for browsing audit logs. Compliance reviews and incident investigations require API queries or direct database access.

### Why fix

HIPAA requires demonstrable access controls and audit trails. During a compliance audit or security incident investigation, a compliance officer needs to answer: "Who accessed Patient X's records in the last 30 days?" and "Who modified alert threshold Y?" Without a UI, this requires technical staff to run queries.

### How to fix

1. Create `AuditLogView.vue` — new route at `/admin/audit`.
2. Filterable table: date range, user, entity type, action type.
3. Show before/after state for modifications (already stored in `ClinicalAuditLog.OldValues` / `NewValues`).
4. Add PHI access log tab with patient-specific filtering.
5. Restrict to `AuditRead` permission.
6. Consume `GET /audit-logs` and `GET /phi-access-logs`.

**Frontend files:** New `views/AuditLogView.vue`, new `api/audit.js`, update `router/index.js`, update `AppSidebar.vue`.

**Backend:** Already complete — both endpoints exist with full filter support.

**Dependency:** Role-based navigation.

---

## P4 — No reconciliation dashboard

### Who needs it

Charge nurses, quality improvement teams.

### Problem

`ReconciliationScheduler` runs periodic checks for data quality issues (unacknowledged alerts > 30 min, pending orders > 4 hours, stale observations > 2 hours for active inpatients). `ReconciliationAlertsController` exposes the findings. The dashboard does not display them.

### Why fix

Reconciliation findings identify systemic workflow failures — not individual patient emergencies, but patterns like "3 patients on Ward B haven't had vitals recorded in 3 hours." Without a dashboard view, these operational insights are invisible.

### How to fix

1. Create `ReconciliationView.vue` — new route at `/admin/reconciliation`.
2. Three sections (matching check types): unacknowledged alerts, pending orders, stale observations.
3. Each item links to the relevant patient or encounter.
4. Show resolved vs. unresolved toggle.
5. Consume `GET /reconciliation-alerts?checkType=X&resolved=false`.

**Frontend files:** New `views/ReconciliationView.vue`, new `api/reconciliation.js`, update `router/index.js`, update `AppSidebar.vue`.

**Backend:** Already complete — controller and scheduler exist.

**Dependency:** None.

---

# Part E — UX & Usability

---

## P3 — No role-based navigation or view scoping

### Who needs it

All users.

### Problem

The dashboard shows the same 4 routes to all authenticated users regardless of role (Nurse, Physician, Admin, Integration). All navigation items are visible in `AppSidebar.vue`. Admin-only views (audit logs, threshold management, user management) do not exist yet, but when they do, they should be role-scoped. Even existing views should adapt — a physician may want SOFA/GCS prominent while a nurse needs vitals entry and alert acknowledgment prominent.

### Why fix

Clinicians have limited screen time. Showing irrelevant navigation and features increases cognitive load and onboarding time. Admin features visible to bedside nurses create confusion and accidental access risk.

### How to fix

1. Read the user's role from the JWT token (already decoded in `auth.js` store).
2. Add `meta.requiredRole` or `meta.requiredPermission` to route definitions.
3. Filter sidebar navigation items by user role.
4. Add `v-if` guards on role-specific components.
5. Define role-view mapping: Nurse (ward, patients, alerts), Physician (ward, patients, alerts, feedback), Admin (all views + admin section), Integration (N/A — API-only role).

**Frontend files:** Update `router/index.js`, update `components/layout/AppSidebar.vue`, update `stores/auth.js`.

**Backend:** No change — role info already in JWT claims.

**Dependency:** None.

---

## P3 — No dark mode consistency across all components

### Who needs it

Clinicians in dimly lit environments (night shift, ICU).

### Problem

Dark mode toggle exists (`useDarkMode.js`, header toggle, localStorage persistence), and Tailwind `dark:` classes are applied throughout. However, Chart.js charts (`VitalChart.vue`, `VitalTrendChart.vue`, `News2History.vue`) use hardcoded colors that do not adapt to dark mode — light gray gridlines and axis labels that become invisible on dark backgrounds.

### Why fix

ICU and night-shift environments are deliberately dimly lit. A bright chart area within a dark dashboard creates eye strain and reduces readability. Clinical dashboards on wall-mounted monitors must be readable in both lighting conditions without manual adjustment.

### How to fix

1. Create a `useChartTheme.js` composable that returns Chart.js options based on current dark mode state.
2. Dynamically set `scales.x.ticks.color`, `scales.y.ticks.color`, `scales.x.grid.color`, `scales.y.grid.color`, `plugins.legend.labels.color` based on dark/light mode.
3. Apply to all chart components: `VitalChart.vue`, `VitalTrendChart.vue`, `News2History.vue`, and any new chart components (SofaHistory, GcsHistory).
4. React to dark mode toggle via `watch` on the dark mode composable.

**Frontend files:** New `composables/useChartTheme.js`, update all chart components.

**Backend:** No change.

**Dependency:** None.

---

## P3 — No keyboard shortcuts for alert triage

### Who needs it

Nurses, charge nurses (high-volume alert triage).

### Problem

Alert acknowledgment requires: click alert card → click Ack button → confirm in modal. During a high-alert-volume period (sepsis onset, code event), this three-click workflow repeated across 10+ alerts is slow. There are no keyboard shortcuts for common alert actions.

### Why fix

Clinical alert triage during critical events needs to be as fast as possible. Keyboard shortcuts (e.g., `A` to acknowledge selected, `R` to resolve, `J`/`K` to navigate alerts) reduce triage time from seconds per alert to milliseconds.

### How to fix

1. Add keyboard event listeners in `AlertCenter.vue` and `PatientDetail.vue`.
2. Implement navigation: `J` (next alert), `K` (previous alert), `Enter` (select).
3. Implement actions: `A` (acknowledge selected), `R` (resolve selected), `Esc` (cancel modal).
4. Show keyboard shortcut hints in the UI (small tooltip or `?` help overlay).
5. Respect focus context — only activate when no input/textarea is focused.

**Frontend files:** New `composables/useAlertKeyboard.js`, update `views/AlertCenter.vue`, `components/patient/AlertsList.vue`.

**Backend:** No change.

**Dependency:** None.

---

## P3 — No responsive mobile optimization for patient detail

### Who needs it

Nurses at bedside (tablet/phone).

### Problem

`WardDashboard.vue` has mobile support (`PatientCard.vue` for mobile, `PatientRow.vue` for desktop). `PatientDetail.vue` uses a grid layout (`sm:grid-cols-2 lg:grid-cols-3`) but the clinical review section with 5 vital trend charts and replay controls is not optimized for mobile. Charts may overflow or be too small to read on a phone screen.

### Why fix

Bedside clinicians increasingly use tablets and phones. A nurse confirming vital signs at the bedside needs to see the patient's current scores and trends on a handheld device. If charts are unreadable or the page requires horizontal scrolling, the nurse will revert to paper.

### How to fix

1. Audit all `PatientDetail.vue` sub-components at 320px, 768px, and 1024px viewports.
2. Stack panels vertically on mobile (scores → vitals → alerts → orders).
3. Make charts full-width on mobile with touch-friendly tooltips.
4. Collapse less-critical sections (replay controls, feedback) behind expandable accordions on mobile.
5. Ensure tap targets are ≥ 44px for all buttons (WCAG minimum).

**Frontend files:** Update `views/PatientDetail.vue`, all `components/patient/*.vue`, `components/charts/*.vue`.

**Backend:** No change.

**Dependency:** None.

---

## P5 — No accessibility (WCAG) compliance

### Who needs it

All users, particularly those with visual or motor impairments.

### Problem

The dashboard has no documented accessibility testing. Common gaps observed: chart components lack `aria-label` descriptions, alert severity relies solely on color (no icon or text pattern for colorblind users), modal focus trapping may be incomplete, keyboard navigation through the ward table is not implemented.

### Why fix

Healthcare facilities must comply with ADA/Section 508 accessibility requirements. Clinical staff may have color vision deficiency (8% of males) — relying on red/amber/green alone for severity classification excludes them from effective alert triage.

### How to fix

1. Add `aria-label` to all interactive elements and chart containers.
2. Add severity icons alongside color badges (e.g., exclamation triangle for critical, warning triangle for warning).
3. Ensure color contrast meets WCAG AA (4.5:1 for text, 3:1 for UI components).
4. Add focus management: trap focus in modals, return focus on close.
5. Add skip links and landmark roles (`role="main"`, `role="navigation"`).
6. Test with screen reader (VoiceOver, NVDA) and keyboard-only navigation.

**Frontend files:** All component files, `App.vue` for landmarks.

**Backend:** No change.

**Dependency:** None.

---

# Part F — Infrastructure & Real-Time Communication

---

## P4 — Grafana dashboard limited to infrastructure metrics

### Who needs it

IT operations, clinical informatics teams.

### Problem

The Grafana dashboard (`infra/grafana/dashboards/vigilcare.json`) has 3 panels: unacknowledged critical alerts (stat), observation ingest rate (timeseries), and clinical alerts by type/severity (bar gauge). Missing: consumer lag per group, outbox pending depth, gateway status, sepsis bundle compliance rates, scoring latency, API endpoint latency, error rates by service.

### Why fix

When a clinical alert is delayed, IT needs to diagnose whether the bottleneck is ingest (observations not arriving), scoring (Kafka consumer lag), alerting (outbox relay delay), or paging (RabbitMQ worker). The current 3 panels provide almost no diagnostic capability.

### How to fix

1. Add **Consumer Lag** panel: `kafka_consumer_lag` by consumer group.
2. Add **Outbox Depth** panel: `outbox_pending_events` over time.
3. Add **Gateway Status** panel: `ward_gateway_status` per gateway (from `WardGatewayMetricsCollector`).
4. Add **Sepsis Bundle Compliance** panel: `sepsis_bundle_compliance_total` by status.
5. Add **Scoring Latency** panels: processing time per scoring service (requires P5 metrics from platform gap analysis).
6. Add **API Latency** panel: `http_request_duration_seconds` p50/p95/p99 (requires platform gap P5-01 fix).
7. Organize into rows: Clinical, Infrastructure, API.

**Files:** `infra/grafana/dashboards/vigilcare.json`.

**Backend:** Some panels depend on metrics from `BackgroundServices/Metrics/` collectors. Additional metrics require platform gap analysis P5 fixes.

**Dependency:** Platform gap P5-01 (request timing) and P5-02 (background service metrics) for full coverage.

---

## P4 — No real-time push (WebSocket/SSE) — polling only

### Who needs it

All dashboard users, especially for critical alerts.

### Problem

All dashboard data is fetched via HTTP polling (10s ward, 5s patient detail). This introduces up to 10 seconds of latency between a critical alert firing and a nurse seeing it on screen. For a SOFA_SEPSIS alert where a 1-hour compliance clock starts immediately, 10 seconds of UI latency is acceptable but not ideal. More importantly, polling generates continuous HTTP traffic even when no data has changed.

### Why fix

Real-time push reduces alert notification latency from 5-10 seconds to sub-second. It also reduces server load — instead of every connected dashboard client polling every 5 seconds, the server pushes only when state changes. For a hospital with 20 dashboard instances, that's 240 requests/minute replaced by event-driven pushes.

### How to fix (phased)

**Phase 1 — SSE for alerts:**
1. Add `GET /api/v1/alerts/stream` SSE endpoint that pushes new alert events.
2. Backend consumes from Kafka `alert.generated` topic and writes to SSE connections.
3. Frontend replaces alert polling with `EventSource` connection.
4. Fall back to polling if SSE connection drops.

**Phase 2 — SignalR for full real-time:**
1. Add SignalR hub for dashboard events: new observations, score updates, alert state changes.
2. Backend bridges Kafka consumers → SignalR groups (by encounter, by department).
3. Frontend uses SignalR client, falling back to SSE, then to polling.

**Backend files:** New `Hubs/DashboardHub.cs` or SSE controller, Kafka → push bridge service.

**Frontend files:** New `composables/useRealTime.js`, update all polling consumers.

**Dependency:** Significant backend work. Phase 1 (SSE) is simpler and covers the most critical use case (alerts).

---

# Summary matrix

| # | Issue | Priority | Part | Backend exists? | Status |
|---|-------|----------|------|----------------|--------|
| 1 | No SOFA trend chart | P0 | A | Yes | Open |
| 2 | No GCS trend chart / history endpoint | P0 | A | Partial (no history endpoint) | Open |
| 3 | No qSOFA history view | P0 | A | No (Redis-only, no persistence) | Open |
| 4 | No medication overlay on vital charts | P0 | A | Yes | Open |
| 5 | No encounter timeline view | P1 | A | Yes | Open |
| 6 | No patient demographics / allergy banner | P1 | A | Yes | Open |
| 7 | Ward table missing SOFA, GCS, staleness | P1 | B | Partial (DTO needs extension) | Open |
| 8 | No sepsis bundle compliance board | P2 | B | Yes | Open |
| 9 | No department overview with aggregates | P2 | B | Partial | Open |
| 10 | No sort controls on ward table | P2 | B | N/A (client-side) | Open |
| 11 | No search/filter on ward table | P2 | B | Yes (analytics search) | Open |
| 12 | No critical alert notification/sound | P1 | C | No (needs push or client detect) | Open |
| 13 | No handoff/shift summary report | P1 | C | Yes (data available) | Open |
| 14 | No discharge summary view | P2 | C | Partial (needs download endpoint) | Open |
| 15 | Alert ack lacks role context | P3 | C | Partial | Open |
| 16 | No vitals entry form | P3 | C | Yes | Open |
| 17 | No threshold management UI | P2 | D | Yes | Open |
| 18 | No user management UI | P2 | D | No (needs backend endpoints) | Open |
| 19 | No gateway status dashboard | P2 | D | Yes | Open |
| 20 | No audit log viewer | P4 | D | Yes | Open |
| 21 | No reconciliation dashboard | P4 | D | Yes | Open |
| 22 | No role-based navigation | P3 | E | N/A (frontend-only) | Open |
| 23 | Dark mode chart inconsistency | P3 | E | N/A | Open |
| 24 | No keyboard shortcuts | P3 | E | N/A | Open |
| 25 | No mobile optimization for patient detail | P3 | E | N/A | Open |
| 26 | No accessibility (WCAG) compliance | P5 | E | N/A | Open |
| 27 | Grafana dashboard incomplete | P4 | F | Partial (needs more metrics) | Open |
| 28 | No real-time push (polling only) | P4 | F | No (needs SSE/SignalR) | Open |

---

## Suggested implementation sequence

```mermaid
flowchart TD
    subgraph safety [Part A — Patient Safety]
        P0A[P0: SOFA history chart]
        P0B[P0: GCS history + endpoint]
        P0C[P0: qSOFA history + persistence]
        P0D[P0: Medication overlay on charts]
        P1A[P1: Encounter timeline view]
        P1B[P1: Patient demographics banner]
    end

    subgraph ward [Part B — Ward Awareness]
        P1C[P1: Ward table clinical columns]
        P2A[P2: Sepsis compliance board]
        P2B[P2: Department overview]
        P2C[P2: Sort controls]
        P2D[P2: Search/filter]
    end

    subgraph workflow [Part C — Clinical Workflows]
        P1D[P1: Critical alert notification]
        P1E[P1: Handoff report]
        P2E[P2: Discharge summary view]
        P3A[P3: Alert role context]
        P3B[P3: Vitals entry form]
    end

    subgraph admin [Part D — Admin Dashboards]
        P2F[P2: Threshold management UI]
        P2G[P2: User management UI]
        P2H[P2: Gateway status dashboard]
        P4A[P4: Audit log viewer]
        P4B[P4: Reconciliation dashboard]
    end

    subgraph ux [Part E — UX & Accessibility]
        P3C[P3: Role-based navigation]
        P3D[P3: Dark mode charts]
        P3E[P3: Keyboard shortcuts]
        P3F[P3: Mobile optimization]
        P5A[P5: WCAG compliance]
    end

    P3C --> P2F
    P3C --> P2G
    P3C --> P4A
    P0B --> P1C
    P0A --> P1C
    P1D --> P2A
```

### Sprint-sized batches

| Batch | Items | Outcome |
|-------|-------|---------|
| **1 — Bedside Decision Support** | P0 SOFA chart, P0 GCS history + chart, P0 medication overlay, P1 patient banner, P1 timeline | Physicians and nurses see full scoring history and medication context for clinical decisions |
| **2 — Ward Awareness** | P0 qSOFA history, P1 ward table columns, P2 sort/search, P1 critical alert notification, P2 sepsis board | Charge nurses have complete situational awareness; critical alerts demand attention |
| **3 — Clinical Workflows** | P1 handoff report, P2 discharge summary, P3 vitals entry form, P3 alert role context | Nurses can chart vitals in-app; shift handoff is printable; discharge documents accessible |
| **4 — Admin Tools** | P3 role-based nav, P2 threshold management, P2 gateway status, P2 user management, P2 department overview | Admins manage thresholds without API calls; IT monitors gateway health; navigation is role-scoped |
| **5 — Polish & Infrastructure** | P3 dark mode charts, P3 keyboard shortcuts, P3 mobile optimization, P4 audit/reconciliation views, P4 Grafana, P5 WCAG | Night-shift readability, power-user efficiency, compliance audit trail, monitoring depth |
| **6 — Real-Time** | P4 SSE/SignalR push | Sub-second alert notification, reduced polling load |

---

## Backend work required

Most dashboard gaps are **frontend-only** — the API endpoints already exist but the dashboard does not consume them. The following items require backend changes:

| Item | Backend work needed |
|------|-------------------|
| GCS history chart | New `GET /encounters/{id}/gcs/history` endpoint |
| qSOFA history | Decide persistence strategy (Redis → DB) + new history endpoint |
| Ward table columns | Extend `WardEncounterSummary` DTO with SOFA/GCS/staleness |
| Discharge summary view | New `GET /encounters/{id}/discharge-summary` download endpoint |
| User management | New `UsersController` with CRUD endpoints |
| Real-time push | New SSE endpoint or SignalR hub |
| Grafana expansion | Depends on platform gap P5 metrics being implemented first |

All other items can be built against existing API endpoints.

---

## Success criteria

When complete, the dashboard should support:

**Bedside Clinicians (Nurses, Physicians)**
- All four scoring trends visible over time (NEWS2, SOFA, GCS, qSOFA) with clinical context.
- Medication administration times overlaid on vital trend charts.
- Patient allergies and demographics visible at all times.
- Chronological encounter timeline for case review and handoff.
- Vital sign entry directly in the dashboard.
- GCS entry form already exists — vitals entry extends this pattern.

**Charge Nurses & Supervisors**
- Ward table shows SOFA, GCS, data staleness, and attending physician.
- Sort and search/filter to find patients by any clinical criterion.
- Hospital-wide sepsis bundle compliance board with countdown timers.
- Audible/visual notification for new critical alerts.
- Printable handoff report for shift change.

**Administrators & Compliance**
- Threshold management without API tools.
- User creation and role management.
- Gateway health monitoring for IT operations.
- Audit log and PHI access log browsing.
- Reconciliation findings (data quality) dashboard.

**All Users**
- Role-appropriate navigation (no admin clutter for bedside users).
- Dark mode that works for charts too (night shift, ICU).
- Keyboard shortcuts for power users.
- Mobile-responsive patient detail for tablet use.
- WCAG AA accessibility compliance.
