/* 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 Link from 'next/link'; import Layout from '../../components/Layout'; import PermissionIndicator from '../../components/PermissionIndicator'; import { Button, SearchBar } from '../../components/ui'; import { useAuth } from '../../lib/use-auth'; import { VOCAB } from '../../lib/collection-vocabulary.js'; export default function CommunityCollections() { const router = useRouter(); const { user, loading: authLoading } = useAuth(); const [collections, setCollections] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [sortBy, setSortBy] = useState('name'); // Fetch collections on mount, regardless of auth status const fetchPublicCollections = async () => { try { // Use public API endpoint that doesn't require authentication const response = await fetch('/api/public/collections?limit=50'); 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`); 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 public collections'); } } catch (error) { console.error('Error fetching public collections:', error); } finally { setLoading(false); } }; useEffect(() => { // eslint-disable-next-line react-hooks/set-state-in-effect -- mount fetch; setLoading runs inside async loader fetchPublicCollections(); }, []); 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: 'Recently Created' } ]; const sortCollections = (collections, sortBy) => { return [...collections].sort((a, b) => { switch (sortBy) { case 'name': return a.name.localeCompare(b.name); case 'value': return (b.value || 0) - (a.value || 0); case 'cardCount': return (b.cardCount || 0) - (a.cardCount || 0); 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 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 (same as in regular collections) const CollectionThumbnail = ({ collection }) => { const { thumbnails = [], image } = collection; // If there's a custom image, show it if (image) { return (
{collection.name}
); } // If no cards, show crying emoji if (!thumbnails || thumbnails.length === 0) { return (
😢

No cards yet

); } 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 ? (
{mainCard.name}
) : (
)}
{/* Grid of 4 smaller cards (right side) */}
{Array.from({ length: 4 }).map((_, index) => { const card = gridCards[index]; return (
{card ? (
{card.name}
) : (
)}
); })}
); }; // Show loading spinner while auth is loading or data is loading if (authLoading || loading) { return (
); } // Redirect to login if not authenticated (handled by useEffect, but this is a fallback) if (!user) { return null; } return ( {/* Header */}

Community Lists

Discover public lists shared by the community

{/* Search and Sort */}
setSearchQuery(e.target.value)} onClear={() => setSearchQuery('')} placeholder="Search lists…" />
{/* Collections Grid */}
{sortedCollections.length === 0 ? (
🌍

{searchQuery ? 'No lists found' : 'No public lists yet'}

{searchQuery ? 'Try adjusting your search terms' : 'Be the first to share a public list with the community!' }

{!searchQuery && ( )}
) : (
{sortedCollections.map(collection => (

{collection.name}

{collection.description && (

{collection.description}

)}

by {collection.creator}

{/* 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, index) => ( {tag} ))} {collection.tags.length > 2 && ( +{collection.tags.length - 2} )}
)}
))}
)}
); }