import React, { useMemo, useState } from 'react'; import { Download, Maximize2, Check, Calendar, Heart, MessageSquare } from 'lucide-react'; import { format, parseISO, startOfDay, startOfWeek, startOfMonth } from 'date-fns'; import { useTheme } from '../../../contexts/ThemeContext'; import { AuthenticatedImage } from '../../common'; import type { BaseGalleryLayoutProps } from './BaseGalleryLayout'; import type { Photo } from '../../../types'; import { FeedbackIdentityModal } from '../../gallery/FeedbackIdentityModal'; import { feedbackService } from '../../../services/feedback.service'; export const TimelineGalleryLayout: React.FC = ({ photos, slug, onPhotoClick, onOpenPhotoWithFeedback, onDownload, selectedPhotos = new Set(), isSelectionMode = false, onPhotoSelect, allowDownloads = true, feedbackEnabled = false, feedbackOptions }) => { const { theme } = useTheme(); const [likedIds, setLikedIds] = useState>(new Set()); const [showIdentityModal, setShowIdentityModal] = useState(false); const [pendingAction, setPendingAction] = useState(null); const [savedIdentity, setSavedIdentity] = useState<{ name: string; email: string } | null>(null); const gallerySettings = theme.gallerySettings || {}; const grouping = gallerySettings.timelineGrouping || 'day'; const showDates = gallerySettings.timelineShowDates !== false; const canQuickComment = Boolean(feedbackEnabled && feedbackOptions?.allowComments && onOpenPhotoWithFeedback); // Group photos by date const groupedPhotos = useMemo(() => { const groups = new Map(); photos.forEach(photo => { const date = parseISO(photo.uploaded_at); let groupKey: string; switch (grouping) { case 'week': const weekStart = startOfWeek(date); groupKey = format(weekStart, 'yyyy-MM-dd'); // groupLabel = `Week of ${format(weekStart, 'MMM d, yyyy')}`; break; case 'month': const monthStart = startOfMonth(date); groupKey = format(monthStart, 'yyyy-MM'); // groupLabel = format(monthStart, 'MMMM yyyy'); break; default: // day const dayStart = startOfDay(date); groupKey = format(dayStart, 'yyyy-MM-dd'); // groupLabel = format(dayStart, 'EEEE, MMMM d, yyyy'); } if (!groups.has(groupKey)) { groups.set(groupKey, []); } groups.get(groupKey)!.push(photo); }); // Convert to array and sort by date return Array.from(groups.entries()) .map(([date, photos]) => ({ date, label: photos[0] ? format(parseISO(photos[0].uploaded_at), grouping === 'month' ? 'MMMM yyyy' : grouping === 'week' ? "'Week of' MMM d, yyyy" : 'EEEE, MMMM d, yyyy') : date, photos: photos.sort((a, b) => new Date(b.uploaded_at).getTime() - new Date(a.uploaded_at).getTime()) })) .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); }, [photos, grouping]); return (
{/* Timeline line */}
{/* Timeline groups */}
{groupedPhotos.map((group) => (
{/* Date marker */} {showDates && (

{group.label}

)} {/* Photos grid for this date */}
{group.photos.map((photo) => { const actualIndex = photos.findIndex(p => p.id === photo.id); return (
onPhotoClick(actualIndex)} > {/* Time label */}
{format(parseISO(photo.uploaded_at), 'h:mm a')}
{!isSelectionMode && ( <> {allowDownloads && ( )} {feedbackOptions?.allowLikes && ( )} {canQuickComment && ( )} )}
{(photo.like_count > 0 || likedIds.has(photo.id)) && (
)} {/* Selection Checkbox (visible on hover or when selected) */}
); })}
))}
{ setShowIdentityModal(false); setPendingAction(null); }} onSubmit={async (name, email) => { setSavedIdentity({ name, email }); setShowIdentityModal(false); if (pendingAction) { await feedbackService.submitFeedback(slug!, String(pendingAction.photoId), { feedback_type: pendingAction.type, guest_name: name, guest_email: email, }); setPendingAction(null); } }} feedbackType="like" />
); };