From dc867f2a2b3dde62ee2fe7fdb8a111764f0b3655 Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Sun, 27 Jul 2025 12:33:14 -0500 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20Tag=20Management=20to=20Colle?= =?UTF-8?q?ction=20Modals?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🏷️ Tag Functionality Added: - Tag input field in Create Collection modal - Tag editing in Edit Collection modal - Add tags with Enter key or Add button - Remove tags with × button - Visual tag display with styling 🎨 Tag Features: - Real-time tag addition/removal - Duplicate tag prevention - Tag input clearing on modal close - Proper tag persistence to database - Clean tag display with hover effects 🔧 Technical Improvements: - Added tagInput and editTagInput state management - Created reusable tag handling functions - Updated API calls to include tags in create/update - Enhanced modal UX with tag management - Proper form cleanup on modal close 🎯 User Experience: - Users can now organize collections with tags - Tags display in collection grid view - Easy tag management in both create and edit flows - Consistent tag styling across the app Tags are now fully functional for collection organization! 🚀 --- pages/collections.js | 207 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 197 insertions(+), 10 deletions(-) diff --git a/pages/collections.js b/pages/collections.js index a05b7b6..32aebbd 100644 --- a/pages/collections.js +++ b/pages/collections.js @@ -24,6 +24,8 @@ export default function Collections() { }); const [showSuccessModal, setShowSuccessModal] = useState(false); const [createdCollection, setCreatedCollection] = useState(null); + const [tagInput, setTagInput] = useState(''); // For creating new collections + const [editTagInput, setEditTagInput] = useState(''); // For editing collections // Redirect to login if not authenticated useEffect(() => { @@ -132,6 +134,7 @@ export default function Collections() { image: '', tags: [] }); + setTagInput(''); // Clear tag input // Refresh collections list fetchCollections(); @@ -145,16 +148,79 @@ export default function Collections() { } }; - const handleUpdateCollection = () => { - setCollections(collections.map(c => - c.id === editingCollection.id ? editingCollection : c - )); - setEditingCollection(null); + const handleUpdateCollection = async () => { + try { + const response = await fetch(`/api/collections/${editingCollection.slug || editingCollection.id}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + name: editingCollection.name, + description: editingCollection.description, + isPublic: editingCollection.isPublic, + image: editingCollection.image, + tags: editingCollection.tags + }) + }); + + if (response.ok) { + // Refresh collections after update + fetchCollections(); + setEditingCollection(null); + setEditTagInput(''); // Clear the tag input + } else { + const error = await response.json(); + alert(error.error || 'Failed to update collection'); + } + } catch (error) { + console.error('Error updating collection:', error); + alert('Network error. Please try again.'); + } }; - const handleDeleteCollection = (id) => { - if (confirm('Are you sure you want to delete this collection?')) { - setCollections(collections.filter(c => c.id !== id)); + const handleDeleteCollection = async (collectionId) => { + if (confirm('Are you sure you want to delete this collection? This action cannot be undone.')) { + try { + const response = await fetch(`/api/collections/${collectionId}`, { + method: 'DELETE' + }); + + if (response.ok) { + fetchCollections(); // Refresh the list + } else { + const error = await response.json(); + alert(error.error || 'Failed to delete collection'); + } + } catch (error) { + console.error('Error deleting collection:', error); + alert('Network error. Please try again.'); + } + } + }; + + // Tag handling functions + const handleAddTag = (tagInput, setTagInput, collection, setCollection) => { + if (tagInput.trim() && !collection.tags.includes(tagInput.trim())) { + setCollection({ + ...collection, + tags: [...collection.tags, tagInput.trim()] + }); + setTagInput(''); + } + }; + + const handleRemoveTag = (tagToRemove, collection, setCollection) => { + setCollection({ + ...collection, + tags: collection.tags.filter(tag => tag !== tagToRemove) + }); + }; + + const handleTagKeyPress = (e, tagInput, setTagInput, collection, setCollection) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleAddTag(tagInput, setTagInput, collection, setCollection); } }; @@ -518,6 +584,65 @@ export default function Collections() { Add a custom thumbnail image, or we'll use your rarest cards

+ + {/* Tags Section */} +
+ +
+
+ setTagInput(e.target.value)} + onKeyPress={(e) => handleTagKeyPress(e, tagInput, setTagInput, newCollection, setNewCollection)} + placeholder="Add tags (press Enter)" + /> + +
+ {newCollection.tags.length > 0 && ( +
+ {newCollection.tags.map((tag, index) => ( + + {tag} + + + ))} +
+ )} +

+ Add tags to help organize and categorize your collection +

+
+
+
+ + {/* Tags Section */} +
+ +
+
+ setEditTagInput(e.target.value)} + onKeyPress={(e) => handleTagKeyPress(e, editTagInput, setEditTagInput, editingCollection, setEditingCollection)} + placeholder="Add tags (press Enter)" + /> + +
+ {editingCollection.tags && editingCollection.tags.length > 0 && ( +
+ {editingCollection.tags.map((tag, index) => ( + + {tag} + + + ))} +
+ )} +
+
+