import { cn } from "@/lib/utils"; /** * Field-level diff row used by the audit-log "CHANGES" view and the * chat tool draft/result cards. * * - Provide both `before` and `after` for an update (renders both lines, * strikethrough on the removed value). * - Provide only `after` for a create (renders a single `+ value` line). * - Provide only `before` for a delete (renders a single `- value` line). * * Use `"before" in row` / `"after" in row` to distinguish "absent" from * "explicitly null" — null/undefined render as `—`. */ export type ChangeRow = { field: string; before?: unknown; after?: unknown; }; export function ChangesTable({ changes }: { changes: ChangeRow[] }) { if (changes.length === 0) return null; return (
{changes.map((change, idx) => (
0 && "border-t", )} >
{change.field}
{"before" in change ? ( ) : null} {"after" in change ? ( ) : null}
))}
); } function DiffLine({ kind, value, }: { kind: "added" | "removed"; value: unknown; }) { const sign = kind === "added" ? "+" : "-"; return (
{sign} {formatValue(value)}
); } const HIDDEN_FIELDS = new Set([ "workspaceId", "createdAt", "updatedAt", "deletedAt", ]); /** Update: rows from `changedFields`. Create: keys of `after`. Delete: keys of `before`. */ export function buildAuditLogChangeRows(input: { before: Record | null | undefined; after: Record | null | undefined; changedFields: string[] | null | undefined; }): ChangeRow[] { const { before, after, changedFields } = input; if (before && after) { if (!changedFields?.length) return []; return changedFields .filter((field) => !HIDDEN_FIELDS.has(field)) .map((field) => ({ field, before: before[field], after: after[field], })); } if (after) { return Object.keys(after) .filter((field) => !HIDDEN_FIELDS.has(field)) .map((field) => ({ field, after: after[field] })); } if (before) { return Object.keys(before) .filter((field) => !HIDDEN_FIELDS.has(field)) .map((field) => ({ field, before: before[field] })); } return []; } export function formatValue(value: unknown): string { if (value === null || value === undefined) return "—"; if (value instanceof Date) return value.toISOString(); if (typeof value === "string") return value === "" ? "—" : value; if (typeof value === "number" || typeof value === "boolean") { return String(value); } if (Array.isArray(value) && value.length === 0) return "—"; return JSON.stringify(value); }