← back to IWasCute
src/app/browse/page.tsx
282 lines
'use client'
import { useState } from 'react'
import { motion, AnimatePresence } from 'framer-motion'
import {
Search,
SlidersHorizontal,
Grid3X3,
LayoutList,
ShieldCheck,
ChevronDown,
ImageIcon,
Tag,
Calendar,
} from 'lucide-react'
import Navbar from '@/components/shared/Navbar'
type ViewMode = 'grid' | 'list'
type LicenseFilter = 'all' | 'editorial' | 'commercial' | 'ai-training'
const LICENSE_FILTERS: { id: LicenseFilter; label: string }[] = [
{ id: 'all', label: 'All' },
{ id: 'editorial', label: 'Editorial' },
{ id: 'commercial', label: 'Commercial' },
{ id: 'ai-training', label: 'AI Training' },
]
// Placeholder categories for when photos are available
const CATEGORIES = [
'All Photos',
'Baby & Toddler',
'School Age',
'Teenager',
'Family',
'Holidays',
'Outdoor',
'Portraits',
]
function SearchBar({
value,
onChange,
}: {
value: string
onChange: (v: string) => void
}) {
return (
<div className="relative flex-1">
<Search
size={16}
className="absolute left-4 top-1/2 -translate-y-1/2 pointer-events-none"
style={{ color: 'var(--color-warm-gray)' }}
/>
<input
type="text"
value={value}
onChange={(e) => onChange(e.target.value)}
placeholder="Search photos by keyword, era, or description..."
className="w-full pl-11 pr-4 py-3 rounded-2xl text-sm border outline-none transition-all focus:ring-2"
style={{
background: 'rgba(255,255,255,0.8)',
borderColor: 'var(--color-cream-dark)',
color: 'var(--color-brown)',
}}
/>
</div>
)
}
function EmptyMarketplace() {
return (
<div
className="flex flex-col items-center justify-center gap-8 py-24 text-center"
>
{/* Decorative placeholder grid */}
<div className="grid grid-cols-3 gap-3 opacity-30">
{Array.from({ length: 9 }).map((_, i) => (
<div
key={i}
className="w-24 h-24 md:w-32 md:h-32 rounded-2xl flex items-center justify-center"
style={{ background: 'var(--color-cream-dark)' }}
>
<ImageIcon size={24} style={{ color: 'var(--color-warm-gray)' }} />
</div>
))}
</div>
<div className="max-w-md">
<h2 className="text-2xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
The marketplace is coming soon
</h2>
<p className="mt-3 text-base leading-relaxed" style={{ color: 'var(--color-warm-gray)' }}>
Photos are being uploaded and reviewed. The first rights-cleared childhood photos
will appear here shortly. Every photo has both copyright and likeness rights verified.
</p>
</div>
{/* Trust badges */}
<div className="flex flex-wrap justify-center gap-4 mt-4">
{[
{ icon: ShieldCheck, label: 'Dual-rights verified' },
{ icon: Tag, label: 'Clear licensing tiers' },
{ icon: Calendar, label: 'AI training available' },
].map(({ icon: Icon, label }) => (
<div
key={label}
className="flex items-center gap-2 px-4 py-2.5 rounded-2xl"
style={{ background: 'rgba(181, 213, 197, 0.2)', border: '1px solid var(--color-sage)' }}
>
<Icon size={14} style={{ color: 'var(--color-sage-dark)' }} />
<span className="text-xs font-semibold" style={{ color: 'var(--color-sage-dark)' }}>
{label}
</span>
</div>
))}
</div>
</div>
)
}
export default function BrowsePage() {
const [search, setSearch] = useState('')
const [viewMode, setViewMode] = useState<ViewMode>('grid')
const [licenseFilter, setLicenseFilter] = useState<LicenseFilter>('all')
const [activeCategory, setActiveCategory] = useState('All Photos')
const [showFilters, setShowFilters] = useState(false)
// No photos yet — marketplace is pre-launch
const photos: unknown[] = []
return (
<div className="min-h-screen" style={{ background: 'var(--color-cream)' }}>
<Navbar />
<div className="max-w-6xl mx-auto px-4 py-8 md:px-8 md:py-12">
{/* Header */}
<div className="mb-8">
<h1 className="text-3xl font-black tracking-tighter" style={{ color: 'var(--color-brown)' }}>
Browse Photos
</h1>
<p className="mt-1 text-sm" style={{ color: 'var(--color-warm-gray)' }}>
Every photo is dual-rights cleared — copyright and likeness verified.
</p>
</div>
{/* Search + controls */}
<div className="flex flex-col gap-4 mb-6">
<div className="flex gap-3">
<SearchBar value={search} onChange={setSearch} />
<button
onClick={() => setShowFilters(!showFilters)}
className="flex items-center gap-2 px-4 py-3 rounded-2xl text-sm font-medium border transition-all hover:scale-105"
style={{
background: showFilters ? 'var(--color-peach)' : 'rgba(255,255,255,0.8)',
borderColor: showFilters ? 'var(--color-peach)' : 'var(--color-cream-dark)',
color: showFilters ? 'var(--color-brown)' : 'var(--color-warm-gray)',
}}
>
<SlidersHorizontal size={14} />
Filters
<ChevronDown
size={12}
className="transition-transform"
style={{ transform: showFilters ? 'rotate(180deg)' : 'none' }}
/>
</button>
{/* View toggle */}
<div
className="flex rounded-2xl border overflow-hidden"
style={{ borderColor: 'var(--color-cream-dark)' }}
>
<button
onClick={() => setViewMode('grid')}
className="px-3 py-3 transition-all"
style={{
background: viewMode === 'grid' ? 'var(--color-cream-dark)' : 'rgba(255,255,255,0.8)',
color: 'var(--color-warm-gray)',
}}
aria-label="Grid view"
>
<Grid3X3 size={16} />
</button>
<button
onClick={() => setViewMode('list')}
className="px-3 py-3 transition-all"
style={{
background: viewMode === 'list' ? 'var(--color-cream-dark)' : 'rgba(255,255,255,0.8)',
color: 'var(--color-warm-gray)',
}}
aria-label="List view"
>
<LayoutList size={16} />
</button>
</div>
</div>
{/* Filter panel */}
<AnimatePresence>
{showFilters && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.2 }}
className="overflow-hidden"
>
<div
className="p-5 rounded-2xl flex flex-col gap-4"
style={{ background: 'rgba(255,255,255,0.6)', border: '1px solid var(--color-cream-dark)' }}
>
{/* License type */}
<div>
<p className="text-xs font-bold tracking-widest uppercase mb-2" style={{ color: 'var(--color-warm-gray)' }}>
License Type
</p>
<div className="flex gap-2 flex-wrap">
{LICENSE_FILTERS.map((f) => (
<button
key={f.id}
onClick={() => setLicenseFilter(f.id)}
className="px-3.5 py-1.5 rounded-xl text-xs font-semibold transition-all hover:scale-105"
style={{
background: licenseFilter === f.id ? 'var(--color-peach)' : 'var(--color-cream-dark)',
color: licenseFilter === f.id ? 'var(--color-brown)' : 'var(--color-warm-gray)',
}}
>
{f.label}
</button>
))}
</div>
</div>
{/* Category chips */}
<div>
<p className="text-xs font-bold tracking-widest uppercase mb-2" style={{ color: 'var(--color-warm-gray)' }}>
Category
</p>
<div className="flex gap-2 flex-wrap">
{CATEGORIES.map((cat) => (
<button
key={cat}
onClick={() => setActiveCategory(cat)}
className="px-3.5 py-1.5 rounded-xl text-xs font-semibold transition-all hover:scale-105"
style={{
background: activeCategory === cat ? 'var(--color-sage)' : 'var(--color-cream-dark)',
color: activeCategory === cat ? 'white' : 'var(--color-warm-gray)',
}}
>
{cat}
</button>
))}
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
{/* Results */}
{photos.length === 0 ? (
<EmptyMarketplace />
) : (
<div
className="grid gap-4"
style={{
gridTemplateColumns:
viewMode === 'grid'
? 'repeat(auto-fill, minmax(220px, 1fr))'
: '1fr',
}}
>
{/* Photo cards will render here when marketplace launches */}
</div>
)}
</div>
</div>
)
}