'use client'; import { useCallback, useEffect, useState } from 'react'; import { formatDate } from '@/lib/format'; type AuditEntry = { id: number; actor_id: string | null; actor_email: string | null; action: string; target_type: string | null; target_id: string | null; details: Record; created_at: string; }; const PER_PAGE = 50; export default function AdminAuditPage() { const [entries, setEntries] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [action, setAction] = useState(''); const [targetType, setTargetType] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const load = useCallback(async () => { setLoading(true); setError(null); try { const params = new URLSearchParams({ page: String(page), perPage: String(PER_PAGE), }); if (action.trim()) params.set('action', action.trim()); if (targetType.trim()) params.set('target_type', targetType.trim()); const res = await fetch(`/api/admin/audit?${params.toString()}`); if (!res.ok) { const b = (await res.json().catch(() => ({}))) as { error?: string }; throw new Error(b.error ?? `Request failed (${res.status})`); } const data = (await res.json()) as { entries: AuditEntry[]; total: number; }; setEntries(data.entries); setTotal(data.total); } catch (e) { setError((e as Error).message); } finally { setLoading(false); } }, [page, action, targetType]); useEffect(() => { const t = setTimeout(load, 250); return () => clearTimeout(t); }, [load]); const totalPages = Math.max(1, Math.ceil(total / PER_PAGE)); return (

Audit log

{ setPage(1); setAction(e.target.value); }} style={{ maxWidth: 240 }} /> { setPage(1); setTargetType(e.target.value); }} style={{ maxWidth: 240 }} />
{error &&

{error}

} {loading ? (

Loading…

) : entries.length === 0 ? (

No audit entries.

) : (
{entries.map((e) => ( ))}
When Actor Action Target Details
{formatDate(e.created_at)} {e.actor_email ?? e.actor_id ?? '—'} {e.action} {e.target_type ? `${e.target_type}:` : ''} {e.target_id ?? ''} {JSON.stringify(e.details ?? {})}
)}
Page {page} of {totalPages} ({total} total)
); }