'use client'; import { useState, useEffect } from 'react'; import { useLanguage } from '@/context/LanguageContext'; import { ticketsApi, eventsApi, Ticket, Event } from '@/lib/api'; import Card from '@/components/ui/Card'; import Button from '@/components/ui/Button'; import { TicketIcon, CheckCircleIcon, XCircleIcon, CurrencyDollarIcon, UserIcon, EnvelopeIcon, PhoneIcon, FunnelIcon, } from '@heroicons/react/24/outline'; import toast from 'react-hot-toast'; interface TicketWithDetails extends Omit { bookingId?: string; event?: Event; payment?: { id: string; ticketId?: string; provider: string; amount: number; currency: string; status: string; reference?: string; createdAt?: string; updatedAt?: string; }; } export default function AdminBookingsPage() { const { locale } = useLanguage(); const [tickets, setTickets] = useState([]); const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); const [processing, setProcessing] = useState(null); // Filters const [selectedEvent, setSelectedEvent] = useState(''); const [selectedStatus, setSelectedStatus] = useState(''); const [selectedPaymentStatus, setSelectedPaymentStatus] = useState(''); useEffect(() => { loadData(); }, []); const loadData = async () => { try { const [ticketsRes, eventsRes] = await Promise.all([ ticketsApi.getAll(), eventsApi.getAll(), ]); // Fetch full ticket details with payment info const ticketsWithDetails = await Promise.all( ticketsRes.tickets.map(async (ticket) => { try { const { ticket: fullTicket } = await ticketsApi.getById(ticket.id); return fullTicket; } catch { return ticket; } }) ); setTickets(ticketsWithDetails); setEvents(eventsRes.events); } catch (error) { toast.error('Failed to load bookings'); } finally { setLoading(false); } }; const handleMarkPaid = async (ticketId: string) => { setProcessing(ticketId); try { await ticketsApi.markPaid(ticketId); toast.success('Payment marked as received'); loadData(); } catch (error: any) { toast.error(error.message || 'Failed to mark payment'); } finally { setProcessing(null); } }; const handleCheckin = async (ticketId: string) => { setProcessing(ticketId); try { await ticketsApi.checkin(ticketId); toast.success('Check-in successful'); loadData(); } catch (error: any) { toast.error(error.message || 'Failed to check in'); } finally { setProcessing(null); } }; const handleCancel = async (ticketId: string) => { if (!confirm('Are you sure you want to cancel this booking?')) return; setProcessing(ticketId); try { await ticketsApi.cancel(ticketId); toast.success('Booking cancelled'); loadData(); } catch (error: any) { toast.error(error.message || 'Failed to cancel'); } finally { setProcessing(null); } }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString(locale === 'es' ? 'es-ES' : 'en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', }); }; const getStatusColor = (status: string) => { switch (status) { case 'confirmed': return 'bg-green-100 text-green-800'; case 'pending': return 'bg-yellow-100 text-yellow-800'; case 'cancelled': return 'bg-red-100 text-red-800'; case 'checked_in': return 'bg-blue-100 text-blue-800'; default: return 'bg-gray-100 text-gray-800'; } }; const getPaymentStatusColor = (status: string) => { switch (status) { case 'paid': return 'bg-green-100 text-green-800'; case 'pending': return 'bg-yellow-100 text-yellow-800'; case 'failed': case 'cancelled': return 'bg-red-100 text-red-800'; case 'refunded': return 'bg-purple-100 text-purple-800'; default: return 'bg-gray-100 text-gray-800'; } }; const getPaymentMethodLabel = (provider: string) => { switch (provider) { case 'bancard': return 'TPago / Card'; case 'lightning': return 'Bitcoin Lightning'; case 'cash': return 'Cash at Event'; default: return provider; } }; // Filter tickets const filteredTickets = tickets.filter((ticket) => { if (selectedEvent && ticket.eventId !== selectedEvent) return false; if (selectedStatus && ticket.status !== selectedStatus) return false; if (selectedPaymentStatus && ticket.payment?.status !== selectedPaymentStatus) return false; return true; }); // Sort by created date (newest first) const sortedTickets = [...filteredTickets].sort( (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime() ); // Stats const stats = { total: tickets.length, pending: tickets.filter(t => t.status === 'pending').length, confirmed: tickets.filter(t => t.status === 'confirmed').length, checkedIn: tickets.filter(t => t.status === 'checked_in').length, cancelled: tickets.filter(t => t.status === 'cancelled').length, pendingPayment: tickets.filter(t => t.payment?.status === 'pending').length, }; // Helper to get booking info for a ticket (ticket count and total) const getBookingInfo = (ticket: TicketWithDetails) => { if (!ticket.bookingId) { return { ticketCount: 1, bookingTotal: Number(ticket.payment?.amount || 0) }; } // Count all tickets with the same bookingId const bookingTickets = tickets.filter( t => t.bookingId === ticket.bookingId ); return { ticketCount: bookingTickets.length, bookingTotal: bookingTickets.reduce((sum, t) => sum + Number(t.payment?.amount || 0), 0), }; }; if (loading) { return (
); } return (

Manage Bookings

{/* Stats Cards */}

{stats.total}

Total

{stats.pending}

Pending

{stats.confirmed}

Confirmed

{stats.checkedIn}

Checked In

{stats.cancelled}

Cancelled

{stats.pendingPayment}

Pending Payment

{/* Filters */}
Filters
{/* Bookings List */}
{sortedTickets.length === 0 ? ( ) : ( sortedTickets.map((ticket) => { const bookingInfo = getBookingInfo(ticket); return ( ); }) )}
Attendee Event Payment Status Booked Actions
No bookings found.
{ticket.attendeeFirstName} {ticket.attendeeLastName || ''}
{ticket.attendeeEmail || 'N/A'}
{ticket.attendeePhone || 'N/A'}
{ticket.event?.title || events.find(e => e.id === ticket.eventId)?.title || 'Unknown'}
{ticket.payment?.status || 'pending'}

{getPaymentMethodLabel(ticket.payment?.provider || 'cash')}

{ticket.payment && (

{bookingInfo.bookingTotal.toLocaleString()} {ticket.payment.currency}

{bookingInfo.ticketCount > 1 && (

📦 {bookingInfo.ticketCount} × {Number(ticket.payment.amount).toLocaleString()} {ticket.payment.currency}

)}
)}
{ticket.status} {ticket.qrCode && (

{ticket.qrCode}

)} {ticket.bookingId && (

📦 Group Booking

)}
{formatDate(ticket.createdAt)}
{/* Mark as Paid (for pending payments) */} {ticket.status === 'pending' && ticket.payment?.status === 'pending' && ( )} {/* Check-in (for confirmed tickets) */} {ticket.status === 'confirmed' && ( )} {/* Cancel (for pending/confirmed) */} {(ticket.status === 'pending' || ticket.status === 'confirmed') && ( )} {ticket.status === 'checked_in' && ( Attended )} {ticket.status === 'cancelled' && ( Cancelled )}
); }