diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f96a40e..3cb5c59 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -72,11 +72,11 @@ Plans: **Plans**: 5 plans Plans: -- [ ] 03-01-PLAN.md — Frontend scaffold: Vite 5 + React 18 + TypeScript + Tailwind + shadcn/ui, ClickHouse design tokens, TanStack Router/Query, Zustand -- [ ] 03-02-PLAN.md — Backend inventory endpoints: GET /api/inventory + GET /api/inventory/:id handlers + router wiring -- [ ] 03-03-PLAN.md — Dashboard + item detail views: grid/list toggle, filters, ItemCard/ItemRow, item detail with photos and custom fields -- [ ] 03-04-PLAN.md — Intake flow UI: DropZone, photo preview, AI result review, confidence meter, wired to POST /api/intake -- [ ] 03-05-PLAN.md — PWA setup: manifest.json, service worker, icons, camera QR scanner at /scan +- [x] 03-01-PLAN.md — Frontend scaffold: Vite 5 + React 18 + TypeScript + Tailwind + shadcn/ui, ClickHouse design tokens, TanStack Router/Query, Zustand +- [x] 03-02-PLAN.md — Backend inventory endpoints: GET /api/inventory + GET /api/inventory/:id handlers + router wiring +- [x] 03-03-PLAN.md — Dashboard + item detail views: grid/list toggle, filters, ItemCard/ItemRow, item detail with photos and custom fields +- [x] 03-04-PLAN.md — Intake flow UI: DropZone, photo preview, AI result review, confidence meter, wired to POST /api/intake +- [x] 03-05-PLAN.md — PWA setup: manifest.json, service worker, icons, camera QR scanner at /scan ### Phase 4: USB Manager & Label Printing **Goal**: USB peripherals are managed by a goroutine-per-device subsystem and any cataloged item can have a QR-coded label printed by the PRT Qutie without operator intervention after intake diff --git a/.planning/phases/03-dashboard-intake-ui/03-HUMAN-UAT.md b/.planning/phases/03-dashboard-intake-ui/03-HUMAN-UAT.md new file mode 100644 index 0000000..a32d2ed --- /dev/null +++ b/.planning/phases/03-dashboard-intake-ui/03-HUMAN-UAT.md @@ -0,0 +1,57 @@ +--- +status: partial +phase: 03-dashboard-intake-ui +source: [03-VERIFICATION.md] +started: 2026-04-10 +updated: 2026-04-10 +--- + +## Current Test + +[awaiting human testing] + +## Tests + +### 1. ClickHouse design system visual review +expected: Dashboard + intake match ~/.claude/DESIGN.md — pure black canvas, neon volt accents, Inter Black display typography +result: [pending] + +### 2. Mobile responsive layout at 390px +expected: All pages render single-column on mobile; PWA install works on Android +result: [pending] + +### 3. Camera QR scanner +expected: Real printed HW-XXXXX QR codes scan correctly and navigate to /item/:id +result: [pending] + +### 4. Category/tags/location filters (scope deviation) +expected: Full filter support per ROADMAP SC-2. Currently implemented: catalog_status + text search only. +result: [deferred — requires backend Device type extension for category/location/tags] +note: Scope expansion — needs decision whether to add in a polish phase or defer to v2 + +### 5. Audit history in item detail (scope deviation) +expected: Item detail view shows audit log per ROADMAP SC-3. Currently: photos/specs/ai_notes/test_data only. +result: [deferred — requires new backend endpoint for NetBox object-changes API] +note: Scope expansion — needs polish phase or defer to v2 + +### 6. Intake correction flow (implementation deviation) +expected: ROADMAP SC-4 "allows correction before creating the NetBox record". Currently: record created on POST, review screen is display-only per INTAKE-04 decision. +result: [deferred — architecturally conflicts with Phase 2 decision] +note: Accept INTAKE-04 decision, or add PATCH endpoint to update name after review + +## Summary + +total: 6 +passed: 0 +issues: 0 +pending: 3 +skipped: 0 +blocked: 0 +deferred: 3 + +## Gaps + +Three deferred items are scope deviations accepted during autonomous overnight execution. Human review required to decide: +- Add polish phase to close them +- Defer to v2 +- Accept as-is diff --git a/.planning/phases/03-dashboard-intake-ui/03-VERIFICATION.md b/.planning/phases/03-dashboard-intake-ui/03-VERIFICATION.md new file mode 100644 index 0000000..360a74d --- /dev/null +++ b/.planning/phases/03-dashboard-intake-ui/03-VERIFICATION.md @@ -0,0 +1,213 @@ +--- +phase: 03-dashboard-intake-ui +verified: 2026-04-10T08:00:00Z +status: human_needed +score: 2/5 +overrides_applied: 3 +override_reason: "Autonomous overnight execution — 3 gaps accepted as scope deviations for human review. User explicitly said 'I will do human review when all phases are done'." +gaps: + - truth: "User can filter inventory by category, tags, catalog status, and location without a page reload" + status: partial + reason: "FilterBar implements only catalog_status dropdown and text search (name/hw_id/asset_tag). No category, tags, or location filters exist. InventoryItem type and backend InventoryItemResponse also lack category and location fields, making this architecturally incomplete." + artifacts: + - path: "web/src/components/inventory/FilterBar.tsx" + issue: "Only catalog_status and text search implemented — no category, tags, or location dropdowns" + - path: "web/src/lib/api.ts" + issue: "InventoryItem type has no category, location, or tags fields" + - path: "internal/api/handlers/inventory.go" + issue: "InventoryItemResponse struct has no category, location, or tags fields" + missing: + - "Category filter dropdown (requires adding category field to InventoryItemResponse + InventoryItem)" + - "Tags filter (requires tags field in API response)" + - "Location filter dropdown (requires location field in API response)" + + - truth: "User can view full item detail including photos, specs, test data, and audit history" + status: partial + reason: "ItemDetailPage renders photos, specs, ai_notes, and test_data (pretty-printed JSON). No audit history section exists. The backend does not expose NetBox audit logs (/api/extras/object-changes/). No later phase in ROADMAP.md explicitly adds audit history to the item detail view." + artifacts: + - path: "web/src/pages/ItemDetailPage.tsx" + issue: "No audit history section — only photos, specs, ai_notes, test_data rendered" + - path: "internal/api/handlers/inventory.go" + issue: "InventoryItemResponse has no audit history / changelog fields" + missing: + - "Audit history section in ItemDetailPage (could be NetBox object-changes endpoint or a simplified last-modified display)" + + - truth: "Intake flow accepts 1-3 photo uploads, shows AI classification result inline, and allows correction before creating the NetBox record" + status: partial + reason: "Intake flow uploads photos, calls POST /api/intake, and shows AI result inline (confidence, category, tags, ai_notes). However, the NetBox record is created immediately on the first POST — the editable name field on the review screen is display-only with no PATCH/update call back to the backend. The roadmap says 'allows correction before creating the NetBox record' but the implementation creates first then reviews. This is the INTAKE-04 architectural decision from Phase 2 carried forward." + artifacts: + - path: "web/src/pages/IntakePage.tsx" + issue: "Name editing in review step is display-only — no second POST or PATCH to update the already-created NetBox record with the corrected name" + missing: + - "Either: (a) defer NetBox record creation until user confirms in review step, OR (b) add a PATCH call to update the NetBox record name when user edits and confirms — currently edits are silently discarded" + +deferred: + - truth: "Quick action 'print label' from dashboard opens label print flow for the item" + addressed_in: "Phase 4" + evidence: "Phase 4 success criteria #3: 'A QR label with HW ID, item name, key spec line, and QR code is printed for any item from the dashboard quick actions'" + +human_verification: + - test: "ClickHouse design system visual review" + expected: "Pure black (#000000) canvas background, volt (#faff69) accent on HW IDs and active states, near-black (#141414) card surfaces, charcoal/80 borders, Inter 900 display headings" + why_human: "Color fidelity and visual hierarchy cannot be verified by code inspection alone" + + - test: "Mobile responsive layout at 390px viewport width" + expected: "Dashboard grid collapses to 1 column, FilterBar wraps cleanly, ItemDetailPage renders as single column, TopBar navigation is usable, intake DropZone fills width" + why_human: "Responsive CSS breakpoints require viewport rendering to verify" + + - test: "PWA install flow on real Android device" + expected: "Chrome shows Add to Home Screen banner or plus icon in address bar; installed PWA launches in standalone mode with no browser chrome; theme color #faff69 visible in status bar" + why_human: "PWA install criteria require real Chrome on Android — DevTools Application tab shows manifest validity but not installability trigger" + + - test: "Camera QR scanner with real HWLab QR codes" + expected: "Rear camera activates, volt reticle overlay appears, scanning a printed HW-XXXXX QR label navigates to /item/:id; scanning a non-HWLab QR code is silently ignored" + why_human: "Requires physical camera hardware and a real printed QR label to verify end-to-end" +--- + +# Phase 3: Dashboard & Intake UI — Verification Report + +**Phase Goal:** Users can browse their full inventory, run intake for new items, and view item detail — all through the React SPA served by the Go binary +**Verified:** 2026-04-10T08:00:00Z +**Status:** gaps_found +**Re-verification:** No — initial verification + +## Goal Achievement + +### Observable Truths (Roadmap Success Criteria) + +| # | Truth | Status | Evidence | +|---|-------|--------|----------| +| 1 | Inventory dashboard loads with grid/list toggle, item cards showing photo, HW ID, status, and key specs | VERIFIED | DashboardPage uses `useInventory()` → GET /api/inventory; ItemCard renders photo/placeholder, volt HW ID, name, StatusBadge, ai_notes preview; grid/list toggle via Zustand viewMode | +| 2 | User can filter inventory by category, tags, catalog status, and location without a page reload | PARTIAL | FilterBar has catalog_status dropdown + text search only; no category/tags/location filters; InventoryItem type and backend response lack these fields | +| 3 | User can view full item detail including photos, specs, test data, and audit history | PARTIAL | ItemDetailPage renders photos, specs, firmware, ai_notes, test_data JSON — no audit history section; backend exposes no audit log endpoint | +| 4 | Intake flow accepts 1-3 photo uploads, shows AI classification result inline, and allows correction before creating the NetBox record | PARTIAL | DropZone + PhotoPreview + POST /api/intake + AIResultReview all work; however NetBox record is created on the first POST (INTAKE-04 decision) — name edits in review step are display-only and discarded | +| 5 | App is installable as a PWA on Android and the camera-based QR scanner resolves items by HW ID | VERIFIED | manifest.json with display=standalone + theme_color=#faff69; sw.js app-shell cache; usePWA() registered in App.tsx; ScanPage with BrowserQRCodeReader + extractHWID() + fetchInventory() lookup; lazy-loaded at /scan | + +**Score:** 2/5 truths fully verified (3 partial/failed) + +### Deferred Items + +Items not yet met but explicitly addressed in later milestone phases. + +| # | Item | Addressed In | Evidence | +|---|------|-------------|----------| +| 1 | Print label quick action from dashboard | Phase 4 | Phase 4 success criteria #3: "A QR label with HW ID, item name, key spec line, and QR code is printed for any item from the dashboard quick actions" | + +### Required Artifacts + +| Artifact | Expected | Status | Details | +|----------|---------|--------|---------| +| `web/src/pages/DashboardPage.tsx` | / route inventory grid/list | VERIFIED | Exists, uses useInventory(), renders ItemCard/ItemRow with filters | +| `web/src/pages/ItemDetailPage.tsx` | /item/$id detail view | VERIFIED | Exists, uses useInventoryItem(id), renders photos/specs/test_data — missing audit history | +| `web/src/pages/IntakePage.tsx` | /intake wizard | VERIFIED | Exists, 3-step wizard wired to POST /api/intake | +| `web/src/pages/ScanPage.tsx` | /scan QR camera scanner | VERIFIED | Exists, @zxing/browser, extractHWID(), fetchInventory() lookup | +| `web/src/lib/api.ts` | Typed fetch wrappers | VERIFIED | fetchInventory, fetchInventoryItem, submitIntake — all present and typed | +| `web/src/hooks/useInventory.ts` | TanStack Query hooks | VERIFIED | useInventory() and useInventoryItem(id) — both present | +| `web/src/components/inventory/ItemCard.tsx` | Grid card | VERIFIED | Photo/placeholder, HW ID, name, StatusBadge, ai_notes, NetBox link | +| `web/src/components/inventory/FilterBar.tsx` | Filter controls | PARTIAL | catalog_status + search only — missing category/tags/location | +| `web/src/components/layout/AppShell.tsx` | Page layout wrapper | VERIFIED | TopBar + main content area | +| `web/public/manifest.json` | PWA manifest | VERIFIED | display=standalone, theme_color=#faff69, 192+512 icons | +| `web/public/sw.js` | Service worker | VERIFIED | App-shell cache strategy, API network-only, install/activate handlers | +| `web/public/icons/icon-192.png` | PWA icon 192px | VERIFIED | Valid PNG (magic bytes 89504e47) | +| `web/src/hooks/usePWA.ts` | SW registration hook | VERIFIED | navigator.serviceWorker.register('/sw.js') in useEffect | +| `internal/api/handlers/inventory.go` | InventoryHandler | VERIFIED | ListInventory + GetInventoryItem, InventoryNetBoxClient interface | +| `internal/api/handlers/inventory_test.go` | Unit tests | VERIFIED | 7/7 TDD tests pass without live NetBox | + +### Key Link Verification + +| From | To | Via | Status | Details | +|------|----|-----|--------|---------| +| `web/src/main.tsx` | `web/src/router.tsx` | RouterProvider | WIRED | App.tsx wraps RouterProvider(router) | +| `web/src/pages/DashboardPage.tsx` | `web/src/hooks/useInventory.ts` | useInventory() | WIRED | Line 6: `import { useInventory }`, line 11: `const { data: items } = useInventory()` | +| `web/src/pages/ItemDetailPage.tsx` | `web/src/hooks/useInventory.ts` | useInventoryItem(id) | WIRED | Line 7: `import { useInventoryItem }`, line 22: `useInventoryItem(numericId)` | +| `web/src/hooks/useInventory.ts` | `web/src/lib/api.ts` | fetchInventory / fetchInventoryItem | WIRED | fetchJSON calls `/api/inventory` and `/api/inventory/:id` | +| `web/src/pages/IntakePage.tsx` | `POST /api/intake` | submitIntake() | WIRED | Line 11: `import { submitIntake }`, line 34: `const result = await submitIntake(photos, false)` | +| `web/src/router.tsx` | `DashboardPage, ItemDetailPage, IntakePage, ScanPage` | lazy() + Suspense | WIRED | All 4 pages code-split as lazy routes | +| `web/index.html` | `web/public/manifest.json` | `` | WIRED | Line 7: `` | +| `web/src/App.tsx` | `web/public/sw.js` | usePWA() → navigator.serviceWorker.register | WIRED | usePWA() called in App; registers `/sw.js` on window load | +| `web/src/pages/ScanPage.tsx` | `GET /api/inventory` | fetchInventory() for HW ID lookup | WIRED | Line 7: `import { fetchInventory }`, line 93: `const items = await fetchInventory()` | +| `internal/api/router.go` | `handlers.InventoryHandler` | r.Get('/inventory') | WIRED | Lines 46-47: GET /inventory + GET /inventory/{id} | +| `cmd/hwlab/main.go` | `handlers.NewInventoryHandler` | inventoryHandler construction | WIRED | Line 80: `inventoryHandler := handlers.NewInventoryHandler(nbClient)` | + +### Data-Flow Trace (Level 4) + +| Artifact | Data Variable | Source | Produces Real Data | Status | +|----------|-------------|--------|-------------------|--------| +| `DashboardPage.tsx` | `items` from useInventory() | GET /api/inventory → ListDevices(ctx, 200) | Yes — NetBox ListDevices; unit-tested | FLOWING | +| `ItemDetailPage.tsx` | `item` from useInventoryItem(id) | GET /api/inventory/:id → GetDevice(ctx, id) | Yes — NetBox GetDevice; unit-tested | FLOWING | +| `IntakePage.tsx` | `aiResult` from submitIntake() | POST /api/intake → AI pipeline | Yes — real API call; loading/error states handled | FLOWING | +| `ScanPage.tsx` | `items` from fetchInventory() | GET /api/inventory (same endpoint) | Yes — real API call on QR decode | FLOWING | + +### Behavioral Spot-Checks + +| Behavior | Command | Result | Status | +|----------|---------|--------|--------| +| Go backend builds | `go build ./...` | Exit 0, no errors | PASS | +| Go inventory unit tests | `go test ./internal/api/handlers/... -run "TestListInventory\|TestGetInventory" -v` | 7/7 PASS | PASS | +| Frontend dist built | `ls web/dist/index.html web/dist/assets/*.js` | 12 JS files + index.html present | PASS | +| Icon PNG validity | `node -e "..."` check on icon-192.png | PNG signature 89504e47 valid | PASS | +| Manifest JSON valid | `grep "standalone" web/public/manifest.json` | display=standalone, theme_color=#faff69 | PASS | +| Vite proxy config | `grep "proxy" web/vite.config.ts` | /api → http://localhost:8080 | PASS | + +### Requirements Coverage + +| Requirement | Source Plan | Description | Status | Evidence | +|-------------|------------|-------------|--------|----------| +| UI-01 | 03-01, 03-03 | Inventory dashboard with grid/list toggle, item cards | SATISFIED | DashboardPage + ItemCard + useInventory + FilterBar (partial) | +| UI-02 | 03-03 | Filter by category, tags, catalog status, and location | PARTIAL | Only catalog_status + text search; no category/tags/location filters or fields | +| UI-03 | — | AI-powered natural language search | DEFERRED | Explicitly assigned to Phase 7 in ROADMAP.md | +| UI-04 | 03-02, 03-03 | Item detail view with photos, specs, test data, audit history | PARTIAL | Photos/specs/test_data rendered; no audit history | +| UI-05 | 03-03 | Quick actions: print label, view in NetBox, edit | PARTIAL | "View in NetBox" implemented; "print label" deferred to Phase 4 SC-3; "edit" not scoped | +| UI-06 | 03-01 | ClickHouse design system (black #000000, volt #faff69) | SATISFIED | Tailwind tokens: volt, canvas, forest, charcoal, near-black; Inter 900 loaded | +| PWA-01 | 03-05 | PWA installable on Android | HUMAN-NEEDED | manifest.json + sw.js + icons verified; actual installability requires Android Chrome | +| PWA-02 | 03-03 | Item detail mobile-accessible | HUMAN-NEEDED | Responsive grid-cols-1 lg:grid-cols-2 in place; visual check required | +| PWA-03 | 03-05 | Camera QR scanner resolves items by HW ID | HUMAN-NEEDED | ScanPage code verified; real camera + QR code test required | + +### Anti-Patterns Found + +| File | Pattern | Severity | Impact | +|------|---------|----------|--------| +| `web/src/pages/IntakePage.tsx` | Name edit in review step is display-only — no API call to update the created record | Warning | User may believe name correction is persisted but it is silently discarded | + +### Human Verification Required + +#### 1. ClickHouse Design System Visual Review + +**Test:** Open `http://localhost:8080` (or `http://localhost:5173` in dev) in a browser after running `go run ./cmd/hwlab/... && cd web && npm run dev` +**Expected:** Pure black (#000000) canvas background; neon volt (#faff69) HW IDs, active states, and intake highlights; near-black (#141414) card backgrounds; charcoal/80 borders; Inter Black (weight 900) display headings +**Why human:** Color rendering and visual hierarchy require viewport rendering + +#### 2. Mobile Responsive Layout at 390px + +**Test:** Open DevTools, set viewport to 390px width, navigate through `/`, `/intake`, `/item/1`, `/scan` +**Expected:** Dashboard collapses to 1-column grid; FilterBar wraps cleanly; ItemDetailPage single-column; intake DropZone fills full width; TopBar navigation usable at mobile size +**Why human:** Responsive breakpoints require browser rendering to verify visual correctness + +#### 3. PWA Install Flow on Android Chrome + +**Test:** Open `http://mac-mini.mg:8080` on an Android device in Chrome; check address bar for install prompt or use Chrome menu > Add to Home Screen +**Expected:** Install banner or option appears; installed app launches without browser chrome; status bar shows volt theme color #faff69 +**Why human:** PWA installability criteria (HTTPS or localhost, SW active, manifest valid) requires real Android Chrome — cannot simulate in DevTools + +#### 4. Camera QR Scanner with Real HWLab Labels + +**Test:** Navigate to `/scan`, tap "Start Camera", grant permission, point at a printed HW-XXXXX QR label +**Expected:** Live viewfinder shows with volt corner-bracket reticle; QR code decoded; app navigates to `/item/:id` for that item; non-HWLab QR codes are silently ignored +**Why human:** Requires physical camera and a real printed QR code + +### Gaps Summary + +Three gaps block full goal achievement: + +**Gap 1 — Category/Tags/Location Filters (SC-2):** The filter bar implements catalog_status and text search but the roadmap requires filtering by category, tags, catalog status, AND location. The backend InventoryItemResponse and the InventoryItem TypeScript type both lack category, location, and tags fields, making this architecturally incomplete. No later phase in ROADMAP.md explicitly addresses adding these filters to the dashboard. + +**Gap 2 — Audit History in Item Detail (SC-3):** The roadmap success criterion says the item detail must show "audit history." ItemDetailPage renders photos, specs, ai_notes, and test_data but has no audit log section. NetBox exposes object change history via its API but neither the backend handler nor the frontend has been wired for it. + +**Gap 3 — Intake Correction Before Record Creation (SC-4):** The roadmap says the intake flow "allows correction before creating the NetBox record." The current implementation creates the NetBox record immediately on the first POST (INTAKE-04 architectural decision from Phase 2), then presents an editable name field in the review step that is display-only. Any name correction is silently discarded. The gap between the roadmap wording and the implementation should either be resolved (add a PATCH call) or accepted with an override documenting the intentional deviation. + +**Note on SC-4:** If the INTAKE-04 decision to create immediately is intentional and acceptable, add an override to this VERIFICATION.md frontmatter to accept the deviation. The alternative approaches are: (a) hold the AI result and only create when user confirms, or (b) add PATCH /api/inventory/:id to update the name on confirmation. + +--- + +_Verified: 2026-04-10T08:00:00Z_ +_Verifier: Claude (gsd-verifier)_