Files
manyangles/apps/web/src/app/admin/page.tsx
T

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>
);
}