deckhearth/lib/use-card-detail.js

401 lines
12 KiB
JavaScript
Raw Permalink Normal View History

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 = null, authLoading = true } = {}) {
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
};
}