deckhearth/components/scanner/DeckModeIndicator.js
Randall Stillwell cf9fea0726 feat(scanner): rebuild as mobile-first three-phase flow
Replace the desktop-first, everything-at-once scanner layout with a
phased mobile-optimized experience: Setup → Scanning → Review.

Phase 1 (Setup): destination picker, game filter, deck mode toggle,
scan history (last 5 sessions).

Phase 2 (Scanning): full-screen camera with auto-start, haptic + sound
feedback on card detection, torch/flash toggle, count pill, bottom-sheet
disambiguation (replaces full-screen modal).

Phase 3 (Review): card list with inline condition/foil/qty edits,
batch confirm, 30-second undo, deck progress indicator.

New features:
- Deck mode (progress toward 40/60/99 card target)
- Scan history (persisted to localStorage)
- Sound feedback (Web Audio oscillator, configurable)
- Offline queue (localStorage persistence + auto-retry on reconnect)
- Camera flash/torch toggle
- Batch ownership API (replaces N+1 per-card fetches)
- Visibility pause (detection loop stops when tab is backgrounded)

Convoy: scanner-rebuild
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-13 08:42:58 -05:00

51 lines
1.5 KiB
JavaScript

export default function DeckModeIndicator({ current, target, game }) {
const progress = target > 0 ? Math.min(current / target, 1) : 0;
const isComplete = current >= target;
return (
<div className="mt-2 px-1">
{/* Progress track */}
<div
className="w-full rounded-full overflow-hidden"
style={{
height: 6,
backgroundColor: 'var(--bg-tertiary)',
}}
role="progressbar"
aria-valuenow={current}
aria-valuemin={0}
aria-valuemax={target}
aria-label={`Deck progress: ${current} of ${target} cards`}
>
<div
className="h-full rounded-full transition-all duration-300 ease-out"
style={{
width: `${progress * 100}%`,
backgroundColor: isComplete
? 'var(--accent-gold)'
: 'var(--accent-ember)',
}}
/>
</div>
{/* Labels */}
<div className="flex items-center justify-between mt-1.5">
<span
className="text-xs font-medium"
style={{
color: isComplete ? 'var(--accent-gold)' : 'var(--text-secondary)',
textShadow: isComplete ? '0 0 8px var(--accent-gold)' : 'none',
}}
>
{isComplete ? 'Deck complete!' : `${current}/${target} cards`}
</span>
<span
className="text-xs"
style={{ color: 'var(--text-secondary)' }}
>
{game}
</span>
</div>
</div>
);
}