feat(F-074): completed feature
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
'use client';
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { auditApi, type AuditEntry } from '@/lib/api-client';
|
||||
|
||||
const PAGE_SIZE = 50;
|
||||
@@ -29,6 +29,9 @@ export default function AuditLogPage() {
|
||||
const [debounced, setDebounced] = useState('');
|
||||
const [page, setPage] = useState(0);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [newCount, setNewCount] = useState(0);
|
||||
const [pollingActive, setPollingActive] = useState(false);
|
||||
const itemsRef = useRef<AuditEntry[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setDebounced(filter), 400);
|
||||
@@ -49,10 +52,50 @@ export default function AuditLogPage() {
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
|
||||
// Keep ref in sync with items state for polling deduplication
|
||||
useEffect(() => { itemsRef.current = items; }, [items]);
|
||||
|
||||
// Real-time polling: fetch new entries every 5s when on page 0 with no filter
|
||||
useEffect(() => {
|
||||
if (page !== 0 || debounced) {
|
||||
setPollingActive(false);
|
||||
return;
|
||||
}
|
||||
setPollingActive(true);
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const data = await auditApi.list({ limit: PAGE_SIZE, offset: 0 });
|
||||
const existingIds = new Set(itemsRef.current.map(i => i.id));
|
||||
const newItems = (data.items ?? []).filter(i => !existingIds.has(i.id));
|
||||
if (newItems.length > 0) {
|
||||
setItems(prev => [...newItems, ...prev].slice(0, PAGE_SIZE));
|
||||
setNewCount(prev => prev + newItems.length);
|
||||
}
|
||||
} catch { /* silent */ }
|
||||
}, 5000);
|
||||
return () => { clearInterval(interval); setPollingActive(false); };
|
||||
}, [page, debounced]);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Log de auditoría</h1>
|
||||
<h1 className="text-2xl font-bold text-gray-900">Log de auditoría
|
||||
{pollingActive && (
|
||||
<span className="ml-3 inline-flex items-center gap-1.5 text-xs text-green-600 font-medium align-middle">
|
||||
<span className="inline-block w-2 h-2 rounded-full bg-green-500 animate-pulse" />
|
||||
En vivo
|
||||
</span>
|
||||
)}
|
||||
{newCount > 0 && (
|
||||
<button
|
||||
onClick={() => setNewCount(0)}
|
||||
title="Nuevas entradas — haz clic para marcar como vistas"
|
||||
className="ml-2 px-2 py-0.5 bg-green-100 text-green-700 text-xs rounded-full hover:bg-green-200 transition-colors"
|
||||
>
|
||||
+{newCount} nueva{newCount !== 1 ? 's' : ''}
|
||||
</button>
|
||||
)}
|
||||
</h1>
|
||||
<p className="text-sm text-gray-500 mt-0.5">
|
||||
{total > 0 ? `${total} entrada${total !== 1 ? 's' : ''}` : 'Sin entradas'}
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user