diff --git a/.planning/phases/03-dashboard-intake-ui/03-03-SUMMARY.md b/.planning/phases/03-dashboard-intake-ui/03-03-SUMMARY.md new file mode 100644 index 0000000..829ed7d --- /dev/null +++ b/.planning/phases/03-dashboard-intake-ui/03-03-SUMMARY.md @@ -0,0 +1,145 @@ +--- +phase: 03-dashboard-intake-ui +plan: "03" +subsystem: ui +tags: [react, typescript, tanstack-query, tanstack-router, zustand, tailwind, clickhouse-design, inventory] + +# Dependency graph +requires: + - web/src/store/ui.ts (useUIStore — viewMode, setViewMode) + - web/src/components/ui/badge.tsx (Badge with status variants) + - web/src/components/ui/button.tsx (Button with forest/ghost/outline variants) + - web/src/components/ui/card.tsx (Card, CardHeader, CardTitle, CardContent, CardFooter) + - internal/api/handlers.InventoryItemResponse (JSON shape from GET /api/inventory) +provides: + - web/src/lib/api.ts (InventoryItem type, fetchInventory, fetchInventoryItem) + - web/src/hooks/useInventory.ts (useInventory, useInventoryItem TanStack Query hooks) + - web/src/components/layout/AppShell.tsx (TopBar + main content wrapper) + - web/src/components/layout/TopBar.tsx (sticky nav with HWLab branding) + - web/src/components/inventory/StatusBadge.tsx (catalog_status color-coded badge) + - web/src/components/inventory/ItemCard.tsx (grid card component) + - web/src/components/inventory/ItemRow.tsx (list-mode row component) + - web/src/components/inventory/FilterBar.tsx (search + status filter + view toggle) + - web/src/pages/DashboardPage.tsx (/ route — inventory grid/list) + - web/src/pages/ItemDetailPage.tsx (/item/$id route — full detail view) +affects: + - web/src/router.tsx (indexRoute + itemRoute updated to lazy-load real pages) + - 03-04, 03-05 (intake + scan pages reuse AppShell and TopBar) + +# Tech tracking +tech-stack: + added: [] + patterns: + - TanStack Query useQuery with typed fetchJSON wrapper (no axios) + - Zustand viewMode persists grid/list toggle across navigation within session + - lazy() + Suspense for route-level code splitting (DashboardPage + ItemDetailPage split into separate chunks) + - client-side filter via useMemo — no server-side filtering for <200 items + - label-upper CSS class applied to uppercase tracked labels + +key-files: + created: + - web/src/lib/api.ts + - web/src/hooks/useInventory.ts + - web/src/components/inventory/StatusBadge.tsx + - web/src/components/inventory/ItemCard.tsx + - web/src/components/inventory/ItemRow.tsx + - web/src/components/layout/TopBar.tsx + - web/src/components/layout/AppShell.tsx + - web/src/components/inventory/FilterBar.tsx + - web/src/pages/DashboardPage.tsx + - web/src/pages/ItemDetailPage.tsx + modified: + - web/src/router.tsx + +key-decisions: + - id: DASH-01 + summary: "lazy() + Suspense for DashboardPage and ItemDetailPage — creates separate JS chunks (9.17KB + 4.10KB) keeping initial bundle lean" + - id: DASH-02 + summary: "Client-side filtering via useMemo on items array — acceptable for <=200 item limit from API; no server-side filter params needed" + - id: DASH-03 + summary: "fetchJSON generic wraps raw fetch with typed error handling — no axios dependency, consistent with project WHAT NOT TO USE list" + +# Metrics +duration: 12min +completed: 2026-04-10 +--- + +# Phase 3 Plan 03: Dashboard + Item Detail Pages Summary + +**Inventory dashboard and item detail views wired to GET /api/inventory with ClickHouse design (volt/canvas/charcoal), grid/list toggle via Zustand, client-side search+filter, and mobile-responsive two-column detail layout** + +## Performance + +- **Duration:** ~12 min +- **Completed:** 2026-04-10 +- **Tasks:** 2 +- **Files created:** 10 +- **Files modified:** 1 + +## Accomplishments + +- `web/src/lib/api.ts` — typed `fetchInventory` / `fetchInventoryItem` using native fetch with error unwrapping; exports `InventoryItem` interface matching backend JSON shape +- `web/src/hooks/useInventory.ts` — `useInventory()` and `useInventoryItem(id)` TanStack Query hooks with proper query keys for cache invalidation +- `AppShell` + `TopBar` — sticky dark header with volt "HWLab" brand, forest-green "Add Item" button, outline "Scan" button; main content area with 7xl max-width +- `StatusBadge` — maps `catalog_status` string to 6 color-coded Badge variants (indexed=green, draft=gray, needs_research=yellow, researched=blue, complete=forest, destructive=red) +- `ItemCard` — grid card with aspect-video photo (or Package placeholder icon), volt HW ID, item name, StatusBadge, ai_notes preview (2-line clamp), hover volt border, View in NetBox link +- `ItemRow` — list-mode row with 4px status color indicator bar, HW ID, name, badge, ai_notes (hidden on mobile), hover reveal NetBox link +- `FilterBar` — search input with icon, catalog_status dropdown, item count label, grid/list toggle (Zustand viewMode) +- `DashboardPage` — full inventory view with loading/error/empty states, responsive grid (1→2→3→4→5 cols by breakpoint), list view in border container +- `ItemDetailPage` — back nav, header with HW ID + name + status + NetBox action, two-column lg (photos left, fields right) single-column mobile, ai_notes card, test_data pretty-printed JSON code block +- `router.tsx` updated — DashboardPage and ItemDetailPage lazy-loaded via `lazy()` + `Suspense` with Spinner fallback; intake/scan stubs preserved + +## Task Commits + +1. **Task 1: API client, hooks, layout, components** — `1867846` +2. **Task 2: Pages, FilterBar, router wiring** — `19c2bb7` + +## Files Created/Modified + +| File | Purpose | +|------|---------| +| `web/src/lib/api.ts` | Typed fetch wrappers + InventoryItem interface | +| `web/src/hooks/useInventory.ts` | TanStack Query hooks | +| `web/src/components/inventory/StatusBadge.tsx` | Status → Badge color mapping | +| `web/src/components/inventory/ItemCard.tsx` | Grid card (photo, HW ID, name, status, action) | +| `web/src/components/inventory/ItemRow.tsx` | List-mode row with status color bar | +| `web/src/components/layout/TopBar.tsx` | Sticky app header with navigation | +| `web/src/components/layout/AppShell.tsx` | TopBar + main content wrapper | +| `web/src/components/inventory/FilterBar.tsx` | Search + status filter + view toggle | +| `web/src/pages/DashboardPage.tsx` | / route — inventory grid/list with filters | +| `web/src/pages/ItemDetailPage.tsx` | /item/$id route — detail view, mobile responsive | +| `web/src/router.tsx` | Lazy-loaded real pages replacing stubs | + +## Deviations from Plan + +None — plan executed exactly as written. + +## Known Stubs + +None — all data flows from `useInventory` / `useInventoryItem` hooks which call the real backend API. No hardcoded or mock data in any component. Empty state and loading state are functional UI states, not stubs. + +## Threat Surface Coverage + +No new network endpoints, auth paths, or trust boundary changes introduced — this plan is purely frontend components consuming the existing GET /api/inventory endpoints established in Plan 03-02. + +## Self-Check + +Files created: +- web/src/lib/api.ts: FOUND +- web/src/hooks/useInventory.ts: FOUND +- web/src/components/inventory/StatusBadge.tsx: FOUND +- web/src/components/inventory/ItemCard.tsx: FOUND +- web/src/components/inventory/ItemRow.tsx: FOUND +- web/src/components/layout/TopBar.tsx: FOUND +- web/src/components/layout/AppShell.tsx: FOUND +- web/src/components/inventory/FilterBar.tsx: FOUND +- web/src/pages/DashboardPage.tsx: FOUND +- web/src/pages/ItemDetailPage.tsx: FOUND + +Commits: +- 1867846: feat(03-03): API client, TanStack Query hooks, layout shell, inventory item components +- 19c2bb7: feat(03-03): DashboardPage, ItemDetailPage, FilterBar, and router wiring + +`npm run build`: PASS (1717 modules, 0 TypeScript errors, dist/assets/ written) + +## Self-Check: PASSED