mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-21 12:51:14 -05:00
Refactor code structure and remove redundant changes
This commit is contained in:
331
src-old/components/moderation/FieldComparison.tsx
Normal file
331
src-old/components/moderation/FieldComparison.tsx
Normal file
@@ -0,0 +1,331 @@
|
||||
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';
|
||||
|
||||
// Helper to format compact values (truncate long strings)
|
||||
function formatCompactValue(value: unknown, precision?: 'day' | 'month' | 'year', 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 (
|
||||
<ArrayFieldDiff
|
||||
fieldName={formatFieldName(field)}
|
||||
oldArray={oldValue}
|
||||
newArray={newValue}
|
||||
compact={compact}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// 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 (
|
||||
<Badge variant="outline" className={getChangeColor()}>
|
||||
{fieldName}: + {formatCompactValue(newValue, precision)}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
if (changeType === 'removed') {
|
||||
return (
|
||||
<Badge variant="outline" className={getChangeColor()}>
|
||||
{fieldName}: <span className="line-through">{formatCompactValue(oldValue, precision)}</span>
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
if (changeType === 'modified') {
|
||||
return (
|
||||
<Badge variant="outline" className={getChangeColor()}>
|
||||
{fieldName}: {formatCompactValue(oldValue, oldPrecision || precision)} → {formatCompactValue(newValue, newPrecision || precision)}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className={getChangeColor()}>
|
||||
{fieldName}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 p-2 rounded-md bg-muted/50">
|
||||
<div className="text-sm font-medium">{formatFieldName(field)}</div>
|
||||
|
||||
{changeType === 'added' && (
|
||||
<div className="text-sm text-green-600 dark:text-green-400">
|
||||
+ {formatFieldValue(newValue, precision)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{changeType === 'removed' && (
|
||||
<div className="text-sm text-red-600 dark:text-red-400 line-through">
|
||||
{formatFieldValue(oldValue, precision)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{changeType === 'modified' && (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<span className="text-red-600 dark:text-red-400 line-through">
|
||||
{formatFieldValue(oldValue, oldPrecision || precision)}
|
||||
</span>
|
||||
<ArrowRight className="h-3 w-3 text-muted-foreground" />
|
||||
<span className="text-green-600 dark:text-green-400">
|
||||
{formatFieldValue(newValue, newPrecision || precision)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Badge variant="outline" className={colorClass}>
|
||||
{imageLabel} Image{action}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
// Determine scenario
|
||||
const isAddition = !oldUrl && newUrl;
|
||||
const isRemoval = oldUrl && !newUrl;
|
||||
const isReplacement = oldUrl && newUrl;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2 p-3 rounded-md bg-muted/50">
|
||||
<div className="text-sm font-medium">
|
||||
{type === 'banner' ? 'Banner' : 'Card'} Image
|
||||
{isAddition && <span className="text-green-600 dark:text-green-400 ml-2">(New)</span>}
|
||||
{isRemoval && <span className="text-red-600 dark:text-red-400 ml-2">(Removed)</span>}
|
||||
{isReplacement && <span className="text-amber-600 dark:text-amber-400 ml-2">(Changed)</span>}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
{oldUrl && (
|
||||
<div className="flex-1">
|
||||
<div className="text-xs text-muted-foreground mb-1">Before</div>
|
||||
<img
|
||||
src={oldUrl}
|
||||
alt="Previous"
|
||||
className="w-full h-32 object-cover rounded border-2 border-red-500/50"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{oldUrl && newUrl && (
|
||||
<ArrowRight className="h-5 w-5 text-muted-foreground flex-shrink-0" />
|
||||
)}
|
||||
|
||||
{newUrl && (
|
||||
<div className="flex-1">
|
||||
<div className="text-xs text-muted-foreground mb-1">{isAddition ? 'New Image' : 'After'}</div>
|
||||
<img
|
||||
src={newUrl}
|
||||
alt="New"
|
||||
className="w-full h-32 object-cover rounded border-2 border-green-500/50"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
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 (
|
||||
<Badge variant="outline" className="text-green-600 dark:text-green-400">
|
||||
Location: + {newLoc}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
if (oldLocation && !newLocation) {
|
||||
return (
|
||||
<Badge variant="outline" className="text-red-600 dark:text-red-400">
|
||||
Location: <span className="line-through">{oldLoc}</span>
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Badge variant="outline" className="text-amber-600 dark:text-amber-400">
|
||||
Location{isCreatingNewLocation && ' (New Entity)'}
|
||||
{onlyCoordinatesChanged && ' (GPS Refined)'}
|
||||
: {oldLoc} → {newLoc}
|
||||
</Badge>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-1 p-2 rounded-md bg-muted/50">
|
||||
<div className="text-sm font-medium">
|
||||
Location
|
||||
{isCreatingNewLocation && (
|
||||
<Badge variant="secondary" className="ml-2">New location entity</Badge>
|
||||
)}
|
||||
{onlyCoordinatesChanged && (
|
||||
<Badge variant="outline" className="ml-2">GPS coordinates refined</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
{oldLocation && (
|
||||
<span className="text-red-600 dark:text-red-400 line-through">
|
||||
{formatLocation(oldLocation)}
|
||||
</span>
|
||||
)}
|
||||
{oldLocation && newLocation && (
|
||||
<ArrowRight className="h-3 w-3 text-muted-foreground" />
|
||||
)}
|
||||
{newLocation && (
|
||||
<span className="text-green-600 dark:text-green-400">
|
||||
{formatLocation(newLocation)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user