diff --git a/src/components/moderation/QueueItem.tsx b/src/components/moderation/QueueItem.tsx index 14832463..a86132f2 100644 --- a/src/components/moderation/QueueItem.tsx +++ b/src/components/moderation/QueueItem.tsx @@ -1,5 +1,8 @@ import { memo, useState, useCallback } from 'react'; -import { CheckCircle, XCircle, Eye, Calendar, MessageSquare, FileText, Image, ListTree, RefreshCw, AlertCircle, Lock, Trash2, AlertTriangle } from 'lucide-react'; +import { CheckCircle, XCircle, Eye, Calendar, MessageSquare, FileText, Image, ListTree, RefreshCw, AlertCircle, Lock, Trash2, AlertTriangle, Edit } from 'lucide-react'; +import { usePhotoSubmissionItems } from '@/hooks/usePhotoSubmissionItems'; +import { PhotoGrid } from '@/components/common/PhotoGrid'; +import type { PhotoItem } from '@/types/photos'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; @@ -78,6 +81,11 @@ export const QueueItem = memo(({ }: QueueItemProps) => { const [validationResult, setValidationResult] = useState(null); + // Fetch relational photo data for photo submissions + const { photos: photoItems, loading: photosLoading } = usePhotoSubmissionItems( + item.submission_type === 'photo' ? item.id : undefined + ); + const handleValidationChange = useCallback((result: ValidationResult) => { setValidationResult(result); }, []); @@ -268,86 +276,23 @@ export const QueueItem = memo(({ )} - {/* Submission Caption */} - {item.content.content?.caption && ( -
-
Caption:
-

{item.content.content.caption}

-
- )} - - {/* Photos */} - {item.content.content?.photos && item.content.content.photos.length > 0 ? ( -
-
Photos ({item.content.content.photos.length}):
- {item.content.content.photos.map((photo: any, index: number) => ( -
-
{ - onOpenPhotos(item.content.content.photos.map((p: any, i: number) => ({ - id: `${item.id}-${i}`, - url: p.url, - filename: p.filename, - caption: p.caption - })), index); - }}> - {`Photo { - console.error('Failed to load photo submission:', photo); - const target = e.target as HTMLImageElement; - target.style.display = 'none'; - const parent = target.parentElement; - if (parent) { - // Create elements safely using DOM API to prevent XSS - const errorContainer = document.createElement('div'); - errorContainer.className = 'absolute inset-0 flex flex-col items-center justify-center text-destructive text-xs'; - - const errorIcon = document.createElement('div'); - errorIcon.textContent = '⚠️ Image failed to load'; - - const urlDisplay = document.createElement('div'); - urlDisplay.className = 'mt-1 font-mono text-xs break-all px-2'; - // Use textContent to prevent XSS - it escapes HTML automatically - urlDisplay.textContent = photo.url; - - errorContainer.appendChild(errorIcon); - errorContainer.appendChild(urlDisplay); - parent.appendChild(errorContainer); - } - }} - /> -
- -
-
-
-
- URL: - {photo.url} -
-
- Filename: - {photo.filename || 'Unknown'} -
-
- Size: - {photo.size ? `${Math.round(photo.size / 1024)} KB` : 'Unknown'} -
-
- Type: - {photo.type || 'Unknown'} -
- {photo.caption && ( -
-
Caption:
-
{photo.caption}
-
- )} -
-
- ))} + {/* Photos from relational table */} + {photosLoading ? ( +
Loading photos...
+ ) : photoItems.length > 0 ? ( +
+
Photos ({photoItems.length}):
+ ({ + id: photo.id, + url: photo.cloudflare_image_url, + filename: (photo as any).filename || `Photo ${photo.order_index + 1}`, + caption: photo.caption, + title: photo.title, + date_taken: (photo as any).date_taken, + }))} + onPhotoClick={onOpenPhotos} + />
) : (
@@ -356,35 +301,15 @@ export const QueueItem = memo(({ )} {/* Context Information */} - {item.content.content?.context && ( -
-
- Context: - - {typeof item.content.content.context === 'object' - ? (item.content.content.context.ride_id ? 'ride' : - item.content.content.context.park_id ? 'park' : 'unknown') - : item.content.content.context} - -
- {item.entity_name && ( -
- - {(typeof item.content.content.context === 'object' - ? (item.content.content.context.ride_id ? 'ride' : 'park') - : item.content.content.context) === 'ride' ? 'Ride:' : 'Park:'} - - {item.entity_name} -
- )} - {item.park_name && - (typeof item.content.content.context === 'object' - ? !!item.content.content.context.ride_id - : item.content.content.context === 'ride') && ( -
- Park: - {item.park_name} -
+ {item.entity_name && ( +
+ For: + {item.entity_name} + {item.park_name && ( + <> + at + {item.park_name} + )}
)} diff --git a/src/lib/photoHelpers.ts b/src/lib/photoHelpers.ts index baefe1ab..4344423e 100644 --- a/src/lib/photoHelpers.ts +++ b/src/lib/photoHelpers.ts @@ -75,10 +75,10 @@ export function normalizePhotoData(source: PhotoDataSource): PhotoItem[] { return source.items.map((item) => ({ id: item.id, url: item.cloudflare_image_url, - filename: item.filename || `Photo ${item.order_index + 1}`, + filename: (item as any).filename || `Photo ${item.order_index + 1}`, caption: item.caption, title: item.title, - date_taken: item.date_taken, + date_taken: (item as any).date_taken, })); default: @@ -95,10 +95,10 @@ export function normalizePhotoSubmissionItems( return items.map((item) => ({ id: item.id, url: item.cloudflare_image_url, - filename: item.filename || `Photo ${item.order_index + 1}`, + filename: (item as any).filename || `Photo ${item.order_index + 1}`, caption: item.caption, title: item.title, - date_taken: item.date_taken, + date_taken: (item as any).date_taken, order_index: item.order_index, })); }