diff --git a/components/CollectionsPageView.js b/components/CollectionsPageView.js
new file mode 100644
index 0000000..f2ff4b7
--- /dev/null
+++ b/components/CollectionsPageView.js
@@ -0,0 +1,335 @@
+/* eslint-disable @next/next/no-img-element -- External or generated image URLs; next/image migration is out of scope. */
+import Link from 'next/link';
+import PermissionIndicator from './PermissionIndicator';
+import { VOCAB, collectionDisplayName } from '../lib/collection-vocabulary.js';
+import CollectionsCreateModal from './CollectionsCreateModal';
+import CollectionsEditModal from './CollectionsEditModal';
+import CollectionsSuccessModal from './CollectionsSuccessModal';
+import CollectionsThumbnail from './CollectionsThumbnail';
+
+export default function CollectionsPageView(props) {
+ const {
+ collections,
+ createdCollection,
+ editTagInput,
+ editingCollection,
+ filteredCollections,
+ formatCurrency,
+ formatDate,
+ handleCreateCollection,
+ handleDeleteCollection,
+ handleUpdateCollection,
+ loading,
+ newCollection,
+ router,
+ searchQuery,
+ setCollections,
+ setCreatedCollection,
+ setEditTagInput,
+ setEditingCollection,
+ setLoading,
+ setNewCollection,
+ setSearchQuery,
+ setShowCreateModal,
+ setShowSuccessModal,
+ setSortBy,
+ setTagInput,
+ showCreateModal,
+ showSuccessModal,
+ sortBy,
+ sortCollections,
+ sortOptions,
+ sortedCollections,
+ tagInput,
+ user
+ } = props;
+
+ return (
+ <>
+ {/* Header */}
+
+
+
+
+ {VOCAB.LISTS}
+
+
+ Lists you own, collaborate on, or have been shared with you
+
+
+
+
+
+ 🌍 Discover Community
+
+
+ setShowCreateModal(true)}
+ className="px-6 py-2 rounded-xl font-medium transition-all duration-200 hover:shadow-md"
+ style={{
+ backgroundColor: 'var(--accent-ember)',
+ color: 'white'
+ }}
+ >
+ + Create List
+
+
+
+
+
+ {/* Filters and Search */}
+
+
+
+ setSearchQuery(e.target.value)}
+ />
+
+
+ setSortBy(e.target.value)}
+ className="input-field w-48"
+ >
+ {sortOptions.map(option => (
+
+ {option.label}
+
+ ))}
+
+
+
+
+
+ {/* Collections Grid */}
+
+ {sortedCollections.length === 0 ? (
+
+
📦
+
+ {searchQuery ? 'No lists found' : 'No lists yet'}
+
+
+ {searchQuery
+ ? 'Try adjusting your search terms'
+ : 'Create your first list to get started'
+ }
+
+ {!searchQuery && (
+
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 List
+
+ )}
+
+ ) : (
+
+ {sortedCollections.map(collection => (
+
router.push(`/collection/${collection.slug || collection.id}`)}
+ >
+ {/* Thumbnail Section */}
+
+
+ {/* Collection Info */}
+
+ {/* Header with name and description - more space */}
+
+
+
+
+ {collectionDisplayName(collection)}
+
+ {/* System collection indicator */}
+ {collection.isSystemCollection && (
+
+
+ 🔒 SYSTEM
+
+
+
+
+
+
+ {VOCAB.SYSTEM_COLLECTION_SYNC_HINT}
+
+
+
+
+ )}
+
+ {/* Edit/Delete buttons - hidden for system collections */}
+ {!collection.isSystemCollection && (
+
+
{
+ e.stopPropagation();
+ setEditingCollection(collection);
+ }}
+ className="p-1.5 rounded-lg transition-colors hover:scale-105"
+ style={{
+ backgroundColor: 'var(--bg-tertiary)',
+ color: 'var(--text-secondary)'
+ }}
+ >
+
+
+
+
+
{
+ e.stopPropagation();
+ handleDeleteCollection(collection.id);
+ }}
+ className="p-1.5 rounded-lg transition-colors hover:scale-105"
+ style={{
+ backgroundColor: 'var(--bg-tertiary)',
+ color: '#ef4444'
+ }}
+ >
+
+
+
+
+
+ )}
+
+ {collection.description && (
+
+ {collection.description}
+
+ )}
+
+
+ {/* Compact Stats */}
+
+
+
+ {collection.cardCount} cards
+
+
+ {formatCurrency(collection.value)}
+
+
+
+ {formatDate(collection.createdAt)}
+
+
+
+ {/* Tags */}
+ {collection.tags && collection.tags.length > 0 && (
+
+ {collection.tags.slice(0, 2).map(tag => (
+
+ {tag}
+
+ ))}
+ {collection.tags.length > 2 && (
+
+ +{collection.tags.length - 2}
+
+ )}
+
+ )}
+
+ {/* Creator/Facepile and View Button Row */}
+
+
+ {/* Creator info or facepile */}
+
+
+ {collection.creator ? collection.creator.charAt(0).toUpperCase() : 'A'}
+
+
+ {collection.creator ? collection.creator.split('@')[0] : 'alice'}
+
+
+ {/* Additional collaborators could go here as overlapping avatars */}
+
+
{
+ e.stopPropagation();
+ router.push(`/collection/${collection.slug || collection.id}`);
+ }}
+ className="px-3 py-1.5 text-xs font-medium rounded-lg transition-colors hover:scale-105"
+ style={{
+ backgroundColor: 'var(--bg-tertiary)',
+ color: 'var(--text-primary)',
+ border: '1px solid var(--border)'
+ }}
+ >
+ View
+
+
+
+
+ ))}
+
+ )}
+
+
+ {
+ setShowCreateModal(false);
+ setTagInput('');
+ }}
+ onCreate={handleCreateCollection}
+ />
+
+ {
+ setShowSuccessModal(false);
+ router.push(`/collection/${createdCollection.slug || createdCollection.id}`);
+ }}
+ onStay={() => setShowSuccessModal(false)}
+ />
+
+ {
+ setEditingCollection(null);
+ setEditTagInput('');
+ }}
+ onUpdate={handleUpdateCollection}
+ />
+ >
+ );
+}
diff --git a/components/CollectionsThumbnail.js b/components/CollectionsThumbnail.js
new file mode 100644
index 0000000..3684911
--- /dev/null
+++ b/components/CollectionsThumbnail.js
@@ -0,0 +1,119 @@
+/* eslint-disable @next/next/no-img-element -- Collection thumbnail images; next/image migration is out of scope. */
+export default function CollectionsThumbnail({ collection }) {
+ const { thumbnails = [], image } = collection;
+
+ if (image) {
+ return (
+
+
+
+ {collection.userRole === 'owner' && (
+
+ 👑 Owner
+
+ )}
+ {collection.isPublic && (
+
+ 🌍 Public
+
+ )}
+
+
+ );
+ }
+
+ if (!thumbnails || thumbnails.length === 0) {
+ return (
+
+
+
+ {collection.userRole === 'owner' && (
+
+ 👑 Owner
+
+ )}
+ {collection.isPublic && (
+
+ 🌍 Public
+
+ )}
+
+
+ );
+ }
+
+ const mainCard = thumbnails[0];
+ const gridCards = thumbnails.slice(1, 5);
+
+ return (
+
+
+ {mainCard ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+ {Array.from({ length: 4 }).map((_, index) => {
+ const card = gridCards[index];
+ return (
+
+ {card ? (
+
+
+
+ ) : (
+
+ )}
+
+ );
+ })}
+
+
+
+ {collection.userRole === 'owner' && (
+
+ 👑 Owner
+
+ )}
+ {collection.isPublic && (
+
+ 🌍 Public
+
+ )}
+
+
+ );
+}
diff --git a/lib/use-collections-page.js b/lib/use-collections-page.js
new file mode 100644
index 0000000..e2b24f1
--- /dev/null
+++ b/lib/use-collections-page.js
@@ -0,0 +1,272 @@
+import { useState, useEffect } from 'react';
+import { useRouter } from 'next/router';
+import { VOCAB, collectionDisplayName } from './collection-vocabulary.js';
+
+/**
+ * Card/collection page state and handlers (god-component split).
+ */
+export function useCollectionsPage({ user = null, authLoading = true } = {}) {
+ const router = useRouter();
+
+ useEffect(() => {
+ if (!authLoading && !user) {
+ router.push('/login');
+ }
+ }, [authLoading, user, router]);
+
+ 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]);
+
+ 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);
+ }
+ }
+
+ useEffect(() => {
+ if (user) {
+ // eslint-disable-next-line react-hooks/set-state-in-effect -- load lists when user is available
+ fetchCollections();
+ }
+ }, [user]);
+
+ ;
+
+ 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 list');
+ }
+ } 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 list');
+ }
+ } 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 list? 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 list');
+ }
+ } catch (error) {
+ console.error('Error deleting collection:', error);
+ alert('Network error. Please try again.');
+ }
+ }
+ };
+
+ const formatCurrency = (amount) => {
+ return new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency: 'USD'
+ }).format(amount);
+ };
+
+ const formatDate = (dateString) => {
+ return new Date(dateString).toLocaleDateString();
+ };
+
+ const showInitialLoading = authLoading || loading;
+
+ return {
+ collections,
+ createdCollection,
+ editTagInput,
+ editingCollection,
+ filteredCollections,
+ formatCurrency,
+ formatDate,
+ handleCreateCollection,
+ handleDeleteCollection,
+ handleUpdateCollection,
+ loading,
+ newCollection,
+ router,
+ searchQuery,
+ setCollections,
+ setCreatedCollection,
+ setEditTagInput,
+ setEditingCollection,
+ setLoading,
+ setNewCollection,
+ setSearchQuery,
+ setShowCreateModal,
+ setShowSuccessModal,
+ setSortBy,
+ setTagInput,
+ showCreateModal,
+ showInitialLoading,
+ showSuccessModal,
+ sortBy,
+ sortCollections,
+ sortOptions,
+ sortedCollections,
+ tagInput
+ };
+}
diff --git a/pages/collections.js b/pages/collections.js
index efc8872..bc6c94e 100644
--- a/pages/collections.js
+++ b/pages/collections.js
@@ -1,655 +1,29 @@
-/* eslint-disable @next/next/no-img-element -- External or generated image URLs; next/image migration is out of scope. */
-import { useState, useEffect } from 'react';
-import { useRouter } from 'next/router';
import Layout from '../components/Layout';
-import PermissionIndicator from '../components/PermissionIndicator';
+import CollectionsPageView from '../components/CollectionsPageView';
import { useAuth } from '../lib/use-auth';
-import Link from 'next/link';
-import { VOCAB, collectionDisplayName } from '../lib/collection-vocabulary.js';
-import CollectionsCreateModal from '../components/CollectionsCreateModal';
-import CollectionsEditModal from '../components/CollectionsEditModal';
-import CollectionsSuccessModal from '../components/CollectionsSuccessModal';
+import { useCollectionsPage } from '../lib/use-collections-page.js';
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 collectionsPage = useCollectionsPage({ user, authLoading });
- 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]);
-
- 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);
- }
- }
-
- useEffect(() => {
- if (user) {
- // eslint-disable-next-line react-hooks/set-state-in-effect -- load lists when user is available
- fetchCollections();
- }
- }, [user]);
-
-;
-
- 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 list');
- }
- } 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 list');
- }
- } 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 list? 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 list');
- }
- } catch (error) {
- console.error('Error deleting collection:', error);
- alert('Network error. Please try again.');
- }
- }
- };
-
- const formatCurrency = (amount) => {
- return new Intl.NumberFormat('en-US', {
- style: 'currency',
- currency: 'USD'
- }).format(amount);
- };
-
- const formatDate = (dateString) => {
- return new Date(dateString).toLocaleDateString();
- };
-
- // Collection thumbnail component - Updated design with card images
- const CollectionThumbnail = ({ collection }) => {
- const { thumbnails = [], image } = collection;
-
- // If there's a custom image, show it
- if (image) {
- return (
-
-
- {/* Floating badges over custom image */}
-
- {collection.userRole === 'owner' && (
-
- 👑 Owner
-
- )}
- {collection.isPublic && (
-
- 🌍 Public
-
- )}
-
-
- );
- }
-
- // If no cards, show crying emoji
- if (!thumbnails || thumbnails.length === 0) {
- return (
-
-
- {/* Floating badges over empty state */}
-
- {collection.userRole === 'owner' && (
-
- 👑 Owner
-
- )}
- {collection.isPublic && (
-
- 🌍 Public
-
- )}
-
-
- );
- }
-
- const mainCard = thumbnails[0];
- const gridCards = thumbnails.slice(1, 5); // Get up to 4 cards for the 2x2 grid
-
- return (
-
- {/* Main card (larger, left side) */}
-
- {mainCard ? (
-
-
-
- ) : (
-
- )}
-
-
- {/* Grid of 4 smaller cards (right side) */}
-
-
- {Array.from({ length: 4 }).map((_, index) => {
- const card = gridCards[index];
- return (
-
- {card ? (
-
-
-
- ) : (
-
- )}
-
- );
- })}
-
-
-
- {/* Floating badges over card layout */}
-
- {collection.userRole === 'owner' && (
-
- 👑 Owner
-
- )}
- {collection.isPublic && (
-
- 🌍 Public
-
- )}
-
-
- );
- };
-
- // Show loading spinner while auth is loading or data is loading
- if (authLoading || loading) {
+ if (collectionsPage.showInitialLoading) {
return (
);
}
- // Redirect to login if not authenticated (handled by useEffect, but this is a fallback)
if (!user) {
return null;
}
return (
- {/* Header */}
-
-
-
-
- {VOCAB.LISTS}
-
-
- Lists you own, collaborate on, or have been shared with you
-
-
-
-
-
- 🌍 Discover Community
-
-
- setShowCreateModal(true)}
- className="px-6 py-2 rounded-xl font-medium transition-all duration-200 hover:shadow-md"
- style={{
- backgroundColor: 'var(--accent-ember)',
- color: 'white'
- }}
- >
- + Create List
-
-
-
-
-
- {/* Filters and Search */}
-
-
-
- setSearchQuery(e.target.value)}
- />
-
-
- setSortBy(e.target.value)}
- className="input-field w-48"
- >
- {sortOptions.map(option => (
-
- {option.label}
-
- ))}
-
-
-
-
-
- {/* Collections Grid */}
-
- {sortedCollections.length === 0 ? (
-
-
📦
-
- {searchQuery ? 'No lists found' : 'No lists yet'}
-
-
- {searchQuery
- ? 'Try adjusting your search terms'
- : 'Create your first list to get started'
- }
-
- {!searchQuery && (
-
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 List
-
- )}
-
- ) : (
-
- {sortedCollections.map(collection => (
-
router.push(`/collection/${collection.slug || collection.id}`)}
- >
- {/* Thumbnail Section */}
-
-
- {/* Collection Info */}
-
- {/* Header with name and description - more space */}
-
-
-
-
- {collectionDisplayName(collection)}
-
- {/* System collection indicator */}
- {collection.isSystemCollection && (
-
-
- 🔒 SYSTEM
-
-
-
-
-
-
- {VOCAB.SYSTEM_COLLECTION_SYNC_HINT}
-
-
-
-
- )}
-
- {/* Edit/Delete buttons - hidden for system collections */}
- {!collection.isSystemCollection && (
-
-
{
- e.stopPropagation();
- setEditingCollection(collection);
- }}
- className="p-1.5 rounded-lg transition-colors hover:scale-105"
- style={{
- backgroundColor: 'var(--bg-tertiary)',
- color: 'var(--text-secondary)'
- }}
- >
-
-
-
-
-
{
- e.stopPropagation();
- handleDeleteCollection(collection.id);
- }}
- className="p-1.5 rounded-lg transition-colors hover:scale-105"
- style={{
- backgroundColor: 'var(--bg-tertiary)',
- color: '#ef4444'
- }}
- >
-
-
-
-
-
- )}
-
- {collection.description && (
-
- {collection.description}
-
- )}
-
-
- {/* Compact Stats */}
-
-
-
- {collection.cardCount} cards
-
-
- {formatCurrency(collection.value)}
-
-
-
- {formatDate(collection.createdAt)}
-
-
-
- {/* Tags */}
- {collection.tags && collection.tags.length > 0 && (
-
- {collection.tags.slice(0, 2).map(tag => (
-
- {tag}
-
- ))}
- {collection.tags.length > 2 && (
-
- +{collection.tags.length - 2}
-
- )}
-
- )}
-
- {/* Creator/Facepile and View Button Row */}
-
-
- {/* Creator info or facepile */}
-
-
- {collection.creator ? collection.creator.charAt(0).toUpperCase() : 'A'}
-
-
- {collection.creator ? collection.creator.split('@')[0] : 'alice'}
-
-
- {/* Additional collaborators could go here as overlapping avatars */}
-
-
{
- e.stopPropagation();
- router.push(`/collection/${collection.slug || collection.id}`);
- }}
- className="px-3 py-1.5 text-xs font-medium rounded-lg transition-colors hover:scale-105"
- style={{
- backgroundColor: 'var(--bg-tertiary)',
- color: 'var(--text-primary)',
- border: '1px solid var(--border)'
- }}
- >
- View
-
-
-
-
- ))}
-
- )}
-
-
- {
- setShowCreateModal(false);
- setTagInput('');
- }}
- onCreate={handleCreateCollection}
- />
-
- {
- setShowSuccessModal(false);
- router.push(`/collection/${createdCollection.slug || createdCollection.id}`);
- }}
- onStay={() => setShowSuccessModal(false)}
- />
-
- {
- setEditingCollection(null);
- setEditTagInput('');
- }}
- onUpdate={handleUpdateCollection}
- />
+
);
-}
\ No newline at end of file
+}