import React, { useState, useEffect } from 'react'; import { Download, Maximize2, Check, ChevronDown, Calendar, Clock, Heart, MessageSquare } from 'lucide-react'; import { parseISO } from 'date-fns'; import { useTranslation } from 'react-i18next'; import { useLocalizedDate } from '../../../hooks/useLocalizedDate'; import { useTheme } from '../../../contexts/ThemeContext'; import { AuthenticatedImage } from '../../common'; import type { BaseGalleryLayoutProps } from './BaseGalleryLayout'; import type { Photo } from '../../../types'; import { buildResourceUrl } from '../../../utils/url'; import { FeedbackIdentityModal } from '../../gallery/FeedbackIdentityModal'; import { feedbackService } from '../../../services/feedback.service'; interface HeroGalleryLayoutProps extends BaseGalleryLayoutProps { eventName?: string; eventLogo?: string | null; eventDate?: string; expiresAt?: string; // Use a static hero photo independent of current filter heroPhotoOverride?: Photo | null; } export const HeroGalleryLayout: React.FC = ({ photos, slug, onPhotoClick, onOpenPhotoWithFeedback, onDownload, selectedPhotos = new Set(), isSelectionMode = false, onPhotoSelect, eventName, eventLogo, eventDate, expiresAt, heroPhotoOverride, allowDownloads = true, feedbackEnabled = false, feedbackOptions }) => { const { t } = useTranslation(); const { format } = useLocalizedDate(); const { theme } = useTheme(); const [heroPhoto, setHeroPhoto] = useState(null); const [hasInitialized, setHasInitialized] = useState(false); 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 overlayOpacity = gallerySettings.heroOverlayOpacity || 0.3; const [likedIds, setLikedIds] = useState>(new Set()); const canQuickComment = Boolean(feedbackEnabled && feedbackOptions?.allowComments && onOpenPhotoWithFeedback); // If an override is provided, always use it and skip initialization logic useEffect(() => { if (heroPhotoOverride) { setHeroPhoto(heroPhotoOverride); setHasInitialized(true); } }, [heroPhotoOverride]); // Reset initialization when heroImageId changes useEffect(() => { if (gallerySettings.heroImageId) { setHasInitialized(false); } }, [gallerySettings.heroImageId]); // Select hero photo (admin-selected or first photo only if gallery was empty) useEffect(() => { // When an override is provided, the effect above has already set the hero. if (heroPhotoOverride) return; if (photos.length > 0) { const heroId = gallerySettings.heroImageId; // If admin has selected a specific hero image, always use it when available if (heroId) { const adminSelectedHero = photos.find(p => p.id === heroId); if (adminSelectedHero) { setHeroPhoto(adminSelectedHero); setHasInitialized(true); return; } } // Only auto-select first photo on initial load if (!hasInitialized) { setHeroPhoto(photos[0]); setHasInitialized(true); } } }, [photos, gallerySettings.heroImageId, hasInitialized, heroPhotoOverride]); if (!heroPhoto) return null; // Show all photos including the hero photo in the grid const remainingPhotos = photos; return ( <>
{/* Hero Section */}
{/* Overlay */}
{/* Hero Content */}
{/* Logo - Show custom logo or fallback to PicPeak logo */}
Event logo
{/* Event Title */} {eventName && (

{eventName}

)} {/* Event Dates */} {(eventDate || expiresAt) && (
{eventDate && ( {format(parseISO(eventDate), 'PP')} )} {expiresAt && ( {t('gallery.expires')} {format(parseISO(expiresAt), 'PP')} )}
)}
{/* Scroll Indicator */}
{/* Grid Section */}
{remainingPhotos.map((photo) => { const actualIndex = photos.findIndex(p => p.id === photo.id); return (
onPhotoClick(actualIndex)} >
{!isSelectionMode && ( <> {allowDownloads && ( )} {feedbackOptions?.allowLikes && ( )} {canQuickComment && ( )} )}
{/* Selection Checkbox (visible on hover or when selected) */} {/* Feedback indicators (always visible, bottom-left). Show like immediately when liked */} {((photo.like_count ?? 0) > 0 || likedIds.has(photo.id) || (photo.average_rating ?? 0) > 0 || (photo.comment_count ?? 0) > 0) && (
{((photo.like_count ?? 0) > 0 || likedIds.has(photo.id)) && ( )} {(photo.average_rating ?? 0) > 0 && ( )} {(photo.comment_count ?? 0) > 0 && ( )}
)}
); })}
{ 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" /> ); };