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}
+
+
+ ))}
+
+ )}
+
+
+