deckhearth/pages/collections.js
Randall Stillwell f11a7fef36 🧹 Remove Debug Logging - Authentication Issue Fixed
 Ownership Indicators Now Working:
- Bob's collections properly show userRole: 'owner'
- Alice's public collections show userRole: null
- Authentication headers fix resolved the issue

🧹 Cleanup:
- Removed debug console.log statements
- Cleaned up server-side logging
- Restored clean, production-ready code

The authentication issue is fully resolved! Bob now sees proper ownership
indicators (👑 Owner badges) on his collections while Alice's public
collections show as viewable without ownership indicators.
2025-07-27 12:41:14 -05:00

906 lines
No EOL
36 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import Layout from '../components/Layout';
import PermissionIndicator from '../components/PermissionIndicator';
import { useAuth } from '../lib/use-auth';
export default function Collections() {
const router = useRouter();
const { user, loading: authLoading } = useAuth();
const [collections, setCollections] = useState([]);
const [loading, setLoading] = useState(true);
const [showCreateModal, setShowCreateModal] = useState(false);
const [editingCollection, setEditingCollection] = useState(null);
const [searchQuery, setSearchQuery] = useState('');
const [sortBy, setSortBy] = useState('name'); // name, value, cardCount, createdAt
const [newCollection, setNewCollection] = useState({
name: '',
description: '',
isPublic: false,
image: '',
tags: []
});
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(() => {
if (!authLoading && !user) {
router.push('/login');
}
}, [authLoading, user, router]);
useEffect(() => {
if (user) {
fetchCollections();
}
}, [user]);
const fetchCollections = async () => {
try {
const token = localStorage.getItem('auth_token');
const headers = {
'Content-Type': 'application/json',
};
if (token) {
headers.Authorization = `Bearer ${token}`;
}
const response = await fetch('/api/collections', { headers });
if (response.ok) {
const data = await response.json();
// Fetch thumbnails for each collection
const collectionsWithThumbnails = await Promise.all(
data.map(async (collection) => {
try {
const identifier = collection.slug || collection.id;
const thumbnailResponse = await fetch(`/api/collections/${identifier}/thumbnails`, { headers });
if (thumbnailResponse.ok) {
const thumbnailData = await thumbnailResponse.json();
return { ...collection, thumbnails: thumbnailData.thumbnails };
}
return { ...collection, thumbnails: [] };
} catch (error) {
console.error(`Error fetching thumbnails for collection ${collection.id}:`, error);
return { ...collection, thumbnails: [] };
}
})
);
setCollections(collectionsWithThumbnails);
} else {
console.error('Failed to fetch collections');
}
} catch (error) {
console.error('Error fetching collections:', error);
} finally {
setLoading(false);
}
};
const sortOptions = [
{ value: 'name', label: 'Name (A-Z)' },
{ value: 'value', label: 'Value (High to Low)' },
{ value: 'cardCount', label: 'Card Count (High to Low)' },
{ value: 'createdAt', label: 'Date Created (Newest)' }
];
const sortCollections = (collections, sortBy) => {
return [...collections].sort((a, b) => {
switch (sortBy) {
case 'name':
return a.name.localeCompare(b.name);
case 'value':
return b.value - a.value;
case 'cardCount':
return b.cardCount - a.cardCount;
case 'createdAt':
return new Date(b.createdAt) - new Date(a.createdAt);
default:
return 0;
}
});
};
const filteredCollections = collections.filter(collection => {
const matchesSearch = collection.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
collection.description.toLowerCase().includes(searchQuery.toLowerCase());
return matchesSearch;
});
const sortedCollections = sortCollections(filteredCollections, sortBy);
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,
isPublic: newCollection.isPublic,
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: '',
isPublic: false,
image: '',
tags: []
});
setTagInput(''); // Clear tag input
// 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 = 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 = 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);
}
};
const formatCurrency = (amount) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD'
}).format(amount);
};
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString();
};
const CollectionThumbnail = ({ collection }) => {
const { thumbnails = [], image } = collection;
// If collection has a custom hero image, use it
if (image) {
return (
<div className="w-full h-48 rounded-xl overflow-hidden mb-4" style={{ backgroundColor: 'var(--bg-tertiary)' }}>
<img
src={image}
alt={collection.name}
className="w-full h-full object-cover"
/>
</div>
);
}
// If no thumbnails available, show placeholder
if (!thumbnails || thumbnails.length === 0) {
return (
<div className="w-full h-48 rounded-xl mb-4 flex items-center justify-center" style={{ backgroundColor: 'var(--bg-tertiary)' }}>
<div className="text-center">
<div className="text-4xl mb-2">📦</div>
<p className="text-sm" style={{ color: 'var(--text-secondary)' }}>No cards yet</p>
</div>
</div>
);
}
// Show main card (rarest) and grid of 4 others
const mainCard = thumbnails[0]; // Rarest card
const gridCards = thumbnails.slice(1, 5); // Next 4 cards
return (
<div className="w-full h-48 rounded-xl overflow-hidden mb-4 flex gap-2">
{/* Main card (rarest) - takes up 2/3 of the space */}
<div className="flex-2 h-full relative group">
{mainCard ? (
<div className="relative h-full">
<img
src={mainCard.image_url || mainCard.stock_image_url || '/placeholder-card.png'}
alt={mainCard.name}
className="w-full h-full object-cover rounded-lg"
/>
{/* Rarity glow effect */}
<div className={`absolute inset-0 rounded-lg rarity-glow-${mainCard.rarity?.toLowerCase() || 'common'}`}></div>
{/* Card name overlay */}
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/70 to-transparent p-2 rounded-b-lg">
<p className="text-white text-xs font-medium truncate">{mainCard.name}</p>
<p className="text-white/80 text-xs capitalize">{mainCard.rarity}</p>
</div>
</div>
) : (
<div className="w-full h-full bg-gray-200 dark:bg-gray-700 rounded-lg flex items-center justify-center">
<span className="text-gray-400 text-xs">No image</span>
</div>
)}
</div>
{/* Grid of 4 other cards - takes up 1/3 of the space */}
<div className="flex-1 h-full">
<div className="grid grid-cols-2 gap-1 h-full">
{Array.from({ length: 4 }).map((_, index) => {
const card = gridCards[index];
return (
<div key={index} className="relative group">
{card ? (
<div className="relative h-full">
<img
src={card.image_url || card.stock_image_url || '/placeholder-card.png'}
alt={card.name}
className="w-full h-full object-cover rounded"
/>
{/* Subtle rarity glow for grid cards */}
<div className={`absolute inset-0 rounded rarity-glow-${card.rarity?.toLowerCase() || 'common'} opacity-50`}></div>
</div>
) : (
<div className="w-full h-full bg-gray-100 dark:bg-gray-800 rounded flex items-center justify-center">
<span className="text-gray-300 text-xs">+</span>
</div>
)}
</div>
);
})}
</div>
</div>
</div>
);
};
// Show loading spinner while auth is loading or data is loading
if (authLoading || loading) {
return (
<Layout user={user}>
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-b-2" style={{ borderColor: 'var(--accent-ember)' }}></div>
</div>
</Layout>
);
}
// Redirect to login if not authenticated (handled by useEffect, but this is a fallback)
if (!user) {
return null;
}
return (
<Layout user={user}>
{/* Header */}
<div className="p-6" style={{ backgroundColor: 'var(--bg-secondary)', borderBottom: '1px solid var(--border)' }}>
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold mb-2" style={{ color: 'var(--text-primary)' }}>
My Collections
</h1>
<p className="text-lg" style={{ color: 'var(--text-secondary)' }}>
Organize and manage your card collections
</p>
</div>
<button
onClick={() => setShowCreateModal(true)}
className="px-6 py-3 rounded-xl font-medium transition-all duration-200 hover:shadow-md flex items-center space-x-2"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
>
<svg className="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 6v6m0 0v6m0-6h6m-6 0H6" />
</svg>
<span>Create Collection</span>
</button>
</div>
</div>
{/* Filters and Search */}
<div className="p-6" style={{ backgroundColor: 'var(--bg-secondary)' }}>
<div className="flex flex-col md:flex-row gap-4">
<div className="flex-1">
<input
type="text"
placeholder="Search collections..."
className="input-field w-full"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
/>
</div>
<div className="flex gap-4">
<select
value={sortBy}
onChange={(e) => setSortBy(e.target.value)}
className="input-field w-48"
>
{sortOptions.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</div>
</div>
</div>
{/* Collections Grid */}
<div className="p-6">
{sortedCollections.length === 0 ? (
<div className="text-center py-12">
<div className="text-6xl mb-4">📦</div>
<h3 className="text-xl font-semibold mb-2" style={{ color: 'var(--text-primary)' }}>
{searchQuery ? 'No collections found' : 'No collections yet'}
</h3>
<p className="mb-6" style={{ color: 'var(--text-secondary)' }}>
{searchQuery
? 'Try adjusting your search terms'
: 'Create your first collection to get started'
}
</p>
{!searchQuery && (
<button
onClick={() => setShowCreateModal(true)}
className="px-6 py-3 rounded-xl font-medium transition-all duration-200 hover:shadow-md"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
>
Create Collection
</button>
)}
</div>
) : (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{sortedCollections.map(collection => (
<div
key={collection.id}
className="card hover:shadow-xl transition-all duration-300 cursor-pointer group"
onClick={() => router.push(`/collection/${collection.slug || collection.id}`)}
>
{/* Thumbnail Section */}
<CollectionThumbnail collection={collection} />
{/* Collection Info */}
<div className="space-y-3">
{/* Header with name and actions */}
<div className="flex items-start justify-between">
<div className="flex-1 min-w-0">
<h3 className="text-lg font-semibold truncate" style={{ color: 'var(--text-primary)' }}>
{collection.name}
</h3>
{collection.description && (
<p className="text-sm mt-1 line-clamp-2" style={{ color: 'var(--text-secondary)' }}>
{collection.description}
</p>
)}
</div>
<div className="flex items-center space-x-1 ml-2">
<PermissionIndicator
userRole={collection.userRole}
isPublic={collection.isPublic}
showTooltip={false}
/>
<div className="opacity-0 group-hover:opacity-100 transition-opacity flex space-x-1">
<button
onClick={(e) => {
e.stopPropagation();
setEditingCollection(collection);
}}
className="p-1.5 rounded-lg transition-colors hover:scale-105"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-secondary)'
}}
>
<svg className="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
</button>
<button
onClick={(e) => {
e.stopPropagation();
handleDeleteCollection(collection.id);
}}
className="p-1.5 rounded-lg transition-colors hover:scale-105"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: '#ef4444'
}}
>
<svg className="h-3.5 w-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
</svg>
</button>
</div>
</div>
</div>
{/* Compact Stats */}
<div className="flex items-center justify-between text-sm" style={{ color: 'var(--text-secondary)' }}>
<div className="flex items-center space-x-4">
<span className="font-medium" style={{ color: 'var(--text-primary)' }}>
{collection.cardCount} cards
</span>
<span className="font-medium" style={{ color: 'var(--accent-ember)' }}>
{formatCurrency(collection.value)}
</span>
</div>
<span className="text-xs">
{formatDate(collection.createdAt)}
</span>
</div>
{/* Tags */}
{collection.tags && collection.tags.length > 0 && (
<div className="flex flex-wrap gap-1">
{collection.tags.slice(0, 2).map(tag => (
<span
key={tag}
className="px-2 py-1 text-xs rounded-full"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-secondary)'
}}
>
{tag}
</span>
))}
{collection.tags.length > 2 && (
<span className="px-2 py-1 text-xs rounded-full" style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-secondary)'
}}>
+{collection.tags.length - 2}
</span>
)}
</div>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* Create Collection Modal */}
{showCreateModal && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="card max-w-md w-full mx-4">
<h2 className="text-2xl font-bold mb-4" style={{ color: 'var(--text-primary)' }}>
Create New Collection
</h2>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Collection Name
</label>
<input
type="text"
className="input-field w-full"
value={newCollection.name}
onChange={(e) => setNewCollection({...newCollection, name: e.target.value})}
placeholder="Enter collection name"
/>
</div>
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Description
</label>
<textarea
className="input-field w-full"
rows="3"
value={newCollection.description}
onChange={(e) => setNewCollection({...newCollection, description: e.target.value})}
placeholder="Describe your collection"
/>
</div>
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Hero Image (Optional)
</label>
<input
type="url"
className="input-field w-full"
value={newCollection.image}
onChange={(e) => setNewCollection({...newCollection, image: e.target.value})}
placeholder="Enter image URL or upload later"
/>
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
Add a custom thumbnail image, or we'll use your rarest cards
</p>
</div>
{/* Tags Section */}
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Tags (Optional)
</label>
<div className="space-y-2">
<div className="flex gap-2">
<input
type="text"
className="input-field flex-1"
value={tagInput}
onChange={(e) => setTagInput(e.target.value)}
onKeyPress={(e) => handleTagKeyPress(e, tagInput, setTagInput, newCollection, setNewCollection)}
placeholder="Add tags (press Enter)"
/>
<button
type="button"
onClick={() => handleAddTag(tagInput, setTagInput, newCollection, setNewCollection)}
className="px-3 py-2 rounded-lg text-sm font-medium transition-colors"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
disabled={!tagInput.trim()}
>
Add
</button>
</div>
{newCollection.tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{newCollection.tags.map((tag, index) => (
<span
key={index}
className="inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-medium"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-primary)',
border: '1px solid var(--border)'
}}
>
{tag}
<button
type="button"
onClick={() => handleRemoveTag(tag, newCollection, setNewCollection)}
className="ml-1 hover:text-red-500 transition-colors"
>
×
</button>
</span>
))}
</div>
)}
<p className="text-xs" style={{ color: 'var(--text-secondary)' }}>
Add tags to help organize and categorize your collection
</p>
</div>
</div>
<div className="flex items-center justify-between p-4 rounded-lg border" style={{ borderColor: 'var(--border)' }}>
<div>
<label className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>
Public Collection
</label>
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
Make this collection discoverable by other users
</p>
</div>
<button
type="button"
onClick={() => setNewCollection({...newCollection, isPublic: !newCollection.isPublic})}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
newCollection.isPublic ? 'bg-green-600' : 'bg-gray-300 dark:bg-gray-600'
}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
newCollection.isPublic ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
</div>
</div>
<div className="flex space-x-3 mt-6">
<button
onClick={() => {
setShowCreateModal(false);
setTagInput(''); // Clear tag input when closing
}}
className="flex-1 py-2 px-4 rounded-xl border transition-colors"
style={{
borderColor: 'var(--border)',
color: 'var(--text-secondary)'
}}
>
Cancel
</button>
<button
onClick={handleCreateCollection}
disabled={!newCollection.name.trim()}
className="flex-1 py-2 px-4 rounded-xl font-medium transition-all duration-200 hover:shadow-md disabled:opacity-50"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
>
Create Collection
</button>
</div>
</div>
</div>
)}
{/* Success Modal */}
{showSuccessModal && createdCollection && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="card max-w-md w-full mx-4 text-center">
<div className="text-6xl mb-4">🎉</div>
<h2 className="text-2xl font-bold mb-4" style={{ color: 'var(--text-primary)' }}>
Collection Created!
</h2>
<p className="mb-6" style={{ color: 'var(--text-secondary)' }}>
Your collection "{createdCollection.name}" has been created successfully.
</p>
<div className="space-y-3">
<button
onClick={() => {
setShowSuccessModal(false);
router.push(`/collection/${createdCollection.slug || createdCollection.id}`);
}}
className="w-full py-2 px-4 rounded-xl font-medium transition-all duration-200 hover:shadow-md"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
>
View Collection
</button>
<button
onClick={() => setShowSuccessModal(false)}
className="w-full py-2 px-4 rounded-xl border transition-colors"
style={{
borderColor: 'var(--border)',
color: 'var(--text-secondary)'
}}
>
Stay on Collections Page
</button>
</div>
</div>
</div>
)}
{/* Edit Collection Modal */}
{editingCollection && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div className="card max-w-md w-full mx-4">
<h2 className="text-2xl font-bold mb-4" style={{ color: 'var(--text-primary)' }}>
Edit Collection
</h2>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Collection Name
</label>
<input
type="text"
className="input-field w-full"
value={editingCollection.name}
onChange={(e) => setEditingCollection({...editingCollection, name: e.target.value})}
/>
</div>
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Description
</label>
<textarea
className="input-field w-full"
rows="3"
value={editingCollection.description}
onChange={(e) => setEditingCollection({...editingCollection, description: e.target.value})}
/>
</div>
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Hero Image
</label>
<input
type="url"
className="input-field w-full"
value={editingCollection.image || ''}
onChange={(e) => setEditingCollection({...editingCollection, image: e.target.value})}
placeholder="Enter image URL"
/>
</div>
{/* Tags Section */}
<div>
<label className="block text-sm font-medium mb-2" style={{ color: 'var(--text-primary)' }}>
Tags
</label>
<div className="space-y-2">
<div className="flex gap-2">
<input
type="text"
className="input-field flex-1"
value={editTagInput}
onChange={(e) => setEditTagInput(e.target.value)}
onKeyPress={(e) => handleTagKeyPress(e, editTagInput, setEditTagInput, editingCollection, setEditingCollection)}
placeholder="Add tags (press Enter)"
/>
<button
type="button"
onClick={() => handleAddTag(editTagInput, setEditTagInput, editingCollection, setEditingCollection)}
className="px-3 py-2 rounded-lg text-sm font-medium transition-colors"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
disabled={!editTagInput.trim()}
>
Add
</button>
</div>
{editingCollection.tags && editingCollection.tags.length > 0 && (
<div className="flex flex-wrap gap-2">
{editingCollection.tags.map((tag, index) => (
<span
key={index}
className="inline-flex items-center gap-1 px-2 py-1 rounded-md text-xs font-medium"
style={{
backgroundColor: 'var(--bg-tertiary)',
color: 'var(--text-primary)',
border: '1px solid var(--border)'
}}
>
{tag}
<button
type="button"
onClick={() => handleRemoveTag(tag, editingCollection, setEditingCollection)}
className="ml-1 hover:text-red-500 transition-colors"
>
×
</button>
</span>
))}
</div>
)}
</div>
</div>
<div className="flex items-center justify-between p-4 rounded-lg border" style={{ borderColor: 'var(--border)' }}>
<div>
<label className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>
Public Collection
</label>
<p className="text-xs mt-1" style={{ color: 'var(--text-secondary)' }}>
Make this collection discoverable by other users
</p>
</div>
<button
type="button"
onClick={() => setEditingCollection({...editingCollection, isPublic: !editingCollection.isPublic})}
className={`relative inline-flex h-6 w-11 items-center rounded-full transition-colors ${
editingCollection.isPublic ? 'bg-green-600' : 'bg-gray-300 dark:bg-gray-600'
}`}
>
<span
className={`inline-block h-4 w-4 transform rounded-full bg-white transition-transform ${
editingCollection.isPublic ? 'translate-x-6' : 'translate-x-1'
}`}
/>
</button>
</div>
</div>
<div className="flex space-x-3 mt-6">
<button
onClick={() => {
setEditingCollection(null);
setEditTagInput(''); // Clear tag input when closing
}}
className="flex-1 py-2 px-4 rounded-xl border transition-colors"
style={{
borderColor: 'var(--border)',
color: 'var(--text-secondary)'
}}
>
Cancel
</button>
<button
onClick={handleUpdateCollection}
disabled={!editingCollection.name.trim()}
className="flex-1 py-2 px-4 rounded-xl font-medium transition-all duration-200 hover:shadow-md disabled:opacity-50"
style={{
backgroundColor: 'var(--accent-ember)',
color: 'white'
}}
>
Update Collection
</button>
</div>
</div>
</div>
)}
</Layout>
);
}