import { Fragment, useState } from 'react'; import { useQuery } from '@tanstack/react-query'; import { ChevronDown, ChevronRight } from 'lucide-react'; import { api } from '../../lib/api-client'; import { AppShell, PageHeader, PageBody } from '../../layout/AppShell'; import { Badge, Card, Input, Select } from '../../components/ui/primitives'; import { Table, THead, TBody, TR, TH, TD } from '../../components/ui/table'; interface AuditEntry { id: number; actor_email: string | null; actor_role: string | null; entity_type: string; entity_id: string | null; action: string; before_state: unknown; after_state: unknown; metadata: Record | null; ip: string | null; created_at: string; } interface AuditData { entries: AuditEntry[]; facets: { actors: string[] | null; entity_types: string[] | null; actions: string[] | null; }; } const ACTION_VARIANTS: Record = { create: 'success', update: 'info', delete: 'danger', enable: 'success', disable: 'warn', sync: 'info', regenerate: 'info', bulk_update: 'info', send: 'info', publish: 'success', reorder: 'muted', }; export function AuditLog() { const [filters, setFilters] = useState({ actor: '', entity_type: '', action: '', from: '', to: '', }); const [expanded, setExpanded] = useState>(new Set()); const { data } = useQuery({ queryKey: ['audit', filters], queryFn: () => api.get('/audit', { actor: filters.actor || undefined, entity_type: filters.entity_type || undefined, action: filters.action || undefined, from: filters.from || undefined, to: filters.to || undefined, limit: 200, }), }); const entries = data?.entries ?? []; const facets = data?.facets ?? { actors: [], entity_types: [], actions: [] }; const toggle = (id: number) => { const next = new Set(expanded); if (next.has(id)) next.delete(id); else next.add(id); setExpanded(next); }; return (
setFilters({ ...filters, from: e.target.value })} />
setFilters({ ...filters, to: e.target.value })} />
{entries.map((e) => { const isOpen = expanded.has(e.id); return ( toggle(e.id)} className="cursor-pointer"> {isOpen && ( )} ); })}
Tijd Actor Action Entity ID IP
{isOpen ? ( ) : ( )} {new Date(e.created_at).toLocaleString('nl-NL')} {e.actor_email ?? '—'} {e.action} {e.entity_type} {e.entity_id ?? '—'} {e.ip ?? '—'}

Before

                                {e.before_state ? JSON.stringify(e.before_state, null, 2) : '—'}
                              

After

                                {e.after_state ? JSON.stringify(e.after_state, null, 2) : '—'}
                              
{e.metadata && (

Metadata

                                  {JSON.stringify(e.metadata, null, 2)}
                                
)}
{entries.length === 0 && (
Geen audit-entries voor deze filters.
)}
); }