import { useState } from "react"; import { useQuery } from "@tanstack/react-query"; import { Loader2, AlertCircle, FileText, Filter, ChevronLeft, ChevronRight, X, } from "lucide-react"; import { getAuditLogs, getAuditActions, getAuditEntityTypes, type AuditLogFilters, } from "../api/audit"; const PAGE_SIZE = 25; export default function AuditLogPage() { const [filters, setFilters] = useState({ offset: 0, limit: PAGE_SIZE, }); const { data: logsData, isLoading: logsLoading, error: logsError, } = useQuery({ queryKey: ["audit-logs", filters], queryFn: () => getAuditLogs(filters), }); const { data: actions } = useQuery({ queryKey: ["audit-actions"], queryFn: getAuditActions, }); const { data: entityTypes } = useQuery({ queryKey: ["audit-entity-types"], queryFn: getAuditEntityTypes, }); const handleFilterChange = (key: keyof AuditLogFilters, value: string) => { setFilters((prev) => ({ ...prev, [key]: value || undefined, offset: 0, // Reset pagination on filter change })); }; const clearFilters = () => { setFilters({ offset: 0, limit: PAGE_SIZE }); }; const goToPage = (newOffset: number) => { setFilters((prev) => ({ ...prev, offset: newOffset })); }; const hasActiveFilters = filters.action || filters.entity_type || filters.start_date || filters.end_date; const formatDate = (dateStr: string | null | undefined) => { if (!dateStr) return "—"; const d = new Date(dateStr); if (isNaN(d.getTime())) return "—"; return d.toLocaleString("en-US", { year: "numeric", month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit", timeZone: "UTC", }) + " UTC"; }; const formatDetails = (details: Record | null) => { if (!details) return null; return JSON.stringify(details, null, 2); }; const totalPages = logsData ? Math.ceil(logsData.total / PAGE_SIZE) : 0; const currentPage = Math.floor((filters.offset || 0) / PAGE_SIZE) + 1; if (logsLoading) { return (
); } if (logsError) { return (

Failed to load audit logs

); } return (
{/* Header */}

Audit Log

System activity and change history

{/* Filters */}
Filters:
{/* Action filter */} {/* Entity type filter */} {/* Date range */} handleFilterChange( "start_date", e.target.value ? `${e.target.value}T00:00:00` : "" ) } className="rounded-lg border border-gray-700 bg-gray-800 px-3 py-1.5 text-sm text-gray-200 focus:border-cyan-500 focus:outline-none" placeholder="Start date" /> to handleFilterChange( "end_date", e.target.value ? `${e.target.value}T23:59:59` : "" ) } className="rounded-lg border border-gray-700 bg-gray-800 px-3 py-1.5 text-sm text-gray-200 focus:border-cyan-500 focus:outline-none" placeholder="End date" /> {hasActiveFilters && ( )}
{logsData?.total || 0} total records
{/* Logs Table */}
{logsData?.items.map((log) => ( ))}
Timestamp User Action Entity Details
{formatDate(log.timestamp)} {log.username || "System"} {log.action.replace(/_/g, " ")} {log.entity_type && (
{log.entity_type} {log.entity_id && ( ({log.entity_id.slice(0, 8)}...) )}
)} {!log.entity_type && }
{log.details ? (
View details
                          {formatDetails(log.details)}
                        
) : ( )}
{logsData?.items.length === 0 && (
No audit logs found
)}
{/* Pagination */} {logsData && logsData.total > PAGE_SIZE && (
Showing {(filters.offset || 0) + 1} to{" "} {Math.min((filters.offset || 0) + PAGE_SIZE, logsData.total)} of{" "} {logsData.total}
Page {currentPage} of {totalPages}
)}
); }