diff --git a/.planning/phases/03-dashboard-restyle/03-HUMAN-UAT.md b/.planning/phases/03-dashboard-restyle/03-HUMAN-UAT.md new file mode 100644 index 0000000..88894a8 --- /dev/null +++ b/.planning/phases/03-dashboard-restyle/03-HUMAN-UAT.md @@ -0,0 +1,45 @@ +--- +status: partial +phase: 03-dashboard-restyle +source: [03-VERIFICATION.md] +started: 2026-05-03T00:00:00Z +updated: 2026-05-03T00:00:00Z +--- + +## Current Test + +[awaiting human testing] + +## Tests + +### 1. Phone-width layout and visual correctness — `/mobile/dashboard` +expected: | + Run `npm run dev`, open Chrome DevTools, enable iPhone 15 Pro (393×852), navigate to + http://localhost:3100/mobile/dashboard. Verify all of: + + a. Sticky header is the new shell HeaderBar (Wulf mark + Pulse wordmark + Bell + avatar) — NOT a page-internal "Ticket Dashboard" bar + b. Exactly one H1 in the page body reading "Dashboard" + c. Four KPI cards in a 2×2 grid; SLA breaches card has a red (destructive) left border when count > 0, neutral border otherwise + d. Below the grid, "Needs attention" label and three horizontally-scrollable cards with snap momentum: + - Overdue tickets → /tickets?overdue=true + - Failed backups → /backup-status + - Stalled workflows → /admin/workflow + e. Below the strip, "Workers & backups" block with three rows, each with a status dot (emerald/amber/red) and external-link icon: + - Analyzer → /admin/analytics + - RMM Overshell → /admin/rmm-overshell + - Backup success (24h) → /backup-status + f. No recharts canvas/SVG anywhere on the page + g. Page scrolls under sticky header; content not hidden behind bottom nav + h. Refresh button (top-right of H1 row) spins icon and reloads data without full-page navigation +result: [pending] + +## Summary + +total: 1 +passed: 0 +issues: 0 +pending: 1 +skipped: 0 +blocked: 0 + +## Gaps diff --git a/.planning/phases/03-dashboard-restyle/03-VERIFICATION.md b/.planning/phases/03-dashboard-restyle/03-VERIFICATION.md new file mode 100644 index 0000000..2d54a00 --- /dev/null +++ b/.planning/phases/03-dashboard-restyle/03-VERIFICATION.md @@ -0,0 +1,134 @@ +--- +phase: 03-dashboard-restyle +verified: 2026-05-03T00:00:00Z +status: human_needed +score: 10/10 must-haves verified (automated) +human_verification: + - test: "Open /mobile/dashboard on a phone-width viewport (e.g. iPhone 15 Pro 393×852 in Chrome DevTools)" + expected: | + a. Sticky header is the new shell HeaderBar (Wulf mark + Pulse wordmark + Bell + avatar), NOT a page-internal title bar + b. Exactly one H1 in the page body that reads "Dashboard" + c. Four KPI cards in a 2x2 grid (Open total, Opened today, Resolved today, SLA breaches); SLA breaches card has a destructive (red) left border when count > 0, neutral border otherwise + d. Below the grid, "Needs attention" strip with three horizontally-scrollable cards: Overdue tickets → /tickets?overdue=true, Failed backups → /backup-status, Stalled workflows → /admin/workflow; momentum/snap scroll works on iOS/Android + e. Below the strip, "Workers & backups" block with three rows: Analyzer → /admin/analytics, RMM Overshell → /admin/rmm-overshell, Backup success (24h) → /backup-status; each row has a status dot (emerald/amber/red) and an external-link icon + f. No recharts canvas/SVG anywhere on the page + g. Page scrolls under sticky header; content not hidden behind the bottom nav + h. Refresh button (top-right of H1 row) spins icon and reloads data without full-page navigation + why_human: "Phone-first layout, visual appearance of color tokens (destructive border, status dots), horizontal scroll momentum behavior, and safe-area/nav overlap require a real or emulated phone-width viewport — not verifiable by static analysis" +--- + +# Phase 3: Dashboard Restyle Verification Report + +**Phase Goal:** A manager opening `/mobile/dashboard` sees the state of the business at a glance — four KPIs, items needing attention, and a worker/backup status row — with no charts. +**Verified:** 2026-05-03 +**Status:** human_needed +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths + +| # | Truth | Status | Evidence | +|----|-------|--------|----------| +| 1 | GET /api/mobile/dashboard returns kpis (4 entries), needsAttention (3 entries), and workers (3 entries) in a single round-trip | VERIFIED | route.ts: 4 KpiResponse entries, 3 AttentionResponse entries, 3 WorkerResponse entries built from a single Promise.all of 6 queries | +| 2 | KpiCardMobile renders a phone-sized KPI card with label, value, optional caption — no chart, no recharts import | VERIFIED | KpiCardMobile.tsx: 40 lines, renders label/value/caption, uses Card/CardContent; `grep recharts` returns nothing | +| 3 | NeedsAttentionStrip renders a horizontal-scrolling strip of compact attention cards, each linking to a destination URL | VERIFIED | NeedsAttentionStrip.tsx: `overflow-x-auto snap-x snap-mandatory`, each item wrapped in `` from next/link | +| 4 | WorkerStatusRow renders a 3-cell status row with status indicators that link to desktop admin pages | VERIFIED | WorkerStatusRow.tsx: `DOT_COLOR` record maps ok/warn/down to emerald/amber/destructive; each entry wrapped in `` | +| 5 | Visiting /mobile/dashboard renders four KPI cards in a 2x2 grid (no 1x4 row, no list) | VERIFIED | page.tsx line 82: `
` iterating over `data.kpis` (4 entries) | +| 6 | Below the grid, a horizontally-scrollable Needs Attention strip surfaces overdue tickets, failed backups, and stalled workflows | VERIFIED | page.tsx lines 95-102: NeedsAttentionStrip wired with `data.needsAttention` (3 items from API); strip component is horizontal-scrollable | +| 7 | Below the strip, a 3-row worker/backup status block links to correct admin pages | VERIFIED | page.tsx lines 105-113: WorkerStatusRow wired with `data.workers` (3 entries); hrefs are /admin/analytics, /admin/rmm-overshell, /backup-status | +| 8 | The page imports zero recharts/chart components and renders no chart on phone widths | VERIFIED | `grep recharts` returns nothing in page.tsx and all 3 mobile components; no dynamic chart imports present | +| 9 | Tapping a Needs Attention card navigates to its href (next/link) | VERIFIED | NeedsAttentionStrip.tsx: each card is `` — no router.push, no JS-only navigation | +| 10 | Tapping a worker status row navigates to its desktop admin href (next/link) | VERIFIED | WorkerStatusRow.tsx: each row is `` — hard-coded server-supplied hrefs | + +**Score:** 10/10 truths verified (automated) + +### Deferred Items + +None. + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|----------|--------|---------| +| `app/api/mobile/dashboard/route.ts` | Single GET endpoint shaped for new mobile dashboard sections | VERIFIED | 215 lines; exports MobileDashboardResponse, KpiResponse, AttentionResponse, WorkerResponse; 6 postgresClient.query calls in 1 Promise.all | +| `components/mobile/KpiCardMobile.tsx` | Reusable phone-sized KPI card component | VERIFIED | 40 lines; exports KpiCardMobile (function) and KpiTone (type); uses shadcn Card | +| `components/mobile/NeedsAttentionStrip.tsx` | Horizontal-scroll strip rendering NeedsAttention cards | VERIFIED | 52 lines; exports NeedsAttentionStrip (function) and NeedsAttentionItem (interface) | +| `components/mobile/WorkerStatusRow.tsx` | Compact 3-cell worker/backup status row | VERIFIED | 56 lines; exports WorkerStatusRow (function), WorkerStatusEntry (interface), WorkerStatus (type) | +| `app/mobile/dashboard/page.tsx` | Replaced page body wiring 3 components against /api/mobile/dashboard | VERIFIED | 118 lines; use client; default export MobileDashboard; imports all 3 components; fetches /api/mobile/dashboard | + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|----|-----|--------|---------| +| components/mobile/NeedsAttentionStrip.tsx | next/link | `import Link from 'next/link'` + `href={item.href}` on each card | WIRED | Line 10: import; line 33: `` | +| components/mobile/WorkerStatusRow.tsx | next/link | `import Link from 'next/link'` + `href={e.href}` on each row | WIRED | Line 10: import; line 41: `` | +| app/api/mobile/dashboard/route.ts | postgresClient | `import postgresClient` + single `Promise.all` of 6 queries | WIRED | Line 13: import; lines 61-133: 6 calls to `postgresClient.query` | +| app/mobile/dashboard/page.tsx | /api/mobile/dashboard | `fetch('/api/mobile/dashboard')` in `load()` called from `useEffect` | WIRED | Line 29: fetch call; line 34: `setData(await r.json())` | +| app/mobile/dashboard/page.tsx | KpiCardMobile | `import { KpiCardMobile } from '@/components/mobile/KpiCardMobile'` | WIRED | Line 15: import; line 84: rendered in JSX | +| app/mobile/dashboard/page.tsx | NeedsAttentionStrip | `import { NeedsAttentionStrip } from '@/components/mobile/NeedsAttentionStrip'` | WIRED | Line 16: import; line 95: rendered in JSX | +| app/mobile/dashboard/page.tsx | WorkerStatusRow | `import { WorkerStatusRow } from '@/components/mobile/WorkerStatusRow'` | WIRED | Line 17: import; line 105: rendered in JSX | + +### Data-Flow Trace (Level 4) + +| Artifact | Data Variable | Source | Produces Real Data | Status | +|----------|---------------|--------|--------------------|--------| +| app/mobile/dashboard/page.tsx | `data` (MobileDashboardResponse) | `fetch('/api/mobile/dashboard')` → `setData(await r.json())` in load() | Yes — route.ts runs 6 live DB queries (postgresClient.query) against tickets, veeam_backup_jobs, veeam_backup_agent_jobs, workflow_executions, analyzer_jobs, rmm_executions | FLOWING | +| app/api/mobile/dashboard/route.ts | kpis, needsAttention, workers | 6 postgresClient.query calls in Promise.all; all return live row data | Yes — COUNT queries on live tables; no static return paths | FLOWING | + +### Behavioral Spot-Checks + +Step 7b: SKIPPED — server must be running to exercise the API endpoint; database connectivity cannot be confirmed without a live Postgres connection. TypeScript clean pass is the best statically-verifiable proxy. + +| Behavior | Command | Result | Status | +|----------|---------|--------|--------| +| Phase files type-check clean | `npx tsc --noEmit --pretty 2>&1 \| grep -E "app/mobile/dashboard\|components/mobile\|app/api/mobile/dashboard"` | `no errors in phase files` | PASS | +| No recharts in dashboard or mobile components | `grep -rn recharts app/mobile/dashboard/ components/mobile/` | `OK: no recharts...` | PASS | +| page.tsx is <= 130 lines | `wc -l app/mobile/dashboard/page.tsx` | 118 lines | PASS | +| No Self-Check: FAILED in summaries | grep across .md files | none found | PASS | +| All 5 commits from summaries exist in git log | git log | 24e20c7, bfe9549, 5256250 confirmed | PASS | + +### Requirements Coverage + +| Requirement | Source Plan | Description | Status | Evidence | +|-------------|------------|-------------|--------|----------| +| DASH-01 | 03-01, 03-02 | 2x2 KPI grid with four primary metric cards drawn from desktop dashboard hero stats | SATISFIED | route.ts returns 4 KpiResponse entries (open_total, opened_today, resolved_today, sla_breaches) from real ticket queries; page.tsx renders `
` iterating kpis | +| DASH-02 | 03-01, 03-02 | "Needs Attention" horizontal-scroll strip (overdue tickets, failed backups, stalled workflows); tapping opens detail view | SATISFIED | NeedsAttentionStrip.tsx has `overflow-x-auto snap-x snap-mandatory`; 3 entries from route.ts with correct hrefs; each wrapped in next/link | +| DASH-03 | 03-01, 03-02 | Compact backup/worker status row (analyzer worker, RMM worker, backup-success-rate); tap opens desktop admin page | SATISFIED | WorkerStatusRow.tsx with DOT_COLOR status indicators; 3 entries from route.ts; hrefs: /admin/analytics, /admin/rmm-overshell, /backup-status | +| DASH-04 | 03-02 | No charts/recharts on the mobile Dashboard | SATISFIED | `grep recharts` returns nothing in page.tsx and all 3 mobile components; no chart-related imports anywhere | + +All 4 requirements assigned to Phase 3 in REQUIREMENTS.md are satisfied. No orphaned requirements. + +### Anti-Patterns Found + +None found. No TODO/FIXME/PLACEHOLDER comments. No `return null` stubs (NeedsAttentionStrip returns null only for empty items array, which is intentional and documented). No hardcoded empty data flowing to render paths. No console.log calls. No legacy recharts or deprecated field names. + +### Human Verification Required + +#### 1. Phone-width layout and visual correctness + +**Test:** Start dev server (`npm run dev`), open Chrome DevTools, enable device emulation at iPhone 15 Pro (393x852), navigate to http://localhost:3100/mobile/dashboard (sign in if prompted), and walk through the following checks: + +a. Sticky header is the new shell HeaderBar (Wulf mark + Pulse wordmark + Bell + avatar) — NOT a page-internal header with "Ticket Dashboard" +b. Exactly one H1 in the page body reading "Dashboard" +c. Four KPI cards in a 2x2 grid; the SLA breaches card has a red left border when count > 0, neutral otherwise +d. Below the grid, a "Needs attention" label and three cards scrolling horizontally with snap momentum; tapping Overdue tickets → /tickets?overdue=true, Failed backups → /backup-status, Stalled workflows → /admin/workflow +e. Below the strip, a "Workers & backups" block with three rows each having a colored status dot (emerald/amber/red) and an external-link icon; tapping Analyzer → /admin/analytics, RMM Overshell → /admin/rmm-overshell, Backup success → /backup-status +f. No recharts canvas or SVG chart anywhere on the page +g. Page scrolls under the sticky header; content is not hidden behind the bottom nav +h. Refresh button in the H1 row spins the icon and reloads data without full-page navigation + +**Expected:** All items a–h pass. + +**Why human:** Phone-first layout, visual appearance of color tokens (destructive border on SLA breaches, emerald/amber/red status dots), horizontal scroll snap momentum behavior on iOS/Android, safe-area/nav overlap, and interactive tap navigation to correct destinations all require a running app at a phone-width viewport. Static analysis cannot validate these rendering and interaction properties. + +### Gaps Summary + +No automated gaps identified. All 10 observable truths are verified. All 4 DASH requirements have evidence. All artifacts exist, are substantive, and are wired. Data flows from live DB queries through the API to the rendered components. Type-check passes clean for all phase files. + +The human_needed status reflects that the plans themselves include a mandatory human checkpoint (03-02-PLAN.md Task 2: `checkpoint:human-verify gate="blocking"`) for visual/UX review of the phone-first layout, which was auto-approved in auto mode. A human walkthrough on a phone-width viewport is required to confirm the spec §6.1 layout as built. + +--- + +_Verified: 2026-05-03_ +_Verifier: Claude (gsd-verifier)_