import { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/hooks/useAuth'; import { useToast } from '@/hooks/use-toast'; import { ActivityCard } from './ActivityCard'; import { Skeleton } from '@/components/ui/skeleton'; import { Activity as ActivityIcon } from 'lucide-react'; import { smartMergeArray } from '@/lib/smartStateUpdate'; import { useAdminSettings } from '@/hooks/useAdminSettings'; interface ActivityItem { id: string; type: 'submission' | 'report' | 'review'; action: 'approved' | 'rejected' | 'reviewed' | 'dismissed' | 'flagged'; entity_type?: string; entity_name?: string; timestamp: string; moderator_id?: string; moderator?: { username: string; display_name?: string; avatar_url?: string; }; } export interface RecentActivityRef { refresh: () => void; } export const RecentActivity = forwardRef((props, ref) => { const [activities, setActivities] = useState([]); const [loading, setLoading] = useState(true); const [isSilentRefresh, setIsSilentRefresh] = useState(false); const { user } = useAuth(); const { toast } = useToast(); const { getAutoRefreshStrategy } = useAdminSettings(); const refreshStrategy = getAutoRefreshStrategy(); useImperativeHandle(ref, () => ({ refresh: () => fetchRecentActivity(false) })); const fetchRecentActivity = async (silent = false) => { if (!user) return; try { if (!silent) { setLoading(true); } else { setIsSilentRefresh(true); } // Fetch recent approved/rejected submissions const { data: submissions, error: submissionsError } = await supabase .from('content_submissions') .select('id, status, reviewed_at, reviewer_id, submission_type') .in('status', ['approved', 'rejected']) .not('reviewed_at', 'is', null) .order('reviewed_at', { ascending: false }) .limit(15); if (submissionsError) throw submissionsError; // Fetch recent report resolutions const { data: reports, error: reportsError } = await supabase .from('reports') .select('id, status, reviewed_at, reviewed_by, reported_entity_type') .in('status', ['reviewed', 'dismissed']) .not('reviewed_at', 'is', null) .order('reviewed_at', { ascending: false }) .limit(15); if (reportsError) throw reportsError; // Fetch recent review moderations const { data: reviews, error: reviewsError } = await supabase .from('reviews') .select('id, moderation_status, moderated_at, moderated_by, park_id, ride_id') .in('moderation_status', ['approved', 'rejected', 'flagged']) .not('moderated_at', 'is', null) .order('moderated_at', { ascending: false }) .limit(15); if (reviewsError) throw reviewsError; // Get unique moderator IDs const moderatorIds = [ ...(submissions?.map(s => s.reviewer_id).filter(Boolean) || []), ...(reports?.map(r => r.reviewed_by).filter(Boolean) || []), ...(reviews?.map(r => r.moderated_by).filter(Boolean) || []), ].filter((id, index, arr) => id && arr.indexOf(id) === index); // Fetch moderator profiles const { data: profiles } = await supabase .from('profiles') .select('user_id, username, display_name, avatar_url') .in('user_id', moderatorIds); const profileMap = new Map(profiles?.map(p => [p.user_id, p]) || []); // Combine all activities const allActivities: ActivityItem[] = [ ...(submissions?.map(s => ({ id: s.id, type: 'submission' as const, action: s.status as 'approved' | 'rejected', entity_type: s.submission_type, timestamp: s.reviewed_at!, moderator_id: s.reviewer_id, moderator: s.reviewer_id ? profileMap.get(s.reviewer_id) : undefined, })) || []), ...(reports?.map(r => ({ id: r.id, type: 'report' as const, action: r.status as 'reviewed' | 'dismissed', entity_type: r.reported_entity_type, timestamp: r.reviewed_at!, moderator_id: r.reviewed_by, moderator: r.reviewed_by ? profileMap.get(r.reviewed_by) : undefined, })) || []), ...(reviews?.map(r => ({ id: r.id, type: 'review' as const, action: r.moderation_status as 'approved' | 'rejected' | 'flagged', timestamp: r.moderated_at!, moderator_id: r.moderated_by, moderator: r.moderated_by ? profileMap.get(r.moderated_by) : undefined, })) || []), ]; // Sort by timestamp (newest first) allActivities.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() ); const recentActivities = allActivities.slice(0, 20); // Keep top 20 most recent // Use smart merging for silent refreshes if strategy is 'merge' if (silent && refreshStrategy === 'merge') { const mergeResult = smartMergeArray(activities, recentActivities, { compareFields: ['timestamp', 'action'], preserveOrder: false, addToTop: true, }); if (mergeResult.hasChanges) { setActivities(mergeResult.items); } } else { // Full replacement for non-silent refreshes or 'replace' strategy setActivities(recentActivities); } } catch (error: any) { console.error('Error fetching recent activity:', error); toast({ title: "Error", description: "Failed to load recent activity", variant: "destructive", }); } finally { if (!silent) { setLoading(false); } setIsSilentRefresh(false); } }; useEffect(() => { fetchRecentActivity(false); }, [user]); if (loading) { return (
); } if (activities.length === 0) { return (

No Recent Activity

Moderation activity will appear here once actions are taken.

); } return (
{activities.map((activity) => ( ))}
); }); RecentActivity.displayName = 'RecentActivity';