06 - Screen Specifications (Health Module)
Detailed specifications for each screen. All screens
(proposed)- the implemented backend is endpoint-only (health.controller.ts:27-37). Wire data references 12_API_Mapping.md; state references 13_State_Management.md.
S1. App Health Screen (/ops/health) (proposed)
Purpose
Operational at-a-glance availability of the API and its four dependencies, mirroring the combined Terminus check. Read-only; the only action is Retry.
Layout (phone)
┌──────────────────────────────────────────────┐
│ ‹ App Health [⚙] │ AppBar
├──────────────────────────────────────────────┤
│ ● All systems operational │ OverallBanner (live region)
│ (or) ● 1 of 5 degraded — Redis is down │
├──────────────────────────────────────────────┤
│ ┌──────────────┐ ┌──────────────┐ │
│ │ API ● up │ │ DB ● up │ │ StatusCard grid
│ └──────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Redis ● up │ │ Storage ● up │ │
│ │ ping: PONG │ └──────────────┘ │
│ └──────────────┘ ┌──────────────┐ │
│ │ Queues ● up │ │
│ │ pending: 12 │ │ PendingJobsTile
│ └──────────────┘ │
│ Last checked 12:04:31 · Retry │ LastCheckedLabel + Retry
└──────────────────────────────────────────────┘
Behavior
| # | Event | Result |
|---|---|---|
| 1 | Enter screen | HealthCubit.fetch() immediately (13 §2); 5 skeleton cards |
| 2 | 200, all up | banner "All systems operational"; cards green; last-check = now |
| 3 | 503, partial | banner "1 of 5 degraded — Redis is down"; red card + detail sheet affordance; healthy cards stay green (health.controller.spec.ts:29-35) |
| 4 | 503, all down | banner "All systems degraded"; all cards red; API card also red |
| 5 | Network error / timeout | AppOfflineBanner + cards show last known state + "stale" badge; Retry prominent |
| 6 | 429 rate-limited | countdown on Retry (remaining seconds from Retry-After-style client math); poll paused |
| 7 | Retry tap | immediate single fetch (rate-safe: skips poll tick on success) |
| 8 | Poll tick (30 s) | silent refetch; only state changes animate (m-fast) |
| 9 | App backgrounded | poll paused; resume → immediate refetch (13 §3) |
| 10 | Tap red card | incident detail sheet (S2) from cached payload - no network |
Data mapping
| Card | Payload key | Extra | Source |
|---|---|---|---|
| API | implicit (request reached server) | - | health.controller.ts:27-37 |
| DB | data.info.mongodb.status / data.error.mongodb | - | health.controller.ts:32 |
| Redis | data.info.redis.status | ping value when up | redis-health.indicator.ts:16 |
| Storage | data.info.storage.status | - | storage-health.indicator.ts:19 |
| Queues | data.info.bullmq.status | pendingJobs (emails, in-app, webhook-deliver, dlq) | bullmq-health.indicator.ts:30 |
| Last-check | client timestamp of 200/503 response | - | - |
States
idle → loading (skeletons) → success(all-up) / success(partial) / success(all-down) ↔ error(offline/timeout, stale data) ↔ rate-limited (429 countdown, poll paused).
Permissions
(proposed) — no health.* permission exists (permissions.constants.ts:1-97).
Screen gated by admin role only; document the new permission
(health.read, proposed) for the RBAC roadmap.
Accessibility
- Overall banner is a live region announcing state changes.
- Each card:
Semantics(label: "Redis: up, ping PONG", button: true). - Red/green never the only signal - text "up"/"down" always present
(
00-shared/09).
Motion
State-change flash 150 ms (m-fast); banner swap slide 250 ms (m-base);
loading skeletons per 00-shared/08.
S2. Incident Detail Sheet (proposed)
| Field | Spec |
|---|---|
| Trigger | tap any card with down state |
| Content | failed key, raw data.error.<key> payload rendered as monospace read-only, pendingJobs for bullmq, last-check time, "no additional detail reported" when payload is bare (redis failure, redis-health.indicator.ts:17-22) |
| CTA | none beyond close; Retry lives on S1 (avoid duplicate retry paths) |
| Notes | renders exclusively from the cached fetch - zero network calls; keeps parity with endpoint truth |