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);
}