---
module: dashboard.overview
title: Fleet Overview
tab: Internal
order: 20
audience: internal
---

Ops/support view of the **Fleet Overview** landing page (`dashboard.bcos.cloud`, hiveops-dashboard, port 5181). This is a **frontend-only SPA** — it holds no data of its own. Every number on the page is fetched live from three other backends. If a card is wrong, the bug is almost always in the owning backend, not the dashboard.

## What the page actually calls
The SPA loads everything through one axios base URL (`window.__APP_CONFIG__.apiUrl` = `VITE_API_URL`, default `http://localhost:8080`) and fans out to four endpoints:

| Card / section | Endpoint (relative to `apiUrl`) | Owning service |
|---|---|---|
| Total ATMs, Connected, Fleet Health bars, By Model | `GET /atms/summary` | **hiveops-devices** |
| Open Incidents, Critical, Incidents this week, Recent Incidents | `GET /atms/dashboard/stats` | **hiveops-incident** |
| Active Tasks, Fleet Operations counts, Artifacts, Modules Reporting | `GET /fleet/tasks/stats` | **hiveops-fleet** |
| Sidebar user / role (hides "View all" for CUSTOMER) | `GET /users/me` | **hiveops-incident** |

`loadAll()` fires all three data calls in parallel every 60s (auto-refresh toggle, persisted in `localStorage['dashboard_autoRefresh']`) plus a manual refresh button.

## Roles required
- **`/atms/summary`** and **`/atms/dashboard/stats`** — no method-level `@PreAuthorize`; access is a valid JWT enforced by each service's SecurityFilterChain. Results are **institution-scoped** via `InstitutionContext.resolveScope()` (a scoped user sees only their institutions' ATMs/incidents).
- **`/fleet/tasks/stats`** — `@PreAuthorize("hasAnyRole('USER','CUSTOMER','ADMIN','MSP_ADMIN','BCOS_ADMIN')")`.
- **CUSTOMER** role only changes one thing on this page: the **"View all →"** link on Recent Incidents is hidden.
- There are **no admin-only actions** on this page — it is read-only. Data mutations happen in the Incidents / Fleet / Devices apps.

## First things to check when it misbehaves
1. **A whole card reads 0 / blank** → open DevTools Network. Each of the 3 data calls fails independently (each has its own `try/catch` that only `console.error`s). A single 401/403/500 silently zeroes just that card; the rest of the page still renders.
2. **"Completed" and "Failed" task counts are always 0, "Modules Reporting" always 0** → this is **expected / a known backend limitation**, not a data outage. `FleetTaskService.getStats()` hard-codes `completed=0`, `failed=0` and never sets `totalModulesReporting`. Do not chase it as a dashboard bug.
3. **"Pending" tasks look too low** → `getStats()` counts only `AUTO_UPDATE` PENDING tasks, not all pending kinds. Also a backend limitation.
4. **Task/Artifact totals ignore institution** → `getStats()` does **not** apply `institutionKeys`; totals are fleet-wide even for a scoped user, while the ATM and incident numbers above ARE scoped. Expect the "Active Tasks" number to look inconsistent with the scoped ATM counts.
5. **Recent Incidents / incident counts wrong** → suspect nginx routing of `/atms/dashboard/stats`. This exact path exists in **both** hiveops-incident and hiveops-devices; only the incident version populates `recentIncidents`, `openIncidents`, `criticalIncidents`, `incidentsThisWeek`. If those fields are empty but ATM counts are fine, the call is landing on devices instead of incident.
6. **Numbers stale for ~a while** → `getDashboardStats` in incident is `@Cacheable("dashboardStats")` keyed by institution scope. A cache TTL/eviction lag, not a broken query.
7. **Auto-refresh "stuck"** → check the toggle; state persists in `localStorage`. A user who turned it off sees a frozen page until manual refresh.
8. **Fleet Health "Never Connected" spike** → device-side status; a machine added but never checked in (`agentConnectionStatus == NEVER_CONNECTED`). Not a dashboard fault.

## Connection-status definitions (as computed in devices)
`connected/disconnected/neverConnected` come from `AtmDTO.getAgentConnectionStatus()`, derived from each ATM's last heartbeat (`CONNECTED` < grace, `DISCONNECTED` beyond it, `NEVER_CONNECTED` = never checked in). If health bars disagree with the Devices app, both read the same `hiveiq_devices` source — reconcile there.

See also: [dashboard.overview] (customer tab), [platform.service-ownership].
