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 */}
+
+
setShowShareModal(true)}
- className="flex items-center space-x-2 px-4 py-2 rounded-lg border transition-all duration-200 hover:shadow-md"
- style={{
- backgroundColor: 'var(--bg-primary)',
- borderColor: 'var(--border)',
- color: 'var(--text-primary)'
- }}
+ onClick={handleShare}
+ 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)' }}
>
- Copy list
+ Share
-
+
-
+
+
+
+
+
+ Download List
-
-
-
-
- Save deck
-
-
-
-
-
- {/* 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)}
- />
+
+
+ 📧 Invite Collaborator
+
+
+
+ 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)' }}
+ />
+
+
+
+
+
-
+
-
+
+
+
+
+
setViewMode('grid')}
- className={`p-3 rounded-l-lg transition-all duration-200 ${
- viewMode === 'grid'
- ? 'gradient-bg-purple text-white'
- : 'bg-transparent'
- }`}
+ className={`p-2 rounded-l-lg ${viewMode === 'grid' ? 'bg-purple-600 text-white' : 'bg-transparent'}`}
style={viewMode !== 'grid' ? { color: 'var(--text-secondary)' } : {}}
>
@@ -413,11 +412,7 @@ export default function CollectionView() {
setViewMode('list')}
- className={`p-3 rounded-r-lg transition-all duration-200 ${
- viewMode === 'list'
- ? 'gradient-bg-purple text-white'
- : 'bg-transparent'
- }`}
+ className={`p-2 rounded-r-lg ${viewMode === 'list' ? 'bg-purple-600 text-white' : 'bg-transparent'}`}
style={viewMode !== 'list' ? { color: 'var(--text-secondary)' } : {}}
>
@@ -425,248 +420,112 @@ export default function CollectionView() {
+
+
+
+
+
+ Add Cards
+
+
+
+
+
+
+
+ Scan Cards
+
-
- {/* Cards Display */}
-
-
- Cards ({sortedCards.length})
-
-
-
- {viewMode === 'grid' ? (
-
- {sortedCards.map(card => (
-
router.push(`/card/${card.id}`)}
- >
-
-

{
- 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'
+ )}
+
+ ))}
+
+ {/* 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 => (
+
router.push(`/card/${card.id}`)}
+ onClick={() => handleAddCard(card)}
+ className="flex items-center p-3 hover:bg-gray-50 cursor-pointer"
>

{
- 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!
-
-
-
router.push('/cards')}
- className="inline-flex items-center space-x-2 px-8 py-4 rounded-xl font-medium gradient-bg-purple text-white hover:shadow-lg transform hover:scale-105 transition-all duration-200"
- >
-
-
-
- Browse Cards to Add
-
-
- 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)}
- >
-

{
- 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
-
-
{
- setSearchQuery('');
- setSelectedRarity('all');
- setSelectedType('all');
- }}
- className="mt-4 px-4 py-2 rounded-lg border transition-all duration-200"
- style={{
- backgroundColor: 'var(--bg-primary)',
- borderColor: 'var(--border)',
- color: 'var(--text-primary)'
- }}
- >
- Clear All Filters
-
-
- ) : null}
-
-
- {/* Share Modal */}
- {showShareModal && (
-
-
-
- Share Collection
-
-
-
-
-
-
-
- Copy
-
-
-
-
-
- setShowShareModal(false)}
- className="flex-1 py-2 px-4 rounded-xl border transition-colors"
- style={{
- borderColor: 'var(--border)',
- color: 'var(--text-secondary)'
- }}
- >
- Close
-
-
-
+ {/* Collaboration Manager */}
+
+
- )}
+
);
}
\ No newline at end of file