'use client'; import { useState, useEffect, useCallback } from 'react'; import { auditApi, type AuditEntry } from '@/lib/api-client'; const PAGE_SIZE = 50; const ACTION_COLORS: Record = { 'admin.mfa.enroll': 'bg-purple-100 text-purple-700', 'admin.mfa.status': 'bg-purple-100 text-purple-700', 'admin.mfa.challenge': 'bg-purple-100 text-purple-700', 'auth.login': 'bg-blue-100 text-blue-700', 'auth.logout': 'bg-gray-100 text-gray-600', 'product.created': 'bg-green-100 text-green-700', 'product.updated': 'bg-green-100 text-green-700', 'product.deleted': 'bg-red-100 text-red-700', 'order.placed': 'bg-indigo-100 text-indigo-700', 'order.state_changed': 'bg-indigo-100 text-indigo-700', }; function colorForAction(action: string): string { return ACTION_COLORS[action] ?? 'bg-gray-100 text-gray-600'; } export default function AuditLogPage() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [filter, setFilter] = useState(''); const [debounced, setDebounced] = useState(''); const [page, setPage] = useState(0); const [total, setTotal] = useState(0); useEffect(() => { const t = setTimeout(() => setDebounced(filter), 400); return () => clearTimeout(t); }, [filter]); useEffect(() => { setPage(0); }, [debounced]); const load = useCallback(async () => { setLoading(true); setError(''); try { const data = await auditApi.list({ action: debounced || undefined, limit: PAGE_SIZE, offset: page * PAGE_SIZE }); setItems(data.items ?? []); setTotal(data.total ?? 0); } catch (e) { setError(e instanceof Error ? e.message : 'Error'); } finally { setLoading(false); } }, [page, debounced]); useEffect(() => { load(); }, [load]); return (

Log de auditoría

{total > 0 ? `${total} entrada${total !== 1 ? 's' : ''}` : 'Sin entradas'}

setFilter(e.target.value)} className="w-full pl-10 pr-4 py-2.5 border border-gray-200 rounded-xl text-sm focus:ring-2 focus:ring-[#2D6A4F] outline-none" /> 🔍
{loading ? (
Cargando...
) : error ? (
{error}
) : items.length === 0 ? (
📋Sin entradas de auditoría
) : ( <> {items.map(entry => ( ))}
Fecha Acción Objetivo Actor Detalles
{new Date(entry.createdAt).toLocaleString('es-ES', { dateStyle: 'short', timeStyle: 'short' })} {entry.action} {entry.target} {entry.actorId?.slice(0, 8) ?? '—'} {Object.keys(entry.metadata ?? {}).length > 0 ? JSON.stringify(entry.metadata) : '—'}
{total > PAGE_SIZE && (
{page * PAGE_SIZE + 1}–{Math.min((page + 1) * PAGE_SIZE, total)} de {total}
)} )}
); }