Initial commit of Vellum, an event photo product for guest uploads, host moderation, and original-quality galleries.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-07 19:36:14 -04:00
co-authored by Cursor
commit 27e2f196eb
149 changed files with 13847 additions and 0 deletions
+98
View File
@@ -0,0 +1,98 @@
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>
);
}