---
module: incident.dashboard
title: Dashboard
tab: Claude
order: 40
audience: dev
---

## Owner
- Service: **hiveops-incident** (port 8080, NGINX `/api/incident/`). Owns all Dashboard endpoints + both source tables.
- Frontend: `hiveops-incident/frontend/src/components/Dashboard/Dashboard.svelte` (Vite+Svelte SPA; h1 renders literal text "Overview").
- Read-only view. No writes, no mutations.

## Endpoints (all authenticated; NONE role-gated)
| Method | Path | Returns | Feeds |
|---|---|---|---|
| GET | `/api/device-summary` | `DeviceSummary[]` | device + agent-connection panels |
| GET | `/api/device-summary/paginated?groupId&page&size` | `Page<DeviceSummary>` | group-scoped device counts |
| GET | `/api/incidents/paginated?page=0&size=2000&dateFrom=<90d>&groupId` | `Page<Incident>` | trends, by-type, "This Week" |
| GET | `/api/incident-management/stats/summary` | `IncidentStatisticsDTO` | ticket cards (all-time) |

- `IncidentStatisticsDTO` fields: `totalIncidents, openIncidents, assignedIncidents, inProgressIncidents, resolvedIncidents, closedIncidents, criticalCount, highCount, avgResolutionTime`.
- Controllers: `DeviceSummaryController`, `IncidentController.getIncidentsPaginated`, `IncidentManagementController.getIncidentStats`.

## Tables (DB `hiveiq_incident`)
- `device_summary` — device/agent state (read via `DeviceSummaryRepository`). **Kafka-fed mirror, not authored here.**
- `incidents` — read via `IncidentController` + `IncidentService.getStatistics` (joins `atm.institution`).
- `atm_groups` — group→ATM-id set when `groupId` present.

## Kafka
- Consume `hiveops.device.events` → `DeviceEventConsumer` (group **`hiveops-incident-device-sync`**) → upserts `device_summary`. Constant: `DeviceEventKafkaConfig.TOPIC`.
- Consume `hiveops.aria.threats` → auto-creates incidents.
- Dashboard produces **no** topics.

## Scoping / auth
- Every endpoint filters by `InstitutionContext.resolveScope()` (from caller JWT). `null` scope = all institutions.
- View requires only a valid JWT. `hasAnyRole('MSP_ADMIN','BCOS_ADMIN')` gates only incident *mutations* elsewhere, NOT this view.

## Gotchas
- Trend/by-type/"This Week" derive from a **90-day, ≤2000-row** client slice (`loadIncidentsForDashboard`) → silently truncates on busy fleets. Ticket cards (server stats) are all-time and unaffected.
- `fetchIncidentStats()` failure is **swallowed** (`catch {}`) → UI silently falls back to capped-store counts; ticket cards under-report with no error shown.
- Device counts stale = `device_summary` mirror stale. **No reconciler.** Root cause is upstream (hiveops-devices not publishing, or consumer group lag) — not incident.
- Agent Connection "Disconnected" bucket = everything not `CONNECTED`/`NEVER_CONNECTED`, including null → can inflate.
- Active `defaultGroupId` scopes both ATMs and incidents; stale group id → 404 "Group not found" on `device-summary/paginated`.
- Auto-refresh toggle persists in `localStorage['incident_dashboard_autoRefresh']`; interval = `settings.dashboardRefreshIntervalSeconds`.
- Frontend `API_BASE` = `/api` (dev) → prod resolves via `window.__APP_CONFIG__.apiUrl`; through NGINX = `/api/incident/...`.

## Do NOT
- Do NOT assume the Dashboard writes/owns device data — it mirrors `hiveops-devices` via Kafka; fix stale counts upstream.
- Do NOT treat trend/type/"This Week" as authoritative counts — they are capped (90d/2000).
- Do NOT add a role gate expecting to "lock down" the Dashboard endpoints; they are read-open by design and shared with other views.
- Do NOT invent a `stats/summary`-style aggregate SQL — current impl loads rows and reduces in memory.
- Do NOT look for Dashboard data in journal/mgmt/fleet — all four endpoints are hiveops-incident.
