import { SearchableSelect } from '../components/SearchableSelect'; import { useEffect, useState } from 'react'; import type { FormEvent } from 'react'; import { Alert, EmptyState, LoadingBlock, errorMessage } from '../components/Feedback'; import { Icon } from '../components/Icon'; import { getAuditEvent, listAudit } from '../lib/api'; import type { AuditEventDetail, AuditEventSummary, PageMeta } from '../lib/api'; import { actionLabel, formatDate } from '../lib/format'; interface AuditFilters { search: string; action: string; source: string; from: string; to: string; } const emptyFilters: AuditFilters = { search: '', action: '', source: '', from: '', to: '' }; export function AuditPage() { const [events, setEvents] = useState([]); const [meta, setMeta] = useState({ page: 1, pageSize: 25, total: 0, totalPages: 0 }); const [draft, setDraft] = useState(emptyFilters); const [filters, setFilters] = useState(emptyFilters); const [page, setPage] = useState(1); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [detail, setDetail] = useState(null); const [detailLoading, setDetailLoading] = useState(false); useEffect(() => { setLoading(true); setError(''); listAudit({ page, pageSize: 25, search: filters.search, action: filters.action, source: filters.source, from: filters.from ? `${filters.from}T00:00:00.000Z` : undefined, to: filters.to ? `${filters.to}T23:59:59.999Z` : undefined, }).then((response) => { setEvents(response.data); setMeta(response.meta); }) .catch((requestError) => setError(errorMessage(requestError))) .finally(() => setLoading(false)); }, [filters, page]); const apply = (event: FormEvent) => { event.preventDefault(); setPage(1); setFilters({ ...draft }); }; const clear = () => { setDraft(emptyFilters); setFilters(emptyFilters); setPage(1); }; const set = (key: keyof AuditFilters, value: string) => setDraft((current) => ({ ...current, [key]: value })); const openDetail = async (id: string) => { setDetail(null); setDetailLoading(true); setError(''); try { setDetail(await getAuditEvent(id)); } catch (requestError) { setError(errorMessage(requestError)); } finally { setDetailLoading(false); } }; return
TRAZABILIDAD

Auditoría

Registro central de accesos y cambios administrativos.

{meta.total} eventos
{error && {error}} {loading ? : events.length === 0 ? :
{events.map((event) => )}
Fecha y horaUsuarioAcciónEntidadOrigenRequest ID
{formatDate(event.occurredAt)}{event.actorUsername ?? 'Sistema'}{event.ip ?? 'IP no registrada'}{actionLabel(event.action)}{event.action}{event.entityType ?? '—'}{event.entityId ?? ''}{event.source}{event.requestId ? `${event.requestId.slice(0, 12)}…` : '—'}
Página {page} de {Math.max(meta.totalPages, 1)}
} {(detailLoading || detail) &&
{ if (event.target === event.currentTarget && !detailLoading) setDetail(null); }}>
}
; } function JsonDetail({ title, value }: { title: string; value: Record | null }) { if (!value) return null; return
{title}
{JSON.stringify(value, null, 2)}
; }