docs(03-05): complete PWA manifest + service worker + QR scanner plan
This commit is contained in:
parent
75c91a5941
commit
83e1725cd9
1 changed files with 156 additions and 0 deletions
156
.planning/phases/03-dashboard-intake-ui/03-05-SUMMARY.md
Normal file
156
.planning/phases/03-dashboard-intake-ui/03-05-SUMMARY.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
phase: 03-dashboard-intake-ui
|
||||
plan: "05"
|
||||
subsystem: ui, pwa
|
||||
tags: [react, typescript, pwa, service-worker, qr-scanner, zxing, camera]
|
||||
|
||||
# Dependency graph
|
||||
requires:
|
||||
- web/src/store/ui.ts (useUIStore.setScannerActive)
|
||||
- web/src/components/ui/button.tsx
|
||||
- @zxing/browser (already in package.json from Plan 01)
|
||||
provides:
|
||||
- web/public/manifest.json (PWA installable config)
|
||||
- web/public/sw.js (app-shell service worker)
|
||||
- web/public/icons/icon-192.png, icon-512.png (PWA icons)
|
||||
- web/src/hooks/usePWA.ts (service worker registration hook)
|
||||
- web/src/pages/ScanPage.tsx (/scan QR camera scanner)
|
||||
- web/src/lib/api.ts (fetchInventory, fetchInventoryItem typed wrappers)
|
||||
- web/src/components/layout/AppShell.tsx (page layout wrapper)
|
||||
affects:
|
||||
- web/index.html (theme-color meta added)
|
||||
- web/src/App.tsx (usePWA() call added)
|
||||
- web/src/router.tsx (scanRoute lazy-loads ScanPage)
|
||||
|
||||
# Tech tracking
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- PWA app-shell cache strategy (navigation cache-first, API network-only, static assets cache-first)
|
||||
- @zxing/browser decodeFromVideoDevice for live camera QR scanning
|
||||
- lazy() + Suspense code-splitting for heavy QR scanner bundle (444KB chunk)
|
||||
- extractHWID() regex — strict pattern matching before any navigation
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- web/public/manifest.json
|
||||
- web/public/sw.js
|
||||
- web/public/icons/icon-192.png
|
||||
- web/public/icons/icon-512.png
|
||||
- web/scripts/gen-icons.cjs
|
||||
- web/src/hooks/usePWA.ts
|
||||
- web/src/pages/ScanPage.tsx
|
||||
- web/src/lib/api.ts
|
||||
- web/src/components/layout/AppShell.tsx
|
||||
modified:
|
||||
- web/index.html (theme-color meta)
|
||||
- web/src/App.tsx (usePWA hook)
|
||||
- web/src/router.tsx (lazy ScanPage + Suspense)
|
||||
|
||||
decisions:
|
||||
- id: PWA-D01
|
||||
summary: "ScanPage code-split as separate lazy chunk (444KB) — @zxing/browser is large; lazy loading keeps initial bundle lean at 275KB"
|
||||
- id: PWA-D02
|
||||
summary: "api.ts and AppShell.tsx created as minimal stubs in this plan — Plans 03-03/03-04 run in parallel and hadn't built them yet; stubs provide exactly the interface ScanPage needs without duplicating plan scope"
|
||||
- id: PWA-D03
|
||||
summary: "Icon generation uses pure Node.js + zlib (no ImageMagick/canvas) — zero external tooling required; gen-icons.cjs is committed for reproducibility"
|
||||
|
||||
# Metrics
|
||||
duration: ~15min
|
||||
completed: 2026-04-10T06:22:00Z
|
||||
tasks_completed: 2
|
||||
files_created: 9
|
||||
files_modified: 3
|
||||
---
|
||||
|
||||
# Phase 3 Plan 05: PWA Manifest + Service Worker + QR Scanner Summary
|
||||
|
||||
**PWA installable manifest with app-shell service worker, programmatically generated icons, and a live camera QR scanner at /scan that resolves HW-XXXXX labels to NetBox inventory items.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~15 min
|
||||
- **Started:** 2026-04-10T06:07:00Z
|
||||
- **Completed:** 2026-04-10T06:22:00Z
|
||||
- **Tasks:** 2
|
||||
- **Files created:** 9
|
||||
- **Files modified:** 3
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `web/public/manifest.json` — PWA web app manifest: `display=standalone`, `theme_color=#faff69`, `background_color=#000000`, 192px + 512px icons, portrait-primary orientation
|
||||
- `web/public/sw.js` — App shell service worker with three-tier fetch strategy: API calls always network-only (no stale inventory), navigation requests cache-first (offline shell), static assets cache-first with network fallback and cache population
|
||||
- `web/public/icons/icon-192.png` + `icon-512.png` — Valid PNG icons generated by `web/scripts/gen-icons.cjs` (pure Node.js, no external deps): black canvas with volt `#faff69` H monogram
|
||||
- `web/src/hooks/usePWA.ts` — Service worker registration hook called from `App.tsx` on window load
|
||||
- `web/src/pages/ScanPage.tsx` — Full camera QR scanner:
|
||||
- Rear camera preference (label matching: `back|rear|environment`)
|
||||
- Volt corner-bracket reticle overlay on live viewfinder
|
||||
- `extractHWID()` regex matches `http://.../hw/HW-XXXXX` URLs and bare `HW-XXXXX` IDs
|
||||
- `lastScanned` debounce prevents duplicate navigation
|
||||
- Resolves HW ID to NetBox numeric ID via `GET /api/inventory`, then navigates to `/item/:id`
|
||||
- Graceful camera-denied error state (no crash)
|
||||
- `web/src/router.tsx` — scanRoute uses `lazy()` + `Suspense` to code-split the 444KB @zxing chunk
|
||||
- `npm run build` exits 0, dist contains correctly split bundles
|
||||
|
||||
## Task Commits
|
||||
|
||||
1. **Task 1: PWA manifest, service worker, icons, registration hook** — `95a50f4`
|
||||
2. **Task 2: QR scanner page + router wiring** — `75c91a5`
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] Created api.ts and AppShell.tsx stubs — parallel plan dependencies missing**
|
||||
- **Found during:** Task 2 (creating ScanPage.tsx)
|
||||
- **Issue:** ScanPage imports `fetchInventory` from `@/lib/api` and `AppShell` from `@/components/layout/AppShell`. Plans 03-03 and 03-04 (which build these) run in wave 2 alongside this plan and had not yet executed.
|
||||
- **Fix:** Created `web/src/lib/api.ts` with `InventoryItem`, `fetchInventory`, `fetchInventoryItem` typed wrappers (full API contract matching Plan 03-02 handler output); created `web/src/components/layout/AppShell.tsx` as a minimal page wrapper. Both files are documented as stubs that Plans 03-03/03-04 will extend.
|
||||
- **Files modified:** `web/src/lib/api.ts` (new), `web/src/components/layout/AppShell.tsx` (new)
|
||||
- **Impact:** No scope creep — the stubs expose the exact interface ScanPage needs. Plans 03-03/03-04 will extend them without conflict.
|
||||
- **Commits:** 75c91a5
|
||||
|
||||
**2. [Rule 1 - Bug] Fixed recursive startScanner call pattern — removed self-reference via useCallback dep**
|
||||
- **Found during:** Task 2 implementation review
|
||||
- **Issue:** Plan's ScanPage template called `startScanner()` recursively inside its own `useCallback`, which would cause an undefined reference (function not yet assigned at first call). Also called `stopScanner` before `stopScanner` was defined.
|
||||
- **Fix:** Reordered hook definitions (stopScanner before startScanner), removed `startScanner` from its own dependency array, and used `void startScanner()` call pattern for the retry paths.
|
||||
- **Files modified:** `web/src/pages/ScanPage.tsx`
|
||||
|
||||
## Known Stubs
|
||||
|
||||
| Stub | File | Reason |
|
||||
|------|------|--------|
|
||||
| `AppShell` (minimal layout, no TopBar/nav) | `web/src/components/layout/AppShell.tsx` | Plan 03-03 will replace with full navigation shell |
|
||||
| `fetchInventory`/`fetchInventoryItem` (no caching) | `web/src/lib/api.ts` | Plan 03-03 will add TanStack Query hooks wrapping these; raw fetch is fully functional |
|
||||
|
||||
Both stubs are wire-complete (they make real API calls). ScanPage functionality is not blocked by stub status.
|
||||
|
||||
## Threat Surface Coverage
|
||||
|
||||
All four threats from the plan's threat register are addressed:
|
||||
|
||||
| Threat | Mitigation | Where |
|
||||
|--------|------------|-------|
|
||||
| T-03-14: QR text → navigation tampering | `extractHWID()` strict regex `HW-\d{5,}` — arbitrary text cannot trigger navigation; NetBox numeric ID comes from server response, not QR content | ScanPage.tsx:extractHWID |
|
||||
| T-03-15: Camera stream info disclosure | @zxing decodes frames locally; no frames sent to server | ScanPage.tsx (client-only) |
|
||||
| T-03-16: SW cache tampering | API responses explicitly excluded (network-only path in sw.js) | sw.js fetch handler |
|
||||
| T-03-17: Rapid QR scan DoS | `lastScanned` state debounce; scanner stops before resolving | ScanPage.tsx:startScanner callback |
|
||||
|
||||
## Self-Check
|
||||
|
||||
Files created:
|
||||
- web/public/manifest.json: FOUND
|
||||
- web/public/sw.js: FOUND
|
||||
- web/public/icons/icon-192.png: FOUND (valid PNG)
|
||||
- web/public/icons/icon-512.png: FOUND (valid PNG)
|
||||
- web/src/hooks/usePWA.ts: FOUND
|
||||
- web/src/pages/ScanPage.tsx: FOUND
|
||||
- web/src/lib/api.ts: FOUND
|
||||
- web/src/components/layout/AppShell.tsx: FOUND
|
||||
|
||||
Commits:
|
||||
- 95a50f4: feat(03-05): PWA manifest, service worker, icons, and registration hook
|
||||
- 75c91a5: feat(03-05): QR scanner page with @zxing/browser and router wiring
|
||||
|
||||
Build: `cd web && npm run build` exits 0, 4 output files in dist/
|
||||
|
||||
## Self-Check: PASSED
|
||||
Loading…
Add table
Reference in a new issue