import { useState, useEffect, useMemo } from 'react'; import { useRouter } from 'next/router'; import { VOCAB, collectionDisplayName } from './collection-vocabulary.js'; /** * Card/collection page state and handlers (god-component split). */ export function useCardDetail({ user, authLoading }) { const router = useRouter(); const isAdmin = user?.role === 'admin'; const adminLoading = authLoading; const { id } = router.query; const [card, setCard] = useState(null); const [loading, setLoading] = useState(true); const [activeTab, setActiveTab] = useState('details'); const [ownedQuantity, setOwnedQuantity] = useState(0); const [showQuantityModal, setShowQuantityModal] = useState(false); const [showCollectionModal, setShowCollectionModal] = useState(false); const [showDeckModal, setShowDeckModal] = useState(false); const [selectedCollection, setSelectedCollection] = useState(''); const [selectedDeck, setSelectedDeck] = useState(''); const [quantity, setQuantity] = useState(1); const [isFavorited, setIsFavorited] = useState(false); const [collections, setCollections] = useState([]); const [decks, setDecks] = useState([]); const [cardCollections, setCardCollections] = useState([]); const [cardDecks, setCardDecks] = useState([]); // Mana symbol settings const [manaSymbolSettings, setManaSymbolSettings] = useState({ useSVG: false }); // Fetch card data from API useEffect(() => { const fetchCard = async () => { if (!id) return; try { const response = await fetch(`/api/cards/${id}`); if (response.ok) { const cardData = await response.json(); setCard(cardData); // Fetch user's ownership of this card const token = localStorage.getItem('auth_token'); if (token) { try { const ownershipResponse = await fetch(`/api/cards/${id}/ownership`, { headers: { 'Authorization': `Bearer ${token}` } }); if (ownershipResponse.ok) { const ownershipData = await ownershipResponse.json(); setOwnedQuantity(ownershipData.quantity || 0); } } catch (error) { console.error('Error fetching ownership:', error); } // Check if card is favorited try { const favoritesResponse = await fetch(`/api/favorites?type=card`, { headers: { 'Authorization': `Bearer ${token}` } }); if (favoritesResponse.ok) { const favoritesData = await favoritesResponse.json(); const isCardFavorited = favoritesData.favorites.some(fav => fav.item_id == id); setIsFavorited(isCardFavorited); } } catch (error) { console.error('Error checking favorites:', error); } } } else { console.error('Failed to fetch card'); } } catch (error) { console.error('Error fetching card:', error); } finally { setLoading(false); } }; fetchCard(); }, [id]); // Fetch user's collections and decks useEffect(() => { const fetchUserData = async () => { try { const token = localStorage.getItem('auth_token'); const headers = { 'Authorization': `Bearer ${token}` }; // Fetch collections const collectionsResponse = await fetch('/api/collections', { headers }); if (collectionsResponse.ok) { const collectionsData = await collectionsResponse.json(); setCollections(collectionsData); } // Fetch decks const decksResponse = await fetch('/api/decks', { headers }); if (decksResponse.ok) { const decksData = await decksResponse.json(); setDecks(decksData); } // Fetch card's current collections and decks if (card) { const cardCollectionsResponse = await fetch(`/api/cards/${id}/collections`, { headers }); if (cardCollectionsResponse.ok) { const cardCollectionsData = await cardCollectionsResponse.json(); setCardCollections(cardCollectionsData); } const cardDecksResponse = await fetch(`/api/cards/${id}/decks`, { headers }); if (cardDecksResponse.ok) { const cardDecksData = await cardDecksResponse.json(); setCardDecks(cardDecksData); } } } catch (error) { console.error('Error fetching user data:', error); } }; if (card) { fetchUserData(); } }, [card, id]); const getRarityGradient = (rarity) => { const rarityKey = rarity?.toLowerCase(); const gradients = { 'common': '#9ca3af, #6b7280, #4b5563', // Subtle gray glow 'uncommon': '#10b981, #059669, #047857', // Green glow 'rare': '#f59e0b, #d97706, #b45309', // Gold glow 'mythic': '#fbbf24, #f59e0b, #d97706', // Rich gold glow 'holographic': '#ec4899, #db2777, #be185d', // Pink glow 'enchanted': '#a855f7, #9333ea, #7c3aed', // Purple glow 'super rare': '#3b82f6, #2563eb, #1d4ed8', // Blue glow 'legendary': '#fbbf24, #f59e0b, #ea580c' // Vibrant gold-orange glow }; return gradients[rarityKey] || '#6b7280, #4b5563, #374151'; }; const getParticleCount = (rarity) => { const rarityKey = rarity?.toLowerCase(); const particleCounts = { 'common': 0, 'uncommon': 15, 'rare': 25, 'mythic': 40, 'holographic': 50, 'enchanted': 60, 'super rare': 45, 'legendary': 80 }; return particleCounts[rarityKey] || 0; }; const particleStyles = useMemo(() => { if (!card) return []; const count = getParticleCount(card.rarity); return Array.from({ length: count }, (_, i) => ({ left: `${((i * 37) % 100)}%`, top: `${((i * 53) % 100)}%`, animationDelay: `${i % 3}s`, animationDuration: `${3 + (i % 2)}s`, })); // eslint-disable-next-line react-hooks/exhaustive-deps -- stable decorative layout per card id/rarity }, [card?.id, card?.rarity]); const getParticleColor = (rarity) => { const rarityKey = rarity?.toLowerCase(); const colors = { 'common': '#ffffff', 'uncommon': '#10b981', 'rare': '#f59e0b', 'mythic': '#ffd700', 'holographic': '#ff6b6b', 'enchanted': '#a855f7', 'super rare': '#3b82f6', 'legendary': '#ffd700' }; return colors[rarityKey] || '#ffffff'; }; const getTCGIcon = (game) => { const icons = { 'MTG': '🔮', 'Pokemon': '⚡', 'Lorcana': '✨' }; return icons[game] || '🃏'; }; const formatCurrency = (amount) => { if (!amount) return '$0.00'; return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(amount); }; const getRarityLabel = (rarity) => { const rarityMap = { 'common': 'Common', 'uncommon': 'Uncommon', 'rare': 'Rare', 'mythic': 'Mythic', 'holographic': 'Holographic', 'enchanted': 'Enchanted', 'super rare': 'Super Rare', 'legendary': 'Legendary' }; return rarityMap[rarity?.toLowerCase()] || rarity; }; const getRarityColor = (rarity) => { const colors = { 'common': '#6B7280', 'uncommon': '#10B981', 'rare': '#F59E0B', 'mythic': '#FFD700', 'holographic': '#FF6B6B', 'enchanted': '#A855F7', 'super rare': '#3B82F6', 'legendary': '#FFD700' }; return colors[rarity?.toLowerCase()] || '#6B7280'; }; const handleOwnershipUpdate = async (newQuantity) => { try { const token = localStorage.getItem('auth_token'); const response = await fetch(`/api/cards/${id}/ownership`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ quantity: newQuantity }) }); if (response.ok) { setOwnedQuantity(newQuantity); setShowQuantityModal(false); } else { console.error('Failed to update ownership'); } } catch (error) { console.error('Error updating ownership:', error); } }; const handleAddToCollection = async () => { try { const token = localStorage.getItem('auth_token'); const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }; const response = await fetch(`/api/cards/${id}/collections`, { method: 'POST', headers, body: JSON.stringify({ collectionId: selectedCollection }) }); if (response.ok) { // Refresh card collections const cardCollectionsResponse = await fetch(`/api/cards/${id}/collections`, { headers: { 'Authorization': `Bearer ${token}` } }); if (cardCollectionsResponse.ok) { const cardCollectionsData = await cardCollectionsResponse.json(); setCardCollections(cardCollectionsData); } setShowCollectionModal(false); setSelectedCollection(''); } else { console.error('Failed to add to collection'); } } catch (error) { console.error('Error adding to collection:', error); } }; const handleAddToDeck = async () => { try { const token = localStorage.getItem('auth_token'); const headers = { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }; const response = await fetch(`/api/cards/${id}/decks`, { method: 'POST', headers, body: JSON.stringify({ deckId: selectedDeck }) }); if (response.ok) { // Refresh card decks const cardDecksResponse = await fetch(`/api/cards/${id}/decks`, { headers: { 'Authorization': `Bearer ${token}` } }); if (cardDecksResponse.ok) { const cardDecksData = await cardDecksResponse.json(); setCardDecks(cardDecksData); } setShowDeckModal(false); setSelectedDeck(''); } else { console.error('Failed to add to deck'); } } catch (error) { console.error('Error adding to deck:', error); } }; const handleToggleFavorite = async () => { try { const token = localStorage.getItem('auth_token'); const response = await fetch(`/api/cards/${id}/favorite`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ favorited: !isFavorited }) }); if (response.ok) { setIsFavorited(!isFavorited); } else { console.error('Failed to toggle favorite'); } } catch (error) { console.error('Error toggling favorite:', error); } }; const showInitialLoading = authLoading || loading; return { activeTab, card, cardCollections, cardDecks, collections, decks, formatCurrency, getParticleColor, getParticleCount, getRarityColor, getRarityGradient, getRarityLabel, getTCGIcon, handleAddToCollection, handleAddToDeck, handleOwnershipUpdate, handleToggleFavorite, id, isAdmin, adminLoading, isFavorited, loading, manaSymbolSettings, ownedQuantity, particleStyles, getParticleCount, quantity, router, selectedCollection, selectedDeck, setActiveTab, setCard, setCardCollections, setCardDecks, setCollections, setDecks, setIsFavorited, setLoading, setManaSymbolSettings, setOwnedQuantity, setQuantity, setSelectedCollection, setSelectedDeck, setShowCollectionModal, setShowDeckModal, setShowQuantityModal, showCollectionModal, showDeckModal, showInitialLoading, showQuantityModal }; }