import { useVirtualizer } from "@tanstack/react-virtual"; import { ChevronDown, ChevronUp, Copy } from "lucide-react"; import * as React from "react"; import { formatAuditDateParts, formatAuditValue, parseAuditDetails, resolveAuditAction, resolveAuditActor, resolveAuditTarget, } from "../../../../common/core/audit"; import { type CommonBadgeVariant, getCommonBadgeClasses, } from "../../../../common/ui/badge"; import { getCommonButtonClasses } from "../../../../common/ui/button"; import { commonStickyTableHeaderClass, commonTableBodyClass, commonTableCellClass, commonTableClass, commonTableHeadClass, commonTableHeaderClass, commonTableRowClass, commonTableShellClass, commonTableViewportClass, commonTableWrapperClass, } from "../../../../common/ui/table"; import { Button } from "../../components/ui/button"; import type { AuditLog } from "../../lib/adminApi"; type AuditTranslate = ( key: string, fallback: string, vars?: Record, ) => string; type VirtualizedAuditLogTableProps = { logs: AuditLog[]; t: AuditTranslate; loading: boolean; hasNextPage: boolean; isFetchingNextPage: boolean; onLoadMore: () => void; className?: string; }; function cx(...classNames: Array) { return classNames.filter(Boolean).join(" "); } function statusVariant(status: string): CommonBadgeVariant { return status === "success" || status === "ok" ? "success" : "warning"; } export function VirtualizedAuditLogTable({ logs, t, loading, hasNextPage, isFetchingNextPage, onLoadMore, className, }: VirtualizedAuditLogTableProps) { const [expandedRows, setExpandedRows] = React.useState< Record >({}); const viewportRef = React.useRef(null); const isTest = (typeof process !== "undefined" && process.env.NODE_ENV === "test") || (typeof window !== "undefined" && (window as Window & { _IS_TEST_MODE?: boolean })._IS_TEST_MODE); const handleCopy = (value: string) => { if (!value) { return; } navigator.clipboard.writeText(value); }; const rowVirtualizer = useVirtualizer({ count: logs.length, getScrollElement: () => viewportRef.current, estimateSize: () => 80, measureElement: (el) => el.getBoundingClientRect().height, overscan: isTest ? logs.length : 10, initialRect: isTest ? { width: 1010, height: 1000 } : undefined, }); const virtualRows = rowVirtualizer.getVirtualItems(); React.useEffect(() => { if (isTest) { return; } const lastItem = virtualRows[virtualRows.length - 1]; if (!lastItem) return; if ( lastItem.index >= logs.length - 1 && hasNextPage && !isFetchingNextPage ) { onLoadMore(); } }, [ virtualRows, logs.length, hasNextPage, isFetchingNextPage, onLoadMore, isTest, ]); const tableMinWidth = 1010; const renderRow = ( row: AuditLog, index: number, virtualRow?: { start: number; end: number }, ) => { if (!row) return null; const details = parseAuditDetails(row.details); const actorLabel = resolveAuditActor(row, details); const actionLabel = resolveAuditAction(row, details); const targetLabel = resolveAuditTarget(details); const rowKey = `${row.event_id}-${row.timestamp}-${index}`; const expanded = Boolean(expandedRows[rowKey]); const { date, time } = formatAuditDateParts(row.timestamp); return (
{date}
{time}
{actorLabel} {actorLabel !== "-" ? ( ) : null}
{actionLabel}
{targetLabel} {targetLabel !== "-" ? ( ) : null}
{row.status}
{expanded && (
{t("ui.common.audit.details.request", "Request")}
{t( "ui.common.audit.details.request_id", "Request ID · {{value}}", { value: formatAuditValue(details.request_id) }, )}
{t( "ui.common.audit.details.event_id", "Event ID · {{value}}", { value: formatAuditValue(row.event_id) }, )}
{t("ui.common.audit.details.ip", "IP · {{value}}", { value: formatAuditValue(row.ip_address), })}
{t("ui.common.audit.details.method", "Method · {{value}}", { value: formatAuditValue(details.method), })}
{t("ui.common.audit.details.path", "Path · {{value}}", { value: formatAuditValue(details.path), })}
{t( "ui.common.audit.details.latency", "Latency · {{value}}", { value: details.latency_ms !== undefined ? `${details.latency_ms}ms` : "-", }, )}
{t("ui.common.audit.details.actor", "Actor")}
{t( "ui.common.audit.details.actor_id", "User ID · {{value}}", { value: actorLabel }, )}
{t("ui.common.audit.details.tenant", "Tenant · {{value}}", { value: formatAuditValue(details.tenant_id), })}
{t("ui.common.audit.details.device", "Device · {{value}}", { value: formatAuditValue(row.device_id), })}
{t( "ui.common.audit.details.target", "Client ID · {{value}}", { value: targetLabel }, )}
{t("ui.common.audit.details.result", "Result")}
{t("ui.common.audit.details.error", "Error · {{value}}", { value: formatAuditValue(details.error), })}
{t("ui.common.audit.details.before", "Before · {{value}}", { value: formatAuditValue(details.before), })}
{t("ui.common.audit.details.after", "After · {{value}}", { value: formatAuditValue(details.after), })}
)} ); }; return (
{isTest ? logs.map((row, index) => renderRow(row, index)) : virtualRows.map((virtualRow) => renderRow( logs[virtualRow.index], virtualRow.index, virtualRow, ), )} {logs.length === 0 && !loading && ( )}
{t("ui.common.audit.table.time", "Time")} {t("ui.common.audit.table.user_id", "User ID")} {t("ui.common.audit.table.action", "Action")} {t("ui.common.audit.table.client_id", "Client ID")} {t("ui.common.audit.table.status", "Status")}
{t("ui.common.audit.table.no_logs", "No audit logs found")}
{hasNextPage ? (
{isFetchingNextPage && ( {t("msg.common.loading", "Loading more...")} )}
) : logs.length > 0 ? ( {t("msg.common.audit.end", "End of audit feed")} ) : null}
); }