'use client'; import { useState, type ChangeEvent, useRef } from 'react'; import { scanTCGCard, type ScanTCGCardOutput } from '@/ai/flows/scan-tcg-card'; import { Button } from '@/components/ui/button'; import { useToast } from '@/hooks/use-toast'; import { Loader2, Upload, AlertCircle, CheckCircle } from 'lucide-react'; import Image from 'next/image'; import CardForm, { type CardFormInputs } from './CardForm'; import { useAuth } from '@/hooks/useAuth'; import { useApiKeys } from '@/hooks/useApiKeys'; import { addCardToCollection } from '@/lib/firestore'; import { useRouter } from 'next/navigation'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '../ui/card'; export default function CardScanner() { const { hasGeminiKey } = useApiKeys(); const [isScanning, setIsScanning] = useState(false); const [scanResult, setScanResult] = useState(null); const [error, setError] = useState(null); const [imagePreview, setImagePreview] = useState(null); const [imageDataUrl, setImageDataUrl] = useState(null); const [showForm, setShowForm] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const { toast } = useToast(); const { user } = useAuth(); const router = useRouter(); const fileInputRef = useRef(null); const handleImageChange = (event: ChangeEvent) => { const file = event.target.files?.[0]; if (file) { // Reset previous scan state setScanResult(null); setError(null); setShowForm(false); setImageDataUrl(null); const reader = new FileReader(); reader.onloadend = () => { const dataUrl = reader.result as string; setImagePreview(dataUrl); // For preview setImageDataUrl(dataUrl); // For sending to AI }; reader.readAsDataURL(file); } }; const handleScan = async () => { if (!imageDataUrl) { toast({ title: 'No image selected', description: 'Please select an image to scan.', variant: 'destructive' }); return; } if (!hasGeminiKey) { setError('Gemini API key is required to scan trading cards. Please configure it in Settings.'); toast({ title: 'API Key Required', description: 'Gemini API key is required to scan trading cards. Please configure it in Settings.', variant: 'destructive' }); return; } setIsScanning(true); setError(null); setScanResult(null); setShowForm(false); try { const result = await scanTCGCard({ photoDataUri: imageDataUrl, userId: user?.uid || '' }); if (result.error) { setError(result.error); toast({ title: 'Scan Failed', description: result.error, variant: 'destructive' }); } else if (result.cardDetails) { setScanResult(result); setShowForm(true); toast({ title: 'Scan Successful', description: 'Card details identified. Please review and save.', variant: 'default' }); } else { setError('Unexpected response from scanner.'); toast({ title: 'Scan Error', description: 'Unexpected response from scanner.', variant: 'destructive' }); } } catch (e) { console.error('Scan error:', e); const errorMessage = (e instanceof Error) ? e.message : 'An unknown error occurred during scanning.'; setError(errorMessage); toast({ title: 'Scan Error', description: errorMessage, variant: 'destructive' }); } finally { setIsScanning(false); } }; const handleSaveCard = async (data: CardFormInputs) => { if (!user) { toast({ title: 'Authentication Error', description: 'You must be logged in to save cards.', variant: 'destructive' }); return; } if (!imageDataUrl) { // Ensure image data is still available toast({ title: 'Image Error', description: 'Image data is missing. Please re-upload.', variant: 'destructive'}); return; } setIsSubmitting(true); try { const cardToSave = { name: data.name, set: data.set, rarity: data.rarity, game: scanResult?.cardDetails?.game || 'pokemon', // Include detected game imageDataUrl: data.imageDataUrl, // Use image from form state, which should be original scan }; await addCardToCollection(user.uid, cardToSave); toast({ title: 'Card Saved!', description: `${data.name} has been added to your collection.` }); // Reset state after successful save setImagePreview(null); setImageDataUrl(null); setScanResult(null); setShowForm(false); setError(null); if(fileInputRef.current) fileInputRef.current.value = ""; // Clear file input router.push('/dashboard/collection'); } catch (e) { console.error('Error saving card:', e); const errorMessage = (e instanceof Error) ? e.message : 'An unknown error occurred while saving.'; toast({ title: 'Save Error', description: errorMessage, variant: 'destructive' }); } finally { setIsSubmitting(false); } }; const handleCancelForm = () => { setShowForm(false); // Optionally clear scanResult or keep it if user might want to re-open form }; return (
Upload Card Image Choose a clear image of your trading card for best scanning results. Supports Pokémon, One Piece, Lorcana, Magic: The Gathering, and Dragon Ball cards.
Choose file or drag and drop

Supported formats: JPG, PNG, WEBP • Max size: 10MB

{imagePreview && (

Preview

Card preview
{imageDataUrl && ( <> {!hasGeminiKey && (

Please configure your Gemini API key in Settings to scan Pokemon cards.

)} )}
)}
{error && !showForm && (

Scan Failed

{error}

Try taking a clearer photo or ensure the card is fully visible.

)} {showForm && scanResult?.cardDetails && imageDataUrl && (

Card Successfully Scanned!

Review the details below and add to your collection

)}
); }