import { Badge } from '@/components/ui/badge';
import { formatFieldName, formatFieldValue } from '@/lib/submissionChangeDetection';
import type { FieldChange, ImageChange } from '@/lib/submissionChangeDetection';
import { ArrowRight } from 'lucide-react';
import { ArrayFieldDiff } from './ArrayFieldDiff';
import { SpecialFieldDisplay } from './SpecialFieldDisplay';
import type { DatePrecision } from '@/components/ui/flexible-date-input';
// Helper to format compact values (truncate long strings)
function formatCompactValue(value: unknown, precision?: DatePrecision, maxLength = 30): string {
const formatted = formatFieldValue(value, precision);
if (formatted.length > maxLength) {
return formatted.substring(0, maxLength) + '...';
}
return formatted;
}
interface FieldDiffProps {
change: FieldChange;
compact?: boolean;
}
export function FieldDiff({ change, compact = false }: FieldDiffProps) {
const { field, oldValue, newValue, changeType, metadata } = change;
// Extract precision for date fields
const precision = metadata?.precision;
const oldPrecision = metadata?.oldPrecision;
const newPrecision = metadata?.newPrecision;
// Check if this is an array field that needs special handling
if (Array.isArray(oldValue) && Array.isArray(newValue)) {
return (
);
}
// Check if this is a special field type that needs custom rendering
const specialDisplay = SpecialFieldDisplay({ change, compact });
if (specialDisplay) {
return specialDisplay;
}
const getChangeColor = () => {
switch (changeType) {
case 'added': return 'text-green-600 dark:text-green-400';
case 'removed': return 'text-red-600 dark:text-red-400';
case 'modified': return 'text-amber-600 dark:text-amber-400';
default: return '';
}
};
if (compact) {
const fieldName = formatFieldName(field);
if (changeType === 'added') {
return (
{fieldName}: + {formatCompactValue(newValue, precision)}
);
}
if (changeType === 'removed') {
return (
{fieldName}: {formatCompactValue(oldValue, precision)}
);
}
if (changeType === 'modified') {
return (
{fieldName}: {formatCompactValue(oldValue, oldPrecision || precision)} → {formatCompactValue(newValue, newPrecision || precision)}
);
}
return (
{fieldName}
);
}
return (
{formatFieldName(field)}
{changeType === 'added' && (
+ {formatFieldValue(newValue, precision)}
)}
{changeType === 'removed' && (
{formatFieldValue(oldValue, precision)}
)}
{changeType === 'modified' && (
{formatFieldValue(oldValue, oldPrecision || precision)}
{formatFieldValue(newValue, newPrecision || precision)}
)}
);
}
interface ImageDiffProps {
change: ImageChange;
compact?: boolean;
}
export function ImageDiff({ change, compact = false }: ImageDiffProps) {
const { type, oldUrl, newUrl } = change;
if (compact) {
const imageLabel = type === 'banner' ? 'Banner' : 'Card';
const isAddition = !oldUrl && newUrl;
const isRemoval = oldUrl && !newUrl;
const isReplacement = oldUrl && newUrl;
let action = '';
let colorClass = 'text-blue-600 dark:text-blue-400';
if (isAddition) {
action = ' (Added)';
colorClass = 'text-green-600 dark:text-green-400';
} else if (isRemoval) {
action = ' (Removed)';
colorClass = 'text-red-600 dark:text-red-400';
} else if (isReplacement) {
action = ' (Changed)';
colorClass = 'text-amber-600 dark:text-amber-400';
}
return (
{imageLabel} Image{action}
);
}
// Determine scenario
const isAddition = !oldUrl && newUrl;
const isRemoval = oldUrl && !newUrl;
const isReplacement = oldUrl && newUrl;
return (
{type === 'banner' ? 'Banner' : 'Card'} Image
{isAddition && (New)}
{isRemoval && (Removed)}
{isReplacement && (Changed)}
{oldUrl && (
Before
)}
{oldUrl && newUrl && (
)}
{newUrl && (
{isAddition ? 'New Image' : 'After'}
)}
);
}
interface LocationData {
location_id?: string;
city?: string;
state_province?: string;
country?: string;
postal_code?: string;
latitude?: number | string;
longitude?: number | string;
}
interface LocationDiffProps {
oldLocation: LocationData | string | null | undefined;
newLocation: LocationData | string | null | undefined;
compact?: boolean;
}
export function LocationDiff({ oldLocation, newLocation, compact = false }: LocationDiffProps) {
// Type guards for LocationData
const isLocationData = (loc: unknown): loc is LocationData => {
return typeof loc === 'object' && loc !== null && !Array.isArray(loc);
};
// Check if we're creating a new location entity
const isCreatingNewLocation = isLocationData(oldLocation) &&
oldLocation.location_id &&
!oldLocation.city &&
isLocationData(newLocation) &&
newLocation.city;
const formatLocation = (loc: LocationData | string | null | undefined) => {
if (!loc) return 'None';
if (typeof loc === 'string') return loc;
// Handle location_id reference
if (loc.location_id && !loc.city) {
return `Location ID: ${loc.location_id.substring(0, 8)}...`;
}
if (typeof loc === 'object') {
const parts: string[] = [];
if (loc.city) parts.push(String(loc.city));
if (loc.state_province) parts.push(String(loc.state_province));
if (loc.country && loc.country !== loc.state_province) parts.push(String(loc.country));
if (loc.postal_code) parts.push(String(loc.postal_code));
let locationStr = parts.join(', ') || 'Unknown';
// Add coordinates if available
if (loc.latitude && loc.longitude) {
const lat = Number(loc.latitude).toFixed(6);
const lng = Number(loc.longitude).toFixed(6);
locationStr += ` (${lat}°, ${lng}°)`;
}
return locationStr;
}
return String(loc);
};
// Check if only coordinates changed
const onlyCoordinatesChanged = isLocationData(oldLocation) &&
isLocationData(newLocation) &&
oldLocation.city === newLocation.city &&
oldLocation.state_province === newLocation.state_province &&
oldLocation.country === newLocation.country &&
oldLocation.postal_code === newLocation.postal_code &&
(Number(oldLocation.latitude) !== Number(newLocation.latitude) ||
Number(oldLocation.longitude) !== Number(newLocation.longitude));
if (compact) {
const oldLoc = formatLocation(oldLocation);
const newLoc = formatLocation(newLocation);
if (!oldLocation && newLocation) {
return (
Location: + {newLoc}
);
}
if (oldLocation && !newLocation) {
return (
Location: {oldLoc}
);
}
return (
Location{isCreatingNewLocation && ' (New Entity)'}
{onlyCoordinatesChanged && ' (GPS Refined)'}
: {oldLoc} → {newLoc}
);
}
return (
Location
{isCreatingNewLocation && (
New location entity
)}
{onlyCoordinatesChanged && (
GPS coordinates refined
)}
{oldLocation && (
{formatLocation(oldLocation)}
)}
{oldLocation && newLocation && (
)}
{newLocation && (
{formatLocation(newLocation)}
)}
);
}