import { useState, useEffect } from 'react'; import toast from 'react-hot-toast'; import { eventsApi, ticketsApi, emailsApi, doorApi, Event, Ticket, EmailTemplate, DoorSummary, type EventPermission } from '@/lib/api'; /** A 403 means "not for this viewer": render without that data instead of failing the page. */ const forbidden = (error: any) => error?.status === 403; /** * Loads the core data for the event detail page and exposes a reload function * used after mutations. The event itself is fetched straight away so the * header can render while the rest loads; tickets, active email templates and * door takings wait for the viewer's permissions on the event and only request * what they may see, so team members get a working page with fewer tabs. * * `eventLoading` and `detailsLoading` cover the first load only: reloads keep * the current data on screen until the new data replaces it. */ export function useEventDetailData(eventId: string, permissions: Set | null) { const [eventLoading, setEventLoading] = useState(true); const [detailsLoading, setDetailsLoading] = useState(true); const [event, setEvent] = useState(null); const [tickets, setTickets] = useState([]); const [templates, setTemplates] = useState([]); const [doorSummary, setDoorSummary] = useState(null); const loadEvent = async () => { try { const { event } = await eventsApi.getById(eventId); setEvent(event); } catch (error) { toast.error('Failed to load event data'); } finally { setEventLoading(false); } }; const loadDetails = async () => { if (!permissions) return; const can = (p: EventPermission) => permissions.has(p); try { const [ticketsRes, templatesRes, doorRes] = await Promise.all([ can('view_attendees_names') ? ticketsApi.getAll({ eventId }).catch((error) => { if (forbidden(error)) return { tickets: [] as Ticket[] }; throw error; }) : { tickets: [] as Ticket[] }, can('email_attendees') ? emailsApi.getTemplates(eventId).catch((error) => { if (forbidden(error)) return { templates: [] as EmailTemplate[] }; throw error; }) : { templates: [] as EmailTemplate[] }, // Door takings split pre-sale from cash/bitcoin/transfer taken on the // night. It is supporting detail, so a failure here must not blank the page. can('view_payments') ? doorApi.summary(eventId).catch(() => null) : null, ]); setTickets(ticketsRes.tickets); setTemplates(templatesRes.templates.filter(t => t.isActive)); setDoorSummary(doorRes); } catch (error) { toast.error('Failed to load event data'); } finally { setDetailsLoading(false); } }; const loadEventData = async () => { await Promise.all([loadEvent(), loadDetails()]); }; useEffect(() => { loadEvent(); }, [eventId]); useEffect(() => { loadDetails(); }, [eventId, permissions]); return { eventLoading, detailsLoading, event, tickets, templates, doorSummary, loadEventData }; }