From ff2215f450e96083db9d4bfd30931c1be0fbcf8c Mon Sep 17 00:00:00 2001 From: "gpt-engineer-app[bot]" <159125892+gpt-engineer-app[bot]@users.noreply.github.com> Date: Thu, 30 Oct 2025 19:07:38 +0000 Subject: [PATCH] Fix: Handle RPC errors gracefully --- src/components/moderation/ReassignDialog.tsx | 18 ++++++-- src/components/moderation/ReportsQueue.tsx | 24 ++++++++-- src/components/moderation/UserRoleManager.tsx | 46 +++++++++++++------ src/lib/systemActivityService.ts | 39 +++++++++++++--- 4 files changed, 100 insertions(+), 27 deletions(-) diff --git a/src/components/moderation/ReassignDialog.tsx b/src/components/moderation/ReassignDialog.tsx index b88558ad..845c03f2 100644 --- a/src/components/moderation/ReassignDialog.tsx +++ b/src/components/moderation/ReassignDialog.tsx @@ -18,7 +18,7 @@ import { SelectValue, } from '@/components/ui/select'; import { supabase } from '@/integrations/supabase/client'; -import { handleError } from '@/lib/errorHandler'; +import { handleError, getErrorMessage } from '@/lib/errorHandler'; import { logger } from '@/lib/logger'; interface Moderator { @@ -69,12 +69,22 @@ export function ReassignDialog({ const userIds = roles.map((r) => r.user_id); - const { data: allProfiles, error: profilesError } = await supabase + let profiles: Array<{ user_id: string; username: string; display_name?: string | null }> | null = null; + const { data: allProfiles, error: rpcError } = await supabase .rpc('get_users_with_emails'); - const profiles = allProfiles?.filter(p => userIds.includes(p.user_id)); + if (rpcError) { + logger.warn('Failed to fetch users with emails, using basic profiles', { error: getErrorMessage(rpcError) }); + const { data: basicProfiles } = await supabase + .from('profiles') + .select('user_id, username, display_name') + .in('user_id', userIds); + profiles = basicProfiles as typeof profiles; + } else { + profiles = allProfiles?.filter(p => userIds.includes(p.user_id)) || null; + } - if (profilesError) throw profilesError; + const moderatorsList = roles.map((role) => { const profile = profiles?.find((p) => p.user_id === role.user_id); diff --git a/src/components/moderation/ReportsQueue.tsx b/src/components/moderation/ReportsQueue.tsx index b1c37461..fd069732 100644 --- a/src/components/moderation/ReportsQueue.tsx +++ b/src/components/moderation/ReportsQueue.tsx @@ -191,10 +191,19 @@ export const ReportsQueue = forwardRef((props, ref) => { const reporterIds = [...new Set((data || []).map(r => r.reporter_id))]; // Fetch reporter profiles with emails (for admins) - const { data: allProfiles } = await supabase + let profiles: Array<{ user_id: string; username: string; display_name?: string | null; avatar_url?: string | null }> | null = null; + const { data: allProfiles, error: rpcError } = await supabase .rpc('get_users_with_emails'); - const profiles = allProfiles?.filter(p => reporterIds.includes(p.user_id)); + if (rpcError) { + const { data: basicProfiles } = await supabase + .from('profiles') + .select('user_id, username, display_name, avatar_url') + .in('user_id', reporterIds); + profiles = basicProfiles as typeof profiles; + } else { + profiles = allProfiles?.filter(p => reporterIds.includes(p.user_id)) || null; + } const profileMap = new Map(profiles?.map(p => [p.user_id, p]) || []); @@ -220,7 +229,16 @@ export const ReportsQueue = forwardRef((props, ref) => { profileIds.length > 0 ? supabase .rpc('get_users_with_emails') - .then(({ data }) => data?.filter(p => profileIds.includes(p.user_id)) || []) + .then(({ data, error }) => { + if (error) { + return supabase + .from('profiles') + .select('user_id, username, display_name') + .in('user_id', profileIds) + .then(({ data: basicProfiles }) => basicProfiles || []); + } + return data?.filter(p => profileIds.includes(p.user_id)) || []; + }) : Promise.resolve([]), submissionIds.length > 0 diff --git a/src/components/moderation/UserRoleManager.tsx b/src/components/moderation/UserRoleManager.tsx index ee3c8853..09a3d7e0 100644 --- a/src/components/moderation/UserRoleManager.tsx +++ b/src/components/moderation/UserRoleManager.tsx @@ -86,11 +86,20 @@ export function UserRoleManager() { const userIds = [...new Set((data || []).map(r => r.user_id))]; // Fetch user profiles with emails (for admins) - const { - data: allProfiles - } = await supabase.rpc('get_users_with_emails'); + let profiles: Array<{ user_id: string; username: string; display_name?: string }> | null = null; + const { data: allProfiles, error: rpcError } = await supabase + .rpc('get_users_with_emails'); - const profiles = allProfiles?.filter(p => userIds.includes(p.user_id)); + if (rpcError) { + logger.warn('Failed to fetch users with emails, using basic profiles', { error: getErrorMessage(rpcError) }); + const { data: basicProfiles } = await supabase + .from('profiles') + .select('user_id, username, display_name') + .in('user_id', userIds); + profiles = basicProfiles as typeof profiles; + } else { + profiles = allProfiles?.filter(p => userIds.includes(p.user_id)) || null; + } const profileMap = new Map(profiles?.map(p => [p.user_id, p]) || []); // Combine data with profiles @@ -114,17 +123,26 @@ export function UserRoleManager() { return; } try { - const { - data: allUsers, - error - } = await supabase.rpc('get_users_with_emails'); + let data; + const { data: allUsers, error: rpcError } = await supabase + .rpc('get_users_with_emails'); - // Filter by search term - const data = allUsers?.filter(user => - user.username.toLowerCase().includes(search.toLowerCase()) || - user.display_name?.toLowerCase().includes(search.toLowerCase()) - ).slice(0, 10); - if (error) throw error; + if (rpcError) { + logger.warn('Failed to fetch users with emails, using basic profiles', { error: getErrorMessage(rpcError) }); + const { data: basicProfiles, error: profilesError } = await supabase + .from('profiles') + .select('user_id, username, display_name') + .ilike('username', `%${search}%`); + + if (profilesError) throw profilesError; + data = basicProfiles?.slice(0, 10); + } else { + // Filter by search term + data = allUsers?.filter(user => + user.username.toLowerCase().includes(search.toLowerCase()) || + user.display_name?.toLowerCase().includes(search.toLowerCase()) + ).slice(0, 10); + } // Filter out users who already have roles const existingUserIds = userRoles.map(ur => ur.user_id); diff --git a/src/lib/systemActivityService.ts b/src/lib/systemActivityService.ts index f0f45a4b..09206769 100644 --- a/src/lib/systemActivityService.ts +++ b/src/lib/systemActivityService.ts @@ -768,10 +768,19 @@ export async function fetchSystemActivities( const uniqueUserIds = [...new Set(filteredActivities.map(a => a.actor_id).filter(Boolean))] as string[]; if (uniqueUserIds.length > 0) { - const { data: allProfiles } = await supabase + let profiles: Array<{ user_id: string; username: string; display_name?: string | null; avatar_url?: string | null }> | null = null; + const { data: allProfiles, error: rpcError } = await supabase .rpc('get_users_with_emails'); - const profiles = allProfiles?.filter(p => uniqueUserIds.includes(p.user_id)); + if (rpcError) { + const { data: basicProfiles } = await supabase + .from('profiles') + .select('user_id, username, display_name, avatar_url') + .in('user_id', uniqueUserIds); + profiles = basicProfiles as typeof profiles; + } else { + profiles = allProfiles?.filter(p => uniqueUserIds.includes(p.user_id)) || null; + } if (profiles) { const profileMap = new Map(profiles.map(p => [p.user_id, p])); @@ -797,10 +806,19 @@ export async function fetchSystemActivities( .filter(Boolean) as string[]; if (targetUserIds.length > 0) { - const { data: allTargetProfiles } = await supabase + let targetProfiles: Array<{ user_id: string; username: string; display_name?: string | null }> | null = null; + const { data: allTargetProfiles, error: targetRpcError } = await supabase .rpc('get_users_with_emails'); - const targetProfiles = allTargetProfiles?.filter(p => targetUserIds.includes(p.user_id)); + if (targetRpcError) { + const { data: basicProfiles } = await supabase + .from('profiles') + .select('user_id, username, display_name') + .in('user_id', targetUserIds); + targetProfiles = basicProfiles as typeof targetProfiles; + } else { + targetProfiles = allTargetProfiles?.filter(p => targetUserIds.includes(p.user_id)) || null; + } if (targetProfiles) { const targetProfileMap = new Map(targetProfiles.map(p => [p.user_id, p])); @@ -826,10 +844,19 @@ export async function fetchSystemActivities( .filter(Boolean) as string[]; if (accountUserIds.length > 0) { - const { data: allAccountProfiles } = await supabase + let accountProfiles: Array<{ user_id: string; username: string; display_name?: string | null }> | null = null; + const { data: allAccountProfiles, error: accountRpcError } = await supabase .rpc('get_users_with_emails'); - const accountProfiles = allAccountProfiles?.filter(p => accountUserIds.includes(p.user_id)); + if (accountRpcError) { + const { data: basicProfiles } = await supabase + .from('profiles') + .select('user_id, username, display_name') + .in('user_id', accountUserIds); + accountProfiles = basicProfiles as typeof accountProfiles; + } else { + accountProfiles = allAccountProfiles?.filter(p => accountUserIds.includes(p.user_id)) || null; + } if (accountProfiles) { const accountProfileMap = new Map(accountProfiles.map(p => [p.user_id, p]));