feat: Implement Phase 4 Streamlined Enhancements

This commit is contained in:
gpt-engineer-app[bot]
2025-10-13 22:46:42 +00:00
parent 787e16753e
commit 68a2572c23
3 changed files with 52 additions and 12 deletions

View File

@@ -184,6 +184,18 @@ export function useModerationQueueManager(config: ModerationQueueManagerConfig):
} }
}, [queueQuery.isLoading, queueQuery.isRefreshing]); }, [queueQuery.isLoading, queueQuery.isRefreshing]);
// Show error toast when query fails
useEffect(() => {
if (queueQuery.error) {
console.error('❌ Queue query error:', queueQuery.error);
toast({
variant: 'destructive',
title: 'Failed to Load Queue',
description: queueQuery.error.message || 'An error occurred while fetching the moderation queue.',
});
}
}, [queueQuery.error, toast]);
// Update total count for pagination // Update total count for pagination
useEffect(() => { useEffect(() => {
paginationRef.current.setTotalCount(queueQuery.totalCount); paginationRef.current.setTotalCount(queueQuery.totalCount);

View File

@@ -8,7 +8,14 @@
import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useQuery, useQueryClient } from '@tanstack/react-query';
import { fetchSubmissions, type QueryConfig } from '@/lib/moderation/queries'; import { fetchSubmissions, type QueryConfig } from '@/lib/moderation/queries';
import { supabase } from '@/integrations/supabase/client'; import { supabase } from '@/integrations/supabase/client';
import type { ModerationItem } from '@/types/moderation'; import type {
ModerationItem,
EntityFilter,
StatusFilter,
QueueTab,
SortField,
SortDirection
} from '@/types/moderation';
/** /**
* Configuration for queue query * Configuration for queue query
@@ -24,13 +31,13 @@ export interface UseQueueQueryConfig {
isSuperuser: boolean; isSuperuser: boolean;
/** Entity filter */ /** Entity filter */
entityFilter: string; entityFilter: EntityFilter;
/** Status filter */ /** Status filter */
statusFilter: string; statusFilter: StatusFilter;
/** Active tab */ /** Active tab */
tab: 'mainQueue' | 'archive'; tab: QueueTab;
/** Current page */ /** Current page */
currentPage: number; currentPage: number;
@@ -40,8 +47,8 @@ export interface UseQueueQueryConfig {
/** Sort configuration */ /** Sort configuration */
sortConfig: { sortConfig: {
field: string; field: SortField;
direction: 'asc' | 'desc'; direction: SortDirection;
}; };
/** Whether query is enabled (defaults to true) */ /** Whether query is enabled (defaults to true) */
@@ -85,12 +92,12 @@ export function useQueueQuery(config: UseQueueQueryConfig): UseQueueQueryReturn
userId: config.userId || '', userId: config.userId || '',
isAdmin: config.isAdmin, isAdmin: config.isAdmin,
isSuperuser: config.isSuperuser, isSuperuser: config.isSuperuser,
entityFilter: config.entityFilter as any, entityFilter: config.entityFilter,
statusFilter: config.statusFilter as any, statusFilter: config.statusFilter,
tab: config.tab, tab: config.tab,
currentPage: config.currentPage, currentPage: config.currentPage,
pageSize: config.pageSize, pageSize: config.pageSize,
sortConfig: config.sortConfig as any, sortConfig: config.sortConfig,
}; };
// Create stable query key (TanStack Query uses this for caching/deduplication) // Create stable query key (TanStack Query uses this for caching/deduplication)
@@ -113,6 +120,7 @@ export function useQueueQuery(config: UseQueueQueryConfig): UseQueueQueryReturn
const result = await fetchSubmissions(supabase, queryConfig); const result = await fetchSubmissions(supabase, queryConfig);
if (result.error) { if (result.error) {
console.error('❌ [TanStack Query] Error:', result.error);
throw result.error; throw result.error;
} }
@@ -122,6 +130,8 @@ export function useQueueQuery(config: UseQueueQueryConfig): UseQueueQueryReturn
enabled: config.enabled !== false && !!config.userId, enabled: config.enabled !== false && !!config.userId,
staleTime: 30000, // 30 seconds staleTime: 30000, // 30 seconds
gcTime: 5 * 60 * 1000, // 5 minutes gcTime: 5 * 60 * 1000, // 5 minutes
retry: 2, // Retry failed requests up to 2 times
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000), // Exponential backoff
}); });
// Invalidate helper // Invalidate helper

View File

@@ -47,7 +47,7 @@ export interface RealtimeSubscriptionConfig {
/** Pause subscriptions when tab is hidden (default: true) */ /** Pause subscriptions when tab is hidden (default: true) */
pauseWhenHidden?: boolean; pauseWhenHidden?: boolean;
/** Debounce delay for UPDATE events in milliseconds (default: 1000) */ /** Debounce delay for UPDATE events in milliseconds (default: 500) */
debounceMs?: number; debounceMs?: number;
/** Entity cache for resolving entity names */ /** Entity cache for resolving entity names */
@@ -95,7 +95,7 @@ export function useRealtimeSubscriptions(
onUpdateItem, onUpdateItem,
onItemRemoved, onItemRemoved,
pauseWhenHidden = true, pauseWhenHidden = true,
debounceMs = 1000, debounceMs = 500,
entityCache, entityCache,
profileCache, profileCache,
recentlyRemovedIds, recentlyRemovedIds,
@@ -314,6 +314,7 @@ export function useRealtimeSubscriptions(
*/ */
const handleUpdate = useCallback(async (payload: any) => { const handleUpdate = useCallback(async (payload: any) => {
const updatedSubmission = payload.new as any; const updatedSubmission = payload.new as any;
const oldSubmission = payload.old as any;
console.log('🔄 Realtime UPDATE:', updatedSubmission.id); console.log('🔄 Realtime UPDATE:', updatedSubmission.id);
@@ -335,7 +336,24 @@ export function useRealtimeSubscriptions(
return; return;
} }
// Debounce the update // Skip debounce for status changes (critical updates)
const isStatusChange = oldSubmission?.status !== updatedSubmission.status;
if (isStatusChange) {
console.log('⚡ Status change detected, invalidating immediately');
await queryClient.invalidateQueries({ queryKey: ['moderation-queue'] });
const matchesEntity = matchesEntityFilter(updatedSubmission, filters.entityFilter);
const matchesStatus = matchesStatusFilter(updatedSubmission, filters.statusFilter);
const shouldBeInQueue = matchesEntity && matchesStatus;
if (!shouldBeInQueue) {
onItemRemoved(updatedSubmission.id);
}
return; // Skip debounced update
}
// Use debounce for non-critical updates
debouncedUpdate(updatedSubmission.id, async () => { debouncedUpdate(updatedSubmission.id, async () => {
console.log('🔄 Invalidating query due to UPDATE:', updatedSubmission.id); console.log('🔄 Invalidating query due to UPDATE:', updatedSubmission.id);