"use client"; import React, { Suspense, useEffect, useMemo, useState } from "react"; import { useAuth } from "@/hooks/useAuth"; import { apiFetch } from "@/lib/api"; import { useDismissingState } from "@/hooks/useDismissingState"; import { useRouter, useSearchParams } from "next/navigation"; import { formatDate } from "@/lib/date"; import { QrImage } from "@/components/shared/QrImage"; function EventTicketsContent() { const { token, user } = useAuth(); const params = useSearchParams(); const router = useRouter(); const paramEventId = params.get("eventId") || ""; const paramUserId = params.get("userId") || ""; const [events, setEvents] = useState([]); const [eventId, setEventId] = useState(paramEventId); const [tickets, setTickets] = useState([]); const [error, setError] = useDismissingState(null); const [loading, setLoading] = useState(false); const [selectedUserId, setSelectedUserId] = useState(paramUserId); const [selectedVariantId, setSelectedVariantId] = useState(""); const canView = useMemo(() => { const role = user?.role; return role === "admin" || role === "supervisor" || role === "staff"; }, [user]); // Load events for dropdown useEffect(() => { (async () => { try { if (!token) return; const evs = await apiFetch("/api/events/all", { authToken: token }); const now = Date.now(); const active = (evs || []).filter((ev) => { const t = new Date(ev.endDate).getTime(); return !isNaN(t) && t > now; }); active.sort( (a, b) => new Date(a.startDate).getTime() - new Date(b.startDate).getTime() ); setEvents(active); if (!paramEventId && active.length > 0) { setEventId(active[0].id); router.replace( `?eventId=${encodeURIComponent(active[0].id)}${ selectedUserId ? `&userId=${encodeURIComponent(selectedUserId)}` : "" }` ); } } catch (e: any) { setError(e?.message || "Failed to load events"); } })(); }, [token]); // Sync params → state useEffect(() => { if (paramEventId && paramEventId !== eventId) setEventId(paramEventId); if (paramUserId && paramUserId !== selectedUserId) setSelectedUserId(paramUserId); }, [paramEventId, paramUserId]); // Load tickets when event changes useEffect(() => { (async () => { if (!token || !eventId) return; setLoading(true); setError(null); try { const list = await apiFetch( `/api/tickets/event/${encodeURIComponent(eventId)}`, { authToken: token } ); setTickets(list); } catch (e: any) { setError(e?.message || "Failed to load tickets"); } finally { setLoading(false); } })(); }, [token, eventId]); // Build variants list for filter (only variants present in current event's tickets) const variantsList = useMemo(() => { const map = new Map(); for (const t of tickets) { const v = t.registrationOption?.variant; if (v?.id) map.set(v.id, v.name); } return Array.from(map.entries()) .map(([id, name]) => ({ id, name })) .sort((a, b) => a.name.localeCompare(b.name)); }, [tickets]); // Build users list depending on event selection const usersList = useMemo(() => { let source = [...tickets]; if (eventId) { source = source.filter((t) => String(t.eventId) === String(eventId)); } const map = new Map(); for (const t of source) { const uid = t.user?.id || t.userId; if (!uid) continue; const name = t.user?.name || t.user?.email || String(uid); if (!map.has(String(uid))) map.set(String(uid), name); } return Array.from(map.entries()) .map(([id, name]) => ({ id, name })) .sort((a, b) => a.name.localeCompare(b.name)); }, [tickets, eventId]); // Filter tickets by event + user + variant const filteredTickets = useMemo(() => { let list = [...tickets]; if (eventId) { list = list.filter((t) => String(t.eventId) === String(eventId)); } if (selectedUserId) { list = list.filter( (t) => String(t.user?.id || t.userId) === String(selectedUserId) ); } if (selectedVariantId) { list = list.filter( (t) => String(t.registrationOption?.variant?.id || "") === String(selectedVariantId) ); } return list; }, [tickets, eventId, selectedUserId, selectedVariantId]); // Printing helpers const buildTicketHtmlCard = (t: any) => { const eventTitle = t.event?.title || t.eventId || "Event"; const eventDate = t.event?.startDate ? new Date(t.event.startDate) : null; const optBase = t.registrationOption?.eventOption?.name || "Ticket"; const optVariant = t.registrationOption?.variant?.name; const type = optVariant ? `${optBase} — ${optVariant}` : optBase; const qty = t.quantity || 1; const holder = t.user?.name || t.userId || ""; const qrSrc = `https://api.qrserver.com/v1/create-qr-code/?size=140x140&data=${encodeURIComponent( t.qrCode || t.id )}`; const dateStr = eventDate ? formatDate(eventDate) : ""; return `
${eventTitle}
${dateStr ? `
${dateStr}
` : ""}
${type}
Qty: ${qty}
QR
ID: ${t.id}
${holder ? `
${holder}
` : ""}
`; }; const openPrintWindow = (pagesHtml: string) => { const w = window.open("", "_blank"); if (!w) return; w.document.write(`Tickets ${pagesHtml} `); w.document.close(); w.focus(); }; const printTickets = (ticketList: any[]) => { if (!ticketList || ticketList.length === 0) return; // Chunk into pages of 8 (2 columns × 4 rows per A4 sheet) const PAGE_SIZE = 8; const pages: string[] = []; for (let i = 0; i < ticketList.length; i += PAGE_SIZE) { const chunk = ticketList.slice(i, i + PAGE_SIZE); pages.push(`
${chunk.map(buildTicketHtmlCard).join("")}
`); } openPrintWindow(pages.join("")); }; return (

Event Tickets

View and print tickets for an event.

{!canView && (
You need staff, supervisor, or admin access to view this page.
)}
{variantsList.length > 0 && ( <> )} {filteredTickets.length > 0 && (
)}
{loading &&
Loading...
} {error &&
{error}
} {filteredTickets.length > 0 && (
Total tickets: {filteredTickets.length} Used: {filteredTickets.filter((t) => t.isUsed).length} Unused: {filteredTickets.filter((t) => !t.isUsed).length}
)}
{filteredTickets.map((t) => { const eventDate = t.event?.startDate ? new Date(t.event.startDate) : null; const optBase = t.registrationOption?.eventOption?.name || "Ticket"; const optVariant = t.registrationOption?.variant?.name; const type = optVariant ? `${optBase} — ${optVariant}` : optBase; return (
{t.event?.title || t.eventId}
{eventDate && ( {formatDate(eventDate)} )}
{type}
#{String(t.id).slice(0, 8)} •{" "} {t.isUsed ? "Used" : "Unused"}
); })}
); } export default function EventTicketsPage() { return ( Loading...}> ); }