import { useMemo, useState } from "react"; import { getApiErrorMessage, useSchemeAuditQuery } from "../../api/queries"; import { ApiErrorView } from "../../shared/ui/ApiErrorView"; import { StatCard } from "../../shared/ui/StatCard"; type Props = { schemeId: string; }; type AuditGroup = "all" | "lifecycle" | "pricing" | "versioning" | "upload"; function valueText(value: unknown): string { if (value === null || value === undefined || value === "") { return "—"; } return String(value); } function detectGroup(eventType: string | null | undefined): AuditGroup { const value = (eventType || "").toLowerCase(); if (value.includes("pricing.")) return "pricing"; if (value.includes("version")) return "versioning"; if (value.includes("upload") || value.includes("import")) return "upload"; if ( value.includes("published") || value.includes("unpublished") || value.includes("rolled_back") || value.includes("scheme.created") ) { return "lifecycle"; } return "all"; } function groupLabel(group: AuditGroup): string { if (group === "all") return "Все"; if (group === "lifecycle") return "Жизненный цикл"; if (group === "pricing") return "Тарифы"; if (group === "versioning") return "Версионирование"; return "Загрузка / импорт"; } function prettyDetails(value: unknown): string { if (value === null || value === undefined) { return "null"; } if (typeof value === "string") { try { const parsed = JSON.parse(value); return JSON.stringify(parsed, null, 2); } catch { return value; } } try { return JSON.stringify(value, null, 2); } catch { return String(value); } } export function SchemeAuditTab({ schemeId }: Props) { const auditQuery = useSchemeAuditQuery(schemeId); const [selectedGroup, setSelectedGroup] = useState("all"); const [expandedRows, setExpandedRows] = useState>({}); const items = useMemo(() => { const raw = auditQuery.data ?? []; return [...raw].sort((a, b) => { const left = new Date(a.created_at || 0).getTime(); const right = new Date(b.created_at || 0).getTime(); return right - left; }); }, [auditQuery.data]); const counts = useMemo(() => { return { all: items.length, lifecycle: items.filter((item) => detectGroup(item.event_type) === "lifecycle").length, pricing: items.filter((item) => detectGroup(item.event_type) === "pricing").length, versioning: items.filter((item) => detectGroup(item.event_type) === "versioning").length, upload: items.filter((item) => detectGroup(item.event_type) === "upload").length }; }, [items]); const filtered = useMemo(() => { if (selectedGroup === "all") { return items; } return items.filter((item) => detectGroup(item.event_type) === selectedGroup); }, [items, selectedGroup]); const detailsMap = useMemo(() => { const result: Record = {}; filtered.forEach((item, index) => { const key = item.audit_event_id || `${item.event_type || "event"}-${index}`; result[key] = prettyDetails(item.details_json); }); return result; }, [filtered]); if (auditQuery.isLoading) { return
Загрузка аудита...
; } if (auditQuery.isError) { return ; } return (

Аудит схемы

История событий по схеме. Список отсортирован от новых к старым.

События

{filtered.length === 0 ? (

Событий для выбранной группы нет.

) : (
{filtered.map((item, index) => { const rowKey = item.audit_event_id || `${item.event_type || "event"}-${index}`; const isOpen = expandedRows[rowKey] === true; const details = detailsMap[rowKey] || "{}"; return (
{valueText(item.event_type)}
Группа: {groupLabel(detectGroup(item.event_type))} · Создано: {valueText(item.created_at)}
Тип объекта: {valueText(item.object_type)}
Ссылка на объект: {valueText(item.object_ref)}
ID события: {valueText(item.audit_event_id)}
{isOpen ? (
{details}
) : null}
); })}
)}
); }