'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useLanguage } from '@/context/LanguageContext'; import { eventsApi, Event } from '@/lib/api'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import Input from '@/components/ui/Input'; import MediaPicker from '@/components/MediaPicker'; import { PlusIcon, PencilIcon, TrashIcon, EyeIcon, PhotoIcon, DocumentDuplicateIcon, ArchiveBoxIcon } from '@heroicons/react/24/outline'; import toast from 'react-hot-toast'; import clsx from 'clsx'; export default function AdminEventsPage() { const { t, locale } = useLanguage(); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingEvent, setEditingEvent] = useState(null); const [saving, setSaving] = useState(false); const [formData, setFormData] = useState<{ title: string; titleEs: string; description: string; descriptionEs: string; shortDescription: string; shortDescriptionEs: string; startDatetime: string; endDatetime: string; location: string; locationUrl: string; price: number; currency: string; capacity: number; status: 'draft' | 'published' | 'cancelled' | 'completed' | 'archived'; bannerUrl: string; externalBookingEnabled: boolean; externalBookingUrl: string; }>({ title: '', titleEs: '', description: '', descriptionEs: '', shortDescription: '', shortDescriptionEs: '', startDatetime: '', endDatetime: '', location: '', locationUrl: '', price: 0, currency: 'PYG', capacity: 50, status: 'draft', bannerUrl: '', externalBookingEnabled: false, externalBookingUrl: '', }); useEffect(() => { loadEvents(); }, []); const loadEvents = async () => { try { const { events } = await eventsApi.getAll(); setEvents(events); } catch (error) { toast.error('Failed to load events'); } finally { setLoading(false); } }; const resetForm = () => { setFormData({ title: '', titleEs: '', description: '', descriptionEs: '', shortDescription: '', shortDescriptionEs: '', startDatetime: '', endDatetime: '', location: '', locationUrl: '', price: 0, currency: 'PYG', capacity: 50, status: 'draft' as const, bannerUrl: '', externalBookingEnabled: false, externalBookingUrl: '', }); setEditingEvent(null); }; // Convert ISO UTC string to local datetime-local format (YYYY-MM-DDTHH:MM) const isoToLocalDatetime = (isoString: string): string => { const date = new Date(isoString); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day}T${hours}:${minutes}`; }; const handleEdit = (event: Event) => { setFormData({ title: event.title, titleEs: event.titleEs || '', description: event.description, descriptionEs: event.descriptionEs || '', shortDescription: event.shortDescription || '', shortDescriptionEs: event.shortDescriptionEs || '', startDatetime: isoToLocalDatetime(event.startDatetime), endDatetime: event.endDatetime ? isoToLocalDatetime(event.endDatetime) : '', location: event.location, locationUrl: event.locationUrl || '', price: event.price, currency: event.currency, capacity: event.capacity, status: event.status, bannerUrl: event.bannerUrl || '', externalBookingEnabled: event.externalBookingEnabled || false, externalBookingUrl: event.externalBookingUrl || '', }); setEditingEvent(event); setShowForm(true); }; const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setSaving(true); try { // Validate external booking URL if enabled if (formData.externalBookingEnabled && !formData.externalBookingUrl) { toast.error('External booking URL is required when external booking is enabled'); setSaving(false); return; } if (formData.externalBookingEnabled && !formData.externalBookingUrl.startsWith('https://')) { toast.error('External booking URL must be a valid HTTPS link'); setSaving(false); return; } const eventData = { title: formData.title, titleEs: formData.titleEs || undefined, description: formData.description, descriptionEs: formData.descriptionEs || undefined, shortDescription: formData.shortDescription || undefined, shortDescriptionEs: formData.shortDescriptionEs || undefined, startDatetime: new Date(formData.startDatetime).toISOString(), endDatetime: formData.endDatetime ? new Date(formData.endDatetime).toISOString() : undefined, location: formData.location, locationUrl: formData.locationUrl || undefined, price: formData.price, currency: formData.currency, capacity: formData.capacity, status: formData.status, bannerUrl: formData.bannerUrl || undefined, externalBookingEnabled: formData.externalBookingEnabled, externalBookingUrl: formData.externalBookingEnabled ? formData.externalBookingUrl : undefined, }; if (editingEvent) { await eventsApi.update(editingEvent.id, eventData); toast.success('Event updated'); } else { await eventsApi.create(eventData); toast.success('Event created'); } setShowForm(false); resetForm(); loadEvents(); } catch (error: any) { toast.error(error.message || 'Failed to save event'); } finally { setSaving(false); } }; const handleDelete = async (id: string) => { if (!confirm('Are you sure you want to delete this event?')) return; try { await eventsApi.delete(id); toast.success('Event deleted'); loadEvents(); } catch (error) { toast.error('Failed to delete event'); } }; const handleStatusChange = async (event: Event, status: Event['status']) => { try { await eventsApi.update(event.id, { status }); toast.success('Status updated'); loadEvents(); } catch (error) { toast.error('Failed to update status'); } }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString(locale === 'es' ? 'es-ES' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric', }); }; const getStatusBadge = (status: string) => { const styles: Record = { draft: 'badge-gray', published: 'badge-success', cancelled: 'badge-danger', completed: 'badge-info', archived: 'badge-gray', }; return {status}; }; const handleDuplicate = async (event: Event) => { try { await eventsApi.duplicate(event.id); toast.success('Event duplicated successfully'); loadEvents(); } catch (error) { toast.error('Failed to duplicate event'); } }; const handleArchive = async (event: Event) => { try { await eventsApi.update(event.id, { status: 'archived' }); toast.success('Event archived'); loadEvents(); } catch (error) { toast.error('Failed to archive event'); } }; if (loading) { return (
); } return (

{t('admin.events.title')}

{/* Event Form Modal */} {showForm && (

{editingEvent ? t('admin.events.edit') : t('admin.events.create')}

setFormData({ ...formData, title: e.target.value })} required /> setFormData({ ...formData, titleEs: e.target.value })} />