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>
51 lines
1.5 KiB
JavaScript
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>
|
|
);
|
|
}
|