import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Layout from '../components/Layout'; import CameraScanner from '../components/CameraScanner'; import OCRSettings from '../components/OCRSettings'; import { ManaCost, ColorIdentity } from '../components/ManaSymbols'; import ManaSymbolSettings from '../components/ManaSymbolSettings'; import { useAuth } from '../lib/auth-context'; export default function Scanner() { const { user } = useAuth(); const router = useRouter(); const [scannedCards, setScannedCards] = useState([]); const [collections, setCollections] = useState([]); const [decks, setDecks] = useState([]); const [showCreateCollection, setShowCreateCollection] = useState(false); const [newCollectionName, setNewCollectionName] = useState(''); const [showOCRSettings, setShowOCRSettings] = useState(false); // Bulk action states const [selectedCards, setSelectedCards] = useState(new Set()); const [bulkAction, setBulkAction] = useState(''); // 'owned', 'collection', 'deck' const [bulkTarget, setBulkTarget] = useState(''); const [isProcessing, setIsProcessing] = useState(false); // Mana symbol settings const [manaSymbolSettings, setManaSymbolSettings] = useState({ useSVG: false }); // Redirect to login if not authenticated useEffect(() => { if (!user) { router.push('/login'); return; } }, [user, router]); // Load collections and decks useEffect(() => { if (user) { loadCollections(); loadDecks(); } }, [user]); const loadCollections = async () => { try { const response = await fetch('/api/collections', { headers: { 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` } }); if (response.ok) { const data = await response.json(); // Filter out system collections (like "All My Cards") const userCollections = data.filter(collection => !collection.is_system_collection); setCollections(userCollections); } } catch (error) { console.error('Error loading collections:', error); } }; const loadDecks = async () => { try { const response = await fetch('/api/decks', { headers: { 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` } }); if (response.ok) { const data = await response.json(); setDecks(data); } } catch (error) { console.error('Error loading decks:', error); } }; const handleCardScanned = async (cardData) => { console.log('Card scanned:', cardData); // Check if this card already exists in the queue setScannedCards(prev => { const existingCardIndex = prev.findIndex(existing => existing.name === cardData.cardName && existing.set === cardData.setName && !existing.processed ); if (existingCardIndex !== -1) { // Increment quantity of existing card const updatedCards = [...prev]; updatedCards[existingCardIndex] = { ...updatedCards[existingCardIndex], quantity: (updatedCards[existingCardIndex].quantity || 1) + 1, timestamp: new Date().toISOString() // Update timestamp }; return updatedCards; } else { // Add new card to queue const scannedCard = { ...cardData, id: Date.now(), // Temporary ID for the queue name: cardData.cardName, set: cardData.setName, quantity: 1, timestamp: new Date().toISOString(), processed: false }; return [scannedCard, ...prev]; } }); }; // Quantity management functions const incrementCardQuantity = (cardId) => { setScannedCards(prev => prev.map(card => card.id === cardId ? { ...card, quantity: (card.quantity || 1) + 1 } : card )); }; const decrementCardQuantity = (cardId) => { setScannedCards(prev => prev.map(card => card.id === cardId ? { ...card, quantity: Math.max(1, (card.quantity || 1) - 1) } : card )); }; const handleError = (error) => { console.error('Scanner error:', error); // You could show a toast notification here }; // Individual card actions const addSingleCardToOwned = async (card) => { try { await addToOwnedCards(card); markCardAsProcessed(card.id, 'owned'); } catch (error) { console.error('Error adding card to owned:', error); } }; const addSingleCardToCollection = async (card, collectionId) => { try { await addToCollection(card, collectionId); markCardAsProcessed(card.id, 'collection'); } catch (error) { console.error('Error adding card to collection:', error); } }; const addSingleCardToDeck = async (card, deckId) => { try { await addToDeck(card, deckId); markCardAsProcessed(card.id, 'deck'); } catch (error) { console.error('Error adding card to deck:', error); } }; // Bulk actions const handleBulkAction = async () => { if (!bulkAction || selectedCards.size === 0) return; setIsProcessing(true); try { const cardsToProcess = scannedCards.filter(card => selectedCards.has(card.id)); for (const card of cardsToProcess) { if (bulkAction === 'owned') { await addToOwnedCards(card); } else if (bulkAction === 'collection' && bulkTarget) { await addToCollection(card, bulkTarget); } else if (bulkAction === 'deck' && bulkTarget) { await addToDeck(card, bulkTarget); } markCardAsProcessed(card.id, bulkAction); } // Clear selections and reset bulk action state setSelectedCards(new Set()); setBulkAction(''); setBulkTarget(''); } catch (error) { console.error('Error processing bulk action:', error); } finally { setIsProcessing(false); } }; const markCardAsProcessed = (cardId, action) => { setScannedCards(prev => prev.map(card => card.id === cardId ? { ...card, processed: true, processedAction: action } : card )); }; // Helper functions for API calls const addToOwnedCards = async (cardData) => { const response = await fetch('/api/user-cards', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ cardId: cardData.databaseId, quantity: 1, condition: 'NM' }) }); if (!response.ok) { throw new Error('Failed to add to owned cards'); } }; const addToCollection = async (cardData, collectionId) => { const response = await fetch(`/api/collections/${collectionId}/cards`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ cardId: cardData.databaseId, quantity: 1 }) }); if (!response.ok) { throw new Error('Failed to add to collection'); } }; const addToDeck = async (cardData, deckId) => { const response = await fetch(`/api/decks/${deckId}/cards`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ cardId: cardData.databaseId, quantity: 1 }) }); if (!response.ok) { throw new Error('Failed to add to deck'); } }; const createCollection = async () => { if (!newCollectionName.trim()) return; try { const response = await fetch('/api/collections', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ name: newCollectionName, description: 'Created from card scanner', is_public: false }) }); if (response.ok) { const newCollection = await response.json(); setCollections(prev => [newCollection, ...prev]); setBulkTarget(newCollection.id.toString()); setNewCollectionName(''); setShowCreateCollection(false); } } catch (error) { console.error('Error creating collection:', error); } }; const clearScannedCards = () => { setScannedCards([]); setSelectedCards(new Set()); }; const removeScannedCard = (cardId) => { setScannedCards(prev => prev.filter(card => card.id !== cardId)); setSelectedCards(prev => { const newSet = new Set(prev); newSet.delete(cardId); return newSet; }); }; const toggleCardSelection = (cardId) => { setSelectedCards(prev => { const newSet = new Set(prev); if (newSet.has(cardId)) { newSet.delete(cardId); } else { newSet.add(cardId); } return newSet; }); }; const selectAllCards = () => { const unprocessedCards = scannedCards.filter(card => !card.processed); setSelectedCards(new Set(unprocessedCards.map(card => card.id))); }; const deselectAllCards = () => { setSelectedCards(new Set()); }; if (!user) { return
Scan cards to identify them, then choose what to do with your collection