homelabby/.planning/phases/04-usb-manager-label-printing/04-05-SUMMARY.md

4.8 KiB

phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
04-usb-manager-label-printing 05 frontend
usb
sse
react
label-printing
real-time
requires provides affects
04-03
USB-04-frontend
DashboardPage
ItemCard
ItemRow
added patterns
EventSource SSE
react-hot-toast
lucide-react icons
created modified
web/src/hooks/useUSBEvents.ts
web/src/components/USBStatusBar.tsx
web/src/lib/api.ts
web/src/components/inventory/ItemCard.tsx
web/src/components/inventory/ItemRow.tsx
web/src/pages/DashboardPage.tsx
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
duration completed tasks_completed files_changed
8m 2026-04-10 2 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<string, DeviceSpec> 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<PrintLabelResponse> — 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 <USBStatusBar /> 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 <ItemCard> and <ItemRow> 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