From 73e9afb7a66efd20c49154f5308bf716970df538 Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Mon, 29 Sep 2025 19:14:56 +0000 Subject: [PATCH] feat: Implement recent activity feed --- src/pages/Profile.tsx | 144 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 134 insertions(+), 10 deletions(-) diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index 9bc68dd9..02cf7b64 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -1,5 +1,6 @@ import { useState, useEffect } from 'react'; -import { useParams, useNavigate } from 'react-router-dom'; +import { useParams, useNavigate, Link } from 'react-router-dom'; +import { formatDistanceToNow } from 'date-fns'; import { Header } from '@/components/layout/Header'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; @@ -54,6 +55,8 @@ export default function Profile() { coasterCount: 0, parkCount: 0 }); + const [recentActivity, setRecentActivity] = useState([]); + const [activityLoading, setActivityLoading] = useState(false); // Profile field access checking const { canViewField, loading: fieldAccessLoading } = useProfileFieldAccess(profile?.user_id); @@ -112,6 +115,45 @@ export default function Profile() { }); } }; + + const fetchRecentActivity = async (userId: string) => { + setActivityLoading(true); + try { + // Fetch last 10 reviews + const { data: reviews, error: reviewsError } = await supabase + .from('reviews') + .select('id, rating, title, created_at, moderation_status, park_id, ride_id, parks(name, slug), rides(name, slug, parks(name, slug))') + .eq('user_id', userId) + .order('created_at', { ascending: false }) + .limit(10); + + if (reviewsError) throw reviewsError; + + // Fetch last 10 ride credits + const { data: credits, error: creditsError } = await supabase + .from('user_ride_credits') + .select('id, ride_count, first_ride_date, created_at, rides(name, slug, parks(name, slug))') + .eq('user_id', userId) + .order('created_at', { ascending: false }) + .limit(10); + + if (creditsError) throw creditsError; + + // Combine and sort by date + const combined = [ + ...(reviews?.map(r => ({ ...r, type: 'review' })) || []), + ...(credits?.map(c => ({ ...c, type: 'credit' })) || []) + ].sort((a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime()) + .slice(0, 15); + + setRecentActivity(combined); + } catch (error: any) { + console.error('Error fetching recent activity:', error); + setRecentActivity([]); + } finally { + setActivityLoading(false); + } + }; const getCurrentUser = async () => { const { data: { @@ -140,8 +182,9 @@ export default function Profile() { setAvatarUrl(data.avatar_url || ''); setAvatarImageId(data.avatar_image_id || ''); - // Fetch calculated stats for this user + // Fetch calculated stats and recent activity for this user await fetchCalculatedStats(data.user_id); + await fetchRecentActivity(data.user_id); } } catch (error: any) { console.error('Error fetching profile:', error); @@ -180,8 +223,9 @@ export default function Profile() { setAvatarUrl(data.avatar_url || ''); setAvatarImageId(data.avatar_image_id || ''); - // Fetch calculated stats for the current user + // Fetch calculated stats and recent activity for the current user await fetchCalculatedStats(user.id); + await fetchRecentActivity(user.id); } } catch (error: any) { console.error('Error fetching profile:', error); @@ -539,13 +583,93 @@ export default function Profile() { -
- -

Activity Feed Coming Soon

-

- Track reviews, ratings, and achievements -

-
+ {activityLoading ? ( +
+ +
+ ) : recentActivity.length === 0 ? ( +
+ +

No recent activity yet

+

+ Reviews and ride credits will appear here +

+
+ ) : ( +
+ {recentActivity.map(activity => ( +
+
+ {activity.type === 'review' ? ( + + ) : ( + + )} +
+ +
+ {activity.type === 'review' ? ( + <> +
+

+ {activity.title || 'Left a review'} +

+ {activity.moderation_status === 'pending' && ( + Pending + )} + {activity.moderation_status === 'flagged' && ( + Flagged + )} +
+
+ {[...Array(5)].map((_, i) => ( + + ))} +
+ {activity.park_id && activity.parks ? ( + + {activity.parks.name} + + ) : activity.ride_id && activity.rides ? ( +
+ + {activity.rides.name} + + {activity.rides.parks && ( + at {activity.rides.parks.name} + )} +
+ ) : null} + + ) : ( + <> +

Added ride credit

+ {activity.rides && ( +
+ + {activity.rides.name} + + {activity.rides.parks && ( + at {activity.rides.parks.name} + )} +
+ )} + {activity.ride_count > 1 && ( +

+ Ridden {activity.ride_count} times +

+ )} + + )} +
+ +
+ {formatDistanceToNow(new Date(activity.created_at), { addSuffix: true })} +
+
+ ))} +
+ )}