import type { components } from '@pipu/api-client'; import { unwrap } from '@pipu/core'; import { useInfiniteQuery } from '@tanstack/react-query'; import { useTranslation } from 'react-i18next'; import { useSession } from '../../app/session'; import { useErrorText } from '../../shared/lib/errors'; import { formatDateTime } from '../../shared/lib/format'; import { Button, EmptyState, FormError, PageSkeleton } from '../../shared/ui/ui'; import s from '../page.module.css'; type Entry = components['schemas']['AuditEntry']; /** Краткое описание полезной нагрузки записи: «user → moderator», «anna (user)». */ function details(e: Entry, roleName: (r: string) => string): string { const p = e.payload ?? {}; if (e.action === 'user.role_change') return `${roleName(String(p.from))} → ${roleName(String(p.to))}`; if (e.action === 'user.username_change') return `${String(p.from)} → ${String(p.to)}`; const parts = [p.username, p.role && roleName(String(p.role)), p.display_name].filter( (v): v is string => typeof v === 'string' && v !== '', ); return parts.join(' · '); } export function AuditPage() { const { t, i18n } = useTranslation(['admin', 'common']); const session = useSession(); const errorText = useErrorText(); const list = useInfiniteQuery({ queryKey: ['admin-audit'], initialPageParam: undefined as string | undefined, queryFn: async ({ pageParam }) => unwrap(await session.client.GET('/api/v1/admin/audit', { params: { query: { cursor: pageParam, limit: 50 } } })), getNextPageParam: (page) => page.next_cursor ?? undefined, }); const entries = list.data?.pages.flatMap((p) => p.items) ?? []; const tr = t as unknown as (key: string, opts?: Record) => string; const roleName = (r: string) => tr(`common:roles.${r}`, { defaultValue: r }); return (

{t('audit.title')}

{t('audit.subtitle')}

{list.error ? errorText(list.error) : null}
{entries.map((e) => ( ))}
{t('audit.col_when')} {t('audit.col_who')} {t('audit.col_action')} {t('audit.col_details')}
{formatDateTime(e.created_at, i18n.language)} {e.actor ? `@${e.actor.username}` : {t('audit.system')}} {tr(`admin:audit.actions.${e.action}`, { defaultValue: e.action })} {details(e, roleName)}
{list.isSuccess && entries.length === 0 && {t('audit.empty')}} {list.isPending && }
{list.hasNextPage && ( )}
); }