"use client"; import { useState, useTransition } from "react"; type AdminUser = { id: string; discordId: string; user?: { name?: string | null; email?: string | null; discordUsername?: string | null; } | null; }; type Grade = { id: string; name: string; price: number; description: string; }; type Event = { id: string; title: string; description: string; eventDate: string; }; type AdminDashboardProps = { initialAdmins: AdminUser[]; initialGrades: Grade[]; initialEvents: Event[]; }; const requestJson = async ( url: string, options: RequestInit ): Promise => { const res = await fetch(url, { headers: { "Content-Type": "application/json" }, ...options, }); const data = (await res.json().catch(() => ({}))) as T & { error?: string; }; if (!res.ok) { throw new Error(data.error ?? "Request failed"); } return data; }; const toInputDate = (iso: string) => { return new Date(iso).toISOString().slice(0, 16); }; export default function AdminDashboard({ initialAdmins, initialGrades, initialEvents, }: AdminDashboardProps) { const [admins, setAdmins] = useState(initialAdmins); const [grades, setGrades] = useState(initialGrades); const [events, setEvents] = useState(initialEvents); const [notice, setNotice] = useState(null); const [isPending, startTransition] = useTransition(); const handleAddAdmin = (form: FormData) => { const discordId = form.get("discordId")?.toString().trim(); if (!discordId) return; startTransition(async () => { try { const admin = await requestJson("/api/admin/admins", { method: "POST", body: JSON.stringify({ discordId }), }); setAdmins((prev) => [admin, ...prev]); setNotice("Admin added"); } catch (error) { setNotice((error as Error).message); } }); }; const handleAddGrade = (form: FormData) => { const name = form.get("name")?.toString().trim(); const price = Number(form.get("price")); const description = form.get("description")?.toString().trim(); if (!name || Number.isNaN(price) || !description) return; startTransition(async () => { try { const grade = await requestJson("/api/admin/grades", { method: "POST", body: JSON.stringify({ name, price, description }), }); setGrades((prev) => [...prev, grade]); setNotice("Grade added"); } catch (error) { setNotice((error as Error).message); } }); }; const handleAddEvent = (form: FormData) => { const title = form.get("title")?.toString().trim(); const description = form.get("description")?.toString().trim(); const eventDate = form.get("eventDate")?.toString(); if (!title || !description || !eventDate) return; startTransition(async () => { try { const event = await requestJson("/api/admin/events", { method: "POST", body: JSON.stringify({ title, description, eventDate: new Date(eventDate).toISOString(), }), }); setEvents((prev) => [...prev, event]); setNotice("Event added"); } catch (error) { setNotice((error as Error).message); } }); }; return (

Admin access

Manage admins, grades, and events. Changes are applied live.

{notice ? (
{notice}
) : null}

Admins

{ event.preventDefault(); handleAddAdmin(new FormData(event.currentTarget)); event.currentTarget.reset(); }} className="mt-4 flex flex-col gap-3 md:flex-row" >
{admins.map((admin) => ( setAdmins((prev) => prev.filter((item) => item.id !== id)) } /> ))}

Create grade

{ event.preventDefault(); handleAddGrade(new FormData(event.currentTarget)); event.currentTarget.reset(); }} className="mt-4 space-y-3" >