From 277600edf0bc974b77f90c4b25105f5dbffcf52a Mon Sep 17 00:00:00 2001 From: sibarchannayak Date: Fri, 29 May 2026 15:40:12 +0530 Subject: [PATCH] feat: implement reusable DataTable component and add AuditLogs pages for superadmin and tenant views --- src/components/shared/DataTable.tsx | 2 +- src/pages/superadmin/AuditLogs.tsx | 120 ++++++++++++++---- src/pages/tenant/AuditLogs.tsx | 182 ++++++++++++++++------------ 3 files changed, 205 insertions(+), 99 deletions(-) diff --git a/src/components/shared/DataTable.tsx b/src/components/shared/DataTable.tsx index 8d16675..142f51b 100644 --- a/src/components/shared/DataTable.tsx +++ b/src/components/shared/DataTable.tsx @@ -228,7 +228,7 @@ export const DataTable = ({ {canExpand && expanded && ( -
+
{renderExpandedRow(item)}
diff --git a/src/pages/superadmin/AuditLogs.tsx b/src/pages/superadmin/AuditLogs.tsx index 70ee158..7e56d5b 100644 --- a/src/pages/superadmin/AuditLogs.tsx +++ b/src/pages/superadmin/AuditLogs.tsx @@ -83,6 +83,7 @@ const getStatusColor = (status: number | null): string => { const AuditLogs = (): ReactElement => { const { primaryColor } = useAppTheme(); + const [expandedId, setExpandedId] = useState(null); const [auditLogs, setAuditLogs] = useState([]); const [tenants, setTenants] = useState([]); const [modules, setModules] = useState>( @@ -192,6 +193,7 @@ const AuditLogs = (): ReactElement => { try { setIsLoading(true); setError(null); + setExpandedId(null); const response = await auditLogService.getAll( currentPage, limit, @@ -287,6 +289,78 @@ const AuditLogs = (): ReactElement => { return response.data; }; + const toggleExpand = (id: string): void => { + setExpandedId((prev) => (prev === id ? null : id)); + }; + + const renderExpanded = (row: AuditLog): ReactElement => { + return ( +
+
+ {/* Column 1: Request Details */} +
+

Request Info

+
+ HTTP Method:{" "} + + {row.request_method || "N/A"} + +
+
+ Request Path:{" "} + + {row.request_path || "N/A"} + +
+
+ IP Address:{" "} + {row.ip_address || "N/A"} +
+
+ User Agent:{" "} + {row.user_agent || "N/A"} +
+
+ + {/* Column 2: Audit Identifiers */} +
+

Audit Details

+
+ Log ID:{" "} + + {row.id} + +
+
+ Resource ID:{" "} + + {row.resource_id || "N/A"} + +
+
+ Correlation ID:{" "} + + {row.correlation_id || "N/A"} + +
+
+ + {/* Column 3: Metadata */} +
+

Metadata

+ {row.metadata && Object.keys(row.metadata).length > 0 ? ( +
+                {JSON.stringify(row.metadata, null, 2)}
+              
+ ) : ( + No additional metadata + )} +
+
+
+ ); + }; + // Define table columns const columns: Column[] = [ { @@ -302,17 +376,20 @@ const AuditLogs = (): ReactElement => { { key: "tenant", label: "Tenant", - render: (log) => ( - - {log.tenant ? log.tenant.name : "N/A"} - - ), + render: (log) => { + const tenantName = log.tenant ? log.tenant.name : "N/A"; + return ( + + {tenantName} + + ); + }, }, { key: "resource_type", label: "Resource Type", render: (log) => ( - + {log.resource_type} ), @@ -338,22 +415,16 @@ const AuditLogs = (): ReactElement => { { key: "user", label: "User", - render: (log) => ( - - {log.user - ? `${log.user.first_name} ${log.user.last_name}` - : log.user_email || "N/A"} - - ), - }, - { - key: "request_method", - label: "Method", - render: (log) => ( - - {log.request_method ? log.request_method.toUpperCase() : "N/A"} - - ), + render: (log) => { + const userName = log.user + ? `${log.user.first_name} ${log.user.last_name}` + : log.user_email || "N/A"; + return ( + + {userName} + + ); + }, }, { key: "response_status", @@ -677,6 +748,11 @@ const AuditLogs = (): ReactElement => { error={error} mobileCardRenderer={mobileCardRenderer} emptyMessage="No platform-wide audit logs found" + expandableRows + isRowExpanded={(row) => expandedId === row.id} + onRowExpandToggle={(row) => toggleExpand(row.id)} + renderExpandedRow={renderExpanded} + onRowClick={(row) => toggleExpand(row.id)} /> {/* Pagination */} diff --git a/src/pages/tenant/AuditLogs.tsx b/src/pages/tenant/AuditLogs.tsx index 8257354..a07b775 100644 --- a/src/pages/tenant/AuditLogs.tsx +++ b/src/pages/tenant/AuditLogs.tsx @@ -300,10 +300,68 @@ const AuditLogs = ({ const renderExpanded = (row: AuditLog): ReactElement => { return ( -
-
-          {JSON.stringify(row.metadata, null, 2)}
-        
+
+
+ {/* Column 1: Request Details */} +
+

Request Info

+
+ HTTP Method:{" "} + + {row.request_method || "N/A"} + +
+
+ Request Path:{" "} + + {row.request_path || "N/A"} + +
+
+ IP Address:{" "} + {row.ip_address || "N/A"} +
+
+ User Agent:{" "} + {row.user_agent || "N/A"} +
+
+ + {/* Column 2: Audit Identifiers */} +
+

Audit Details

+
+ Log ID:{" "} + + {row.id} + +
+
+ Resource ID:{" "} + + {row.resource_id || "N/A"} + +
+
+ Correlation ID:{" "} + + {row.correlation_id || "N/A"} + +
+
+ + {/* Column 3: Metadata */} +
+

Metadata

+ {row.metadata && Object.keys(row.metadata).length > 0 ? ( +
+                {JSON.stringify(row.metadata, null, 2)}
+              
+ ) : ( + No additional metadata + )} +
+
); }; @@ -324,7 +382,7 @@ const AuditLogs = ({ key: "resource_type", label: "Resource Type", render: (log) => ( - + {log.resource_type} ), @@ -349,28 +407,22 @@ const AuditLogs = ({ }, ...(isTenantAdmin ? [ - { - key: "user", - label: "User", - render: (log: AuditLog) => ( - - {log.user - ? `${log.user.first_name} ${log.user.last_name}` - : log.user_email || "N/A"} + { + key: "user", + label: "User", + render: (log: AuditLog) => { + const userName = log.user + ? `${log.user.first_name} ${log.user.last_name}` + : log.user_email || "N/A"; + return ( + + {userName} - ), + ); }, - ] + }, + ] : []), - { - key: "request_method", - label: "Method", - render: (log) => ( - - {log.request_method ? log.request_method.toUpperCase() : "N/A"} - - ), - }, { key: "response_status", label: "Status", @@ -382,15 +434,6 @@ const AuditLogs = ({ ), }, - { - key: "ip_address", - label: "IP Address", - render: (log) => ( - - {log.ip_address || "N/A"} - - ), - }, { key: "actions", label: "Actions", @@ -505,19 +548,6 @@ const AuditLogs = ({ { value: "SUBMIT", label: "SUBMIT" }, { value: "APPROVE", label: "APPROVE" }, { value: "REJECT", label: "REJECT" }, - // {value: 'PUBLISH', label: 'PUBLISH'}, - // {value: 'ARCHIVE', label: 'ARCHIVE'}, - // {value: 'CHECKOUT', label: 'CHECKOUT'}, - // {value: 'CHECKIN', label: 'CHECKIN'}, - // {value: 'TRANSITION', label: 'TRANSITION'}, - // {value: 'CANCEL', label: 'CANCEL'}, - // {value: 'COMPLETE', label: 'COMPLETE'}, - // {value: 'ACTIVATE', label: 'ACTIVATE'}, - // {value: 'REVOKE', label: 'REVOKE'}, - // {value: 'STATUS', label: 'STATUS'}, - // {value: 'VOID', label: 'VOID'}, - // {value: 'SEND', label: 'SEND'}, - // {value: 'API_CALL', label: 'API_CALL'}, ]} value={actionFilter} onChange={(value) => { @@ -625,23 +655,23 @@ const AuditLogs = ({ methodFilter || moduleIdFilter || search) && ( - - )} + + )}
@@ -667,19 +697,19 @@ const AuditLogs = ({ {/* Pagination */} {pagination.total > 0 && ( -
- setCurrentPage(page)} - onLimitChange={(newLimit: number) => { - setLimit(newLimit); - setCurrentPage(1); - }} - /> -
+ //
+ setCurrentPage(page)} + onLimitChange={(newLimit: number) => { + setLimit(newLimit); + setCurrentPage(1); + }} + /> + //
)}