'use client'; import { useEffect, useState, useCallback } from 'react'; import { RefreshCw } from 'lucide-react'; import type { AuditEntry } from '@/lib/admin/types'; import { apiFetch } from '@/lib/browser-navigation'; export default function AdminLogsPage() { const [entries, setEntries] = useState([]); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); const [actionFilter, setActionFilter] = useState(''); const limit = 50; const fetchLogs = useCallback(async () => { setLoading(true); const params = new URLSearchParams({ page: String(page), limit: String(limit) }); if (actionFilter) params.set('action', actionFilter); const res = await apiFetch(`/api/admin/audit?${params}`); if (res.ok) { const data = await res.json(); setEntries(data.entries || []); setTotal(data.total || 0); } setLoading(false); }, [page, actionFilter]); useEffect(() => { fetchLogs(); }, [fetchLogs]); const totalPages = Math.max(1, Math.ceil(total / limit)); return (

Audit Log

{total} total entries

{/* Filter */}
{/* Table */}
{loading && entries.length === 0 ? ( ) : entries.length === 0 ? ( ) : ( entries.map((entry, i) => ( )) )}
Time Action Details IP
Loading...
No entries found
{new Date(entry.ts).toLocaleString()} {entry.action} {formatDetail(entry.detail)} {entry.ip}
{/* Pagination */} {totalPages > 1 && (

Page {page} of {totalPages}

)}
); } function formatDetail(detail: Record): string { if (!detail || Object.keys(detail).length === 0) return '-'; if (detail.reason) return String(detail.reason); if (detail.key) return `${detail.key}: ${JSON.stringify(detail.old)} → ${JSON.stringify(detail.new)}`; if (detail.changes && Array.isArray(detail.changes)) { return detail.changes.map((c: Record) => `${c.key}`).join(', '); } if (detail.restrictionCount !== undefined) return `${detail.restrictionCount} restriction(s)`; return JSON.stringify(detail).slice(0, 100); }