diff --git a/components/CollectionSelectionModal.js b/components/CollectionSelectionModal.js
new file mode 100644
index 0000000..eb7380a
--- /dev/null
+++ b/components/CollectionSelectionModal.js
@@ -0,0 +1,346 @@
+import { useState, useEffect } from 'react';
+
+export default function CollectionSelectionModal({
+ isOpen,
+ onClose,
+ cards, // Array of cards to add
+ onAddToCollections
+}) {
+ const [collections, setCollections] = useState([]);
+ const [selectedCollections, setSelectedCollections] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [submitting, setSubmitting] = useState(false);
+ const [searchQuery, setSearchQuery] = useState('');
+
+ useEffect(() => {
+ if (isOpen) {
+ fetchCollections();
+ setSelectedCollections([]);
+ setSearchQuery('');
+ }
+ }, [isOpen]);
+
+ const fetchCollections = async () => {
+ setLoading(true);
+ try {
+ const response = await fetch('/api/collections', {
+ headers: {
+ 'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
+ }
+ });
+
+ if (response.ok) {
+ const data = await response.json();
+ setCollections(data.collections || []);
+ } else {
+ console.error('Failed to fetch collections');
+ }
+ } catch (error) {
+ console.error('Error fetching collections:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const handleCollectionToggle = (collectionId) => {
+ setSelectedCollections(prev => {
+ if (prev.includes(collectionId)) {
+ return prev.filter(id => id !== collectionId);
+ } else {
+ return [...prev, collectionId];
+ }
+ });
+ };
+
+ const handleSelectAll = () => {
+ const filteredCollections = getFilteredCollections();
+ const allSelected = filteredCollections.every(collection =>
+ selectedCollections.includes(collection.id)
+ );
+
+ if (allSelected) {
+ // Deselect all filtered collections
+ setSelectedCollections(prev =>
+ prev.filter(id => !filteredCollections.some(c => c.id === id))
+ );
+ } else {
+ // Select all filtered collections
+ const newSelections = filteredCollections
+ .filter(collection => !selectedCollections.includes(collection.id))
+ .map(collection => collection.id);
+ setSelectedCollections(prev => [...prev, ...newSelections]);
+ }
+ };
+
+ const getFilteredCollections = () => {
+ if (!searchQuery.trim()) return collections;
+
+ return collections.filter(collection =>
+ collection.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
+ collection.description?.toLowerCase().includes(searchQuery.toLowerCase())
+ );
+ };
+
+ const handleSubmit = async () => {
+ if (selectedCollections.length === 0) return;
+
+ setSubmitting(true);
+ try {
+ const results = [];
+
+ // Add cards to each selected collection
+ for (const collectionId of selectedCollections) {
+ for (const card of cards) {
+ const response = await fetch(`/api/collections/${collectionId}/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) {
+ results.push({ collectionId, cardId: card.id, success: true });
+ } else {
+ results.push({ collectionId, cardId: card.id, success: false });
+ }
+ }
+ }
+
+ // Call callback with results
+ onAddToCollections(results, selectedCollections, cards);
+ onClose();
+ } catch (error) {
+ console.error('Error adding cards to collections:', error);
+ } finally {
+ setSubmitting(false);
+ }
+ };
+
+ const filteredCollections = getFilteredCollections();
+ const allFilteredSelected = filteredCollections.length > 0 &&
+ filteredCollections.every(collection => selectedCollections.includes(collection.id));
+
+ if (!isOpen) return null;
+
+ return (
+
+
+ {/* Header */}
+
+
+
+ Add to Collections
+
+
+
+
+ {/* Card Summary */}
+
+
+ {cards.slice(0, 3).map((card, index) => (
+
+ {card.image_url ? (
+

+ ) : (
+
+ No Image
+
+ )}
+
+ ))}
+ {cards.length > 3 && (
+
+
+ +{cards.length - 3}
+
+
+ )}
+
+
+
+ {cards.length} card{cards.length !== 1 ? 's' : ''} selected
+
+
+ Choose collections to add {cards.length === 1 ? 'this card' : 'these cards'} to
+
+
+
+
+ {/* Search */}
+
+
setSearchQuery(e.target.value)}
+ className="w-full px-4 py-2 pl-10 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white"
+ />
+
+
+
+
+ {/* Collections List */}
+
+ {loading ? (
+
+
+
Loading collections...
+
+ ) : filteredCollections.length === 0 ? (
+
+
+
+ {searchQuery ? 'No collections match your search' : 'No collections found'}
+
+
+ {searchQuery ? 'Try a different search term' : 'Create your first collection to get started'}
+
+
+ ) : (
+
+ {/* Select All */}
+ {filteredCollections.length > 1 && (
+
+
+
+ )}
+
+ {/* Collection Items */}
+
+ {filteredCollections.map((collection) => (
+
handleCollectionToggle(collection.id)}
+ >
+
+
handleCollectionToggle(collection.id)}
+ className="w-4 h-4 text-blue-600 bg-gray-100 dark:bg-gray-700 border-gray-300 dark:border-gray-600 rounded focus:ring-blue-500 focus:ring-2"
+ />
+
+ {/* Collection Image */}
+
+ {collection.image ? (
+

+ ) : (
+
+ )}
+
+
+ {/* Collection Info */}
+
+
+ {collection.name}
+
+ {collection.description && (
+
+ {collection.description}
+
+ )}
+
+
+ {collection.card_count || 0} cards
+
+ {collection.is_public && (
+
+ Public
+
+ )}
+
+
+
+
+ ))}
+
+
+ )}
+
+
+ {/* Footer */}
+
+
+
+ {selectedCollections.length > 0 && (
+
+ {selectedCollections.length} collection{selectedCollections.length !== 1 ? 's' : ''} selected
+
+ )}
+
+
+
+
+
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/pages/card/[id].js b/pages/card/[id].js
index 4367e9a..aefb234 100644
--- a/pages/card/[id].js
+++ b/pages/card/[id].js
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import Layout from '../../components/Layout';
import { useIsAdmin } from '../../lib/admin-auth';
+import CollectionSelectionModal from '../../components/CollectionSelectionModal';
export default function CardDetail() {
const router = useRouter();
@@ -693,66 +694,30 @@ export default function CardDetail() {
)}
- {/* Collection Modal */}
- {showCollectionModal && (
-
-
-
- Add to Collection
-
-
-
-
-
-
-
-
-
-
-
- )}
+ {/* Collection Selection Modal */}
+ setShowCollectionModal(false)}
+ cards={card ? [card] : []}
+ onAddToCollections={(results, selectedCollectionIds, cards) => {
+ const successCount = results.filter(r => r.success).length;
+ if (successCount > 0) {
+ // Refresh card collections
+ const fetchCardCollections = async () => {
+ try {
+ const response = await fetch(`/api/cards/${id}/collections`);
+ if (response.ok) {
+ const data = await response.json();
+ setCardCollections(data);
+ }
+ } catch (error) {
+ console.error('Error refreshing card collections:', error);
+ }
+ };
+ fetchCardCollections();
+ }
+ }}
+ />
{/* Deck Modal */}
{showDeckModal && (
diff --git a/pages/cards.js b/pages/cards.js
index 8f39d26..a8478ea 100644
--- a/pages/cards.js
+++ b/pages/cards.js
@@ -3,6 +3,7 @@ import { useRouter } from 'next/router';
import Layout from '../components/Layout';
import CardItem from '../components/CardItem';
import BulkSelectionToolbar from '../components/BulkSelectionToolbar';
+import CollectionSelectionModal from '../components/CollectionSelectionModal';
export default function Cards() {
const router = useRouter();
@@ -38,6 +39,10 @@ export default function Cards() {
// Bulk selection state
const [selectedCards, setSelectedCards] = useState([]);
const [favoritedCards, setFavoritedCards] = useState(new Set());
+
+ // Modal states
+ const [showCollectionModal, setShowCollectionModal] = useState(false);
+ const [cardsToAdd, setCardsToAdd] = useState([]);
// Fetch cards from database
const fetchCards = async (isLoadMore = false) => {
@@ -355,8 +360,8 @@ export default function Cards() {
// Bulk action handlers
const handleBulkAddToCollection = (cards) => {
- console.log('Adding to collection:', cards);
- alert(`Adding ${cards.length} cards to collection (functionality coming soon)`);
+ setCardsToAdd(cards);
+ setShowCollectionModal(true);
};
const handleBulkAddToDeck = (cards) => {
@@ -409,6 +414,21 @@ export default function Cards() {
alert(`Bulk delete functionality coming soon for ${cards.length} cards`);
};
+ // Collection modal handlers
+ const handleAddToCollections = (results, selectedCollectionIds, cards) => {
+ const successCount = results.filter(r => r.success).length;
+ const totalAttempts = results.length;
+
+ if (successCount === totalAttempts) {
+ alert(`Successfully added ${cards.length} card${cards.length !== 1 ? 's' : ''} to ${selectedCollectionIds.length} collection${selectedCollectionIds.length !== 1 ? 's' : ''}!`);
+ } else {
+ alert(`Added ${successCount} out of ${totalAttempts} cards. Some additions may have failed.`);
+ }
+
+ // Clear selection after successful addition
+ setSelectedCards([]);
+ };
+
if (loading && cards.length === 0) {
return (
@@ -707,7 +727,7 @@ export default function Cards() {
viewMode={viewMode}
isSelected={selectedCards.some(c => c.id === card.id)}
onToggleSelect={handleToggleSelect}
- onAddToCollection={handleBulkAddToCollection}
+ onAddToCollection={(card) => handleBulkAddToCollection([card])}
onAddToDeck={handleBulkAddToDeck}
onToggleFavorite={handleToggleFavorite}
isFavorited={favoritedCards.has(card.id)}
@@ -763,6 +783,14 @@ export default function Cards() {
onBulkFavorite={handleBulkFavorite}
onBulkDelete={handleBulkDelete}
/>
+
+ {/* Collection Selection Modal */}
+ setShowCollectionModal(false)}
+ cards={cardsToAdd}
+ onAddToCollections={handleAddToCollections}
+ />
);
}