99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
import { createServerCaller } from "@/trpc/server";
|
|
import { getPlatformRole } from "@/server/roles";
|
|
import { auth } from "@/server/auth";
|
|
import { headers } from "next/headers";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
CardDescription,
|
|
CardHeader,
|
|
CardTitle,
|
|
} from "@/components/ui/card";
|
|
import { PlatformUsers } from "./platform-users";
|
|
import { PlatformCodes } from "./platform-codes";
|
|
|
|
export default async function AdminPage() {
|
|
const session = await auth.api.getSession({ headers: await headers() });
|
|
const role = session ? await getPlatformRole(session.user.id) : null;
|
|
const caller = await createServerCaller();
|
|
const [groups, events, audit] = await Promise.all([
|
|
caller.platform.groups(),
|
|
caller.platform.events(),
|
|
caller.platform.audit(),
|
|
]);
|
|
const canManageUsers = role === "super_admin" || role === "admin";
|
|
|
|
return (
|
|
<div className="flex flex-col gap-8">
|
|
<div>
|
|
<h1 className="text-4xl font-semibold tracking-tight">Platform</h1>
|
|
<p className="text-sm text-muted-foreground">
|
|
Full access for deployment operators.
|
|
</p>
|
|
</div>
|
|
<div className="grid gap-4 md:grid-cols-2">
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Groups</CardTitle>
|
|
<CardDescription>{groups.length} groups</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-col gap-2 text-sm">
|
|
{groups.map((group) => (
|
|
<div key={group.id} className="flex justify-between gap-3">
|
|
<span>{group.name}</span>
|
|
<span className="text-muted-foreground">
|
|
{group.quota.unlimited
|
|
? "unlimited"
|
|
: `${group.quota.used}/${group.quota.eventLimit ?? 0}`}
|
|
{group.quota.complimentary ? " · comp" : ""}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Events</CardTitle>
|
|
<CardDescription>{events.length} events</CardDescription>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-col gap-2 text-sm">
|
|
{events.map((event) => (
|
|
<div key={event.id} className="flex justify-between gap-3">
|
|
<span>{event.title}</span>
|
|
<div className="flex gap-1">
|
|
<Badge variant="secondary">{event.status}</Badge>
|
|
{event.listed ? <Badge variant="outline">listed</Badge> : null}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
{canManageUsers ? <PlatformUsers /> : null}
|
|
{canManageUsers ? (
|
|
<PlatformCodes groups={groups.map((group) => ({ id: group.id, name: group.name }))} />
|
|
) : null}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Audit</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<ul className="flex flex-col gap-2 text-sm">
|
|
{audit.slice(0, 40).map((row) => (
|
|
<li key={row.id} className="flex justify-between gap-3">
|
|
<span>
|
|
{row.action} · {row.subjectType}
|
|
</span>
|
|
<span className="text-muted-foreground">
|
|
{new Date(row.createdAt).toLocaleString()}
|
|
</span>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|