import { useState, useEffect, useCallback } from 'react'; import api from '../../services/api'; import { History, User as UserIcon, CheckCircle, XCircle, FileText, Search, Calendar, Users, AlertCircle, Info } from 'lucide-react'; import clsx from 'clsx'; interface AuditLog { id: number; action: string; username: string; createdAt: string; details: string; userId: number; } export default function AuditTimeline() { const todayStr = new Date().toISOString().split('T')[0]; const [logs, setLogs] = useState([]); const [users, setUsers] = useState<{ id: number, username: string }[]>([]); const [loading, setLoading] = useState(true); // ESTADO DE FILTROS const [filters, setFilters] = useState({ from: todayStr, to: todayStr, userId: "" }); const loadInitialData = async () => { try { const res = await api.get('/users'); setUsers(res.data); } catch (e) { console.error(e); } }; const loadLogs = useCallback(async () => { setLoading(true); try { const res = await api.get('/reports/audit', { params: { from: filters.from, to: filters.to, userId: filters.userId || null } }); setLogs(res.data); } catch (e) { console.error(e); } finally { setLoading(false); } }, [filters]); useEffect(() => { loadInitialData(); }, []); useEffect(() => { loadLogs(); }, [loadLogs]); const translateDetails = (details: string) => { if (!details) return details; return details .replace(/Published/g, 'Publicado') .replace(/Pending/g, 'Pendiente') .replace(/Rejected/g, 'Rechazado') .replace(/Draft/g, 'Borrador') .replace(/True/g, 'Sí') .replace(/False/g, 'No') .replace(/Total:/g, 'Total:') .replace(/Featured:/g, 'Destacado:') .replace(/El usuario (\d+) cambió el estado del aviso #(\d+) a/g, 'Se cambió el estado del aviso #$2 a') .replace(/Aviso creado por usuario autenticado/g, 'Nuevo aviso creado'); }; const getLogMeta = (action: string) => { switch (action) { case 'APPROVE_LISTING': case 'Published': case 'Aprobar': return { icon: , color: 'bg-emerald-50 text-emerald-700 border-emerald-100', label: 'Aprobación' }; case 'REJECT_LISTING': case 'Rejected': case 'Rechazar': return { icon: , color: 'bg-rose-50 text-rose-700 border-rose-100', label: 'Rechazo' }; case 'CREATE_LISTING': case 'CREATE_USER': case 'CREATE_COUPON': case 'CREATE_CATEGORY': return { icon: , color: 'bg-blue-50 text-blue-700 border-blue-100', label: 'Creación' }; case 'UPDATE_USER': case 'UPDATE_CLIENT': case 'UPDATE_CATEGORY': case 'SAVE_PRICING': case 'UPDATE_PROMOTION': return { icon: , color: 'bg-amber-50 text-amber-700 border-amber-100', label: 'Actualización' }; case 'DELETE_USER': case 'DELETE_COUPON': case 'DELETE_CATEGORY': case 'DELETE_PROMOTION': return { icon: , color: 'bg-slate-100 text-slate-700 border-slate-200', label: 'Eliminación' }; case 'LOGIN': return { icon: , color: 'bg-indigo-50 text-indigo-700 border-indigo-100', label: 'Acceso' }; case 'CONFIRM_PAYMENT': return { icon: , color: 'bg-cyan-50 text-cyan-700 border-cyan-100', label: 'Pago' }; default: return { icon: , color: 'bg-slate-50 text-slate-500 border-slate-200', label: action.replace('_', ' ') }; } }; return (
{/* HEADER PREMIUM */}

Auditoría de Sistema

Trazabilidad completa de acciones y cambios de estado

{/* BARRA DE FILTROS */}
setFilters({ ...filters, from: e.target.value })} />
setFilters({ ...filters, to: e.target.value })} />
{/* LISTA DE EVENTOS TIPO TIMELINE */}
{/* Línea vertical de fondo para el timeline */}
{loading ? (

Sincronizando logs...

) : logs.length === 0 ? (

No hay eventos para el período seleccionado

) : logs.map((log, idx) => { const meta = getLogMeta(log.action); return (
{/* HORA Y PUNTO (Timeline) */}
{new Date(log.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {new Date(log.createdAt).toLocaleDateString()}
{/* ICONO CENTRAL BUBBLE */}
{meta.icon}
{/* CARD DE CONTENIDO */}
{meta.label} {log.username}
#{log.id}
{new Date(log.createdAt).toLocaleString()}

{translateDetails(log.details)}

); })}
); }