deckhearth/.convoys/scanner-desktop-layout/brief-4-history-strip.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

4.1 KiB

convoy brief_number depends_on recommended_model model_tier files cross_brief_commitments
scanner-desktop-layout 4
composer-2.5-fast fast
components/scanner/ScannerHistoryStrip.js
test/components/ScannerHistoryStrip.test.js
brief description
6 Brief 6 supplies `focusedCardId`, `onFocusCard`, `batchProgress` (`{ active, current, total, onCancel }`), bulk commit handlers, and `onClearAll`. Row click calls `onFocusCard(card.id)` — no auto-commit.

Brief 4: History / queue / duplicates strip

Goal (1 sentence)

Build the bottom ScannerHistoryStrip with Recent Scans, Scan Queue, and Duplicates tabs over the existing cart model.

Files in scope (do not edit anything else)

  • components/scanner/ScannerHistoryStrip.js
  • test/components/ScannerHistoryStrip.test.js

Conventions to follow

  • GlassSurface tint mid for strip container; chips use solid --bg-secondary fill — no per-chip backdrop-filter.
  • Tab labels: Recent Scans, Scan Queue, Duplicates with badge counts on Queue and Duplicates.
  • role="tablist" / role="tab" / role="tabpanel"; badges in aria-label (e.g. Scan Queue, 3 unprocessed cards).
  • Empty tab copy (UX §5): Recent → No scans yet this session. · Queue → Scan queue is empty — matches appear here before you add them. · Duplicates → No duplicates detected.
  • Clear All text link right-aligned; disabled while batchProgress?.active.
  • View All Scans ghost button at strip bottom scrolls chip row to end (v1 — no expanded modal).
  • Chip row: horizontal overflow-x-auto, min height 44px, ember rim on focused chip (focusedCardId === card.id--ember-rim-pronounced).
  • Row click → onFocusCard(card.id) only — never auto-commit.
  • Batch progress UI (when batchProgress.active): inline banner in Scan Queue tabpanel — Scanning {current} of {total}… determinate bar, ghost Cancel calling batchProgress.onCancel.
  • Failed identify rows: identifyFailed flag on card (Brief 6 sets) → ember left border + Identify failed label + optional reason text-sm.

Duplicate membership helper (export from this file)

export function getDuplicateCards(scannedCards, ownershipMap) {
  const sessionRepeatIds = new Set();
  const seen = new Map(); // key: name+set → first id
  for (const card of scannedCards) {
    const key = `${card.name}::${card.set}`;
    if (seen.has(key)) sessionRepeatIds.add(card.id);
    else seen.set(key, card.id);
    if ((card.quantity || 1) > 1) sessionRepeatIds.add(card.id);
  }
  return scannedCards.filter(
    (card) =>
      sessionRepeatIds.has(card.id) ||
      (card.databaseId && ownershipMap[card.databaseId])
  );
}

Recent Scans tab: all scannedCards session history (processed + unprocessed), newest first. Scan Queue tab: !card.processed rows; badge = unprocessed count. Duplicates tab: getDuplicateCards(...); badge = duplicate set length.

Props shape

export default function ScannerHistoryStrip({
  scannedCards,
  ownershipMap,
  focusedCardId,
  onFocusCard,
  activeTab,
  onTabChange,
  batchProgress = null,
  onClearAll,
  onCommitSelectedToOwned,
  onOpenListPicker,
  isProcessing = false,
}) {

Bulk commit buttons in Queue tab footer: VOCAB.ADD_TO_MY_COLLECTION + VOCAB.ADD_TO_LIST calling parent handlers (Brief 6 wires queue.commitSelectedToOwned).

Acceptance criteria

  • Three tabs with correct labels and badge counts
  • getDuplicateCards covers ownershipMap + session name/set repeats + quantity>1
  • Row click invokes onFocusCard without commit
  • Batch progress banner with Cancel when batchProgress.active
  • Clear All disabled during active batch
  • Chip focus ring matches focusedCardId
  • tests: tab switching, duplicate helper, empty states, batch banner
  • no scope expansion (do not edit files outside files: above)

Rationale (≤3 sentences)

The strip is the second commit surface (C5) alongside the inspector. Centralizing duplicate logic in one exported helper keeps pages/scanner.js thin. Tab + chip UX is independent of camera device picker, so this brief parallelizes cleanly.