import { ChevronDown, ChevronUp, Copy } from "lucide-react"; import * as React from "react"; import { type CommonBadgeVariant, getCommonBadgeClasses, } from "../../../ui/badge"; import { getCommonButtonClasses } from "../../../ui/button"; import { commonStickyTableHeaderClass, commonTableBodyClass, commonTableCellClass, commonTableClass, commonTableHeadClass, commonTableHeaderClass, commonTableRowClass, commonTableShellClass, commonTableViewportClass, commonTableWrapperClass, } from "../../../ui/table"; import type { CommonAuditLog } from "../../audit"; import { formatAuditDateParts, formatAuditValue, parseAuditDetails, resolveAuditAction, resolveAuditActor, resolveAuditTarget, } from "../../audit"; type AuditTranslate = ( key: string, fallback: string, vars?: Record, ) => string; type AuditLogTableProps = { logs: CommonAuditLog[]; 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 { switch (status.toLowerCase()) { case "success": case "ok": return "success"; case "failure": case "error": case "blocked": return "destructive"; case "pending": case "warning": return "warning"; default: return "default"; } } export function AuditLogTable({ logs, t, loading, hasNextPage, isFetchingNextPage, onLoadMore, className, }: AuditLogTableProps) { const [expandedRows, setExpandedRows] = React.useState< Record >({}); const handleCopy = (value: string) => { if (!value) { return; } navigator.clipboard.writeText(value); }; return (
{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")} {logs.map((log, index) => { const details = parseAuditDetails(log.details); const actorLabel = resolveAuditActor(log, details); const actionLabel = resolveAuditAction(log, details); const targetLabel = resolveAuditTarget(details); const rowKey = `${log.event_id}-${log.timestamp}-${index}`; const expanded = Boolean(expandedRows[rowKey]); const { date, time } = formatAuditDateParts(log.timestamp); return (
{date}
{time}
{actorLabel} {actorLabel !== "-" ? ( ) : null}
{actionLabel}
{targetLabel} {targetLabel !== "-" ? ( ) : null}
{log.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(log.event_id) }, )}
{t( "ui.common.audit.details.ip", "IP · {{value}}", { value: formatAuditValue(log.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(log.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) }, )}
)}
); })} {logs.length === 0 && !loading && ( {t("msg.common.audit.empty", "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}
); } // Internal table components for cleaner implementation function Table({ className, children, style, }: { className?: string; children: React.ReactNode; style?: React.CSSProperties; }) { return ( {children}
); } function TableHeader({ className, children, }: { className?: string; children: React.ReactNode; }) { return {children}; } function TableBody({ className, children, }: { className?: string; children: React.ReactNode; }) { return {children}; } function TableRow({ className, children, }: { className?: string; children: React.ReactNode; }) { return {children}; } function TableHead({ className, children, }: { className?: string; children?: React.ReactNode; }) { return {children}; } function TableCell({ className, children, colSpan, }: { className?: string; children: React.ReactNode; colSpan?: number; }) { return ( {children} ); }