import { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; import { supabase } from '@/lib/supabaseClient'; import { useAuth } from '@/hooks/useAuth'; import { handleError } from '@/lib/errorHandler'; 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 | null; moderator?: { username: string; display_name?: string | null; avatar_url?: string | null; }; } 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 { 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 let submissions: any[] = []; try { const { data, error } = 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 (error) { handleError(error, { action: 'Load Recent Activity - Submissions', userId: user?.id, metadata: { silent } }); } else { submissions = data || []; } } catch (error) { handleError(error, { action: 'Load Recent Activity - Submissions Query', userId: user?.id, metadata: { silent } }); } // Fetch recent report resolutions let reports: any[] = []; try { const { data, error } = 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 (error) { handleError(error, { action: 'Load Recent Activity - Reports', userId: user?.id, metadata: { silent } }); } else { reports = data || []; } } catch (error) { handleError(error, { action: 'Load Recent Activity - Reports Query', userId: user?.id, metadata: { silent } }); } // Fetch recent review moderations let reviews: any[] = []; try { const { data, error } = 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 (error) { handleError(error, { action: 'Load Recent Activity - Reviews', userId: user?.id, metadata: { silent } }); } else { reviews = data || []; } } catch (error) { handleError(error, { action: 'Load Recent Activity - Reviews Query', userId: user?.id, metadata: { silent } }); } // Get unique moderator IDs with safe filtering const moderatorIds: string[] = [ ...(submissions.map(s => s.reviewer_id).filter((id): id is string => id != null)), ...(reports.map(r => r.reviewed_by).filter((id): id is string => id != null)), ...(reviews.map(r => r.moderated_by).filter((id): id is string => id != null)), ].filter((id, index, arr) => arr.indexOf(id) === index); // Fetch moderator profiles only if we have IDs let profileMap = new Map(); if (moderatorIds.length > 0) { try { const { data: profiles, error: profilesError } = await supabase .from('profiles') .select('user_id, username, display_name, avatar_url') .in('user_id', moderatorIds); if (profilesError) { handleError(profilesError, { action: 'Load Recent Activity - Profiles', userId: user?.id, metadata: { moderatorIds: moderatorIds.length } }); } else if (profiles) { profileMap = new Map(profiles.map(p => [p.user_id, p])); } } catch (error) { handleError(error, { action: 'Load Recent Activity - Profiles Query', userId: user?.id, metadata: { moderatorIds: moderatorIds.length } }); } } // 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: unknown) { handleError(error, { action: 'Load Recent Activity', userId: user?.id }); } 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';