'use client' import { useState, useEffect } from 'react' import { IconCamera, IconMapPin, IconCalendar, IconEye, IconHeart, IconStar } from '@tabler/icons-react' import { Photo } from '@/types/photo' interface PhotoThumbnailProps { photo: Photo size?: 'small' | 'medium' | 'large' showMetadata?: boolean onPhotoClick?: (photo: Photo) => void } export default function PhotoThumbnail({ photo, size = 'medium', showMetadata = false, onPhotoClick }: PhotoThumbnailProps) { const [imageError, setImageError] = useState(false) const [showDetails, setShowDetails] = useState(false) const [tags, setTags] = useState>([]) // Fetch tags for this photo useEffect(() => { const fetchTags = async () => { try { const response = await fetch(`/api/photos/${photo.id}/tags`) if (response.ok) { const photoTags = await response.json() setTags(photoTags) } } catch (error) { console.warn('Failed to fetch tags for photo:', photo.id) } } fetchTags() }, [photo.id]) // Parse metadata let metadata: any = {} try { metadata = photo.metadata ? JSON.parse(photo.metadata) : {} } catch (error) { console.warn('Failed to parse photo metadata:', error) } const exif = metadata.exif || {} // Size configurations - keep square containers for grid layout const sizeConfig = { small: { container: 'aspect-square', thumbnail: 150 }, medium: { container: 'aspect-square', thumbnail: 200 }, large: { container: 'aspect-square', thumbnail: 300 } } const config = sizeConfig[size] // Format metadata for display const formatExposureTime = (time: number) => { if (time >= 1) return `${time}s` return `1/${Math.round(1 / time)}s` } const formatFocalLength = (length: number) => `${Math.round(length)}mm` const formatISO = (iso: number | number[]) => { const isoValue = Array.isArray(iso) ? iso[0] : iso return `ISO ${isoValue}` } const formatDate = (dateString: string) => { try { return new Date(dateString).toLocaleDateString() } catch { return 'Unknown' } } const formatDateTime = (dateString: string) => { try { return new Date(dateString).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }) } catch { return 'Unknown' } } // Get the best available date from EXIF data const getBestDate = () => { if (exif.date_time_original) return exif.date_time_original if (exif.date_time_digitized) return exif.date_time_digitized if (exif.date_time) return exif.date_time if (photo.created_at) return photo.created_at return null } const formatFileSize = (bytes: number) => { if (bytes === 0) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } return (
onPhotoClick?.(photo)} onMouseEnter={() => setShowDetails(true)} onMouseLeave={() => setShowDetails(false)} > {/* Thumbnail Image */} {!imageError ? ( {photo.filename} setImageError(true)} loading="lazy" /> ) : (
)} {/* Favorite indicator */} {photo.favorite && (
)} {/* Rating indicator */} {photo.rating && photo.rating > 0 && (
{[...Array(photo.rating)].map((_, i) => ( ))}
)} {/* Tag pills */} {tags.length > 0 && (
{tags.slice(0, size === 'large' ? 6 : size === 'medium' ? 4 : 2).map((tag) => ( {tag.name} ))} {tags.length > (size === 'large' ? 6 : size === 'medium' ? 4 : 2) && ( +{tags.length - (size === 'large' ? 6 : size === 'medium' ? 4 : 2)} )}
)} {/* Metadata overlay - appears at bottom, leaving image visible */} {showMetadata && (showDetails || size === 'large') && (
{/* Filename */}
{photo.filename}
{/* Camera info and settings in a compact row */}
{(exif.camera_make || exif.camera_model) && (
{[exif.camera_make, exif.camera_model].filter(Boolean).join(' ')}
)} {/* Photo settings - compact display */} {(exif.f_number || exif.exposure_time || exif.iso_speed) && (
{exif.f_number && f/{exif.f_number}} {exif.exposure_time && {formatExposureTime(exif.exposure_time)}} {exif.iso_speed && {formatISO(exif.iso_speed)}}
)}
{/* Second row: location, date, dimensions */}
{/* GPS location */} {exif.gps && (exif.gps.latitude || exif.gps.longitude) && (
{exif.gps.latitude?.toFixed(2)}, {exif.gps.longitude?.toFixed(2)}
)} {/* Date taken with time */} {getBestDate() && (
{formatDateTime(getBestDate()!)}
)}
{/* Image dimensions and file size */}
{photo.width && photo.height && (
{photo.width} × {photo.height}
)}
{formatFileSize(photo.filesize)}
{/* Third row: Additional EXIF date info for large thumbnails */} {size === 'large' && (exif.date_time_original || exif.date_time_digitized || exif.date_time) && (
{exif.date_time_original && exif.date_time_original !== getBestDate() && (
📷 Taken: {formatDateTime(exif.date_time_original)}
)} {exif.date_time_digitized && exif.date_time_digitized !== getBestDate() && (
💾 Digitized: {formatDateTime(exif.date_time_digitized)}
)} {exif.date_time && exif.date_time !== getBestDate() && (
📝 Modified: {formatDateTime(exif.date_time)}
)}
)}
)} {/* Simple overlay for small sizes */} {size === 'small' && showDetails && (
{photo.filename}
)}
) }