Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM, PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source files covering clients, berths, interests/pipeline, documents/EOI, expenses/invoices, email, notifications, dashboard, admin, and client portal. CI/CD via Gitea Actions with Docker builds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
32 lines
1006 B
TypeScript
32 lines
1006 B
TypeScript
import { diffFields } from '@/lib/audit';
|
|
|
|
const SKIP_FIELDS = new Set(['createdAt', 'updatedAt', 'portId']);
|
|
|
|
/**
|
|
* Wraps `diffFields` with automatic exclusion of metadata fields
|
|
* (createdAt, updatedAt, portId).
|
|
*/
|
|
export function diffEntity<T extends Record<string, unknown>>(
|
|
oldRecord: T,
|
|
newRecord: Partial<T>,
|
|
): { changed: boolean; diff: Record<string, { old: unknown; new: unknown }> } {
|
|
// Only compare keys present in newRecord
|
|
const filteredOld: Record<string, unknown> = {};
|
|
const filteredNew: Record<string, unknown> = {};
|
|
|
|
for (const key of Object.keys(newRecord)) {
|
|
if (SKIP_FIELDS.has(key)) continue;
|
|
filteredOld[key] = oldRecord[key];
|
|
filteredNew[key] = newRecord[key];
|
|
}
|
|
|
|
const changes = diffFields(filteredOld, filteredNew);
|
|
|
|
const diff: Record<string, { old: unknown; new: unknown }> = {};
|
|
for (const change of changes) {
|
|
diff[change.field] = { old: change.oldValue, new: change.newValue };
|
|
}
|
|
|
|
return { changed: changes.length > 0, diff };
|
|
}
|