From fd8fd3a4e881e4d7dd6da5c7c95b43f604447adf Mon Sep 17 00:00:00 2001 From: SibarchanNayak Date: Wed, 29 Jul 2026 17:18:13 +0530 Subject: [PATCH] feat: implement HTTP method filtering, parallelize initialization requests, and replace native alerts with toast notifications for audit log exports --- package-lock.json | 3 ++ src/pages/superadmin/AuditLogs.tsx | 26 ++++++++++++--- src/pages/tenant/AuditLogs.tsx | 52 +++++++++++++++++++++++++----- 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 09acde8..1bb3baf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -58,6 +58,9 @@ "typescript": "~5.9.3", "typescript-eslint": "^8.46.4", "vite": "^7.2.4" + }, + "engines": { + "node": ">=22.0.0" } }, "node_modules/@babel/code-frame": { diff --git a/src/pages/superadmin/AuditLogs.tsx b/src/pages/superadmin/AuditLogs.tsx index 00f0330..eb595fa 100644 --- a/src/pages/superadmin/AuditLogs.tsx +++ b/src/pages/superadmin/AuditLogs.tsx @@ -21,6 +21,7 @@ import { tenantService } from "@/services/tenant-service"; import type { AuditLog } from "@/types/audit-log"; import type { Tenant } from "@/types/tenant"; import { useAppTheme } from "@/hooks/useAppTheme"; +import { showToast } from "@/utils/toast"; // Helper function to format date const formatDate = (dateString: string): string => { @@ -137,7 +138,7 @@ const AuditLogs = (): ReactElement => { null, ); - // Fetch tenants on mount for the selector + // FE-1: Run all three independent fetches in parallel instead of sequentially useEffect(() => { const fetchTenants = async () => { try { @@ -176,9 +177,11 @@ const AuditLogs = (): ReactElement => { } }; - fetchTenants(); - fetchResourceTypes(); - fetchModules(); + Promise.all([ + fetchTenants(), + fetchResourceTypes(), + fetchModules(), + ]); }, []); @@ -254,6 +257,15 @@ const AuditLogs = (): ReactElement => { tenantId: tenantFilter || undefined, }); if (response.success) { + // FE-3 (BE-8 truncation warning): Notify when results were silently limited + if (response.data.truncated) { + showToast.warning( + "Export Truncated", + response.data.warning || + `Only ${(response.data.exported ?? 10000).toLocaleString()} of ${response.data.total.toLocaleString()} records were exported. Use date filters to narrow the range.` + ); + } + const blob = new Blob( [JSON.stringify(response.data.records, null, 2)], { type: "application/json" }, @@ -267,7 +279,11 @@ const AuditLogs = (): ReactElement => { document.body.removeChild(link); } } catch (err: any) { - alert("Export failed: " + err.message); + // FE-3: Use app toast system instead of native alert() + showToast.error( + "Export Failed", + err?.response?.data?.error?.message || err.message || "An unexpected error occurred" + ); } }; diff --git a/src/pages/tenant/AuditLogs.tsx b/src/pages/tenant/AuditLogs.tsx index 053634c..625525d 100644 --- a/src/pages/tenant/AuditLogs.tsx +++ b/src/pages/tenant/AuditLogs.tsx @@ -20,6 +20,7 @@ import type { AuditLog } from "@/types/audit-log"; import { useAppTheme } from "@/hooks/useAppTheme"; import { PrimaryButton } from "@/components/shared"; import { useAppSelector } from "@/hooks/redux-hooks"; +import { showToast } from "@/utils/toast"; export interface AuditLogsProps { customTenantId?: string; @@ -123,7 +124,8 @@ const AuditLogs = ({ }); // Filter state - const methodFilter = null; + // FE-2: methodFilter converted from a hardcoded null constant to real controllable state + const [methodFilter, setMethodFilter] = useState(null); const [actionFilter, setActionFilter] = useState(null); const [resourceTypeFilter, setResourceTypeFilter] = useState( null, @@ -226,10 +228,12 @@ const AuditLogs = ({ } }; - // Fetch resource types and modules on mount + // FE-1: Fetch resource types and modules in parallel (were sequential before) useEffect(() => { - fetchResourceTypes(); - fetchModules(); + Promise.all([ + fetchResourceTypes(), + fetchModules(), + ]); }, [tenantId]); // Debouncing for Search @@ -275,9 +279,15 @@ const AuditLogs = ({ }); if (response.success) { - // In a real app, we'd trigger a file download here. - // For now, we'll just log and show a message since the response is JSON. - console.log("Export data:", response.data.records); + // FE-3 (export truncation warning from BE-8): Inform the user if results were truncated + if (response.data.truncated) { + showToast.warning( + "Export Truncated", + response.data.warning || + `Only ${(response.data.exported ?? 10000).toLocaleString()} of ${response.data.total.toLocaleString()} records were exported. Use date filters to narrow the range.` + ); + } + const blob = new Blob( [JSON.stringify(response.data.records, null, 2)], { type: "application/json" }, @@ -291,7 +301,11 @@ const AuditLogs = ({ document.body.removeChild(link); } } catch (err: any) { - alert("Failed to export audit logs: " + (err.message || "Unknown error")); + // FE-3: Use app toast system instead of native alert() + showToast.error( + "Export Failed", + err?.response?.data?.error?.message || err.message || "An unexpected error occurred" + ); } }; @@ -564,6 +578,26 @@ const AuditLogs = ({ /> )} + {/* FE-2: HTTP Method filter — methodFilter is now real state */} + {isTenantAdmin && ( + { + setMethodFilter(value as string | null); + setCurrentPage(1); + }} + placeholder="All Methods" + /> + )} + {/* Module Filter */}