From 0ff424fceee359c52395802b5f606f9d566bae9f Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Fri, 17 Oct 2025 12:34:10 +0000 Subject: [PATCH] feat: Display user activity history --- src/pages/Profile.tsx | 159 ++++++++++++++++++++++++++++++++++++++++-- src/types/database.ts | 19 ++++- 2 files changed, 169 insertions(+), 9 deletions(-) diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index 6b300107..a97eb5a1 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -174,7 +174,7 @@ export default function Profile() { if (creditsError) throw creditsError; - // Fetch last 10 submissions + // Fetch last 10 submissions with enriched data let submissionsQuery = supabase .from('content_submissions') .select('id, submission_type, content, status, created_at') @@ -191,6 +191,59 @@ export default function Profile() { const { data: submissions, error: submissionsError } = await submissionsQuery; if (submissionsError) throw submissionsError; + // Enrich submissions with entity data and photos + const enrichedSubmissions = await Promise.all((submissions || []).map(async (sub) => { + const enriched: any = { ...sub }; + + // For photo submissions, get photo count and preview + if (sub.submission_type === 'photo') { + const { data: photoSubs } = await supabase + .from('photo_submissions') + .select('id, entity_type, entity_id') + .eq('submission_id', sub.id) + .maybeSingle(); + + if (photoSubs) { + const { data: photoItems, count } = await supabase + .from('photo_submission_items') + .select('cloudflare_image_url', { count: 'exact' }) + .eq('photo_submission_id', photoSubs.id) + .order('order_index', { ascending: true }) + .limit(1); + + enriched.photo_count = count || 0; + enriched.photo_preview = photoItems?.[0]?.cloudflare_image_url; + enriched.entity_type = photoSubs.entity_type; + enriched.entity_id = photoSubs.entity_id; + + // Get entity name/slug for linking + if (photoSubs.entity_type === 'park') { + const { data: park } = await supabase + .from('parks') + .select('name, slug') + .eq('id', photoSubs.entity_id) + .single(); + enriched.content = { ...enriched.content, entity_name: park?.name, entity_slug: park?.slug }; + } else if (photoSubs.entity_type === 'ride') { + const { data: ride } = await supabase + .from('rides') + .select('name, slug, parks!inner(name, slug)') + .eq('id', photoSubs.entity_id) + .single(); + enriched.content = { + ...enriched.content, + entity_name: ride?.name, + entity_slug: ride?.slug, + park_name: ride?.parks?.name, + park_slug: ride?.parks?.slug + }; + } + } + } + + return enriched; + })); + // Fetch last 10 rankings (public top lists) let rankingsQuery = supabase .from('user_top_lists') @@ -210,7 +263,7 @@ export default function Profile() { const combined = [ ...(reviews?.map(r => ({ ...r, type: 'review' as const })) || []), ...(credits?.map(c => ({ ...c, type: 'credit' as const })) || []), - ...(submissions?.map(s => ({ ...s, type: 'submission' as const })) || []), + ...(enrichedSubmissions?.map(s => ({ ...s, type: 'submission' as const })) || []), ...(rankings?.map(r => ({ ...r, type: 'ranking' as const })) || []) ].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) .slice(0, 15) as ActivityEntry[]; @@ -731,8 +784,9 @@ export default function Profile() { <>

- Submitted {(activity as any).submission_type || 'content'} - {(activity as any).content?.name && `: ${(activity as any).content.name}`} + {(activity as any).content?.action === 'edit' ? 'Edited' : 'Submitted'}{' '} + {(activity as any).submission_type === 'photo' ? 'photos for' : (activity as any).submission_type || 'content'} + {(activity as any).content?.name && ` ${(activity as any).content.name}`}

{(activity as any).status === 'pending' && ( Pending @@ -743,8 +797,93 @@ export default function Profile() { {(activity as any).status === 'rejected' && ( Rejected )} + {(activity as any).status === 'partially_approved' && ( + Partially Approved + )}
- {(activity as any).content?.description && ( + + {/* Photo preview for photo submissions */} + {(activity as any).submission_type === 'photo' && (activity as any).photo_preview && ( +
+ Photo preview + {(activity as any).photo_count > 1 && ( + + +{(activity as any).photo_count - 1} more + + )} +
+ )} + + {/* Entity link for photo submissions */} + {(activity as any).submission_type === 'photo' && (activity as any).content?.entity_slug && ( +
+ {(activity as any).entity_type === 'park' ? ( + + {(activity as any).content.entity_name || 'View park'} + + ) : (activity as any).entity_type === 'ride' ? ( + <> + + {(activity as any).content.entity_name || 'View ride'} + + {(activity as any).content.park_name && ( + at {(activity as any).content.park_name} + )} + + ) : null} +
+ )} + + {/* Links for entity submissions */} + {(activity as any).status === 'approved' && (activity as any).submission_type !== 'photo' && ( + <> + {(activity as any).submission_type === 'park' && (activity as any).content?.slug && ( + + View park → + + )} + {(activity as any).submission_type === 'ride' && (activity as any).content?.slug && (activity as any).content?.park_slug && ( +
+ + View ride → + + {(activity as any).content.park_name && ( + + at {(activity as any).content.park_name} + + )} +
+ )} + {(activity as any).submission_type === 'company' && (activity as any).content?.slug && ( + + View {(activity as any).content.company_type || 'company'} → + + )} + {(activity as any).submission_type === 'ride_model' && (activity as any).content?.slug && (activity as any).content?.manufacturer_slug && ( + + View model → + + )} + + )} + + {(activity as any).content?.description && (activity as any).submission_type !== 'photo' && (

{(activity as any).content.description}

@@ -753,10 +892,18 @@ export default function Profile() { ) : activity.type === 'ranking' ? ( <>
-

Created ranking: {(activity as any).title || 'Untitled'}

+ + Created ranking: {(activity as any).title || 'Untitled'} + {((activity as any).list_type || '').replace('_', ' ')} + {(activity as any).is_public === false && ( + Private + )}
{(activity as any).description && (

diff --git a/src/types/database.ts b/src/types/database.ts index 16521043..6d8d23b1 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -331,15 +331,28 @@ export interface SubmissionActivity { submission_type?: string; entity_type?: string; action?: string; + content?: { + name?: string; + slug?: string; + description?: string; + entity_id?: string; + entity_slug?: string; + park_slug?: string; + park_name?: string; + action?: string; + }; + photo_count?: number; + photo_preview?: string; } export interface RankingActivity { id: string; type: 'ranking'; created_at: string; - parks?: { slug?: string; name?: string } | null; - name?: string; - position?: number; + title?: string; + description?: string; + list_type?: string; + is_public?: boolean; } export interface GenericActivity {