'use client' import { useState, useEffect } from 'react' import PhotoThumbnail from './PhotoThumbnail' import ImageModal from './ImageModal' import { Photo } from '@/types/photo' import { IconPhoto, IconFilter, IconSearch, IconSortAscending, IconSortDescending } from '@tabler/icons-react' interface PhotoGridProps { directoryPath?: string showMetadata?: boolean thumbnailSize?: 'small' | 'medium' | 'large' } type SortOption = 'created_at' | 'modified_at' | 'filename' | 'filesize' type SortOrder = 'ASC' | 'DESC' export default function PhotoGrid({ directoryPath, showMetadata = true, thumbnailSize = 'medium' }: PhotoGridProps) { const [photos, setPhotos] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [sortBy, setSortBy] = useState('created_at') const [sortOrder, setSortOrder] = useState('DESC') const [searchTerm, setSearchTerm] = useState('') const [selectedPhoto, setSelectedPhoto] = useState(null) const fetchPhotos = async () => { try { setLoading(true) setError(null) const params = new URLSearchParams({ sortBy, sortOrder, limit: '100', // Load first 100 photos }) if (directoryPath) { params.append('directory', directoryPath) } const response = await fetch(`/api/photos?${params}`) if (!response.ok) { throw new Error('Failed to fetch photos') } const data = await response.json() setPhotos(data.photos || []) } catch (error) { console.error('Error fetching photos:', error) setError('Failed to load photos') } finally { setLoading(false) } } useEffect(() => { fetchPhotos() }, [directoryPath, sortBy, sortOrder]) // Filter photos based on search term const filteredPhotos = photos.filter(photo => { if (!searchTerm) return true const searchLower = searchTerm.toLowerCase() // Search in filename if (photo.filename.toLowerCase().includes(searchLower)) return true // Search in metadata try { const metadata = photo.metadata ? JSON.parse(photo.metadata) : {} const exif = metadata.exif || {} // Search in camera info if (exif.camera_make?.toLowerCase().includes(searchLower)) return true if (exif.camera_model?.toLowerCase().includes(searchLower)) return true if (exif.lens_model?.toLowerCase().includes(searchLower)) return true } catch (error) { // Ignore metadata parsing errors for search } return false }) const handlePhotoClick = (photo: Photo) => { setSelectedPhoto(photo) } const handleSortChange = (newSortBy: SortOption) => { if (newSortBy === sortBy) { // Toggle sort order if same field setSortOrder(sortOrder === 'ASC' ? 'DESC' : 'ASC') } else { setSortBy(newSortBy) setSortOrder('DESC') // Default to descending for new field } } // Grid classes based on thumbnail size - made thumbnails larger const gridClasses = { small: 'grid-cols-4 sm:grid-cols-6 md:grid-cols-8 lg:grid-cols-10 xl:grid-cols-12 gap-2', medium: 'grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4', large: 'grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-6' } if (loading) { return (

Loading photos...

) } if (error) { return (

Error Loading Photos

{error}

) } if (photos.length === 0) { return (

No Photos Found

{directoryPath ? `No photos found in ${directoryPath}` : 'No photos have been scanned yet. Select a directory and click scan to get started.' }

) } return (
{/* Controls */}

Photos {directoryPath && `in ${directoryPath.split('/').pop()}`}

{filteredPhotos.length} photo{filteredPhotos.length !== 1 ? 's' : ''}
{/* Search */}
setSearchTerm(e.target.value)} className="pl-10 pr-4 py-2 border border-gray-300 dark:border-gray-600 rounded-lg bg-white dark:bg-gray-800 text-gray-900 dark:text-white text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent" />
{/* Sort controls */}
{/* Photo Grid */}
{filteredPhotos.map((photo) => ( ))}
{/* Full Resolution Image Modal */} setSelectedPhoto(null)} photos={filteredPhotos} onNavigate={setSelectedPhoto} />
) }