diff --git a/src/components/moderation/ModerationQueue.tsx b/src/components/moderation/ModerationQueue.tsx index 86472fcb..f1394a04 100644 --- a/src/components/moderation/ModerationQueue.tsx +++ b/src/components/moderation/ModerationQueue.tsx @@ -130,6 +130,7 @@ export const ModerationQueue = forwardRef((props, ref) => { const isMountingRef = useRef(true); const initialFetchCompleteRef = useRef(false); const FETCH_COOLDOWN_MS = 1000; + const pauseFetchingRef = useRef(false); // Pagination state const [currentPage, setCurrentPage] = useState(1); @@ -217,6 +218,12 @@ export const ModerationQueue = forwardRef((props, ref) => { return; } + // Check if tab is hidden - pause all fetching + if (pauseFetchingRef.current || document.hidden) { + console.log('⏸️ Fetch paused (tab hidden)'); + return; + } + // Prevent concurrent calls - race condition guard if (fetchInProgressRef.current) { console.log('⚠️ Fetch already in progress, skipping duplicate call'); @@ -803,6 +810,12 @@ export const ModerationQueue = forwardRef((props, ref) => { async (payload) => { const newSubmission = payload.new as any; + // Queue updates if tab is hidden + if (document.hidden) { + console.log('πŸ“΄ Realtime event received while hidden - queuing for later'); + return; + } + // Ignore if recently removed (optimistic update) if (recentlyRemovedRef.current.has(newSubmission.id)) { return; @@ -977,6 +990,12 @@ export const ModerationQueue = forwardRef((props, ref) => { async (payload) => { const updatedSubmission = payload.new as any; + // Queue updates if tab is hidden + if (document.hidden) { + console.log('πŸ“΄ Realtime UPDATE received while hidden - queuing for later'); + return; + } + // Ignore if recently removed (optimistic update in progress) if (recentlyRemovedRef.current.has(updatedSubmission.id)) { console.log('⏭️ Ignoring UPDATE for recently removed submission:', updatedSubmission.id); @@ -1146,6 +1165,27 @@ export const ModerationQueue = forwardRef((props, ref) => { }; }, [user, useRealtimeQueue, debouncedRealtimeUpdate]); + // Visibility change handler - pause queue updates when tab is hidden + useEffect(() => { + const handleVisibilityChange = () => { + if (document.hidden) { + console.log('πŸ“΄ Tab hidden - pausing queue updates'); + pauseFetchingRef.current = true; + } else { + console.log('πŸ“± Tab visible - resuming queue updates'); + pauseFetchingRef.current = false; + // Optional: trigger single refresh when tab becomes visible + if (initialFetchCompleteRef.current && !isMountingRef.current) { + console.log('πŸ”„ Tab became visible - triggering refresh'); + fetchItems(filtersRef.current.entityFilter, filtersRef.current.statusFilter, true, activeTab); + } + } + }; + + document.addEventListener('visibilitychange', handleVisibilityChange); + return () => document.removeEventListener('visibilitychange', handleVisibilityChange); + }, [fetchItems, activeTab]); + const handleResetToPending = async (item: ModerationItem) => { setActionLoading(item.id); try { diff --git a/src/lib/systemActivityService.ts b/src/lib/systemActivityService.ts index afeeed01..b5e8fbff 100644 --- a/src/lib/systemActivityService.ts +++ b/src/lib/systemActivityService.ts @@ -104,9 +104,11 @@ export async function fetchSystemActivities( const activities: SystemActivity[] = []; // Fetch entity versions (entity changes) + // Use simplified query without foreign key join - we'll fetch profiles separately const { data: versions, error: versionsError } = await supabase .from('entity_versions') .select('id, entity_type, entity_id, version_number, version_data, changed_by, changed_at, change_type, change_reason') + .eq('is_current', true) .order('changed_at', { ascending: false }) .limit(limit * 2); // Fetch more to account for filtering @@ -117,7 +119,7 @@ export async function fetchSystemActivities( id: version.id, type: 'entity_change', timestamp: version.changed_at, - actor_id: version.changed_by, + actor_id: version.changed_by || null, action: `${version.change_type} ${version.entity_type}`, details: { entity_type: version.entity_type, diff --git a/src/pages/AdminSystemLog.tsx b/src/pages/AdminSystemLog.tsx index 97b027ee..ed32941d 100644 --- a/src/pages/AdminSystemLog.tsx +++ b/src/pages/AdminSystemLog.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react'; +import { useEffect, Component, ReactNode } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole } from '@/hooks/useUserRole'; @@ -6,6 +6,48 @@ import { AdminLayout } from '@/components/layout/AdminLayout'; import { SystemActivityLog } from '@/components/admin/SystemActivityLog'; import { Skeleton } from '@/components/ui/skeleton'; import { Card, CardContent } from '@/components/ui/card'; +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { AlertCircle } from 'lucide-react'; + +interface ErrorBoundaryProps { + children: ReactNode; +} + +interface ErrorBoundaryState { + hasError: boolean; + error: Error | null; +} + +class ErrorBoundary extends Component { + constructor(props: ErrorBoundaryProps) { + super(props); + this.state = { hasError: false, error: null }; + } + + static getDerivedStateFromError(error: Error) { + return { hasError: true, error }; + } + + componentDidCatch(error: Error, errorInfo: any) { + console.error('System Activity Log Error:', error, errorInfo); + } + + render() { + if (this.state.hasError) { + return ( + + + Error loading system activity log + + {this.state.error?.message || 'An unexpected error occurred'} + + + ); + } + + return this.props.children; + } +} export default function AdminSystemLog() { const { user, loading: authLoading } = useAuth(); @@ -69,7 +111,9 @@ export default function AdminSystemLog() {

- + + + );