diff --git a/src/locales/en.json b/src/locales/en.json index c0a42f7..1f8fdec 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -3097,6 +3097,7 @@ "filters": { "title": "Filters", "active": "Active", + "user": "User", "action": "Action", "actionPlaceholder": "Search by action...", "resource": "Resource type", diff --git a/src/locales/fa.json b/src/locales/fa.json index a9b2c93..3cda2af 100644 --- a/src/locales/fa.json +++ b/src/locales/fa.json @@ -2826,6 +2826,7 @@ "filters": { "title": "فیلترها", "active": "فعال", + "user": "کاربر", "action": "عملیات", "actionPlaceholder": "جستجو بر اساس عملیات...", "resource": "نوع منبع", diff --git a/src/locales/ru.json b/src/locales/ru.json index 4f5fc71..96d9915 100644 --- a/src/locales/ru.json +++ b/src/locales/ru.json @@ -3645,6 +3645,7 @@ "filters": { "title": "Фильтры", "active": "Активны", + "user": "Пользователь", "action": "Действие", "actionPlaceholder": "Поиск по действию...", "resource": "Тип ресурса", diff --git a/src/locales/zh.json b/src/locales/zh.json index c177bf2..d6560b1 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -2825,6 +2825,7 @@ "filters": { "title": "筛选", "active": "已激活", + "user": "用户", "action": "操作", "actionPlaceholder": "按操作搜索...", "resource": "资源类型", diff --git a/src/pages/AdminAuditLog.tsx b/src/pages/AdminAuditLog.tsx index 6d27586..a4dc8f3 100644 --- a/src/pages/AdminAuditLog.tsx +++ b/src/pages/AdminAuditLog.tsx @@ -1,4 +1,4 @@ -import { useState, useCallback, useMemo, useEffect, useRef } from 'react'; +import { useState, useCallback, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router'; import { useQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; @@ -84,6 +84,7 @@ const RESOURCE_TYPES = [ 'users', 'tickets', 'stats', + 'sales_stats', 'broadcasts', 'tariffs', 'promocodes', @@ -106,6 +107,7 @@ const RESOURCE_TYPES = [ 'apps', 'email_templates', 'pinned_messages', + 'landings', 'updates', ] as const; @@ -116,6 +118,7 @@ const PAGE_SIZE_OPTIONS = [20, 50, 100] as const; const AUTO_REFRESH_INTERVAL = 30_000; interface FiltersState { + userId: string; action: string; resource: string; status: string; @@ -124,6 +127,7 @@ interface FiltersState { } const INITIAL_FILTERS: FiltersState = { + userId: '', action: '', resource: '', status: '', @@ -411,6 +415,11 @@ function LogEntryCard({ entry, isExpanded, onToggle }: LogEntryCardProps) { // === Main Page === +interface KnownUser { + name: string; + email: string | null; +} + export default function AdminAuditLog() { const { t } = useTranslation(); const navigate = useNavigate(); @@ -431,8 +440,8 @@ export default function AdminAuditLog() { const [exporting, setExporting] = useState(false); const [exportError, setExportError] = useState(null); - // Auto-refresh interval ref - const intervalRef = useRef | null>(null); + // Known users accumulated from audit log entries + const [knownUsers, setKnownUsers] = useState>(new Map()); // Build query params const queryParams = useMemo((): AuditLogFilters => { @@ -441,6 +450,10 @@ export default function AdminAuditLog() { offset: page * pageSize, }; + const parsedUserId = parseInt(appliedFilters.userId, 10); + if (!isNaN(parsedUserId) && parsedUserId > 0) { + params.user_id = parsedUserId; + } if (appliedFilters.action.trim()) { params.action = appliedFilters.action.trim(); } @@ -467,21 +480,23 @@ export default function AdminAuditLog() { refetchInterval: autoRefresh ? AUTO_REFRESH_INTERVAL : false, }); - // Auto-refresh visual indicator + // Accumulate unique users from audit log entries useEffect(() => { - if (autoRefresh) { - intervalRef.current = setInterval(() => { - // The visual indicator updates are driven by isFetching from react-query - }, AUTO_REFRESH_INTERVAL); - } - - return () => { - if (intervalRef.current) { - clearInterval(intervalRef.current); - intervalRef.current = null; + if (!data?.items?.length) return; + setKnownUsers((prev) => { + const next = new Map(prev); + let changed = false; + for (const entry of data.items) { + const name = entry.user_first_name || entry.user_email || `#${entry.user_id}`; + const existing = next.get(entry.user_id); + if (!existing || existing.name !== name || existing.email !== entry.user_email) { + next.set(entry.user_id, { name, email: entry.user_email }); + changed = true; + } } - }; - }, [autoRefresh]); + return changed ? next : prev; + }); + }, [data?.items]); const entries = data?.items ?? []; const total = data?.total ?? 0; @@ -517,8 +532,11 @@ export default function AdminAuditLog() { setExporting(true); try { const exportParams: AuditLogFilters = {}; + const exportUserId = parseInt(appliedFilters.userId, 10); + if (!isNaN(exportUserId) && exportUserId > 0) exportParams.user_id = exportUserId; if (appliedFilters.action.trim()) exportParams.action = appliedFilters.action.trim(); if (appliedFilters.resource) exportParams.resource_type = appliedFilters.resource; + if (appliedFilters.status) exportParams.status = appliedFilters.status; if (appliedFilters.dateFrom) exportParams.date_from = appliedFilters.dateFrom; if (appliedFilters.dateTo) exportParams.date_to = appliedFilters.dateTo; @@ -548,6 +566,7 @@ export default function AdminAuditLog() { const hasActiveFilters = useMemo(() => { return ( + appliedFilters.userId.trim() !== '' || appliedFilters.action.trim() !== '' || appliedFilters.resource !== '' || appliedFilters.status !== '' || @@ -649,6 +668,49 @@ export default function AdminAuditLog() { {filtersOpen && (
+ {/* User filter */} + {knownUsers.size > 0 && ( +
+ +
+ {Array.from(knownUsers.entries()).map(([uid, user]) => { + const isSelected = filters.userId === String(uid); + return ( + + ); + })} +
+
+ )} + {/* Action search */}