Files
yourwillyourwish/app/admin/registrations/page.tsx
2026-02-06 21:44:04 -06:00

197 lines
7.8 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
interface Registration {
id: string;
status: string;
paymentStatus: string;
createdAt: string;
user: {
email: string;
firstName: string;
lastName: string;
};
webinar: {
title: string;
dateTime: string;
price: number;
};
}
export default function AdminRegistrationsPage() {
const [registrations, setRegistrations] = useState<Registration[]>([]);
const [loading, setLoading] = useState(true);
const [filter, setFilter] = useState<string>("ALL");
useEffect(() => {
fetchRegistrations();
}, []);
const fetchRegistrations = async () => {
try {
const response = await fetch("/api/registrations");
if (response.ok) {
const data = await response.json();
setRegistrations(data.registrations || []);
}
} catch (error) {
console.error("Failed to fetch registrations:", error);
} finally {
setLoading(false);
}
};
const filteredRegistrations =
filter === "ALL"
? registrations
: registrations.filter((reg) => reg.status === filter);
const stats = {
total: registrations.length,
confirmed: registrations.filter((r) => r.status === "CONFIRMED").length,
pending: registrations.filter((r) => r.status === "PENDING").length,
cancelled: registrations.filter((r) => r.status === "CANCELLED").length,
};
return (
<main className="max-w-7xl mx-auto px-6 py-16">
<div className="mb-8">
<h1 className="text-4xl font-bold mb-4 bg-gradient-to-r from-primary to-secondary bg-clip-text text-transparent">
Registrations Management
</h1>
<p className="text-lg text-gray-600 dark:text-gray-400">
View and manage webinar registrations
</p>
</div>
<div className="grid md:grid-cols-4 gap-6 mb-8">
<div className="card p-6">
<div className="text-3xl font-bold text-primary mb-2">{stats.total}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Total Registrations</div>
</div>
<div className="card p-6">
<div className="text-3xl font-bold text-green-600 mb-2">{stats.confirmed}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Confirmed</div>
</div>
<div className="card p-6">
<div className="text-3xl font-bold text-yellow-600 mb-2">{stats.pending}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Pending</div>
</div>
<div className="card p-6">
<div className="text-3xl font-bold text-red-600 mb-2">{stats.cancelled}</div>
<div className="text-sm text-gray-600 dark:text-gray-400">Cancelled</div>
</div>
</div>
<div className="card p-6 mb-6">
<div className="flex gap-2">
{["ALL", "CONFIRMED", "PENDING", "CANCELLED"].map((status) => (
<button
key={status}
onClick={() => setFilter(status)}
className={`px-4 py-2 rounded-lg font-semibold transition-all ${
filter === status
? "bg-primary text-white"
: "bg-gray-100 dark:bg-slate-800 text-gray-700 dark:text-gray-300 hover:bg-gray-200 dark:hover:bg-slate-700"
}`}
>
{status}
</button>
))}
</div>
</div>
{loading ? (
<div className="text-center py-12">
<div className="inline-block w-8 h-8 border-4 border-primary border-r-transparent rounded-full animate-spin"></div>
<p className="mt-4 text-gray-600 dark:text-gray-400">Loading registrations...</p>
</div>
) : (
<div className="card overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead className="bg-gray-50 dark:bg-slate-800">
<tr>
<th className="px-6 py-4 text-left text-xs font-bold text-gray-700 dark:text-gray-300 uppercase">
User
</th>
<th className="px-6 py-4 text-left text-xs font-bold text-gray-700 dark:text-gray-300 uppercase">
Webinar
</th>
<th className="px-6 py-4 text-left text-xs font-bold text-gray-700 dark:text-gray-300 uppercase">
Status
</th>
<th className="px-6 py-4 text-left text-xs font-bold text-gray-700 dark:text-gray-300 uppercase">
Payment
</th>
<th className="px-6 py-4 text-left text-xs font-bold text-gray-700 dark:text-gray-300 uppercase">
Date
</th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-slate-700">
{filteredRegistrations.length === 0 ? (
<tr>
<td colSpan={5} className="px-6 py-12 text-center text-gray-500">
No registrations found
</td>
</tr>
) : (
filteredRegistrations.map((reg) => (
<tr key={reg.id} className="hover:bg-gray-50 dark:hover:bg-slate-800/50">
<td className="px-6 py-4">
<div className="font-semibold text-gray-900 dark:text-white">
{reg.user.firstName} {reg.user.lastName}
</div>
<div className="text-sm text-gray-500">{reg.user.email}</div>
</td>
<td className="px-6 py-4">
<div className="font-semibold text-gray-900 dark:text-white">
{reg.webinar.title}
</div>
<div className="text-sm text-gray-500">
{new Date(reg.webinar.dateTime).toLocaleDateString()}
</div>
</td>
<td className="px-6 py-4">
<span
className={`inline-flex px-3 py-1 rounded-full text-xs font-semibold ${
reg.status === "CONFIRMED"
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
: reg.status === "PENDING"
? "bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200"
: "bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200"
}`}
>
{reg.status}
</span>
</td>
<td className="px-6 py-4">
<span
className={`inline-flex px-3 py-1 rounded-full text-xs font-semibold ${
reg.paymentStatus === "COMPLETED"
? "bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200"
: reg.paymentStatus === "FREE"
? "bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200"
: "bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300"
}`}
>
{reg.paymentStatus}
</span>
</td>
<td className="px-6 py-4 text-gray-600 dark:text-gray-400">
{new Date(reg.createdAt).toLocaleDateString()}
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
)}
</main>
);
}