diff --git a/src/components/moderation/FieldComparison.tsx b/src/components/moderation/FieldComparison.tsx
index 96876565..c35b13b3 100644
--- a/src/components/moderation/FieldComparison.tsx
+++ b/src/components/moderation/FieldComparison.tsx
@@ -3,6 +3,7 @@ import { formatFieldName, formatFieldValue } from '@/lib/submissionChangeDetecti
import type { FieldChange, ImageChange } from '@/lib/submissionChangeDetection';
import { ArrowRight } from 'lucide-react';
import { ArrayFieldDiff } from './ArrayFieldDiff';
+import { SpecialFieldDisplay } from './SpecialFieldDisplay';
interface FieldDiffProps {
change: FieldChange;
@@ -24,6 +25,12 @@ export function FieldDiff({ change, compact = false }: FieldDiffProps) {
);
}
+ // 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';
diff --git a/src/components/moderation/ItemReviewCard.tsx b/src/components/moderation/ItemReviewCard.tsx
index 16c69f49..a67f1729 100644
--- a/src/components/moderation/ItemReviewCard.tsx
+++ b/src/components/moderation/ItemReviewCard.tsx
@@ -11,9 +11,10 @@ interface ItemReviewCardProps {
item: SubmissionItemWithDeps;
onEdit: () => void;
onStatusChange: (status: 'approved' | 'rejected') => void;
+ submissionId: string;
}
-export function ItemReviewCard({ item, onEdit, onStatusChange }: ItemReviewCardProps) {
+export function ItemReviewCard({ item, onEdit, onStatusChange, submissionId }: ItemReviewCardProps) {
const isMobile = useIsMobile();
const getItemIcon = () => {
@@ -40,8 +41,15 @@ export function ItemReviewCard({ item, onEdit, onStatusChange }: ItemReviewCardP
};
const renderItemPreview = () => {
- // Use standardized change detection display
- return ;
+ // Use detailed view for review manager with photo detection
+ return (
+
+ );
};
return (
diff --git a/src/components/moderation/SpecialFieldDisplay.tsx b/src/components/moderation/SpecialFieldDisplay.tsx
new file mode 100644
index 00000000..296d0f24
--- /dev/null
+++ b/src/components/moderation/SpecialFieldDisplay.tsx
@@ -0,0 +1,327 @@
+import { useState } from 'react';
+import { Badge } from '@/components/ui/badge';
+import { MeasurementDisplay } from '@/components/ui/measurement-display';
+import { SpeedDisplay } from '@/components/ui/speed-display';
+import { MapPin, ArrowRight, Calendar, ExternalLink } from 'lucide-react';
+import type { FieldChange } from '@/lib/submissionChangeDetection';
+import { formatFieldValue } from '@/lib/submissionChangeDetection';
+
+interface SpecialFieldDisplayProps {
+ change: FieldChange;
+ compact?: boolean;
+}
+
+export function SpecialFieldDisplay({ change, compact = false }: SpecialFieldDisplayProps) {
+ const fieldName = change.field.toLowerCase();
+
+ // Detect field type
+ if (fieldName.includes('speed') || fieldName === 'max_speed_kmh') {
+ return ;
+ }
+
+ if (fieldName.includes('height') || fieldName.includes('length') ||
+ fieldName === 'max_height_meters' || fieldName === 'length_meters' ||
+ fieldName === 'drop_height_meters') {
+ return ;
+ }
+
+ if (fieldName === 'status') {
+ return ;
+ }
+
+ if (fieldName.includes('date') && !fieldName.includes('updated') && !fieldName.includes('created')) {
+ return ;
+ }
+
+ if (fieldName.includes('_id') && fieldName !== 'id' && fieldName !== 'user_id') {
+ return ;
+ }
+
+ if (fieldName === 'latitude' || fieldName === 'longitude') {
+ return ;
+ }
+
+ // Fallback to null, will be handled by regular FieldDiff
+ return null;
+}
+
+function SpeedFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ if (compact) {
+ return (
+
+ Speed
+
+ );
+ }
+
+ const formatFieldName = (name: string) =>
+ name.replace(/_/g, ' ').replace(/([A-Z])/g, ' $1').trim()
+ .replace(/^./, str => str.toUpperCase());
+
+ return (
+
+
{formatFieldName(change.field)}
+
+ {change.changeType === 'modified' && (
+
+ )}
+
+ {change.changeType === 'added' && (
+
+ +
+
+ )}
+
+ {change.changeType === 'removed' && (
+
+
+
+ )}
+
+ );
+}
+
+function MeasurementFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ if (compact) {
+ return (
+
+ Measurement
+
+ );
+ }
+
+ const formatFieldName = (name: string) =>
+ name.replace(/_/g, ' ').replace(/([A-Z])/g, ' $1').trim()
+ .replace(/^./, str => str.toUpperCase());
+
+ return (
+
+
{formatFieldName(change.field)}
+
+ {change.changeType === 'modified' && (
+
+ )}
+
+ {change.changeType === 'added' && (
+
+ +
+
+ )}
+
+ {change.changeType === 'removed' && (
+
+
+
+ )}
+
+ );
+}
+
+function StatusFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ const getStatusColor = (status: string) => {
+ const statusLower = String(status).toLowerCase();
+ if (statusLower === 'operating' || statusLower === 'active') return 'bg-green-500/10 text-green-600 dark:text-green-400 border-green-500/20';
+ if (statusLower === 'closed' || statusLower === 'inactive') return 'bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20';
+ if (statusLower === 'under_construction' || statusLower === 'pending') return 'bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20';
+ return 'bg-muted/30 text-muted-foreground';
+ };
+
+ if (compact) {
+ return (
+
+ Status
+
+ );
+ }
+
+ const formatFieldName = (name: string) =>
+ name.replace(/_/g, ' ').replace(/([A-Z])/g, ' $1').trim()
+ .replace(/^./, str => str.toUpperCase());
+
+ return (
+
+
{formatFieldName(change.field)}
+
+ {change.changeType === 'modified' && (
+
+
+ {formatFieldValue(change.oldValue)}
+
+
+
+ {formatFieldValue(change.newValue)}
+
+
+ )}
+
+ {change.changeType === 'added' && (
+
+ {formatFieldValue(change.newValue)}
+
+ )}
+
+ {change.changeType === 'removed' && (
+
+ {formatFieldValue(change.oldValue)}
+
+ )}
+
+ );
+}
+
+function DateFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ if (compact) {
+ return (
+
+
+ Date
+
+ );
+ }
+
+ const formatFieldName = (name: string) =>
+ name.replace(/_/g, ' ').replace(/([A-Z])/g, ' $1').trim()
+ .replace(/^./, str => str.toUpperCase());
+
+ return (
+
+
+
+ {formatFieldName(change.field)}
+
+
+ {change.changeType === 'modified' && (
+
+
+ {formatFieldValue(change.oldValue)}
+
+
+
+ {formatFieldValue(change.newValue)}
+
+
+ )}
+
+ {change.changeType === 'added' && (
+
+ + {formatFieldValue(change.newValue)}
+
+ )}
+
+ {change.changeType === 'removed' && (
+
+ {formatFieldValue(change.oldValue)}
+
+ )}
+
+ );
+}
+
+function RelationshipFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ // This would ideally fetch entity names, but for now we show IDs with better formatting
+ const formatFieldName = (name: string) =>
+ name.replace(/_id$/, '').replace(/_/g, ' ').trim()
+ .replace(/^./, str => str.toUpperCase());
+
+ if (compact) {
+ return (
+
+ {formatFieldName(change.field)}
+
+ );
+ }
+
+ return (
+
+
{formatFieldName(change.field)}
+
+ {change.changeType === 'modified' && (
+
+
+ {String(change.oldValue).slice(0, 8)}...
+
+
+
+ {String(change.newValue).slice(0, 8)}...
+
+
+ )}
+
+ {change.changeType === 'added' && (
+
+ + {String(change.newValue).slice(0, 8)}...
+
+ )}
+
+ {change.changeType === 'removed' && (
+
+ {String(change.oldValue).slice(0, 8)}...
+
+ )}
+
+ );
+}
+
+function CoordinateFieldDisplay({ change, compact }: { change: FieldChange; compact: boolean }) {
+ if (compact) {
+ return (
+
+
+ Coordinates
+
+ );
+ }
+
+ const formatFieldName = (name: string) =>
+ name.replace(/_/g, ' ').replace(/([A-Z])/g, ' $1').trim()
+ .replace(/^./, str => str.toUpperCase());
+
+ return (
+
+
+
+ {formatFieldName(change.field)}
+
+
+ {change.changeType === 'modified' && (
+
+
+ {Number(change.oldValue).toFixed(6)}°
+
+
+
+ {Number(change.newValue).toFixed(6)}°
+
+
+ )}
+
+ {change.changeType === 'added' && (
+
+ + {Number(change.newValue).toFixed(6)}°
+
+ )}
+
+ {change.changeType === 'removed' && (
+
+ {Number(change.oldValue).toFixed(6)}°
+
+ )}
+
+ );
+}
diff --git a/src/components/moderation/SubmissionReviewManager.tsx b/src/components/moderation/SubmissionReviewManager.tsx
index 111d223c..fb49be7c 100644
--- a/src/components/moderation/SubmissionReviewManager.tsx
+++ b/src/components/moderation/SubmissionReviewManager.tsx
@@ -420,6 +420,7 @@ export function SubmissionReviewManager({
// Status changes handled via approve/reject actions
await loadSubmissionItems();
}}
+ submissionId={submissionId}
/>
))}