/* eslint-disable @next/next/no-img-element -- External card image URLs; next/image migration is out of scope. */ import { VOCAB } from '../../lib/collection-vocabulary.js'; import Button from '../ui/Button.js'; import GlassSurface from '../ui/GlassSurface.js'; const CONDITION_OPTIONS = ['NM', 'LP', 'MP', 'HP', 'DMG']; function confidencePercent(confidence) { if (confidence == null || Number.isNaN(confidence)) return null; return confidence <= 1 ? Math.round(confidence * 100) : Math.round(confidence); } function confidenceRingColor(pct) { if (pct == null) return 'var(--border)'; if (pct < 70) return 'var(--accent-ember)'; if (pct < 85) return 'var(--color-warning)'; return 'var(--color-success)'; } function confidenceCaption(pct) { if (pct == null) return null; if (pct >= 90) return 'Excellent match.'; if (pct >= 70) return 'Good match.'; return 'Low confidence — verify before adding.'; } function formatMetadataLine(card) { return [card.set, card.rarity, card.cardNumber].filter(Boolean).join(' · '); } function EmptyStateIllustration() { return ( ); } export default function ScannerResultPanel({ focusedCard = null, queue, isIdentifying = false, commitError = null, onAddToOwned, onAddToList, onRescan, }) { if (!focusedCard) { return ( Scan Result {isIdentifying ? ( Identifying… ) : ( <> Point your camera at a card or upload an image to see a match. > )} ); } const card = focusedCard; const confidencePct = confidencePercent(card.confidence); const isAdding = queue?.addingCardIds?.has(card.id) ?? false; const metadataLine = formatMetadataLine(card); const handleUpdateMetadata = (patch) => { queue?.updateCardMetadata?.(card.id, patch); }; return ( Scan Result {confidencePct != null && ( {confidencePct}% Match )} {card.image_url ? ( ) : ( )} {card.name} {metadataLine && ( {metadataLine} )} Condition handleUpdateMetadata({ condition: e.target.value })} className="w-full px-3 py-2 rounded-xl text-sm focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2" style={{ backgroundColor: 'var(--bg-secondary)', border: '1px solid var(--border)', color: 'var(--text-primary)', minHeight: 44, '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'transparent', }} aria-label={`Condition for ${card.name}`} > {CONDITION_OPTIONS.map((opt) => ( {opt} ))} Foil handleUpdateMetadata({ isFoil: !card.isFoil })} className="relative inline-flex h-7 w-12 flex-shrink-0 rounded-full transition-colors duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2" style={{ backgroundColor: card.isFoil ? 'var(--accent-ember)' : 'var(--bg-tertiary)', border: '1px solid var(--border)', minWidth: 44, minHeight: 44, '--tw-ring-color': 'var(--accent-ember)', '--tw-ring-offset-color': 'transparent', }} > {confidencePct != null && ( Confidence {confidencePct}% {confidenceCaption(confidencePct)} )} {commitError && ( {commitError} )} onAddToOwned?.(card)} > {VOCAB.ADD_TO_MY_COLLECTION} onRescan?.(card)}> Rescan onAddToList?.(card)}> {VOCAB.ADD_TO_LIST} ); }
Identifying…
Point your camera at a card or upload an image to see a match.
{metadataLine}
{confidenceCaption(confidencePct)}