diff --git a/pages/collection/[id].js b/pages/collection/[id].js index 7dd3e15..130ac06 100644 --- a/pages/collection/[id].js +++ b/pages/collection/[id].js @@ -1,5 +1,6 @@ import { useState, useEffect } from 'react'; import { useRouter } from 'next/router'; +import Link from 'next/link'; import CollaborationManager from '../../components/CollaborationManager'; import Layout from '../../components/Layout'; @@ -19,19 +20,19 @@ export default function CollectionView() { 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'); - const [selectedType, setSelectedType] = useState('all'); - const [sortBy, setSortBy] = useState('name'); - const [viewMode, setViewMode] = useState('grid'); // grid or list + 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); - - useEffect(() => { if (id) { fetchCollectionData(); @@ -59,28 +60,8 @@ export default function CollectionView() { } }; - const handleCopyLink = async () => { - try { - await navigator.clipboard.writeText(window.location.href); - setCopySuccess(true); - setTimeout(() => setCopySuccess(false), 2000); - } catch (err) { - console.error('Failed to copy link:', err); - } - }; - - const handleFavorite = () => { - setIsFavorited(!isFavorited); - // Here you would typically make an API call - }; - - const handleSaveCopy = () => { - // Logic to save a copy to user's collections - console.log('Saving copy of collection'); - }; - const handleSearchCards = async (query) => { - if (!query.trim()) { + if (query.length < 2) { setSearchResults([]); setShowSearchResults(false); return; @@ -89,8 +70,8 @@ export default function CollectionView() { try { const response = await fetch(`/api/cards/search?q=${encodeURIComponent(query)}&limit=10`); if (response.ok) { - const results = await response.json(); - setSearchResults(results); + const data = await response.json(); + setSearchResults(data.cards || []); setShowSearchResults(true); } } catch (error) { @@ -98,65 +79,86 @@ export default function CollectionView() { } }; - const handleAddCard = async (card, quantity = 1) => { + 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 + quantity: 1 }) }); if (response.ok) { - // Refresh collection data - fetchCollectionData(); setSearchCards(''); - setSearchResults([]); setShowSearchResults(false); - } else { - const error = await response.json(); - alert(error.error || 'Failed to add card'); + fetchCollectionData(); // Refresh the collection data } } catch (error) { console.error('Error adding card:', error); - alert('Network error. Please try again.'); } }; - const filteredCards = cards.filter(card => { - const matchesSearch = card.name.toLowerCase().includes(searchQuery.toLowerCase()); - const matchesRarity = selectedRarity === 'all' || card.rarity === selectedRarity; - const matchesType = selectedType === 'all' || card.card_type === selectedType; - return matchesSearch && matchesRarity && matchesType; - }); + const handleShare = () => { + const url = window.location.href; + navigator.clipboard.writeText(url).then(() => { + setCopySuccess(true); + setTimeout(() => setCopySuccess(false), 2000); + }); + }; - const sortedCards = [...filteredCards].sort((a, b) => { - switch (sortBy) { - case 'name': - return a.name.localeCompare(b.name); - case 'cost': - return b.market_price - a.market_price; - case 'rarity': - return a.rarity.localeCompare(b.rarity); - case 'type': - return a.card_type.localeCompare(b.card_type); - default: - return 0; + const toggleFavorite = () => { + setIsFavorited(!isFavorited); + }; + + 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 totalValue = cards.reduce((sum, card) => sum + (card.market_price * card.quantity), 0); - const totalCards = cards.reduce((sum, card) => sum + card.quantity, 0); + // 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; + }, {}); + + // Get game display names and counts + const gameStats = { + 'MTG': groupedCards['MTG']?.length || 0, + 'Lorcana': groupedCards['Lorcana']?.length || 0, + 'Pokemon': groupedCards['Pokemon']?.length || 0 + }; if (loading) { return (
-
+
); @@ -170,12 +172,11 @@ export default function CollectionView() {

Collection not found

- + + + @@ -184,227 +185,225 @@ export default function CollectionView() { return ( - {/* Hero Section with Collection Info */} -
-
-
-
-
🃏
-
-
-

- {collection.name} -

-
-
-
- 👤 -
- Created by {collection.creator_email} -
- - TCG: {collection.tcg} - - Value: ${totalValue.toFixed(2)} - - Cards: {totalCards} +
+ {/* Header */} +
+
+ {/* Back button and TCG tabs */} +
+ + + + + {/* TCG Tabs */} +
+ {['MTG', 'Lorcana', 'Pokemon'].map((tcg) => ( + + ))}
-
- -
- Created {new Date(collection.created_at).toLocaleDateString()} - - Last updated {new Date(collection.updated_at).toLocaleDateString()} - {collection.is_public && ( - <> - - - 🌍 Public - - - )} -
-
-
- {/* Action Buttons */} -
-
-
+ {/* Action buttons */}
- + +
+
+ {/* Collection Info */} +
+

+ {collection.name} +

+

+ {collection.description} +

+ + {/* Creator and Stats */} +
+
+ Crafted by +
+
+ + {collection.creator_email?.charAt(0).toUpperCase()} + +
+ {collection.creator_email} +
+
+
Cards: {cards.length}
+
Cost: ${collection.totalValue || '0'}
+
+ +
+ Created {new Date(collection.created_at).toLocaleDateString()} + Last updated {new Date(collection.updated_at).toLocaleDateString()} +
+
+ + {/* Action Bar */} +
+
- + + +
- -
-
-
- - {/* Stats and Filters */} -
- {/* Collection Stats */} -
-
-
{totalCards}
-
Total Cards
-
-
-
${totalValue.toFixed(2)}
-
Total Value
-
-
-
{collection.tcg}
-
Game
-
-
-
{collection.is_public ? 'Public' : 'Private'}
-
Visibility
-
-
- - {/* Collaboration Manager */} - - - {/* Filters */} -
-
-
- setSearchQuery(e.target.value)} - /> +
+ + +
+ Public + +
+ +
+ 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)' }} + /> + + + +
+ - + - + + + +
+
+ + + +
-
- {/* Cards Display */} -
-

- Cards ({sortedCards.length}) -

-
- - {viewMode === 'grid' ? ( -
- {sortedCards.map(card => ( -
router.push(`/card/${card.id}`)} - > -
- {card.name} { - e.target.src = 'https://via.placeholder.com/250x350/6366f1/ffffff?text=No+Image'; - }} - /> - {card.quantity > 1 && ( -
- {card.quantity}x -
- )} -
-
- {card.name} -
-
- ${card.market_price} -
-
-
+ {/* Game Sections */} + {Object.entries(groupedCards).map(([game, gameCards]) => ( +
+
+

+ {game === 'MTG' ? 'Magic The Gathering' : game} + + {gameCards.length} + +

- ))} -
- ) : ( -
- {sortedCards.map(card => ( -
+ {gameCards.map((card, index) => ( +
+ {card.image_url ? ( + {card.name} + ) : ( + '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 +

+ +
+ )} +
+ + {/* Search Results Dropdown */} + {showSearchResults && searchResults.length > 0 && ( +
+ {searchResults.map(card => ( +
router.push(`/card/${card.id}`)} + onClick={() => handleAddCard(card)} + className="flex items-center p-3 hover:bg-gray-50 cursor-pointer" > {card.name} { - e.target.src = 'https://via.placeholder.com/64x88/6366f1/ffffff?text=No+Image'; + e.target.src = 'https://via.placeholder.com/48x64/6366f1/ffffff?text=No+Image'; }} /> -
-

- {card.name} -

-
- {card.set_name} • {card.rarity} • {card.card_type} -
+
+
{card.name}
+
{card.set_name} • ${card.market_price}
-
-
- ${card.market_price} -
-
- Qty: {card.quantity} -
-
))}
)} - {cards.length === 0 ? ( - // Empty collection state -
-
🃏
-

- Start Building Your Collection -

-

- This collection is empty. Add your first cards to get started! -

-
- -
- Or search for specific cards to add to your collection -
-
- - {/* Quick add section */} -
-

- Quick Add -

-
- { - setSearchCards(e.target.value); - handleSearchCards(e.target.value); - }} - className="w-full px-4 py-3 rounded-lg border transition-all duration-200" - style={{ - backgroundColor: 'var(--bg-primary)', - borderColor: 'var(--border)', - color: 'var(--text-primary)' - }} - /> - - {/* Search Results */} - {showSearchResults && searchResults.length > 0 && ( -
- {searchResults.map((card) => ( -
handleAddCard(card)} - > - {card.name} { - e.target.src = 'https://via.placeholder.com/40x56/6366f1/ffffff?text=?'; - }} - /> -
-
{card.name}
-
{card.set_name} • {card.rarity}
-
-
-
${card.market_price}
-
{card.game}
-
-
- ))} -
- )} -
-
-
- ) : sortedCards.length === 0 ? ( - // No results for current filters -
-
🔍
-

- No cards match your filters -

-

- Try adjusting your search or filter criteria -

- -
- ) : null} -
- - {/* Share Modal */} - {showShareModal && ( -
-
-

- Share Collection -

-
-
- -
- - -
-
-
-
- -
-
+ {/* Collaboration Manager */} +
+
- )} +
); } \ No newline at end of file