Refactor: Complete error handling overhaul

This commit is contained in:
gpt-engineer-app[bot]
2025-11-02 23:19:46 +00:00
parent d057ddc8cc
commit 35c7c3e957
7 changed files with 303 additions and 26 deletions

View File

@@ -11,6 +11,7 @@ import { supabase } from '@/integrations/supabase/client';
import { logger } from '@/lib/logger';
import { getErrorMessage } from '@/lib/errorHandler';
import { MODERATION_CONSTANTS } from '@/lib/moderation/constants';
import { validateModerationItems } from '@/lib/moderation/validation';
import type {
ModerationItem,
EntityFilter,
@@ -20,6 +21,49 @@ import type {
SortDirection
} from '@/types/moderation';
/**
* Get specific, actionable error message based on error type
*/
function getSpecificErrorMessage(error: unknown): string {
// Offline detection
if (!navigator.onLine) {
return 'You appear to be offline. Check your internet connection and try again.';
}
// Timeout
if (error instanceof Error && error.name === 'AbortError') {
return 'Request timed out. The server is taking too long to respond. Please try again.';
}
// Check for Supabase-specific errors
if (typeof error === 'object' && error !== null) {
const err = error as any;
// 500 errors
if (err.status === 500 || err.code === '500') {
return 'Server error occurred. Our team has been notified. Please try again in a few minutes.';
}
// 429 Rate limiting
if (err.status === 429 || err.message?.includes('rate limit')) {
return 'Too many requests. Please wait a moment before trying again.';
}
// Authentication errors
if (err.status === 401 || err.message?.includes('JWT')) {
return 'Your session has expired. Please refresh the page and sign in again.';
}
// Permission errors
if (err.status === 403 || err.message?.includes('permission')) {
return 'You do not have permission to access the moderation queue.';
}
}
// Fallback
return getErrorMessage(error) || 'Failed to load moderation queue. Please try again.';
}
/**
* Configuration for queue query
*/
@@ -124,21 +168,44 @@ export function useQueueQuery(config: UseQueueQueryConfig): UseQueueQueryReturn
queryKey,
queryFn: async () => {
logger.log('🔍 [TanStack Query] Fetching queue data:', queryKey);
const result = await fetchSubmissions(supabase, queryConfig);
if (result.error) {
logger.error('❌ [TanStack Query] Error:', { error: getErrorMessage(result.error) });
throw result.error;
// Create timeout controller (30s timeout)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 30000);
try {
const result = await fetchSubmissions(supabase, queryConfig);
clearTimeout(timeoutId);
if (result.error) {
const specificMessage = getSpecificErrorMessage(result.error);
logger.error('❌ [TanStack Query] Error:', { error: specificMessage });
throw new Error(specificMessage);
}
// Validate data shape before returning
const validation = validateModerationItems(result.submissions);
if (!validation.success) {
logger.error('❌ Invalid data shape', { error: validation.error });
throw new Error(validation.error || 'Invalid data format');
}
logger.log('✅ [TanStack Query] Fetched', validation.data!.length, 'items');
return { ...result, submissions: validation.data! };
} catch (error) {
clearTimeout(timeoutId);
throw error;
}
logger.log('✅ [TanStack Query] Fetched', result.submissions.length, 'items');
return result;
},
enabled: config.enabled !== false && !!config.userId,
staleTime: MODERATION_CONSTANTS.QUERY_STALE_TIME,
gcTime: MODERATION_CONSTANTS.QUERY_GC_TIME,
retry: MODERATION_CONSTANTS.QUERY_RETRY_COUNT,
retryDelay: (attemptIndex) => Math.min(1000 * 2 ** attemptIndex, 30000),
networkMode: 'offlineFirst', // Handle offline gracefully
meta: {
errorMessage: 'Failed to load moderation queue',
},
});
// Invalidate helper