import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; import Link from 'next/link'; import UploadImageModal from '../../components/UploadImageModal'; import ShareModal from '../../components/ShareModal'; import CollaboratorFacepile from '../../components/CollaboratorFacepile'; import Layout from '../../components/Layout'; export default function CollectionView() { const router = useRouter(); const { id } = router.query; // Get user from auth context - for now using admin user const user = { email: 'admin@tcgvault.com', role: 'admin' }; const [collection, setCollection] = useState(null); const [cards, setCards] = useState([]); const [loading, setLoading] = useState(true); const [isFavorited, setIsFavorited] = useState(false); const [showShareModal, setShowShareModal] = useState(false); const [copySuccess, setCopySuccess] = useState(false); const [selectedTCG, setSelectedTCG] = useState('MTG'); // Filter states const [searchQuery, setSearchQuery] = useState(''); const [selectedRarity, setSelectedRarity] = useState('All Rarities'); const [selectedType, setSelectedType] = useState('All Types'); const [groupBy, setGroupBy] = useState('Group by Game'); const [sortBy, setSortBy] = useState('Sort by Name'); const [viewMode, setViewMode] = useState('grid'); const [searchCards, setSearchCards] = useState(''); const [searchResults, setSearchResults] = useState([]); const [showSearchResults, setShowSearchResults] = useState(false); const [showUploadModal, setShowUploadModal] = useState(false); useEffect(() => { if (id) { fetchCollectionData(); } }, [id]); const fetchCollectionData = async () => { try { const response = await fetch(`/api/collections/${id}`); if (response.ok) { const data = await response.json(); setCollection(data.collection); setCards(data.cards || []); // Check if collection is favorited checkIfFavorited(); } else { console.error('Failed to fetch collection'); setCollection(null); setCards([]); } } catch (error) { console.error('Error fetching collection:', error); setCollection(null); setCards([]); } finally { setLoading(false); } }; const checkIfFavorited = async () => { try { const response = await fetch(`/api/favorites?type=collection`, { headers: { 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` } }); if (response.ok) { const data = await response.json(); const isFav = data.favorites.some(fav => fav.item_id === parseInt(id)); setIsFavorited(isFav); } else { console.error('Failed to check favorites:', response.status); // Keep default false state } } catch (error) { console.error('Error checking favorites:', error); // Keep default false state } }; const handleSearchCards = async (query) => { if (query.length < 2) { setSearchResults([]); setShowSearchResults(false); return; } try { const response = await fetch(`/api/cards/search?q=${encodeURIComponent(query)}&limit=10`); if (response.ok) { const data = await response.json(); setSearchResults(data.cards || []); setShowSearchResults(true); } } catch (error) { console.error('Error searching cards:', error); } }; const handleAddCard = async (card) => { try { const response = await fetch(`/api/collections/${id}/cards`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ cardId: card.id, quantity: 1 }) }); if (response.ok) { setSearchCards(''); setShowSearchResults(false); fetchCollectionData(); // Refresh the collection data } } catch (error) { console.error('Error adding card:', error); } }; const toggleFavorite = async () => { try { if (isFavorited) { // Remove from favorites const response = await fetch('/api/favorites', { method: 'DELETE', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ itemType: 'collection', itemId: parseInt(id) }) }); if (response.ok) { setIsFavorited(false); } else { console.error('Failed to remove favorite:', response.status); } } else { // Add to favorites const response = await fetch('/api/favorites', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ itemType: 'collection', itemId: parseInt(id) }) }); if (response.ok) { setIsFavorited(true); } else { console.error('Failed to add favorite:', response.status); } } } catch (error) { console.error('Error toggling favorite:', error); } }; const togglePublic = async () => { try { const response = await fetch(`/api/collections/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ is_public: !collection.is_public }) }); if (response.ok) { setCollection(prev => ({ ...prev, is_public: !prev.is_public })); } } catch (error) { console.error('Error updating collection:', error); } }; const handleImageUpload = async (imageUrl) => { try { const response = await fetch(`/api/collections/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('auth_token')}` }, body: JSON.stringify({ image: imageUrl }) }); if (response.ok) { setCollection(prev => ({ ...prev, image: imageUrl })); } } catch (error) { console.error('Error updating collection image:', error); } }; const handleDownloadCSV = () => { if (cards.length === 0) { alert('No cards to download'); return; } // Create CSV headers const headers = [ 'Name', 'Set Name', 'Set Code', 'Card Number', 'Rarity', 'Game', 'Mana Cost', 'CMC', 'Type', 'Colors', 'Oracle Text', 'Power', 'Toughness', 'Market Price', 'Quantity', 'Added Date' ]; // Create CSV rows const csvRows = [ headers.join(','), // Header row ...cards.map(card => [ `"${(card.name || '').replace(/"/g, '""')}"`, `"${(card.set_name || '').replace(/"/g, '""')}"`, `"${card.set_code || ''}"`, `"${card.card_number || ''}"`, `"${card.rarity || ''}"`, `"${card.game || ''}"`, `"${card.mana_cost || ''}"`, `"${card.cmc || ''}"`, `"${card.card_type || ''}"`, `"${Array.isArray(card.colors) ? card.colors.join(', ') : (card.colors || '')}"`, `"${(card.oracle_text || '').replace(/"/g, '""')}"`, `"${card.power || ''}"`, `"${card.toughness || ''}"`, `"${card.market_price || ''}"`, `"${card.quantity || 1}"`, `"${card.created_at ? new Date(card.created_at).toLocaleDateString() : ''}"` ].join(',')) ]; // Create and download the CSV file const csvContent = csvRows.join('\n'); const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }); const link = document.createElement('a'); if (link.download !== undefined) { const url = URL.createObjectURL(blob); link.setAttribute('href', url); link.setAttribute('download', `${collection.name || 'collection'}_cards.csv`); link.style.visibility = 'hidden'; document.body.appendChild(link); link.click(); document.body.removeChild(link); } }; // Group cards by game const groupedCards = cards.reduce((acc, card) => { const game = card.game || 'Other'; if (!acc[game]) acc[game] = []; acc[game].push(card); return acc; }, {}); // Debug: Log the grouped cards to see what games we have console.log('Grouped cards:', groupedCards); console.log('Available games:', Object.keys(groupedCards)); console.log('Sample card data:', cards[0]); // Check what fields the card actually has // Get game display names and counts - make it more flexible const gameStats = {}; Object.keys(groupedCards).forEach(game => { if (game && game !== 'Other') { gameStats[game] = groupedCards[game].length; } }); if (loading) { return ( ); } if (!collection) { return ( Collection not found Back to Collections ); } return ( {/* Header */} {/* Back button */} Back to collections {/* Action buttons */} setShowUploadModal(true)} className="px-4 py-2 text-sm font-medium border rounded-lg hover:bg-gray-50" style={{ borderColor: 'var(--border)', color: 'var(--text-primary)' }} > Upload Image 🪄 Generate AI Image {/* Collection Info */} {collection.name} {collection.description} {/* TCG Tags */} {Object.entries(gameStats).map(([game, count]) => count > 0 ? ( {game} ) : null )} {/* Creator and Stats */} {console.log('Collection data for facepile:', { id, creator_email: collection.creator_email, collection })} {collection.creator_email ? ( ) : ( Crafted by Unknown User )} Cards: {cards.length} Cost: ${collection.totalValue || '0'} Created {new Date(collection.created_at).toLocaleDateString()} Last updated {new Date(collection.updated_at).toLocaleDateString()} {/* Action Bar */} setShowShareModal(true)} className="flex items-center space-x-2 px-3 py-2 text-sm font-medium border rounded-lg hover:bg-gray-50" style={{ borderColor: 'var(--border)', color: 'var(--text-primary)' }} > Share Favorite Download List Activity 123 {/* Content */} {/* Search and Filters */} { setSearchCards(e.target.value); handleSearchCards(e.target.value); }} className="w-64 px-4 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500 focus:border-transparent" style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }} /> setSelectedRarity(e.target.value)} className="px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }} > All Rarities Common Uncommon Rare Mythic Legendary setSelectedType(e.target.value)} className="px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }} > All Types Creature Instant Sorcery Artifact Enchantment setGroupBy(e.target.value)} className="px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }} > Group by Game Group by Rarity Group by Type setSortBy(e.target.value)} className="px-3 py-2 border rounded-lg focus:ring-2 focus:ring-purple-500" style={{ borderColor: 'var(--border)', backgroundColor: 'var(--bg-secondary)' }} > Sort by Name Sort by Price Sort by Rarity Sort by Date Added setViewMode('grid')} className={`p-2 rounded-l-lg ${viewMode === 'grid' ? 'bg-white text-gray-900 shadow-sm' : 'bg-transparent hover:bg-gray-50'}`} style={viewMode !== 'grid' ? { color: 'var(--text-secondary)' } : {}} > setViewMode('list')} className={`p-2 rounded-r-lg ${viewMode === 'list' ? 'bg-white text-gray-900 shadow-sm' : 'bg-transparent hover:bg-gray-50'}`} style={viewMode !== 'list' ? { color: 'var(--text-secondary)' } : {}} > router.push('/cards')} className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 flex items-center space-x-2" > Add Cards Scan Cards {/* Game Sections */} {Object.entries(groupedCards).map(([game, gameCards]) => ( {game === 'MTG' ? 'Magic The Gathering' : game} {gameCards.length} {gameCards.map((card, index) => ( {card.image_url ? ( ) : ( 'Card' )} ))} {/* Add empty card slots to fill the row */} {Array.from({ length: Math.max(0, 7 - (gameCards.length % 7)) }, (_, index) => ( Card ))} ))} {cards.length === 0 && ( 🃏 Start Building Your Collection Add cards to get started with your collection Browse Cards to Add )} {/* Search Results Dropdown */} {showSearchResults && searchResults.length > 0 && ( {searchResults.map(card => ( handleAddCard(card)} className="flex items-center p-3 hover:bg-gray-50 cursor-pointer" > { e.target.src = 'https://via.placeholder.com/48x64/6366f1/ffffff?text=No+Image'; }} /> {card.name} {card.set_name} • ${card.market_price} ))} )} {/* Upload Image Modal */} setShowUploadModal(false)} onUpload={handleImageUpload} currentImage={collection.image} /> {/* Share Modal */} setShowShareModal(false)} collectionId={id} isPublic={collection.is_public} onTogglePublic={togglePublic} onInviteUser={(email) => console.log('Invited:', email)} /> ); }
{collection.description}
Add cards to get started with your collection