Files
podcastdistributiona/app/(admin)/admin/audit/page.tsx
T

68 lines
2.5 KiB
TypeScript
Raw Normal View History

import type { Metadata } from "next";
import { listAudit, getAuditActions, AUDIT_PAGE_SIZE } from "@/lib/admin/audit";
import { PageHeader } from "@/components/app/page-header";
import { DataTable, TableToolbar, type Column } from "@/components/admin/ui/data-table";
import { SearchInput, FilterSelect, Pagination } from "@/components/admin/ui/table-controls";
import { AuditExport } from "@/components/admin/audit-export";
import { AuditMetaViewer } from "@/components/admin/audit-meta-viewer";
import { Badge } from "@/components/ui/badge";
export const metadata: Metadata = { title: "Admin · Audit log" };
type Row = Awaited<ReturnType<typeof listAudit>>["rows"][number];
export default async function AdminAuditPage({
searchParams,
}: {
searchParams: Promise<Record<string, string | undefined>>;
}) {
const sp = await searchParams;
const page = Math.max(1, Number(sp.page ?? "1"));
const [{ rows, total }, actions] = await Promise.all([
listAudit({ action: sp.action, actor: sp.q, page }),
getAuditActions(),
]);
const columns: Column<Row>[] = [
{
key: "when",
header: "When",
cell: (l) => <span className="whitespace-nowrap text-muted-foreground">{l.createdAt.toLocaleString()}</span>,
},
{ key: "actor", header: "Actor", cell: (l) => l.actor?.email ?? l.actorType },
{ key: "action", header: "Action", cell: (l) => <Badge variant="outline">{l.action}</Badge> },
{
key: "target",
header: "Target",
cell: (l) => <span className="font-mono text-xs text-muted-foreground">{l.target ?? "—"}</span>,
},
{
key: "meta",
header: "Details",
cell: (l) => <AuditMetaViewer metadata={l.metadata} action={l.action} />,
},
];
return (
<>
<PageHeader title="Audit log" description="Every administrative action, recorded." />
<TableToolbar>
<SearchInput placeholder="Filter by actor email…" />
<div className="flex flex-wrap items-center gap-2">
<FilterSelect
param="action"
placeholder="Action"
allLabel="All actions"
options={actions.map((a) => ({ value: a, label: a }))}
/>
<AuditExport filters={{ action: sp.action, actor: sp.q }} />
</div>
</TableToolbar>
<DataTable columns={columns} rows={rows} getRowKey={(l) => l.id} empty="No audit entries yet." />
<div className="mt-4">
<Pagination page={page} pageSize={AUDIT_PAGE_SIZE} total={total} />
</div>
</>
);
}