- fix(studio): stop markBatchDone infinite recursion so batches complete - refactor(api): merge api-response into api and drop the duplicate module - refactor(media): extract shared media loader and URL validator - refactor(ui): extract shared LoadingSpinner for site and admin groups - refactor(dates): consolidate raw date formatting into formatDate util - refactor(logs): share a single generic log-list loader across tables - refactor(theme): merge both ColorField components and reuse contrast helpers - refactor(import): extract shared ImportErrorBanner and SearchInput - chore(): remove dead theme-editor-tabs after inlining tab components
50 lines
1.2 KiB
TypeScript
50 lines
1.2 KiB
TypeScript
"use client";
|
|
|
|
import { useTranslations } from "next-intl";
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { formatDate } from "@/lib/format-date";
|
|
|
|
interface Registration {
|
|
id: number;
|
|
userId: number;
|
|
username: string;
|
|
createdAt: Date;
|
|
}
|
|
|
|
interface Props {
|
|
registrations: Registration[];
|
|
}
|
|
|
|
export function EventRegistrations({ registrations }: Props) {
|
|
const t = useTranslations("pages.admin.events");
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>
|
|
{t("registrationsTitle", { count: registrations.length })}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{registrations.length === 0 ? (
|
|
<div className="admin-empty">{t("noRegistrations")}</div>
|
|
) : (
|
|
<div className="divide-y max-h-[400px] overflow-y-auto">
|
|
{registrations.map((r) => (
|
|
<div
|
|
key={r.id}
|
|
className="flex items-center justify-between py-2"
|
|
>
|
|
<span className="text-sm font-medium">{r.username}</span>
|
|
<span className="text-xs text-muted-foreground">
|
|
{formatDate(new Date(r.createdAt))}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|