feat(inventory): scan-to-location QR assignment#1606
Conversation
maziggy
left a comment
There was a problem hiding this comment.
Please also add it to Spoolbuddy frontend to keep feature parity.
maziggy
left a comment
There was a problem hiding this comment.
(feature/inventory-qr-scan-to-location *)$ ./test_frontend.sh
frontend@0.0.0 lint
eslint .
frontend@0.0.0 test:run
vitest run && npm run check:i18n
RUN v4.1.8 /opt/claude/projects/bambuddy/frontend
✓ src/tests/i18n/parity-script.test.ts (8 tests) 10ms
Error: Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)
at module.exports (/opt/claude/projects/bambuddy/frontend/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at HTMLCanvasElementImpl.getContext (/opt/claude/projects/bambuddy/frontend/node_modules/jsdom/lib/jsdom/living/nodes/HTMLCanvasElement-impl.js:42:5)
at HTMLCanvasElement.getContext (/opt/claude/projects/bambuddy/frontend/node_modules/jsdom/lib/jsdom/living/generated/HTMLCanvasElement.js:131:58)
at /opt/claude/projects/bambuddy/frontend/src/components/QrAssignTargetModal.tsx:118:24
at Object.react_stack_bottom_frame (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20)
at runWithFiberInDEV (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:874:13)
at commitHookEffectListMount (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29)
at commitHookPassiveMountEffects (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:13336:11)
at commitPassiveMountOnFiber (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:15484:13)
at recursivelyTraversePassiveMountEffects (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:15439:11) undefined
Error: Not implemented: HTMLCanvasElement.prototype.getContext (without installing the canvas npm package)
at module.exports (/opt/claude/projects/bambuddy/frontend/node_modules/jsdom/lib/jsdom/browser/not-implemented.js:9:17)
at HTMLCanvasElementImpl.getContext (/opt/claude/projects/bambuddy/frontend/node_modules/jsdom/lib/jsdom/living/nodes/HTMLCanvasElement-impl.js:42:5)
at HTMLCanvasElement.getContext (/opt/claude/projects/bambuddy/frontend/node_modules/jsdom/lib/jsdom/living/generated/HTMLCanvasElement.js:131:58)
at /opt/claude/projects/bambuddy/frontend/src/components/QrAssignTargetModal.tsx:118:24
at Object.react_stack_bottom_frame (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:25989:20)
at runWithFiberInDEV (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:874:13)
at commitHookEffectListMount (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:13249:29)
at commitHookPassiveMountEffects (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:13336:11)
at commitPassiveMountOnFiber (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:15484:13)
at recursivelyTraversePassiveMountEffects (/opt/claude/projects/bambuddy/frontend/node_modules/react-dom/cjs/react-dom-client.development.js:15439:11) undefined
Add an in-page camera QR scanner to the inventory page. The user opens "Assign by QR", picks a target (AMS slot or storage location), then scans a spool's label QR; the scanned spool is assigned to that target directly instead of opening the edit modal. - QrAssignTargetModal: two-step picker -> live getUserMedia + jsqr scanner - Move semantics: an AMS assign clears the spool's previous slot (local + Spoolman) so a spool lives in one slot at a time - Nudge the printer to republish after an AMS assign (maziggy#1414 parity) - parseSpoolIdFromQr util (+ unit tests) extracts the id from the deeplink - i18n: inventory.qrAssign.* added to all 9 locales (parity green) - adds jsqr dependency Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…1574) Refine the scan-to-location flow: - Hybrid scan step: a live in-page camera scanner (BarcodeDetector -> jsQR fallback) plus a native-camera "Take photo" fallback for small/dense QRs the live preview can't focus on. Captured photos are decoded in-browser and discarded — never uploaded or stored. - Move semantics: assigning a spool to a new AMS slot clears its previous slot (local + Spoolman) so a spool occupies one slot at a time. - Nudge the printer to republish after an AMS assign (maziggy#1414 parity). - parseSpoolIdFromQr handles full URLs, bare ?spool= fragments (incl. hash), and bare ids. - Cleanups: single error state, drop unused camera-reason plumbing. - i18n reworded across all 9 locales (parity green). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ziggy#1574) Address PR review: - Add the "Assign by QR" scan-to-location flow to the SpoolBuddy inventory page (reuses the generic QrAssignTargetModal) for feature parity with the main inventory page. - Guard LiveScanner's canvas.getContext so the test suite no longer errors under jsdom (HTMLCanvasElement.getContext is unimplemented there); the camera never starts in that env so a null context is harmless. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…aziggy#1574) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
b1daf7a to
0988cfe
Compare
|
Thanks for the review! Addressed both points:
Also rebased onto the latest Still a draft while I attach before/after screenshots — will mark it ready for review once they're in. |
…ggy#1574) Quality cleanup (no behavior change): - Extract distinctStorageLocations() into utils/inventorySearch and use it in InventoryPage (both the QR autocomplete and the maziggy#1400 filter datalist) and SpoolBuddyInventoryPage — removes three copies and unifies trim handling. - Extract decodeCanvas() shared by the live scanner and the photo decoder, collapsing the duplicated canvas+jsQR block and the getContext guard into one place. - Collapse parseSpoolIdFromQr's two-alternative regex into one. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Please rebase to resolve conflicts. Thanks. |
maziggy
left a comment
There was a problem hiding this comment.
Merged dev in and resolved the two conflicts in InventoryPage.tsx and SpoolBuddyInventoryPage.tsx (both in import/setup blocks where dev landed the new structured Locations + CSV import/export work). All your QrCode/storageSuggestions additions kept, no behaviour change to the scan flow. Dropped the uniqueStorageLocations const since it ended up unreferenced after the merge.
One change I'd like before merging: dev landed a structured Location entity (location_id FK) and that's now the canonical model — the Locations page, the Locations dropdown filter on the inventory page, and the LocationsModal picker all operate on location_id. Your QR-assign currently writes only to the legacy free-form storage_location string, which means a spool assigned via QR scan is invisible to the new Locations filter and to anyone using the Locations UI as the primary surface.
What I'd suggest for the storage tab in QrAssignTargetModal:
- Replace the free-text storage_location input + autocomplete with a picker over api.getLocations() (same data source as the inventory page filter dropdown), with a "create new location" affordance — LocationsModal already exposes the pick-by-id pattern.
- On scan confirm, write { location_id: pickedId } via api.updateSpool / api.updateSpoolmanInventorySpool instead of { storage_location: name }. Both the local and Spoolman update endpoints accept location_id.
- The AMS-slot tab stays unchanged.
Description
Adds a "scan-to-location" mode to the inventory page: pick an assignment target (an AMS slot or a storage location) first, then scan the spool's QR to assign it there immediately — no edit modal. Hybrid scanning: a live in-page camera (BarcodeDetector → jsQR) with a native-camera Take photo fallback for small/dense QRs. A spool stays in one slot (assigning to a new AMS slot clears the old one).
Related Issue
Fixes #1574
Documentation
Companion docs PRs:
Pick one:
Type of Change
Changes Made
QrAssignTargetModal: two-step (pick target → scan) with a live camera scanner and a photo-capture fallbackparseSpoolIdFromQrutil (+ unit tests) extracts the spool id from the label deeplink (URL /?spool=/ bare id)inventory.qrAssign.*added across all 9 locales; adds thejsqrdependencyScreenshots
Testing
Checklist
Additional Notes
storage_locationAPIs).