import { useMutation, useQuery } from "@tanstack/react-query"; import { createFileRoute, Link, redirect, useNavigate, } from "@tanstack/react-router"; import { Check, Download, LogOut, Search, Users, X } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { authClient } from "@/lib/auth-client"; import { orpc } from "@/utils/orpc"; export const Route = createFileRoute("/admin")({ component: AdminPage, beforeLoad: async () => { const session = await authClient.getSession(); if (!session.data?.user) { throw redirect({ to: "/login" }); } const user = session.data.user as { role?: string }; if (user.role !== "admin") { throw redirect({ to: "/login" }); } }, }); function AdminPage() { const navigate = useNavigate(); const [search, setSearch] = useState(""); const [registrationType, setRegistrationType] = useState< "performer" | "watcher" | "" >(""); const [artForm, setArtForm] = useState(""); const [fromDate, setFromDate] = useState(""); const [toDate, setToDate] = useState(""); const [page, setPage] = useState(1); const pageSize = 20; const statsQuery = useQuery(orpc.getRegistrationStats.queryOptions()); const registrationsQuery = useQuery( orpc.getRegistrations.queryOptions({ input: { search: search || undefined, registrationType: registrationType || undefined, artForm: artForm || undefined, fromDate: fromDate || undefined, toDate: toDate || undefined, page, pageSize, }, }), ); const adminRequestsQuery = useQuery(orpc.getAdminRequests.queryOptions()); const exportMutation = useMutation({ ...orpc.exportRegistrations.mutationOptions(), onSuccess: (data: { csv: string; filename: string }) => { const blob = new Blob([data.csv], { type: "text/csv" }); const url = window.URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = data.filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); window.URL.revokeObjectURL(url); }, }); const approveRequestMutation = useMutation({ ...orpc.approveAdminRequest.mutationOptions(), onSuccess: () => { toast.success("Admin toegang goedgekeurd"); adminRequestsQuery.refetch(); }, onError: (error) => { toast.error(`Fout: ${error.message}`); }, }); const rejectRequestMutation = useMutation({ ...orpc.rejectAdminRequest.mutationOptions(), onSuccess: () => { toast.success("Admin toegang geweigerd"); adminRequestsQuery.refetch(); }, onError: (error) => { toast.error(`Fout: ${error.message}`); }, }); const handleSignOut = async () => { await authClient.signOut(); navigate({ to: "/" }); }; const handleExport = () => { exportMutation.mutate(undefined); }; const handleApprove = (requestId: string) => { approveRequestMutation.mutate({ requestId }); }; const handleReject = (requestId: string) => { rejectRequestMutation.mutate({ requestId }); }; const stats = statsQuery.data; const registrations = registrationsQuery.data?.data ?? []; const pagination = registrationsQuery.data?.pagination; const adminRequests = adminRequestsQuery.data ?? []; const pendingRequests = adminRequests.filter((r) => r.status === "pending"); const performerCount = stats?.byType.find((t) => t.registrationType === "performer")?.count ?? 0; const watcherCount = stats?.byType.find((t) => t.registrationType === "watcher")?.count ?? 0; // Calculate total attendees including guests const totalRegistrations = registrationsQuery.data?.data ?? []; const watcherWithGuests = totalRegistrations.filter( (r) => r.registrationType === "watcher" && r.guests, ); const totalGuestCount = watcherWithGuests.reduce((sum, r) => { try { const guests = JSON.parse(r.guests as string); return sum + (Array.isArray(guests) ? guests.length : 0); } catch { return sum; } }, 0); const totalWatcherAttendees = watcherCount + totalGuestCount; const totalDrinkCardValue = totalRegistrations .filter((r) => r.registrationType === "watcher") .reduce((sum, r) => sum + (r.drinkCardValue ?? 0), 0); return (
{/* Header */}
← Terug naar website

Admin Dashboard

{/* Main Content */}
{/* Pending Admin Requests */} {pendingRequests.length > 0 && ( Openstaande Admin Aanvragen ({pendingRequests.length}) Gebruikers die admin toegang hebben aangevraagd
{pendingRequests.map((request) => (

{request.userName}

{request.userEmail}

Aangevraagd:{" "} {new Date(request.requestedAt).toLocaleDateString( "nl-BE", )}

))}
)} {/* Stats Cards */}
Totaal inschrijvingen {stats?.total ?? 0} Vandaag ingeschreven {stats?.today ?? 0} Nieuwe registraties vandaag Artiesten {performerCount}
{stats?.byArtForm.slice(0, 4).map((item) => (
{item.artForm || "Onbekend"} {item.count}
))}
Bezoekers {watcherCount}
{totalGuestCount > 0 && (
Inclusief gasten +{totalGuestCount}
)}
Totaal aanwezig {totalWatcherAttendees}
Drinkkaart €{totalDrinkCardValue}
{/* Filters */} Filters
setSearch(e.target.value)} className="border-white/20 bg-white/10 pl-10 text-white placeholder:text-white/40" />
setArtForm(e.target.value)} className="border-white/20 bg-white/10 text-white placeholder:text-white/40" />
setFromDate(e.target.value)} className="border-white/20 bg-white/10 text-white [color-scheme:dark]" />
setToDate(e.target.value)} className="border-white/20 bg-white/10 text-white [color-scheme:dark]" />
{/* Export Button */}

{pagination?.total ?? 0} registraties gevonden

{/* Registrations Table */}
{registrationsQuery.isLoading ? ( ) : registrations.length === 0 ? ( ) : ( registrations.map((reg) => { const isPerformer = reg.registrationType === "performer"; return ( ); }) )}
Naam Email Telefoon Type Kunstvorm / Drinkkaart Gezelschap Ervaring 16+ Betaald Betaald Datum
Laden...
Geen registraties gevonden
{reg.firstName} {reg.lastName} {reg.email} {reg.phone || "-"} {isPerformer ? "Artiest" : "Bezoeker"} {isPerformer ? reg.artForm || "-" : `€${reg.drinkCardValue ?? 5} drinkkaart`} {isPerformer ? "-" : (() => { if (!reg.guests) return "-"; try { const guests = JSON.parse( reg.guests as string, ); const count = Array.isArray(guests) ? guests.length : 0; return count > 0 ? `${count} gast${count === 1 ? "" : "en"}` : "-"; } catch { return "-"; } })()} {isPerformer ? reg.experience || "-" : "-"} {isPerformer ? ( reg.isOver16 ? ( ) : ( ) ) : ( "-" )} {isPerformer ? ( - ) : reg.paymentStatus === "paid" ? ( Betaald icoon Betaald ) : ( In afwachting icoon Open )} {new Date(reg.createdAt).toLocaleDateString( "nl-BE", )}
{/* Pagination */} {pagination && pagination.totalPages > 1 && (
Pagina {page} van {pagination.totalPages}
)}
); }