import { useState, useEffect } from 'react'; import type { ReactElement } from 'react'; import { Layout } from '@/components/layout/Layout'; import { ViewAuditLogModal, DataTable, Pagination, FilterDropdown, StatusBadge, type Column, } from '@/components/shared'; import { Download, ArrowUpDown } from 'lucide-react'; import { auditLogService } from '@/services/audit-log-service'; import type { AuditLog } from '@/types/audit-log'; import { useAppSelector } from '@/hooks/redux-hooks'; // Helper function to format date const formatDate = (dateString: string): string => { const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit', }); }; // Helper function to get action badge variant const getActionVariant = (action: string): 'success' | 'failure' | 'info' | 'process' => { const lowerAction = action.toLowerCase(); if (lowerAction.includes('create') || lowerAction.includes('register')) return 'success'; if (lowerAction.includes('update') || lowerAction.includes('version_update') || lowerAction.includes('login')) return 'info'; if (lowerAction.includes('delete') || lowerAction.includes('deregister')) return 'failure'; if (lowerAction.includes('read') || lowerAction.includes('get') || lowerAction.includes('status_change')) return 'process'; return 'info'; }; // Helper function to get method badge variant const getMethodVariant = (method: string | null): 'success' | 'failure' | 'info' | 'process' => { if (!method) return 'info'; const upperMethod = method.toUpperCase(); if (upperMethod === 'GET') return 'success'; if (upperMethod === 'POST') return 'info'; if (upperMethod === 'PUT' || upperMethod === 'PATCH') return 'process'; if (upperMethod === 'DELETE') return 'failure'; return 'info'; }; // Helper function to get status badge color based on response status const getStatusColor = (status: number | null): string => { if (!status) return 'text-[#6b7280]'; if (status >= 200 && status < 300) return 'text-[#10b981]'; if (status >= 300 && status < 400) return 'text-[#f59e0b]'; if (status >= 400) return 'text-[#ef4444]'; return 'text-[#6b7280]'; }; const AuditLogs = (): ReactElement => { const tenantId = useAppSelector((state) => state.auth.tenantId); const [auditLogs, setAuditLogs] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Pagination state const [currentPage, setCurrentPage] = useState(1); const [limit, setLimit] = useState(5); const [pagination, setPagination] = useState<{ page: number; limit: number; total: number; totalPages: number; hasMore: boolean; }>({ page: 1, limit: 5, total: 0, totalPages: 1, hasMore: false, }); // Filter state const [methodFilter, setMethodFilter] = useState(null); const [orderBy, setOrderBy] = useState(null); // View modal const [viewModalOpen, setViewModalOpen] = useState(false); const [selectedAuditLogId, setSelectedAuditLogId] = useState(null); const fetchAuditLogs = async ( page: number, itemsPerPage: number, method: string | null = null, sortBy: string[] | null = null ): Promise => { try { setIsLoading(true); setError(null); const response = await auditLogService.getAll(page, itemsPerPage, method, sortBy, tenantId); if (response.success) { setAuditLogs(response.data); setPagination(response.pagination); } else { setError('Failed to load audit logs'); } } catch (err: any) { setError(err?.response?.data?.error?.message || 'Failed to load audit logs'); } finally { setIsLoading(false); } }; // Fetch audit logs on mount and when pagination/filters change useEffect(() => { if (tenantId) { fetchAuditLogs(currentPage, limit, methodFilter, orderBy); } }, [currentPage, limit, methodFilter, orderBy, tenantId]); // View audit log handler const handleViewAuditLog = (auditLogId: string): void => { setSelectedAuditLogId(auditLogId); setViewModalOpen(true); }; // Load audit log for view const loadAuditLog = async (id: string): Promise => { const response = await auditLogService.getById(id); return response.data; }; // Define table columns const columns: Column[] = [ { key: 'created_at', label: 'Timestamp', render: (log) => ( {formatDate(log.created_at)} ), mobileLabel: 'Time', }, { key: 'resource_type', label: 'Resource Type', render: (log) => ( {log.resource_type} ), }, { key: 'action', label: 'Action', render: (log) => ( {log.action} ), }, { key: 'resource_id', label: 'Resource ID', render: (log) => ( {log.resource_id || 'N/A'} ), }, { key: 'user', label: 'User', render: (log) => ( {log.user ? `${log.user.first_name} ${log.user.last_name}` : 'N/A'} ), }, { key: 'request_method', label: 'Method', render: (log) => ( {log.request_method ? log.request_method.toUpperCase() : 'N/A'} ), }, { key: 'response_status', label: 'Status', render: (log) => ( {log.response_status || 'N/A'} ), }, { key: 'ip_address', label: 'IP Address', render: (log) => ( {log.ip_address || 'N/A'} ), }, { key: 'actions', label: 'Actions', align: 'right', render: (log) => (
), }, ]; // Mobile card renderer const mobileCardRenderer = (log: AuditLog) => (

{log.resource_type}

{log.action}
Timestamp:

{formatDate(log.created_at)}

User:

{log.user ? `${log.user.first_name} ${log.user.last_name}` : 'N/A'}

Method:
{log.request_method ? log.request_method.toUpperCase() : 'N/A'}
Status:

{log.response_status || 'N/A'}

Resource ID:

{log.resource_id || 'N/A'}

IP Address:

{log.ip_address || 'N/A'}

); return ( {/* Table Container */}
{/* Table Header with Filters */}
{/* Filters */}
{/* Method Filter */} { setMethodFilter(value as string | null); setCurrentPage(1); }} placeholder="All" /> {/* Sort Filter */} { setOrderBy(value as string[] | null); setCurrentPage(1); }} placeholder="Default" showIcon icon={} />
{/* Actions */}
{/* Export Button */}
{/* Table */} log.id} isLoading={isLoading} error={error} mobileCardRenderer={mobileCardRenderer} emptyMessage="No audit logs found" /> {/* Pagination */} {pagination.total > 0 && (
setCurrentPage(page)} onLimitChange={(newLimit: number) => { setLimit(newLimit); setCurrentPage(1); }} />
)}
{/* View Audit Log Modal */} { setViewModalOpen(false); setSelectedAuditLogId(null); }} auditLogId={selectedAuditLogId} onLoadAuditLog={loadAuditLog} />
); }; export default AuditLogs;