Refactor: Optimize user role fetching

This commit is contained in:
gpt-engineer-app[bot]
2025-10-30 22:16:47 +00:00
parent edcad90817
commit d7ef581220
3 changed files with 136 additions and 44 deletions

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { useAuth } from '@/hooks/useAuth';
import { queryKeys } from '@/lib/queryKeys';
export type UserRole = 'admin' | 'moderator' | 'user' | 'superuser';
@@ -15,53 +16,54 @@ export interface UserPermissions {
export function useUserRole() {
const { user } = useAuth();
const [roles, setRoles] = useState<UserRole[]>([]);
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
if (!user) {
setRoles([]);
setLoading(false);
return;
}
// Fetch user roles with TanStack Query for automatic caching
const rolesQuery = useQuery({
queryKey: queryKeys.userRoles(user?.id),
queryFn: async () => {
if (!user) return [];
const fetchRoles = async () => {
try {
// Fetch user roles
const { data: rolesData, error: rolesError } = await supabase
const { data, error } = await supabase
.from('user_roles')
.select('role')
.eq('user_id', user.id);
if (rolesError) {
console.error('Error fetching user roles:', rolesError);
setRoles([]);
} else {
setRoles(rolesData?.map(r => r.role as UserRole) || []);
if (error) {
console.error('Error fetching user roles:', error);
return [];
}
// Fetch user permissions using the new function
const { data: permissionsData, error: permissionsError } = await supabase
return data?.map(r => r.role as UserRole) || [];
},
enabled: !!user,
staleTime: 5 * 60 * 1000, // 5 minutes - roles don't change often
gcTime: 10 * 60 * 1000, // 10 minutes garbage collection
});
// Fetch user permissions with TanStack Query for automatic caching
const permissionsQuery = useQuery({
queryKey: queryKeys.userPermissions(user?.id),
queryFn: async () => {
if (!user) return null;
const { data, error } = await supabase
.rpc('get_user_management_permissions', { _user_id: user.id });
if (permissionsError) {
console.error('Error fetching user permissions:', permissionsError);
setPermissions(null);
} else {
setPermissions(permissionsData as unknown as UserPermissions);
if (error) {
console.error('Error fetching user permissions:', error);
return null;
}
} catch (error: unknown) {
console.error('Error fetching user roles:', error);
setRoles([]);
setPermissions(null);
} finally {
setLoading(false);
}
};
fetchRoles();
}, [user]);
return data as unknown as UserPermissions;
},
enabled: !!user,
staleTime: 5 * 60 * 1000, // 5 minutes - permissions don't change often
gcTime: 10 * 60 * 1000, // 10 minutes garbage collection
});
const roles = rolesQuery.data || [];
const permissions = permissionsQuery.data || null;
const loading = rolesQuery.isLoading || permissionsQuery.isLoading;
const hasRole = (role: UserRole) => roles.includes(role);
const isModerator = () => hasRole('admin') || hasRole('moderator') || hasRole('superuser');

View File

@@ -0,0 +1,72 @@
/**
* Query invalidation helpers for TanStack Query
*
* Use these helpers to invalidate cached queries when data changes.
* This ensures UI stays in sync with backend state.
*/
import { useQueryClient } from '@tanstack/react-query';
import { queryKeys } from './queryKeys';
/**
* Hook providing query invalidation helpers
*/
export function useQueryInvalidation() {
const queryClient = useQueryClient();
return {
/**
* Invalidate user roles cache
* Call this after assigning/revoking roles
*/
invalidateUserRoles: (userId?: string) => {
if (userId) {
queryClient.invalidateQueries({ queryKey: queryKeys.userRoles(userId) });
} else {
queryClient.invalidateQueries({ queryKey: ['user-roles'] });
}
},
/**
* Invalidate user permissions cache
* Call this after role changes that affect permissions
*/
invalidateUserPermissions: (userId?: string) => {
if (userId) {
queryClient.invalidateQueries({ queryKey: queryKeys.userPermissions(userId) });
} else {
queryClient.invalidateQueries({ queryKey: ['user-permissions'] });
}
},
/**
* Invalidate both roles and permissions for a user
* Use this as a convenience method after role updates
*/
invalidateUserAuth: (userId?: string) => {
if (userId) {
queryClient.invalidateQueries({ queryKey: queryKeys.userRoles(userId) });
queryClient.invalidateQueries({ queryKey: queryKeys.userPermissions(userId) });
} else {
queryClient.invalidateQueries({ queryKey: ['user-roles'] });
queryClient.invalidateQueries({ queryKey: ['user-permissions'] });
}
},
/**
* Invalidate moderation queue
* Call this after moderation actions
*/
invalidateModerationQueue: () => {
queryClient.invalidateQueries({ queryKey: ['moderation-queue'] });
},
/**
* Invalidate moderation stats
* Call this after queue changes
*/
invalidateModerationStats: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.moderationStats() });
},
};
}

18
src/lib/queryKeys.ts Normal file
View File

@@ -0,0 +1,18 @@
/**
* Centralized query key definitions for TanStack Query
*
* This ensures consistent query keys across the application
* and makes cache invalidation easier to manage.
*/
export const queryKeys = {
// User-related queries
userRoles: (userId?: string) => ['user-roles', userId] as const,
userPermissions: (userId?: string) => ['user-permissions', userId] as const,
// Moderation queue queries
moderationQueue: (config: Record<string, any>) => ['moderation-queue', config] as const,
moderationStats: () => ['moderation-stats'] as const,
// Add more query keys as needed
} as const;