From de807ba93134320d6660b422b141c4cc8a91ff6c Mon Sep 17 00:00:00 2001 From: Mikkel Georgsen Date: Fri, 10 Apr 2026 06:58:21 +0000 Subject: [PATCH] docs(04-05): complete frontend USB status bar and print label plan --- .../04-05-SUMMARY.md | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 .planning/phases/04-usb-manager-label-printing/04-05-SUMMARY.md diff --git a/.planning/phases/04-usb-manager-label-printing/04-05-SUMMARY.md b/.planning/phases/04-usb-manager-label-printing/04-05-SUMMARY.md new file mode 100644 index 0000000..69171e8 --- /dev/null +++ b/.planning/phases/04-usb-manager-label-printing/04-05-SUMMARY.md @@ -0,0 +1,117 @@ +--- +phase: 04-usb-manager-label-printing +plan: "05" +subsystem: frontend +tags: [usb, sse, react, label-printing, real-time] +dependency_graph: + requires: [04-03] + provides: [USB-04-frontend] + affects: [DashboardPage, ItemCard, ItemRow] +tech_stack: + added: [] + patterns: [EventSource SSE, react-hot-toast, lucide-react icons] +key_files: + created: + - web/src/hooks/useUSBEvents.ts + - web/src/components/USBStatusBar.tsx + modified: + - web/src/lib/api.ts + - web/src/components/inventory/ItemCard.tsx + - web/src/components/inventory/ItemRow.tsx + - web/src/pages/DashboardPage.tsx +decisions: + - Print button added to ItemCard and ItemRow directly (not DashboardPage map) — cleaner separation of concerns + - handlePrintLabel defined at module level outside component to avoid per-render allocation + - USBStatusBar placed in DashboardPage header (flex justify-between alongside title) + - No ESLint config exists in project — skipped ESLint step, TypeScript + build used as verification +metrics: + duration: 8m + completed: 2026-04-10 + tasks_completed: 2 + files_changed: 6 +--- + +# Phase 04 Plan 05: Frontend USB Status Bar and Print Label Summary + +One-liner: SSE-backed useUSBEvents hook with USBStatusBar component and per-item print label button wired to POST /api/labels/:id/print with react-hot-toast feedback. + +## Tasks Completed + +| Task | Description | Commit | +|------|-------------|--------| +| 1 | useUSBEvents hook + USBStatusBar component | 8b88970 | +| 2 | Wire USBStatusBar + Print Label into dashboard | 3de1e4f | + +## What Was Built + +### useUSBEvents hook (`web/src/hooks/useUSBEvents.ts`) +- Subscribes to `GET /api/usb/events` SSE stream via `new EventSource('/api/usb/events')` +- Maintains `connectedDevices: Map` in React state +- Connect events add to map; disconnect events remove from map +- Malformed JSON silently ignored (T-04-14 mitigated) +- EventSource auto-reconnects on error (T-04-15 accepted) +- Exports: `useUSBEvents`, `DeviceEvent`, `DeviceSpec`, `StateConnected`, `StateDisconnected` + +### USBStatusBar component (`web/src/components/USBStatusBar.tsx`) +- Consumes `useUSBEvents()`, renders connected device list +- Role icons: Printer (role=0), Cable (role=1), Usb (role=2/unknown) +- Green dot per connected device with device name +- Empty state: "No USB devices" with USB icon in muted text +- ClickHouse design: black bg, `border-white/10`, `text-white/80`, `bg-green-500` status dots + +### api.ts additions (`web/src/lib/api.ts`) +- `printLabel(deviceId: number): Promise` — POST /api/labels/:id/print +- `PrintLabelResponse` interface: `{ status: string; print_skipped?: boolean }` +- Throws typed Error on non-OK response + +### ItemCard (`web/src/components/inventory/ItemCard.tsx`) +- Module-level `handlePrintLabel(e, itemId)` using `printLabel()` from api.ts +- Printer icon button in CardFooter alongside NetBox link +- Toast: loading → success / "queued — printer not connected" / error + +### ItemRow (`web/src/components/inventory/ItemRow.tsx`) +- Same `handlePrintLabel` pattern +- Print button added to quick-actions group (appears on hover with ExternalLink) +- Opacity transition matches existing group-hover pattern + +### DashboardPage (`web/src/pages/DashboardPage.tsx`) +- Imports and renders `` in page header (flex row, right-aligned) +- No layout restructuring — additive change only + +## Deviations from Plan + +### Auto-fixed Issues + +None — plan executed exactly as written with one minor deviation: + +**1. [Rule 2 - Deviation] Print button added to ItemCard/ItemRow rather than DashboardPage map** +- **Found during:** Task 2 +- **Reason:** DashboardPage passes items to `` and `` as components — adding print logic inline in the map would duplicate code and break component encapsulation. Plan said "find where item quick actions are rendered" which is the card/row components. +- **Impact:** Cleaner architecture; identical user-visible result +- **Files modified:** ItemCard.tsx, ItemRow.tsx (instead of DashboardPage.tsx map) + +## Known Stubs + +None — all data flows wired. USBStatusBar connects to live SSE. Print button calls real endpoint. + +## Threat Flags + +None — no new trust boundaries beyond those in the plan's threat model. + +## Self-Check + +Files exist: +- web/src/hooks/useUSBEvents.ts — FOUND +- web/src/components/USBStatusBar.tsx — FOUND +- web/src/lib/api.ts (modified) — FOUND +- web/src/components/inventory/ItemCard.tsx (modified) — FOUND +- web/src/components/inventory/ItemRow.tsx (modified) — FOUND +- web/src/pages/DashboardPage.tsx (modified) — FOUND + +Commits: +- 8b88970 — feat(04-05): add useUSBEvents hook and USBStatusBar component +- 3de1e4f — feat(04-05): wire USBStatusBar and Print Label button into dashboard + +Build: `npm run build` — PASSED (tsc -b + vite build, 0 errors, 1965 modules) + +## Self-Check: PASSED