import Link from "next/link"; import { DateTime } from "@/components/time"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import type { AuditEntry } from "@/lib/api"; const tagLinks: Record string> = { catalog: (id) => `/console/catalogs/${id}`, product: (id) => `/products/${id}`, room: (id) => `/rooms/${id}/edit`, }; /** Audit entries: when, who, what, which resources (linked), and the details. */ export function AuditTable({ entries, empty = "Nothing yet." }: { entries: AuditEntry[]; empty?: string }) { if (entries.length === 0) return

{empty}

; return ( When Who What About Detail {entries.map((e) => ( {e.actor ? ( {e.actor} ) : ( hub )} {e.action} {Object.entries(e.tags as Record).map(([kind, id]) => { if (!id) return null; const name = (e.tagNames as Record)[id] ?? id; const href = tagLinks[kind]?.(id); return ( {kind} {href ? ( {name} ) : ( name )} ); })} {Object.keys(e.tags).length === 0 && e.target} {detailText(e.detail)} ))}
); } function detailText(detail: unknown): string { return Object.entries((detail ?? {}) as Record) .map(([k, v]) => { if (k === "changed" && v && typeof v === "object") return Object.entries(v as Record) .map(([f, c]) => `${f}: ${String(c?.from)} → ${String(c?.to)}`) .join(", "); return `${k}: ${typeof v === "string" ? v : JSON.stringify(v)}`; }) .join(", "); }