import { useState, useEffect } from 'react'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { FieldDiff, ImageDiff, LocationDiff } from './FieldComparison'; import { PhotoAdditionPreview, PhotoEditPreview, PhotoDeletionPreview } from './PhotoComparison'; import { detectChanges, type ChangesSummary } from '@/lib/submissionChangeDetection'; import type { SubmissionItemData } from '@/types/submissions'; import type { SubmissionItemWithDeps } from '@/lib/submissionItemsService'; import { Building2, Train, MapPin, Building, User, ImageIcon, Trash2, Edit, Plus, AlertTriangle, Calendar } from 'lucide-react'; import { TimelineEventPreview } from './TimelineEventPreview'; import type { TimelineSubmissionData } from '@/types/timeline'; interface SubmissionChangesDisplayProps { item: SubmissionItemData | SubmissionItemWithDeps | { item_data?: unknown; original_data?: unknown; item_type: string; action_type?: 'create' | 'edit' | 'delete' }; view?: 'summary' | 'detailed'; showImages?: boolean; submissionId?: string; } // Helper to determine change magnitude function getChangeMagnitude(totalChanges: number, hasImages: boolean, action: string) { if (action === 'delete') return { label: 'Deletion', variant: 'destructive' as const, icon: AlertTriangle }; if (action === 'create') return { label: 'New', variant: 'default' as const, icon: Plus }; if (hasImages) return { label: 'Major', variant: 'default' as const, icon: Edit }; if (totalChanges >= 5) return { label: 'Major', variant: 'default' as const, icon: Edit }; if (totalChanges >= 3) return { label: 'Moderate', variant: 'secondary' as const, icon: Edit }; return { label: 'Minor', variant: 'outline' as const, icon: Edit }; } export function SubmissionChangesDisplay({ item, view = 'summary', showImages = true, submissionId }: SubmissionChangesDisplayProps) { const [changes, setChanges] = useState(null); const [loading, setLoading] = useState(true); useEffect(() => { const loadChanges = async () => { setLoading(true); const detectedChanges = await detectChanges(item, submissionId); setChanges(detectedChanges); setLoading(false); }; loadChanges(); }, [item, submissionId]); if (loading || !changes) { return ; } // Get appropriate icon for entity type const getEntityIcon = () => { const iconClass = "h-4 w-4"; switch (item.item_type) { case 'park': return ; case 'ride': return ; case 'ride_model': return ; case 'manufacturer': case 'operator': case 'property_owner': case 'designer': return ; case 'photo': case 'photo_edit': case 'photo_delete': return ; case 'milestone': case 'timeline_event': return ; default: return ; } }; // Get action badge const getActionBadge = () => { switch (changes.action) { case 'create': return New; case 'edit': return Edit; case 'delete': return Delete; } }; const magnitude = getChangeMagnitude( changes.totalChanges, changes.imageChanges.length > 0, changes.action ); if (view === 'summary') { // Special compact display for photo deletions if (item.item_type === 'photo_delete') { return (
{getEntityIcon()} {changes.entityName} {getActionBadge()}
{changes.photoChanges.length > 0 && changes.photoChanges[0].type === 'deleted' && ( )?.cloudflare_image_url as string || '', title: changes.photoChanges[0].photo?.title || (item.item_data as Record)?.title as string, caption: changes.photoChanges[0].photo?.caption || (item.item_data as Record)?.caption as string, entity_type: (item.item_data as Record)?.entity_type as string, entity_name: changes.entityName, deletion_reason: changes.photoChanges[0].photo?.deletion_reason || (item.item_data as Record)?.deletion_reason as string || (item.item_data as Record)?.reason as string }} compact={true} /> )}
); } // Special compact display for milestone/timeline events if (item.item_type === 'milestone' || item.item_type === 'timeline_event') { const milestoneData = item.item_data as TimelineSubmissionData; const eventType = milestoneData.event_type?.replace(/_/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase()) || 'Event'; const eventDate = milestoneData.event_date ? new Date(milestoneData.event_date).toLocaleDateString() : 'No date'; return (
{getEntityIcon()} {milestoneData.title} {getActionBadge()} {eventType}
{eventDate} {milestoneData.from_value && milestoneData.to_value && ( • {milestoneData.from_value} → {milestoneData.to_value} )}
); } return (
{getEntityIcon()} {changes.entityName} {getActionBadge()} {changes.action === 'edit' && ( {magnitude.label} Change )}
{(changes.action === 'edit' || changes.action === 'create') && changes.totalChanges > 0 && (
{changes.fieldChanges.slice(0, 5).map((change, idx) => ( ))} {changes.imageChanges.map((change, idx) => ( ))} {changes.photoChanges.map((change, idx) => { if (change.type === 'added' && change.photos) { return ; } if (change.type === 'edited' && change.photo) { return ; } if (change.type === 'deleted' && change.photo) { return ; } return null; })} {changes.hasLocationChange && ( Location )} {changes.totalChanges > 5 && ( +{changes.totalChanges - 5} more )}
)} {changes.action === 'delete' && (
Marked for deletion
)}
); } // Detailed view - special handling for photo deletions if (item.item_type === 'photo_delete') { return (
{getEntityIcon()}

{changes.entityName}

{getActionBadge()}
{changes.photoChanges.length > 0 && changes.photoChanges[0].type === 'deleted' && ( )?.cloudflare_image_url as string || '', title: changes.photoChanges[0].photo?.title || (item.item_data as Record)?.title as string, caption: changes.photoChanges[0].photo?.caption || (item.item_data as Record)?.caption as string, entity_type: (item.item_data as Record)?.entity_type as string, entity_name: changes.entityName, deletion_reason: changes.photoChanges[0].photo?.deletion_reason || (item.item_data as Record)?.deletion_reason as string || (item.item_data as Record)?.reason as string }} compact={false} /> )}
); } // Detailed view - special handling for milestone/timeline events if (item.item_type === 'milestone' || item.item_type === 'timeline_event') { const milestoneData = item.item_data as TimelineSubmissionData; return (
{getEntityIcon()}

{milestoneData.title}

{getActionBadge()} Timeline Event
); } // Detailed view for other items return (
{getEntityIcon()}

{changes.entityName}

{getActionBadge()}
{changes.action === 'create' && ( <> {/* Show if moderator edited the creation */} {item.original_data && Object.keys(item.original_data).length > 0 ? ( <>
Moderator Edits Applied
This creation was modified by a moderator before review. Changed fields are highlighted below.
{changes.fieldChanges.length > 0 && (

Creation Data (with moderator edits highlighted)

{changes.fieldChanges.map((change, idx) => { // Highlight fields that were added OR modified by moderator const wasEditedByModerator = item.original_data && Object.keys(item.original_data).length > 0 && ( // Field was modified from original value (change.changeType === 'modified') || // Field was added by moderator (not in original submission) (change.changeType === 'added' && item.original_data[change.field] === undefined) ); return (
{wasEditedByModerator ? (
✓ Modified by moderator
) : null}
); })}
)} ) : ( // Show all creation fields (no moderator edits) <> {changes.fieldChanges.length > 0 && (

Creation Data

{changes.fieldChanges.map((change, idx) => (
))}
)} {changes.imageChanges.length > 0 && (

Images

{changes.imageChanges.map((change, idx) => ( ))}
)} {changes.hasLocationChange && (

Location

)?.location || (item.item_data as Record)?.location_id) as string | undefined} />
)} )} )} {changes.action === 'delete' && (
This {item.item_type} will be deleted
)} {changes.action === 'edit' && changes.totalChanges > 0 && ( <> {changes.fieldChanges.length > 0 && (

Field Changes ({changes.fieldChanges.length})

{changes.fieldChanges.map((change, idx) => ( ))}
)} {showImages && changes.imageChanges.length > 0 && (

Image Changes

{changes.imageChanges.map((change, idx) => ( ))}
)} {showImages && changes.photoChanges.length > 0 && (

Photo Changes

{changes.photoChanges.map((change, idx) => { if (change.type === 'added' && change.photos) { return ; } if (change.type === 'edited' && change.photo) { return ; } if (change.type === 'deleted' && change.photo) { return ; } return null; })}
)} {changes.hasLocationChange && (

Location Change

)?.location || (item.original_data as Record)?.location_id) as string | undefined} newLocation={((item.item_data as Record)?.location || (item.item_data as Record)?.location_id) as string | undefined} />
)} )} {changes.action === 'edit' && changes.totalChanges === 0 && (
No changes detected
)}
); }