'use client'; import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useRouter, useSearchParams } from 'next/navigation'; import { useLanguage } from '@/context/LanguageContext'; import { eventsApi, siteSettingsApi, Event } from '@/lib/api'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import { AdminPageSkeleton } from '@/components/ui/Skeleton'; import { MoreMenu, DropdownItem, AdminMobileStyles } from '@/components/admin/MobileComponents'; import { PlusIcon, PencilIcon, TrashIcon, EyeIcon, PhotoIcon, DocumentDuplicateIcon, ArchiveBoxIcon, StarIcon, LinkIcon } from '@heroicons/react/24/outline'; import { StarIcon as StarIconSolid } from '@heroicons/react/24/solid'; import toast from 'react-hot-toast'; import clsx from 'clsx'; import { parseDate } from '@/lib/utils'; import EventFormModal from './_components/EventFormModal'; export default function AdminEventsPage() { const router = useRouter(); const { t, locale } = useLanguage(); const searchParams = useSearchParams(); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [showForm, setShowForm] = useState(false); const [editingEvent, setEditingEvent] = useState(null); const [featuredEventId, setFeaturedEventId] = useState(null); const [settingFeatured, setSettingFeatured] = useState(null); useEffect(() => { loadEvents(); loadFeaturedEvent(); }, []); useEffect(() => { const editId = searchParams.get('edit'); if (editId && events.length > 0) { const event = events.find(e => e.id === editId); if (event) handleEdit(event); } }, [searchParams, events]); const loadEvents = async () => { try { const { events } = await eventsApi.getAll(); setEvents(events); } catch (error) { toast.error('Failed to load events'); } finally { setLoading(false); } }; const loadFeaturedEvent = async () => { try { const { settings } = await siteSettingsApi.get(); setFeaturedEventId(settings.featuredEventId || null); } catch (error) { // Ignore - settings may not exist yet } }; const handleSetFeatured = async (eventId: string | null) => { setSettingFeatured(eventId || 'clearing'); try { await siteSettingsApi.setFeaturedEvent(eventId); setFeaturedEventId(eventId); toast.success(eventId ? 'Event set as featured' : 'Featured event removed'); } catch (error: any) { toast.error(error.message || 'Failed to update featured event'); } finally { setSettingFeatured(null); } }; const handleEdit = (event: Event) => { setEditingEvent(event); setShowForm(true); }; const handleCloseForm = () => { setShowForm(false); setEditingEvent(null); }; const handleFormSaved = () => { handleCloseForm(); loadEvents(); }; 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 parseDate(dateStr).toLocaleDateString(locale === 'es' ? 'es-ES' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric', timeZone: 'America/Asuncion', }); }; const isEventOver = (event: Event) => { const refDate = event.endDatetime || event.startDatetime; return new Date(refDate) < new Date(); }; const getStatusBadge = (status: string) => { const styles: Record = { draft: 'badge-gray', published: 'badge-success', unlisted: 'badge-warning', 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')}

{/* Desktop: Table */}
{events.length === 0 ? ( ) : ( events.map((event) => ( router.push(`/admin/events/${event.id}`)} className={clsx("hover:bg-gray-50 cursor-pointer", featuredEventId === event.id && "bg-amber-50")} > )) )}
Event Date Capacity Status Actions
No events found. Create your first event!
{event.bannerUrl ? ( {event.title} ) : (
)}

{event.title}

{featuredEventId === event.id && ( Featured )}

{event.location}

{formatDate(event.startDatetime)} {(event.bookedCount || 0) + (event.claimedCount || 0)} / {event.capacity} {(event.claimedCount || 0) > 0 && ( {event.claimedCount} pending approval )}
{getStatusBadge(event.status)} {isEventOver(event) && event.status !== 'completed' && event.status !== 'cancelled' && event.status !== 'archived' && ( Over )}
e.stopPropagation()}>
{event.status === 'draft' && ( )} {event.status === 'published' && ( )} {(event.status === 'draft' || event.status === 'published') && ( handleStatusChange(event, 'unlisted')}> Make Unlisted )} {event.status === 'unlisted' && ( handleStatusChange(event, 'published')}> Make Public )} {(event.status === 'published' || event.status === 'unlisted') && ( handleStatusChange(event, 'draft')}> Unpublish )} handleDuplicate(event)}> Duplicate {event.status !== 'archived' && ( handleArchive(event)}> Archive )} handleDelete(event.id)} className="text-red-600"> Delete
{/* Mobile: Card List */}
{events.length === 0 ? (
No events found. Create your first event!
) : ( events.map((event) => ( router.push(`/admin/events/${event.id}`)} >
{event.bannerUrl ? ( {event.title} ) : (
)}

{event.title}

{formatDate(event.startDatetime)}

{event.location}

{getStatusBadge(event.status)} {isEventOver(event) && event.status !== 'completed' && event.status !== 'cancelled' && event.status !== 'archived' && ( Over )} {featuredEventId === event.id && ( )}

{(event.bookedCount || 0) + (event.claimedCount || 0)} / {event.capacity} spots {(event.claimedCount || 0) > 0 && ( ยท {event.claimedCount} pending )}

e.stopPropagation()}> handleEdit(event)}> Edit {event.status === 'draft' && ( handleStatusChange(event, 'published')}> Publish )} {(event.status === 'draft' || event.status === 'published') && ( handleStatusChange(event, 'unlisted')}> Make Unlisted )} {event.status === 'unlisted' && ( handleStatusChange(event, 'published')}> Make Public )} {(event.status === 'published' || event.status === 'unlisted') && ( handleStatusChange(event, 'draft')}> Unpublish )} {event.status === 'published' && ( handleSetFeatured(featuredEventId === event.id ? null : event.id)}> {featuredEventId === event.id ? 'Unfeature' : 'Set Featured'} )} handleDuplicate(event)}> Duplicate {event.status !== 'archived' && ( handleArchive(event)}> Archive )} handleDelete(event.id)} className="text-red-600"> Delete
)) )}
{/* Mobile FAB */}
); }