mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 08:31:15 -05:00
Refactor: Optimize user role fetching
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { supabase } from '@/integrations/supabase/client';
|
import { supabase } from '@/integrations/supabase/client';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
|
import { queryKeys } from '@/lib/queryKeys';
|
||||||
|
|
||||||
export type UserRole = 'admin' | 'moderator' | 'user' | 'superuser';
|
export type UserRole = 'admin' | 'moderator' | 'user' | 'superuser';
|
||||||
|
|
||||||
@@ -15,53 +16,54 @@ export interface UserPermissions {
|
|||||||
|
|
||||||
export function useUserRole() {
|
export function useUserRole() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const [roles, setRoles] = useState<UserRole[]>([]);
|
|
||||||
const [permissions, setPermissions] = useState<UserPermissions | null>(null);
|
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
// Fetch user roles with TanStack Query for automatic caching
|
||||||
if (!user) {
|
const rolesQuery = useQuery({
|
||||||
setRoles([]);
|
queryKey: queryKeys.userRoles(user?.id),
|
||||||
setLoading(false);
|
queryFn: async () => {
|
||||||
return;
|
if (!user) return [];
|
||||||
}
|
|
||||||
|
|
||||||
const fetchRoles = async () => {
|
const { data, error } = await supabase
|
||||||
try {
|
|
||||||
// Fetch user roles
|
|
||||||
const { data: rolesData, error: rolesError } = await supabase
|
|
||||||
.from('user_roles')
|
.from('user_roles')
|
||||||
.select('role')
|
.select('role')
|
||||||
.eq('user_id', user.id);
|
.eq('user_id', user.id);
|
||||||
|
|
||||||
if (rolesError) {
|
if (error) {
|
||||||
console.error('Error fetching user roles:', rolesError);
|
console.error('Error fetching user roles:', error);
|
||||||
setRoles([]);
|
return [];
|
||||||
} else {
|
|
||||||
setRoles(rolesData?.map(r => r.role as UserRole) || []);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch user permissions using the new function
|
return data?.map(r => r.role as UserRole) || [];
|
||||||
const { data: permissionsData, error: permissionsError } = await supabase
|
},
|
||||||
|
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 });
|
.rpc('get_user_management_permissions', { _user_id: user.id });
|
||||||
|
|
||||||
if (permissionsError) {
|
if (error) {
|
||||||
console.error('Error fetching user permissions:', permissionsError);
|
console.error('Error fetching user permissions:', error);
|
||||||
setPermissions(null);
|
return null;
|
||||||
} else {
|
|
||||||
setPermissions(permissionsData as unknown as UserPermissions);
|
|
||||||
}
|
}
|
||||||
} catch (error: unknown) {
|
|
||||||
console.error('Error fetching user roles:', error);
|
|
||||||
setRoles([]);
|
|
||||||
setPermissions(null);
|
|
||||||
} finally {
|
|
||||||
setLoading(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fetchRoles();
|
return data as unknown as UserPermissions;
|
||||||
}, [user]);
|
},
|
||||||
|
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 hasRole = (role: UserRole) => roles.includes(role);
|
||||||
const isModerator = () => hasRole('admin') || hasRole('moderator') || hasRole('superuser');
|
const isModerator = () => hasRole('admin') || hasRole('moderator') || hasRole('superuser');
|
||||||
|
|||||||
72
src/lib/queryInvalidation.ts
Normal file
72
src/lib/queryInvalidation.ts
Normal 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
18
src/lib/queryKeys.ts
Normal 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;
|
||||||
Reference in New Issue
Block a user