'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import Image from 'next/image'; import { useLanguage } from '@/context/LanguageContext'; import { eventsApi, Event } from '@/lib/api'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import ShareButtons from '@/components/ShareButtons'; import { CalendarIcon, MapPinIcon, UserGroupIcon, ArrowLeftIcon, } from '@heroicons/react/24/outline'; interface EventDetailClientProps { eventId: string; initialEvent: Event; } export default function EventDetailClient({ eventId, initialEvent }: EventDetailClientProps) { const { t, locale } = useLanguage(); const [event, setEvent] = useState(initialEvent); const [mounted, setMounted] = useState(false); // Ensure consistent hydration by only rendering dynamic content after mount useEffect(() => { setMounted(true); }, []); // Refresh event data on client for real-time availability useEffect(() => { eventsApi.getById(eventId) .then(({ event }) => setEvent(event)) .catch(console.error); }, [eventId]); const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString(locale === 'es' ? 'es-ES' : 'en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', }); }; const formatTime = (dateStr: string) => { return new Date(dateStr).toLocaleTimeString(locale === 'es' ? 'es-ES' : 'en-US', { hour: '2-digit', minute: '2-digit', }); }; const isSoldOut = event.availableSeats === 0; const isCancelled = event.status === 'cancelled'; // Only calculate isPastEvent after mount to avoid hydration mismatch const isPastEvent = mounted ? new Date(event.startDatetime) < new Date() : false; const canBook = !isSoldOut && !isCancelled && !isPastEvent && event.status === 'published'; return (
{t('common.back')}
{/* Event Details */}
{/* Banner - LCP element, loaded with high priority */} {/* Using unoptimized for backend-served images via /uploads/ rewrite */} {event.bannerUrl ? (
{`${event.title}
) : (
)}

{locale === 'es' && event.titleEs ? event.titleEs : event.title}

{isCancelled && ( {t('events.details.cancelled')} )} {isSoldOut && !isCancelled && ( {t('events.details.soldOut')} )}

{t('events.details.date')}

{formatDate(event.startDatetime)}

{t('events.details.time')}

{formatTime(event.startDatetime)}

{t('events.details.location')}

{event.location}

{event.locationUrl && ( View on map )}

{t('events.details.capacity')}

{event.availableSeats} / {event.capacity} {t('events.details.spotsLeft')}

About this event

{locale === 'es' && event.descriptionEs ? event.descriptionEs : event.description}

{/* Social Sharing */}
{/* Booking Card */}

{t('events.details.price')}

{event.price === 0 ? t('events.details.free') : `${event.price.toLocaleString()} ${event.currency}`}

{canBook ? ( ) : ( )}

{event.availableSeats} {t('events.details.spotsLeft')}

); }