import React, { useState } from 'react'; import CameraScanner from '../components/CameraScanner'; import GlowingCard from '../components/GlowingCard'; import CardImageDisplay from '../components/CardImageDisplay'; import { cardMatcher } from '../services/cardMatcher'; interface ScannedCard { id: string; originalName: string; ocrText: string; ocrConfidence: number; matches: any[]; selectedMatch?: any; timestamp: number; } const Scanner: React.FC = () => { const [isDragging, setIsDragging] = useState(false); const [scannedCards, setScannedCards] = useState([]); const [isProcessingMatch, setIsProcessingMatch] = useState(false); const [error, setError] = useState(null); const handleDragOver = (e: React.DragEvent) => { e.preventDefault(); setIsDragging(true); }; const handleDragLeave = (e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); }; // Handle card scanned from camera const handleCardScanned = async (ocrData: any) => { setIsProcessingMatch(true); setError(null); try { // Use card matcher to find potential matches const matches = await cardMatcher.matchCard({ name: ocrData.name, set: ocrData.set, ocrText: ocrData.ocrText, confidence: ocrData.confidence }); // Create scanned card entry const scannedCard: ScannedCard = { id: Date.now().toString(), originalName: ocrData.name, ocrText: ocrData.ocrText, ocrConfidence: ocrData.confidence, matches: matches, selectedMatch: matches.length > 0 ? matches[0].card : undefined, timestamp: Date.now() }; setScannedCards(prev => [scannedCard, ...prev]); console.log('Card scan processed:', scannedCard); } catch (err) { console.error('Card matching error:', err); setError('Failed to match card against database.'); } finally { setIsProcessingMatch(false); } }; // Handle scanner errors const handleScannerError = (errorMessage: string) => { setError(errorMessage); }; // Select a different match for a scanned card const selectMatch = (scannedCardId: string, match: any) => { setScannedCards(prev => prev.map(card => card.id === scannedCardId ? { ...card, selectedMatch: match.card } : card )); }; // Remove a scanned card const removeScannedCard = (scannedCardId: string) => { setScannedCards(prev => prev.filter(card => card.id !== scannedCardId)); }; // Add selected card to collection const addToCollection = async (scannedCardId: string) => { const scannedCard = scannedCards.find(c => c.id === scannedCardId); if (!scannedCard || !scannedCard.selectedMatch) return; try { // TODO: Implement collection service console.log('Adding to collection:', scannedCard.selectedMatch); // For now, just remove from scan results removeScannedCard(scannedCardId); } catch (err) { console.error('Failed to add to collection:', err); setError('Failed to add card to collection.'); } }; const handleDrop = (e: React.DragEvent) => { e.preventDefault(); setIsDragging(false); // TODO: Handle file upload and OCR processing }; return (

Card Scanner

Use OCR technology to quickly scan and identify your trading cards

{/* Error Display */} {error && (
❌

Error

{error}

)} {/* Camera Scanner */}

πŸ“Έ Live Camera Scanner

{isProcessingMatch && (
Matching card against database...
This may take a moment
)}
{/* Upload Area */}
πŸ“Έ

Upload Card Images

Drag and drop your card images here, or click to select files

Supported formats: JPG, PNG, HEIC β€’ Max size: 10MB per image

{/* Supported Games */}

Supported Trading Card Games

βœ…

Magic: The Gathering

Full OCR support

βœ…

PokΓ©mon

Full OCR support

πŸ†•

Disney Lorcana

Ready for scanning

{/* Scan Results */} {scannedCards.length > 0 && (

🎯 Scan Results ({scannedCards.length})

{scannedCards.map((scannedCard) => (
{/* Scan Info Header */}

OCR Result: "{scannedCard.originalName}"

Confidence: {Math.round(scannedCard.ocrConfidence)}% β€’ {new Date(scannedCard.timestamp).toLocaleTimeString()}
{/* Card Matches */} {scannedCard.matches.length > 0 ? (
Found {scannedCard.matches.length} potential match{scannedCard.matches.length !== 1 ? 'es' : ''}:
{/* Selected Match Display */} {scannedCard.selectedMatch && (
{scannedCard.selectedMatch.name}

{scannedCard.selectedMatch.set_name} β€’ {scannedCard.selectedMatch.rarity}

Match confidence: {Math.round((scannedCard.matches.find(m => m.card.id === scannedCard.selectedMatch.id)?.confidence || 0) * 100)}%

)} {/* Alternative Matches */} {scannedCard.matches.length > 1 && (

Other potential matches:

{scannedCard.matches .filter(match => match.card.id !== scannedCard.selectedMatch?.id) .slice(0, 6) .map((match, index) => (
selectMatch(scannedCard.id, match)} >

{match.card.name}

{match.card.set_name}

{Math.round(match.confidence * 100)}% match

))}
)}
) : (
⚠️

No matches found

The card couldn't be matched against our database. Try scanning again with better lighting.

{/* Show OCR text for debugging */}
View raw OCR text
                        {scannedCard.ocrText}
                      
)}
))}
)} {/* Tips */}

πŸ’‘ Tips for Best Results

  • β€’ Ensure good lighting and minimal shadows
  • β€’ Keep cards flat and avoid glare
  • β€’ Capture the entire card including borders
  • β€’ Use high resolution images when possible
  • β€’ For foil cards, angle to reduce glare
); }; export default Scanner;