import React, { useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { ArrowLeft, ExternalLink, Calendar, Download, Archive, Edit2, Save, X, AlertTriangle, Copy, CheckCircle, Upload, Image, Key, Palette, Settings } from 'lucide-react'; import { parseISO, differenceInDays } from 'date-fns'; import { toast } from 'react-toastify'; import { useLocalizedDate } from '../../hooks/useLocalizedDate'; import { Button, Input, Card, Loading } from '../../components/common'; import { PhotoUpload, EventCategoryManager, AdminPhotoGrid, AdminPhotoViewer, PhotoFilters, PasswordResetModal, ThemeDisplay, ThemeCustomizerEnhanced, ThemeEditorModal, HeroPhotoSelector } from '../../components/admin'; import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { eventsService } from '../../services/events.service'; import { archiveService } from '../../services/archive.service'; import { photosService, AdminPhoto } from '../../services/photos.service'; import { ThemeConfig, GALLERY_THEME_PRESETS } from '../../types/theme.types'; export const EventDetailsPage: React.FC = () => { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const queryClient = useQueryClient(); const { t } = useTranslation(); const { format } = useLocalizedDate(); // Validate ID parameter React.useEffect(() => { if (!id || isNaN(parseInt(id))) { navigate('/admin/events'); } }, [id, navigate]); const [isEditing, setIsEditing] = useState(false); const [editForm, setEditForm] = useState({ welcome_message: '', color_theme: '', expires_at: '', allow_user_uploads: false, upload_category_id: null as number | null, hero_photo_id: null as number | null, host_name: '', }); const [copiedLink, setCopiedLink] = useState(false); const [showPhotoUpload, setShowPhotoUpload] = useState(false); const [activeTab, setActiveTab] = useState<'overview' | 'photos' | 'categories'>('overview'); const [selectedPhoto, setSelectedPhoto] = useState<{ photo: AdminPhoto; index: number } | null>(null); const [showPasswordReset, setShowPasswordReset] = useState(false); const [showThemeCustomizer, setShowThemeCustomizer] = useState(false); const [currentTheme, setCurrentTheme] = useState(null); const [currentPresetName, setCurrentPresetName] = useState('default'); const [showThemeEditorModal, setShowThemeEditorModal] = useState(false); // Photo filters state const [photoFilters, setPhotoFilters] = useState({ category_id: undefined as number | null | undefined, search: '', sort: 'date' as 'date' | 'name' | 'size', order: 'desc' as 'asc' | 'desc' }); // Fetch event details const { data: event, isLoading: eventLoading } = useQuery({ queryKey: ['admin-event', id], queryFn: () => eventsService.getEvent(parseInt(id!)), enabled: !!id, }); // Statistics are now fetched with the event details from the admin API // Fetch photos (needed for both photos tab and hero photo selector) const { data: photos = [], isLoading: photosLoading, refetch: refetchPhotos } = useQuery({ queryKey: ['admin-event-photos', id, photoFilters], queryFn: () => photosService.getEventPhotos(parseInt(id!), photoFilters), enabled: !!id && (activeTab === 'photos' || isEditing), }); // Fetch categories for the event const { data: categories = [] } = useQuery({ queryKey: ['admin-event-categories', id], queryFn: async () => { const response = await eventsService.getEventCategories(parseInt(id!)); return response || []; }, enabled: !!id, }); // Update mutation const updateMutation = useMutation({ mutationFn: (data: any) => eventsService.updateEvent(parseInt(id!), data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-event', id] }); toast.success(t('toast.eventUpdated')); setIsEditing(false); }, onError: (error: any) => { console.error('Update event error:', error.response?.data || error); if (error.response?.data?.errors) { console.error('Validation errors:', error.response.data.errors); const errorMessage = error.response.data.errors[0].msg + ' (field: ' + error.response.data.errors[0].path + ')'; toast.error(errorMessage); } else { toast.error(error.response?.data?.error || t('toast.saveError')); } }, }); // Archive mutation const archiveMutation = useMutation({ mutationFn: () => eventsService.archiveEvent(parseInt(id!)), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-event', id] }); toast.success(t('toast.eventArchived')); }, onError: () => { toast.error(t('errors.somethingWentWrong')); }, }); // Extend expiration mutation const extendMutation = useMutation({ mutationFn: (days: number) => { return eventsService.extendExpiration(parseInt(id!), days); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['admin-event', id] }); toast.success(t('toast.saveSuccess')); }, onError: () => { toast.error(t('toast.saveError')); }, }); if (eventLoading || !event) { return (
); } const daysUntilExpiration = differenceInDays(parseISO(event.expires_at), new Date()); const isExpired = daysUntilExpiration <= 0; const isExpiring = daysUntilExpiration > 0 && daysUntilExpiration <= 7; const handleStartEdit = () => { setEditForm({ welcome_message: event.welcome_message || '', color_theme: event.color_theme || '', expires_at: format(parseISO(event.expires_at), 'yyyy-MM-dd'), allow_user_uploads: event.allow_user_uploads || false, upload_category_id: event.upload_category_id || null, hero_photo_id: event.hero_photo_id || null, host_name: event.host_name || '', }); // Parse theme configuration if (event.color_theme) { try { if (event.color_theme.startsWith('{')) { const parsedTheme = JSON.parse(event.color_theme); setCurrentTheme(parsedTheme); // Try to find matching preset const matchingPreset = Object.entries(GALLERY_THEME_PRESETS).find( ([_, preset]) => JSON.stringify(preset.config) === JSON.stringify(parsedTheme) ); setCurrentPresetName(matchingPreset ? matchingPreset[0] : 'custom'); } else { // Legacy theme name const preset = GALLERY_THEME_PRESETS[event.color_theme]; if (preset) { setCurrentTheme(preset.config); setCurrentPresetName(event.color_theme); } } } catch (e) { console.error('Failed to parse theme:', e); setCurrentTheme(GALLERY_THEME_PRESETS.default.config); setCurrentPresetName('default'); } } else { setCurrentTheme(GALLERY_THEME_PRESETS.default.config); setCurrentPresetName('default'); } setIsEditing(true); }; const handleSaveEdit = () => { // Prepare color_theme - if we have a custom theme, serialize it let themeToSave = editForm.color_theme; if (currentTheme && (currentPresetName === 'custom' || showThemeCustomizer)) { themeToSave = JSON.stringify(currentTheme); } else if (currentPresetName && currentPresetName !== 'custom') { // Use preset name for non-custom themes themeToSave = currentPresetName; } // Clean up the data - remove undefined values const updateData: any = { expires_at: editForm.expires_at, allow_user_uploads: editForm.allow_user_uploads, }; // Only include fields that have defined values if (editForm.welcome_message !== undefined && editForm.welcome_message !== null) { updateData.welcome_message = editForm.welcome_message; } if (themeToSave) { updateData.color_theme = themeToSave; } if (editForm.upload_category_id !== undefined) { updateData.upload_category_id = editForm.upload_category_id; } if (editForm.hero_photo_id !== undefined) { updateData.hero_photo_id = editForm.hero_photo_id; } if (editForm.host_name !== undefined && editForm.host_name !== null) { updateData.host_name = editForm.host_name; } // Remove any keys with undefined values Object.keys(updateData).forEach(key => { if (updateData[key] === undefined) { delete updateData[key]; } }); console.log('Updating event with data:', updateData); console.log('Theme length:', updateData.color_theme ? updateData.color_theme.length : 0); updateMutation.mutate(updateData); }; const handleCopyLink = async () => { try { await navigator.clipboard.writeText(event.share_link); setCopiedLink(true); setTimeout(() => setCopiedLink(false), 2000); toast.success(t('toast.linkCopied')); } catch (err) { toast.error(t('errors.somethingWentWrong')); } }; const handleThemeModalSave = async (theme: ThemeConfig, presetName: string) => { // Prepare theme for saving let themeToSave: string; if (presetName !== 'custom' && GALLERY_THEME_PRESETS[presetName]) { // Save preset name for standard presets themeToSave = presetName; } else { // Save full theme config for custom themes themeToSave = JSON.stringify(theme); } try { // Update the event with new theme await eventsService.updateEvent(parseInt(id!), { color_theme: themeToSave }); // Invalidate queries to refresh the data await queryClient.invalidateQueries({ queryKey: ['admin-event', id] }); // Close modal and show success setShowThemeEditorModal(false); toast.success(t('toast.themeUpdated')); } catch (error) { console.error('Failed to save theme:', error); toast.error(t('toast.saveError')); } }; return (
{/* Page Header */}

{event.event_name}

{format(parseISO(event.event_date), 'PPP')} {event.event_type} {event.is_archived ? ( {t('events.archived')} ) : null}
{!event.is_archived && ( <> {isEditing ? ( <> ) : ( )} )} {event.share_link && ( {t('events.viewGallery')} )}
{/* Expiration Warning */} {!event.is_archived && (isExpired || isExpiring) && (

{isExpired ? t('events.eventExpiredMessage') : t('events.eventExpiresIn', { days: daysUntilExpiration }) }

{isExpired ? t('events.guestsCannotAccessGallery') : t('events.warningEmailsHaveBeenSent')}

{!isExpired && ( )}
)} {/* Tabs */}
{/* Tab Content */} {activeTab === 'overview' && (
{/* Left Column - Main Details */}
{/* Event Information */}

{t('events.eventInformation')}

{isEditing ? (