import { useState, useEffect } from "react"; import { format } from "date-fns"; export default function AuditLogViewer() { const [logs, setLogs] = useState([]); const [stats, setStats] = useState(null); const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [filters, setFilters] = useState({ action: "", resourceType: "", userId: "", startDate: "", endDate: "", limit: 50, offset: 0, }); const [actionTypes, setActionTypes] = useState([]); const [resourceTypes, setResourceTypes] = useState([]); const fetchAuditLogs = async () => { setLoading(true); setError(null); try { const queryParams = new URLSearchParams(); Object.entries(filters).forEach(([key, value]) => { if (value && value !== "") { queryParams.append(key, value); } }); queryParams.append("includeStats", "true"); const response = await fetch(`/api/audit-logs?${queryParams}`); if (!response.ok) { throw new Error("Failed to fetch audit logs"); } const result = await response.json(); setLogs(result.data); setStats(result.stats); } catch (err) { setError(err.message); } finally { setLoading(false); } }; useEffect(() => { // Set available filter options setActionTypes([ "login", "logout", "login_failed", "project_create", "project_update", "project_delete", "project_view", "task_create", "task_update", "task_delete", "task_status_change", "project_task_create", "project_task_update", "project_task_delete", "contract_create", "contract_update", "contract_delete", "note_create", "note_update", "note_delete", "user_create", "user_update", "user_delete", "user_role_change", ]); setResourceTypes([ "project", "task", "project_task", "contract", "note", "user", "session", "system", ]); fetchAuditLogs(); }, []); // eslint-disable-line react-hooks/exhaustive-deps const handleFilterChange = (key, value) => { setFilters((prev) => ({ ...prev, [key]: value, offset: 0, // Reset pagination when filters change })); }; const handleSearch = () => { fetchAuditLogs(); }; const handleClearFilters = () => { setFilters({ action: "", resourceType: "", userId: "", startDate: "", endDate: "", limit: 50, offset: 0, }); }; const loadMore = () => { setFilters((prev) => ({ ...prev, offset: prev.offset + prev.limit, })); }; useEffect(() => { if (filters.offset > 0) { fetchAuditLogs(); } }, [filters.offset]); // eslint-disable-line react-hooks/exhaustive-deps const formatTimestamp = (timestamp) => { try { return format(new Date(timestamp), "yyyy-MM-dd HH:mm:ss"); } catch { return timestamp; } }; const getActionColor = (action) => { const colorMap = { login: "text-green-600", logout: "text-blue-600", login_failed: "text-red-600", create: "text-green-600", update: "text-yellow-600", delete: "text-red-600", view: "text-gray-600", }; for (const [key, color] of Object.entries(colorMap)) { if (action.includes(key)) { return color; } } return "text-gray-600"; }; return (

Audit Logs

View system activity and user actions

{/* Filters */}

Filters

handleFilterChange("userId", e.target.value)} placeholder="Enter user ID" className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm" />
handleFilterChange("startDate", e.target.value)} className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm" />
handleFilterChange("endDate", e.target.value)} className="w-full border border-gray-300 rounded-md px-3 py-2 text-sm" />
{/* Statistics */} {stats && stats.total > 0 && (

Total Events

{stats.total}

Top Action

{stats.actionBreakdown && stats.actionBreakdown[0]?.action || "N/A"}

{stats.actionBreakdown && stats.actionBreakdown[0]?.count || 0}

Active Users

{stats.userBreakdown ? stats.userBreakdown.length : 0}

Resource Types

{stats.resourceBreakdown ? stats.resourceBreakdown.length : 0}

)} {/* Error Message */} {error && (
{error}
)} {/* Audit Logs Table */}
{logs.map((log) => ( ))}
Timestamp User Action Resource IP Address Details
{formatTimestamp(log.timestamp)}
{log.user_name || "Anonymous"}
{log.user_email}
{log.action.replace(/_/g, " ").toUpperCase()}
{log.resource_type || "N/A"}
ID: {log.resource_id || "N/A"}
{log.ip_address || "Unknown"} {log.details && (
View Details
													{JSON.stringify(log.details, null, 2)}
												
)}
{logs.length === 0 && !loading && (
No audit logs found matching your criteria.
)} {logs.length > 0 && (
Showing {filters.offset + 1} to {filters.offset + logs.length}{" "} results
)}
); }