/* eslint-disable @next/next/no-img-element -- External card thumbnails; next/image migration is out of scope. */
import { useRef } from 'react';
import { VOCAB } from '../../lib/collection-vocabulary.js';
import Button from '../ui/Button.js';
import GlassSurface from '../ui/GlassSurface.js';
export const TAB_RECENT = 'recent';
export const TAB_QUEUE = 'queue';
export const TAB_DUPLICATES = 'duplicates';
const EMPTY_COPY = {
[TAB_RECENT]: 'No scans yet this session.',
[TAB_QUEUE]: 'Scan queue is empty — matches appear here before you add them.',
[TAB_DUPLICATES]: 'No duplicates detected.',
};
export function getDuplicateCards(scannedCards, ownershipMap) {
const sessionRepeatIds = new Set();
const seen = new Map();
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])
);
}
function queueTabAriaLabel(count) {
if (count === 0) return 'Scan Queue';
if (count === 1) return 'Scan Queue, 1 unprocessed card';
return `Scan Queue, ${count} unprocessed cards`;
}
function duplicatesTabAriaLabel(count) {
if (count === 0) return 'Duplicates';
if (count === 1) return 'Duplicates, 1 duplicate card';
return `Duplicates, ${count} duplicate cards`;
}
function HistoryChip({ card, isFocused, onFocus }) {
const isFailed = Boolean(card.identifyFailed);
return (
);
}
function BatchProgressBanner({ batchProgress }) {
if (!batchProgress?.active) return null;
const { current, total, onCancel } = batchProgress;
const percent = total > 0 ? Math.round((current / total) * 100) : 0;
return (
Scanning {current} of {total}…
);
}
export default function ScannerHistoryStrip({
scannedCards,
ownershipMap,
focusedCardId,
onFocusCard,
activeTab,
onTabChange,
batchProgress = null,
onClearAll,
onCommitSelectedToOwned,
onOpenListPicker,
isProcessing = false,
}) {
const chipScrollerRef = useRef(null);
const unprocessedCards = scannedCards.filter((card) => !card.processed);
const duplicateCards = getDuplicateCards(scannedCards, ownershipMap || {});
const recentCards = [...scannedCards].reverse();
const tabCards = {
[TAB_RECENT]: recentCards,
[TAB_QUEUE]: unprocessedCards,
[TAB_DUPLICATES]: duplicateCards,
};
const visibleCards = tabCards[activeTab] ?? [];
const handleViewAllScans = () => {
const scroller = chipScrollerRef.current;
if (scroller) {
scroller.scrollLeft = scroller.scrollWidth;
}
};
const tabs = [
{
id: TAB_RECENT,
label: 'Recent Scans',
badge: null,
ariaLabel: 'Recent Scans',
},
{
id: TAB_QUEUE,
label: 'Scan Queue',
badge: unprocessedCards.length,
ariaLabel: queueTabAriaLabel(unprocessedCards.length),
},
{
id: TAB_DUPLICATES,
label: 'Duplicates',
badge: duplicateCards.length,
ariaLabel: duplicatesTabAriaLabel(duplicateCards.length),
},
];
return (
{tabs.map((tab) => {
const isSelected = activeTab === tab.id;
return (
);
})}
{tabs.map((tab) => (
{tab.id === TAB_QUEUE &&
}
{visibleCards.length === 0 && activeTab === tab.id ? (
{EMPTY_COPY[tab.id]}
) : activeTab === tab.id ? (
{visibleCards.map((card) => (
))}
) : null}
{tab.id === TAB_QUEUE && activeTab === TAB_QUEUE && unprocessedCards.length > 0 && (
)}
))}
);
}