'use client'; import { useState, useEffect } from 'react'; import { db, Item } from '@/lib/db'; import { inventoryApi } from '@/lib/api'; import PageShell from '@/components/PageShell'; import { History, Search, Activity, ArrowDownCircle, ArrowUpCircle, RefreshCw } from 'lucide-react'; import { cn } from '@/lib/utils'; import { fetchAndCacheItems } from '@/lib/sync'; import StatCard from '@/components/StatCard'; import LogsTable from '@/components/LogsTable'; export default function LogsPage() { const [auditLogs, setAuditLogs] = useState([]); const [inventory, setInventory] = useState([]); const [loading, setLoading] = useState(true); const [searchQuery, setSearchQuery] = useState(''); const [filterAction, setFilterAction] = useState('ALL'); useEffect(() => { loadData(); }, []); const loadData = async () => { setLoading(true); try { // 1. First, try to get fresh items to resolve names let freshItems: Item[] = []; try { freshItems = await fetchAndCacheItems(); } catch (itemErr) { console.warn("Item sync failed, using local cache for names", itemErr); freshItems = await db.items.toArray(); } setInventory(freshItems); // 2. Then, fetch fresh logs const logs = await inventoryApi.getAuditLogs(100); // 3. Pre-resolve names to avoid UI flickering/mismatches const enrichedLogs = (logs || []).map((log: any) => { // [AUDIT HARDENING] Prioritize the historical snapshot from the backend if (log.target_item_name) { return { ...log, resolved_name: log.target_item_name }; } // Fallback for legacy logs or system operations const hasTarget = log.target_item_id && String(log.target_item_id) !== 'null'; const item = hasTarget ? freshItems.find(i => String(i.id) === String(log.target_item_id)) : null; return { ...log, resolved_name: item ? item.name : (hasTarget ? `Item #${log.target_item_id}` : "System Operation") }; }); setAuditLogs(enrichedLogs); } catch (err: any) { console.error("Critical log load failure:", err); } finally { setLoading(false); } }; const filteredLogs = auditLogs.filter(log => { const matchesSearch = (log.resolved_name || '').toLowerCase().includes(searchQuery.toLowerCase()) || log.action.toLowerCase().includes(searchQuery.toLowerCase()) || (log.username || '').toLowerCase().includes(searchQuery.toLowerCase()); const matchesAction = filterAction === 'ALL' || log.action.includes(filterAction); return matchesSearch && matchesAction; }); // Calculate stats const totalCount = auditLogs.length; const inCount = auditLogs.filter(l => l.action.includes('IN') || l.action.includes('ADD')).length; const outCount = auditLogs.filter(l => l.action.includes('OUT') || l.action.includes('TRASH') || l.action.includes('DELETE')).length; const criticalLogsCount = auditLogs.filter(l => l.action.includes('DELETE') || l.action.includes('TRASH')).length; const mostActiveUser = auditLogs.length > 0 ? Object.entries(auditLogs.reduce((acc: any, curr) => { const user = curr.username || 'System'; acc[user] = (acc[user] || 0) + 1; return acc; }, {})).sort((a: any, b: any) => b[1] - a[1])[0]?.[0] : 'N/A'; return (

Operations Audit

Real-time Intervention Tracking

{/* Stats Grid */}
setSearchQuery(e.target.value)} className="w-full bg-surface/50 border border-slate-800 py-3 lg:py-4 xl:py-5.5 pr-4 pl-12 text-sm lg:text-base xl:text-lg lg:text-xl xl:text-2xl lg:text-3xl xl:text-4xl focus:border-primary outline-none transition-all placeholder:text-secondary" />
{['ADD', 'REMOVE', 'ADJUST', 'DELETE', 'LOGIN'].map((f) => ( ))}
); }