import React, { useState, useEffect, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { ColumnDef } from '@tanstack/react-table'; import { Link } from 'react-router-dom'; import { useAudit } from '@/hooks/useAudit'; import { useAdmin, useAdmins } from '@/hooks/useAdmins'; import { useUser } from '@/hooks/useUsers'; import { useEvent } from '@/hooks/useEvents'; import { AuditRecord, AuditListParams } from '@/types/api'; import { getSavedPageSize } from '@/utils/tablePagination'; import { DataTable } from '@/components/data-table/DataTable'; import { ListPageHeader } from '@/components/ListPageHeader'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; const TABLE_KEY = 'audit'; const ALL = '__all__'; const roleBadgeVariant = (role: string) => { if (role === 'superadmin') return 'destructive' as const; if (role === 'admin') return 'default' as const; if (role === 'moderator') return 'secondary' as const; return 'outline' as const; }; const actionBadgeVariant = (action: string) => { if (action === 'create') return 'success' as const; if (action === 'delete') return 'destructive' as const; if (action === 'update') return 'default' as const; if (action === 'freeze' || action === 'block' || action === 'hide') return 'warning' as const; return 'outline' as const; }; const entityTypeBadgeVariant = (type: string) => { if (type === 'user') return 'default' as const; if (type === 'event') return 'success' as const; if (type === 'review') return 'secondary' as const; if (type === 'report') return 'destructive' as const; return 'outline' as const; }; const isBadValue = (val: unknown) => val === '-' || val === 'undefined' || val === '' || val === null || val === undefined; const toDateInput = (iso: string | undefined): string => { if (!iso) return ''; return iso.slice(0, 10); }; const dateToIso = (date: string, endOfDay = false): string | undefined => { if (!date) return undefined; const d = new Date(date); if (endOfDay) d.setHours(23, 59, 59, 999); else d.setHours(0, 0, 0, 0); return d.toISOString(); }; const AdminCell: React.FC<{ adminId: string }> = ({ adminId }) => { const { data: admin, isLoading: loading } = useAdmin(adminId); if (loading) return ; if (!admin) return {adminId}; const name = admin.nickname && admin.nickname !== '-' ? admin.nickname : admin.email; return {name || admin.id}; }; const EntityNameCell: React.FC<{ entityType: string; entityId: string }> = ({ entityType, entityId }) => { const { data: user, isLoading: loadingUser } = useUser(entityType === 'user' ? entityId : ''); const { data: event, isLoading: loadingEvent } = useEvent(entityType === 'event' ? entityId : ''); if (entityType === 'user') { if (loadingUser) return ; if (!user) return {entityId}; const name = !isBadValue(user.nickname) ? user.nickname : user.email; return {!isBadValue(name) ? name : user.id}; } if (entityType === 'event') { if (loadingEvent) return ; if (!event) return {entityId}; const name = !isBadValue(event.title) ? event.title : event.id; return {name}; } if (entityType === 'review') { return {entityId}; } return {entityId}; }; const AuditPage: React.FC = () => { const { t } = useTranslation(); const [params, setParams] = useState({ limit: getSavedPageSize(TABLE_KEY), offset: 0, sort: 'timestamp', order: 'desc', }); const { data, isLoading } = useAudit(params); const { data: admins, isLoading: loadingAdmins } = useAdmins({ limit: 1000, offset: 0 }); const [uniqueActions, setUniqueActions] = useState([]); const [adminSearch, setAdminSearch] = useState(''); useEffect(() => { if (data?.data) { const actions = new Set(data.data.map((record) => record.action).filter(Boolean)); setUniqueActions((prev) => { const merged = new Set([...prev, ...actions]); return Array.from(merged).sort(); }); } }, [data]); const filteredAdmins = useMemo(() => { const list = admins?.data || []; if (!adminSearch.trim()) return list; const q = adminSearch.toLowerCase(); return list.filter((a) => (a.email || a.id).toLowerCase().includes(q)); }, [admins, adminSearch]); const columns = useMemo[]>( () => [ { id: 'admin', header: t('audit.admin'), enableSorting: false, cell: ({ row }) => , }, { accessorKey: 'role', header: t('common.role'), enableSorting: true, cell: ({ getValue }) => { const role = getValue(); return {role}; }, }, { accessorKey: 'action', header: t('audit.action'), enableSorting: true, cell: ({ getValue }) => { const action = getValue(); return {action}; }, }, { accessorKey: 'entity_type', header: t('common.type'), enableSorting: true, cell: ({ getValue }) => { const type = getValue(); return {type}; }, }, { id: 'entity_name', header: t('audit.name'), enableSorting: false, cell: ({ row }) => ( ), }, { accessorKey: 'timestamp', header: t('common.date'), enableSorting: true }, { accessorKey: 'ip', header: t('common.ip'), enableSorting: false }, { accessorKey: 'reason', header: t('common.reason'), enableSorting: false, cell: ({ getValue }) => { const reason = getValue(); return {reason}; }, }, ], [t] ); return (
setAdminSearch(e.target.value)} className="w-[200px]" />
setParams((prev) => ({ ...prev, date_from: dateToIso(e.target.value, false), offset: 0, })) } className="w-[160px]" />
setParams((prev) => ({ ...prev, date_to: dateToIso(e.target.value, true), offset: 0, })) } className="w-[160px]" />
); }; export default AuditPage;