From af7593d884f48cd8d5f22cae2b1641aa676c6169 Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Fri, 25 Jul 2025 10:06:39 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Enhanced=20Collections=20UI=20with?= =?UTF-8?q?=20API=20Integration?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit šŸŽÆ Collections Page Improvements: - Removed TCG selection from creation modal - Added image URL field for collection hero images - Changed public checkbox to visibility dropdown (Private/Invite-Only/Public) - Added success modal with navigation to created collection - Integrated real API calls for creating and fetching collections - Added Permission indicators throughout the interface šŸƒ Collection Detail Page Enhancements: - Created comprehensive empty state for new collections - Added 'Browse Cards to Add' call-to-action button - Included quick add search functionality - Improved filtered results empty state with clear filters option - Integrated API calls for real collection data - Distinguished between empty collection vs no search results šŸ—„ļø Database & API Updates: - Added image column to collections table - Updated collections API to handle image field - Enhanced API to return proper collection data structure - Added fallback to mock data for development šŸŽØ User Experience: - Beautiful success confirmation after collection creation - Direct navigation to newly created collection - Clear visual distinction between different empty states - Intuitive call-to-action buttons for collection building - Permission badges visible on collection cards Ready for users to create collections with images and start building their card collections! šŸš€ --- pages/api/collections.js | 6 +- pages/collection/[id].js | 105 ++++++++++++++++++++++-- pages/collections.js | 158 +++++++++++++++++++++++++++--------- scripts/add-image-column.js | 29 +++++++ 4 files changed, 249 insertions(+), 49 deletions(-) create mode 100755 scripts/add-image-column.js diff --git a/pages/api/collections.js b/pages/api/collections.js index bf17d96..2cb1603 100644 --- a/pages/api/collections.js +++ b/pages/api/collections.js @@ -61,7 +61,7 @@ export default async function handler(req, res) { } } else if (req.method === 'POST') { try { - const { name, description, tcg = 'MTG', visibility = 'private', tags = [] } = req.body; + const { name, description, tcg = 'MTG', visibility = 'private', image = '', tags = [] } = req.body; if (!name || !description) { return res.status(400).json({ error: 'Name and description are required' }); @@ -75,8 +75,8 @@ export default async function handler(req, res) { const userId = 1; const result = await sql` - INSERT INTO collections (name, description, tcg, visibility, tags, user_id) - VALUES (${name}, ${description}, ${tcg}, ${visibility}, ${tags.join(',')}, ${userId}) + INSERT INTO collections (name, description, tcg, visibility, image, tags, user_id) + VALUES (${name}, ${description}, ${tcg}, ${visibility}, ${image}, ${tags.join(',')}, ${userId}) RETURNING * `; diff --git a/pages/collection/[id].js b/pages/collection/[id].js index 9865055..27550d8 100644 --- a/pages/collection/[id].js +++ b/pages/collection/[id].js @@ -114,15 +114,33 @@ export default function CollectionView() { useEffect(() => { if (id) { - // Simulate API call - setTimeout(() => { - setCollection(mockCollection); - setCards(mockCards); - setLoading(false); - }, 500); + fetchCollectionData(); } }, [id]); + const fetchCollectionData = async () => { + try { + const response = await fetch(`/api/collections/${id}`); + if (response.ok) { + const data = await response.json(); + setCollection(data.collection); + setCards(data.cards || []); + } else { + console.error('Failed to fetch collection'); + // Fallback to mock data for now + setCollection(mockCollection); + setCards(mockCards); + } + } catch (error) { + console.error('Error fetching collection:', error); + // Fallback to mock data for now + setCollection(mockCollection); + setCards(mockCards); + } finally { + setLoading(false); + } + }; + const handleCopyLink = async () => { try { await navigator.clipboard.writeText(window.location.href); @@ -514,17 +532,86 @@ export default function CollectionView() { )} - {sortedCards.length === 0 && ( + {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 +

+
+ + +
+
+
+ ) : sortedCards.length === 0 ? ( + // No results for current filters
šŸ”

- No cards found + No cards match your filters

Try adjusting your search or filter criteria

+
- )} + ) : null} {/* Share Modal */} diff --git a/pages/collections.js b/pages/collections.js index 4d0fdb2..133be40 100644 --- a/pages/collections.js +++ b/pages/collections.js @@ -86,14 +86,33 @@ export default function Collections() { const [newCollection, setNewCollection] = useState({ name: '', description: '', - tcg: 'MTG', - isPublic: false, + visibility: 'private', + image: '', tags: [] }); + const [showSuccessModal, setShowSuccessModal] = useState(false); + const [createdCollection, setCreatedCollection] = useState(null); + + const fetchCollections = async () => { + try { + const response = await fetch('/api/collections'); + if (response.ok) { + const data = await response.json(); + setCollections(data); + } else { + console.error('Failed to fetch collections'); + setCollections(sampleCollections); // Fallback to sample data + } + } catch (error) { + console.error('Error fetching collections:', error); + setCollections(sampleCollections); // Fallback to sample data + } finally { + setLoading(false); + } + }; useEffect(() => { - setCollections(sampleCollections); - setLoading(false); + fetchCollections(); }, []); const tcgOptions = [ @@ -119,19 +138,47 @@ export default function Collections() { return acc; }, {}); - const handleCreateCollection = () => { - const newId = Math.max(...collections.map(c => c.id)) + 1; - const collection = { - ...newCollection, - id: newId, - cardCount: 0, - value: 0, - lastViewed: new Date().toISOString().split('T')[0], - createdAt: new Date().toISOString().split('T')[0] - }; - setCollections([collection, ...collections]); - setNewCollection({ name: '', description: '', tcg: 'MTG', isPublic: false, tags: [] }); - setShowCreateModal(false); + const handleCreateCollection = async () => { + try { + const response = await fetch('/api/collections', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: newCollection.name, + description: newCollection.description, + visibility: newCollection.visibility, + image: newCollection.image, + tags: newCollection.tags + }) + }); + + if (response.ok) { + const createdCollection = await response.json(); + setCreatedCollection(createdCollection); + setShowCreateModal(false); + setShowSuccessModal(true); + + // Reset form + setNewCollection({ + name: '', + description: '', + visibility: 'private', + image: '', + tags: [] + }); + + // Refresh collections list + fetchCollections(); + } else { + const error = await response.json(); + alert(error.error || 'Failed to create collection'); + } + } catch (error) { + console.error('Error creating collection:', error); + alert('Network error. Please try again.'); + } }; const handleUpdateCollection = () => { @@ -400,32 +447,33 @@ export default function Collections() {
+ setNewCollection({...newCollection, image: e.target.value})} + placeholder="Enter image URL" + /> +

+ Add a hero image for your collection +

+
+
+
-
- setNewCollection({...newCollection, isPublic: e.target.checked})} - className="mr-2" - /> - -
+ +
+ + + )} + {/* Edit Collection Modal */} {editingCollection && (
diff --git a/scripts/add-image-column.js b/scripts/add-image-column.js new file mode 100755 index 0000000..a76c774 --- /dev/null +++ b/scripts/add-image-column.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node + +import { config } from 'dotenv'; +import { sql } from '@vercel/postgres'; + +// Load environment variables +config({ path: '.env.local' }); + +async function addImageColumn() { + try { + console.log('šŸ–¼ļø Adding image column to collections table...\n'); + + // Add image column to collections table + await sql` + ALTER TABLE collections + ADD COLUMN IF NOT EXISTS image TEXT + `; + console.log('āœ… Added image column to collections table'); + + console.log('\nšŸŽ‰ Image column added successfully!'); + console.log('\nšŸ“‹ Collections can now have hero images!'); + + } catch (error) { + console.error('āŒ Migration failed:', error.message); + process.exit(1); + } +} + +addImageColumn();