import { Badge } from '@/components/ui/badge'; import { Plus, Minus, Edit, Check } from 'lucide-react'; import { formatFieldValue } from '@/lib/submissionChangeDetection'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; interface ArrayFieldDiffProps { fieldName: string; oldArray: unknown[]; newArray: unknown[]; compact?: boolean; } interface ArrayDiffItem { type: 'added' | 'removed' | 'modified' | 'unchanged'; oldValue?: unknown; newValue?: unknown; index: number; } export function ArrayFieldDiff({ fieldName, oldArray, newArray, compact = false }: ArrayFieldDiffProps) { const [showUnchanged, setShowUnchanged] = useState(false); // Compute array differences const differences = computeArrayDiff(oldArray || [], newArray || []); const changedItems = differences.filter(d => d.type !== 'unchanged'); const unchangedCount = differences.filter(d => d.type === 'unchanged').length; const totalChanges = changedItems.length; if (compact) { return ( {fieldName} ({totalChanges} changes) ); } return (
{fieldName} ({differences.length} items, {totalChanges} changed)
{unchangedCount > 0 && ( )}
{differences.map((diff, idx) => { if (diff.type === 'unchanged' && !showUnchanged) { return null; } return ( ); })}
); } function ArrayDiffItemDisplay({ diff }: { diff: ArrayDiffItem }) { const isObject = typeof diff.newValue === 'object' || typeof diff.oldValue === 'object'; switch (diff.type) { case 'added': return (
{isObject ? ( ) : ( {formatFieldValue(diff.newValue)} )}
); case 'removed': return (
{isObject ? ( ) : ( {formatFieldValue(diff.oldValue)} )}
); case 'modified': return (
{isObject ? ( ) : ( formatFieldValue(diff.oldValue) )}
{isObject ? ( ) : ( formatFieldValue(diff.newValue) )}
); case 'unchanged': return (
{isObject ? ( ) : ( formatFieldValue(diff.newValue) )}
); } } function ObjectDisplay({ value, className = '' }: { value: unknown; className?: string }) { if (!value || typeof value !== 'object') { return {formatFieldValue(value)}; } return (
{Object.entries(value).map(([key, val]) => (
{key.replace(/_/g, ' ')}: {formatFieldValue(val)}
))}
); } /** * Compute differences between two arrays */ function computeArrayDiff(oldArray: unknown[], newArray: unknown[]): ArrayDiffItem[] { const results: ArrayDiffItem[] = []; const maxLength = Math.max(oldArray.length, newArray.length); // Simple position-based comparison for (let i = 0; i < maxLength; i++) { const oldValue = i < oldArray.length ? oldArray[i] : undefined; const newValue = i < newArray.length ? newArray[i] : undefined; if (oldValue === undefined && newValue !== undefined) { // Added results.push({ type: 'added', newValue, index: i }); } else if (oldValue !== undefined && newValue === undefined) { // Removed results.push({ type: 'removed', oldValue, index: i }); } else if (!isEqual(oldValue, newValue)) { // Modified results.push({ type: 'modified', oldValue, newValue, index: i }); } else { // Unchanged results.push({ type: 'unchanged', oldValue, newValue, index: i }); } } return results; } /** * Deep equality check */ function isEqual(a: unknown, b: unknown): boolean { if (a === b) return true; if (a == null || b == null) return a === b; if (typeof a !== typeof b) return false; if (typeof a === 'object') { if (Array.isArray(a) && Array.isArray(b)) { if (a.length !== b.length) return false; return a.every((item, i) => isEqual(item, b[i])); } const keysA = Object.keys(a); const keysB = Object.keys(b); if (keysA.length !== keysB.length) return false; return keysA.every(key => isEqual(a[key], b[key])); } return false; }