diff --git a/src/pages/activity-log/index.tsx b/src/pages/activity-log/index.tsx index 981b464..2b0137f 100644 --- a/src/pages/activity-log/index.tsx +++ b/src/pages/activity-log/index.tsx @@ -1,3 +1,5 @@ +import { useState } from "react" +import { useQuery } from "@tanstack/react-query" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" @@ -10,14 +12,60 @@ import { TableHeader, TableRow, } from "@/components/ui/table" -import { Search, Download, Eye, MoreVertical } from "lucide-react" +import { Search, Download, Eye, ChevronLeft, ChevronRight } from "lucide-react" +import { auditService } from "@/services" +import { format } from "date-fns" export default function ActivityLogPage() { + const [page, setPage] = useState(1) + const [limit] = useState(20) + const [search, setSearch] = useState("") + const [actionFilter, setActionFilter] = useState("") + const [resourceTypeFilter, setResourceTypeFilter] = useState("") + + const { data: auditData, isLoading } = useQuery({ + queryKey: ['activity-log', page, limit, search, actionFilter, resourceTypeFilter], + queryFn: async () => { + const params: any = { page, limit } + if (search) params.search = search + if (actionFilter) params.action = actionFilter + if (resourceTypeFilter) params.resourceType = resourceTypeFilter + return await auditService.getAuditLogs(params) + }, + }) + + const handleExport = async () => { + try { + const blob = await auditService.exportAuditLogs({ format: 'csv' }) + const url = window.URL.createObjectURL(blob) + const a = document.createElement('a') + a.href = url + a.download = `activity-log-${format(new Date(), 'yyyy-MM-dd')}.csv` + document.body.appendChild(a) + a.click() + window.URL.revokeObjectURL(url) + document.body.removeChild(a) + } catch (error) { + console.error('Export failed:', error) + } + } + + const getActionBadgeColor = (action: string) => { + const colors: Record = { + Create: "bg-blue-500", + Update: "bg-green-500", + Delete: "bg-red-500", + Login: "bg-purple-500", + Logout: "bg-gray-500", + } + return colors[action] || "bg-gray-500" + } + return (

Activity Log

- @@ -33,109 +81,113 @@ export default function ActivityLogPage() { setSearch(e.target.value)} />
- setActionFilter(e.target.value)} + > + + + + + + - setResourceTypeFilter(e.target.value)} + > + + + + + -
- - - - Log ID - User - Action - Entity - Description - IP Address - Timestamp - Action - - - - - LOG001 - john.smith@example.com - - Create - - Client - Created new client record - 192.168.1.1 - 2024-01-15 10:30:45 - -
-
+ + + Log ID + User + Action + Resource + Resource ID + IP Address + Timestamp + Actions + + + + {auditData?.data?.map((log: any) => ( + + {log.id} + {log.userId || 'N/A'} + + + {log.action} + + + {log.resourceType} + {log.resourceId} + {log.ipAddress || 'N/A'} + + {format(new Date(log.timestamp || log.createdAt), 'MMM dd, yyyy HH:mm:ss')} + + + + + + ))} + +
+ {auditData?.data?.length === 0 && ( +
+ No activity logs found +
+ )} + {auditData && auditData.totalPages > 1 && ( +
+
+ Page {auditData.page} of {auditData.totalPages} ({auditData.total} total) +
+
+ -
- - - - LOG002 - jane.doe@example.com - - Update - - Subscription - Updated subscription status - 192.168.1.2 - 2024-01-15 09:15:22 - -
- - -
-
-
- - LOG003 - admin@example.com - - Login - - System - User logged in successfully - 192.168.1.3 - 2024-01-15 08:00:00 - -
- - -
-
-
- - +
+ )} + + )}
diff --git a/src/services/api/client.ts b/src/services/api/client.ts index e1c2971..4de9c8f 100644 --- a/src/services/api/client.ts +++ b/src/services/api/client.ts @@ -10,6 +10,18 @@ const apiClient: AxiosInstance = axios.create({ }, withCredentials: true, // Send cookies with requests timeout: 30000, // 30 second timeout + paramsSerializer: { + serialize: (params) => { + // Custom serializer to preserve number types + const searchParams = new URLSearchParams() + Object.entries(params).forEach(([key, value]) => { + if (value !== undefined && value !== null) { + searchParams.append(key, String(value)) + } + }) + return searchParams.toString() + } + } }) // Request interceptor - Add auth token diff --git a/src/services/user.service.ts b/src/services/user.service.ts index 7d4998b..c3ed4e5 100644 --- a/src/services/user.service.ts +++ b/src/services/user.service.ts @@ -41,7 +41,14 @@ class UserService { * Get paginated list of users */ async getUsers(params?: GetUsersParams): Promise> { - const response = await apiClient.get>('/admin/users', { params }) + // Ensure numeric params are sent as numbers, not strings + const queryParams = params ? { + ...params, + page: params.page ? Number(params.page) : undefined, + limit: params.limit ? Number(params.limit) : undefined, + } : undefined + + const response = await apiClient.get>('/admin/users', { params: queryParams }) return response.data }