"use client"; import * as React from "react"; import { useParams } from "next/navigation"; import { History, Loader2, ScrollText, ShieldOff } from "lucide-react"; import { api } from "@/lib/trpc"; import { Button } from "@/components/ui/button"; import { Skeleton } from "@/components/ui/skeleton"; /** * Owner-only debugging view over `audit_log`. Intentionally dumb: no * filters, no search, no time-zone toggles. The job is "show me what * happened, most recent first." If we add filters later they should be * server-side keyset queries, not client-side slicing. */ const ACTION_LABELS: Record = { "workspace.create": "Workspace created", "workspace.update": "Workspace updated", "workspace.archive": "Workspace archived", "workspace.restore": "Workspace restored", "member.role_change": "Role changed", "member.remove": "Member removed", "member.leave": "Member left", "invite.create": "Invite sent", "invite.revoke": "Invite revoked", "invite.accept": "Invite accepted", }; function actionLabel(action: string): string { return ACTION_LABELS[action] ?? action; } function formatTimestamp(when: Date | string): string { const d = when instanceof Date ? when : new Date(when); return d.toLocaleString(undefined, { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit", }); } function metadataSummary(metadata: Record | null): string { if (!metadata || Object.keys(metadata).length === 0) return "—"; // Truncate aggressively. The expanded raw JSON is one row away via the //
below; this is just the at-a-glance hint. const json = JSON.stringify(metadata); return json.length > 120 ? `${json.slice(0, 117)}…` : json; } export default function AuditLogPage() { const params = useParams(); const workspaceSlug = params?.workspaceSlug as string | undefined; const [cursor, setCursor] = React.useState(undefined); const auditQuery = api.audit.list.useQuery( { workspace: workspaceSlug ?? "", cursorCreatedAt: cursor, limit: 50 }, { enabled: Boolean(workspaceSlug) }, ); const isForbidden = auditQuery.error?.data?.code === "FORBIDDEN"; return (

Audit log

Append-only record of meaningful actions in this workspace. Owner-only.

{isForbidden ? (

Owner-only view

Only the workspace owner can read the audit log. Ask an owner to share specific events if you need them for support.

) : auditQuery.isLoading ? (
{Array.from({ length: 6 }).map((_, i) => ( ))}
) : auditQuery.error ? (
Couldn't load the audit log: {auditQuery.error.message}
) : !auditQuery.data || auditQuery.data.rows.length === 0 ? (

No events yet

Actions like creating invites, changing roles, or archiving the workspace will appear here.

) : ( <>
{auditQuery.data.rows.map((row) => ( ))}
When Action Actor Target Details
{formatTimestamp(row.createdAt)} {actionLabel(row.action)} {row.actorUserId ? ( {row.actorName ?? row.actorEmail ?? row.actorUserId.slice(0, 8)} ) : ( system {row.metadata && typeof (row.metadata as { system_actor?: unknown }).system_actor === "string" ? `: ${(row.metadata as { system_actor: string }).system_actor}` : ""} )} {row.targetType} {row.targetId ? ( {row.targetId.slice(0, 8)} ) : null} {row.metadata && Object.keys(row.metadata).length > 0 ? (
{metadataSummary(row.metadata)}
                            {JSON.stringify(row.metadata, null, 2)}
                          
) : ( )}
{auditQuery.data.nextCursor ? (
) : null} )}
); }