deckhearth/.convoys/scanner-desktop-layout/brief-3-result-inspector.md
varutasu 938c161a26
feat(scanner): add desktop workstation layout (#165)
Give /scanner a md+ camera, live match inspector, and history strip
(with device picker, batch scan, and tips) without regressing the
mobile immersive checkout.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-15 17:21:23 -05:00

78 lines
3.4 KiB
Markdown

---
convoy: scanner-desktop-layout
brief_number: 3
depends_on: []
recommended_model: composer-2.5-fast
model_tier: fast
files:
- components/scanner/ScannerResultPanel.js
- test/components/ScannerResultPanel.test.js
cross_brief_commitments:
- brief: 6
description: |
Brief 6 passes `focusedCard`, `queue`, `isIdentifying`, handlers
(`onAddToOwned`, `onAddToList`, `onRescan`), and `commitError`. On
successful add, Brief 6 advances `focusedCardId` to the next unprocessed
entry and fires `ScannerToast` — this component calls `onAddToOwned` only.
---
# Brief 3: Live match inspector rail
## Goal (1 sentence)
Build the right-rail `ScannerResultPanel` that inspects the focused cart entry and commits one card via `VOCAB.ADD_TO_MY_COLLECTION`.
## Files in scope (do not edit anything else)
- `components/scanner/ScannerResultPanel.js`
- `test/components/ScannerResultPanel.test.js`
## Conventions to follow
- Import `VOCAB` from `lib/collection-vocabulary.js` — never ship forbidden strings
(`forbidden-stale-strings` CI).
- Primitives: `GlassSurface`, `Button` from `components/ui`.
- Duplicate minimally from `ReviewCardItem.js`: `CONDITION_OPTIONS`,
`confidencePercent`, `confidenceRingColor` (do not edit `ReviewCardItem` in this brief).
- Confidence captions (Design direction): ≥90% *Excellent match.* · ≥70% *Good match.*
· &lt;70% *Low confidence — verify before adding.*
- Inspector thumbnail + tiles: solid `--bg-secondary`**no `backdrop-filter`** on per-card elements.
- Empty state: centered muted scan/camera SVG illustration + single line:
*Point your camera at a card or upload an image to see a match.***no** Upload/Batch CTAs in rail.
- Populated: thumbnail, name (`text-lg font-semibold`), set · rarity · collector #,
condition `<select>` (`aria-label` includes card name), foil `role="switch"`,
confidence % + horizontal bar (300ms width transition; `@media (prefers-reduced-motion: reduce)` → instant).
- Actions: primary `VOCAB.ADD_TO_MY_COLLECTION` · ghost **Rescan** · ghost `VOCAB.ADD_TO_LIST`.
- Primary `Button` uses `loading` when `isAdding` (from `queue.addingCardIds.has(card.id)`).
- Commit errors: `role="alert"` div with `color-mix(in srgb, var(--color-error) …)` pattern from `pages/scanner.js`.
- `isIdentifying` state: show *Identifying…* placeholder when true and no focused card yet.
## Props shape (for Brief 6 wiring)
```js
export default function ScannerResultPanel({
focusedCard = null,
queue,
isIdentifying = false,
commitError = null,
onAddToOwned,
onAddToList,
onRescan,
}) {
```
`onUpdateMetadata` via `queue.updateCardMetadata(focusedCard.id, patch)` inline.
## Acceptance criteria
- [ ] Empty state: illustration + one line only (no duplicate Upload/Batch)
- [ ] Populated state shows metadata, condition select, foil switch, confidence bar
- [ ] Primary button label is `VOCAB.ADD_TO_MY_COLLECTION`
- [ ] Secondary row: Rescan + `VOCAB.ADD_TO_LIST` (no wishlist)
- [ ] `prefers-reduced-motion` disables confidence bar width animation
- [ ] tests: empty state, populated card, add button disabled while adding
- [ ] no scope expansion (do not edit files outside `files:` above)
## Rationale (≤3 sentences)
The inspector replaces the 360px cart side panel as the primary right-hand surface on desktop. Keeping it a dumb view lets Brief 6 own focus/advance/toast orchestration. Confidence and condition UX matches checkout patterns users already know from `ReviewCardItem`.