deckhearth/components/scanner/ScannerScanPeek.js

98 lines
3 KiB
JavaScript
Raw Normal View History

import { useEffect, useState } from 'react';
import GlassSurface from '../ui/GlassSurface.js';
const PEEK_DURATION_MS = 3000;
function formatConfidence(confidence) {
if (confidence == null || Number.isNaN(confidence)) return null;
const pct = confidence <= 1 ? Math.round(confidence * 100) : Math.round(confidence);
return `${pct}%`;
}
export default function ScannerScanPeek({ card, onOpenCheckout = () => {} }) {
const [visible, setVisible] = useState(true);
useEffect(() => {
const timerId = window.setTimeout(() => {
setVisible(false);
}, PEEK_DURATION_MS);
return () => window.clearTimeout(timerId);
}, []);
if (!card || !visible) return null;
const cardName = card.name || card.card?.name || 'Card';
const thumbnail =
card.image_url ||
card.scanImageUrl ||
card.card?.image_url ||
null;
const confidenceLabel = formatConfidence(card.confidence);
return (
<div
className="absolute left-4 right-4 z-20 pointer-events-none"
style={{
bottom: 'calc(5.5rem + env(safe-area-inset-bottom, 0px))',
opacity: visible ? 1 : 0,
transform: `translateY(${visible ? '0' : '12px'})`,
transition: 'opacity 150ms ease, transform 150ms ease',
}}
>
<button
type="button"
onClick={onOpenCheckout}
className="w-full pointer-events-auto text-left focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 rounded-2xl"
style={{
'--tw-ring-color': 'var(--accent-ember)',
'--tw-ring-offset-color': 'transparent',
}}
aria-label={`Open cart to review ${cardName}`}
>
<GlassSurface
tint="mid"
rim="subtle"
blur="mid"
className="flex items-center gap-3 rounded-2xl px-3 py-2.5"
>
{thumbnail ? (
// eslint-disable-next-line @next/next/no-img-element -- ephemeral scan thumbnail blob/url
<img
src={thumbnail}
alt=""
className="w-12 h-[4.25rem] object-cover rounded-md flex-shrink-0"
style={{ border: '1px solid var(--border)' }}
/>
) : (
<div
className="w-12 h-[4.25rem] rounded-md flex-shrink-0 flex items-center justify-center text-xs font-medium"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-secondary)',
border: '1px solid var(--border)',
}}
aria-hidden="true"
>
?
</div>
)}
<div className="flex-1 min-w-0">
<p
className="text-sm font-semibold truncate"
style={{ color: 'var(--text-primary)' }}
>
{cardName}
</p>
{confidenceLabel && (
<p className="text-xs mt-0.5" style={{ color: 'var(--text-secondary)' }}>
{confidenceLabel} match
</p>
)}
</div>
</GlassSurface>
</button>
</div>
);
}