79 lines
3.4 KiB
Markdown
79 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.*
|
||
|
|
· <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`.
|