mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 12:31:26 -05:00
Fix critical Phase 1 issues
This commit is contained in:
@@ -10,6 +10,30 @@ import { supabase } from '@/integrations/supabase/client';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import { useUserRole } from '@/hooks/useUserRole';
|
||||
import { useToast } from '@/hooks/use-toast';
|
||||
|
||||
// Type-safe role definitions
|
||||
const VALID_ROLES = ['admin', 'moderator', 'user'] as const;
|
||||
type ValidRole = typeof VALID_ROLES[number];
|
||||
|
||||
/**
|
||||
* Type guard to validate role strings
|
||||
* Prevents unsafe casting and ensures type safety
|
||||
*/
|
||||
function isValidRole(role: string): role is ValidRole {
|
||||
return VALID_ROLES.includes(role as ValidRole);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get display label for a role
|
||||
*/
|
||||
function getRoleLabel(role: string): string {
|
||||
const labels: Record<ValidRole, string> = {
|
||||
admin: 'Administrator',
|
||||
moderator: 'Moderator',
|
||||
user: 'User',
|
||||
};
|
||||
return isValidRole(role) ? labels[role] : role;
|
||||
}
|
||||
interface UserRole {
|
||||
id: string;
|
||||
user_id: string;
|
||||
@@ -109,8 +133,19 @@ export function UserRoleManager() {
|
||||
}, 300);
|
||||
return () => clearTimeout(debounceTimer);
|
||||
}, [newUserSearch, userRoles]);
|
||||
const grantRole = async (userId: string, role: 'admin' | 'moderator' | 'user') => {
|
||||
const grantRole = async (userId: string, role: ValidRole) => {
|
||||
if (!isAdmin()) return;
|
||||
|
||||
// Double-check role validity before database operation
|
||||
if (!isValidRole(role)) {
|
||||
toast({
|
||||
title: "Invalid Role",
|
||||
description: "The selected role is not valid",
|
||||
variant: "destructive"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setActionLoading('grant');
|
||||
try {
|
||||
const {
|
||||
@@ -120,10 +155,12 @@ export function UserRoleManager() {
|
||||
role,
|
||||
granted_by: user?.id
|
||||
}]);
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
toast({
|
||||
title: "Role Granted",
|
||||
description: `User has been granted ${role} role`
|
||||
description: `User has been granted ${getRoleLabel(role)} role`
|
||||
});
|
||||
setNewUserSearch('');
|
||||
setNewRole('');
|
||||
@@ -223,10 +260,20 @@ export function UserRoleManager() {
|
||||
|
||||
<Button onClick={() => {
|
||||
const selectedUser = searchResults.find(p => (p.display_name || p.username) === newUserSearch);
|
||||
if (selectedUser && newRole) {
|
||||
grantRole(selectedUser.user_id, newRole as 'admin' | 'moderator' | 'user');
|
||||
|
||||
// Type-safe validation before calling grantRole
|
||||
if (selectedUser && newRole && isValidRole(newRole)) {
|
||||
grantRole(selectedUser.user_id, newRole);
|
||||
} else if (selectedUser && newRole) {
|
||||
// This should never happen due to Select component constraints,
|
||||
// but provides safety in case of UI bugs
|
||||
toast({
|
||||
title: "Invalid Role",
|
||||
description: "Please select a valid role",
|
||||
variant: "destructive"
|
||||
});
|
||||
}
|
||||
}} disabled={!newRole || !searchResults.find(p => (p.display_name || p.username) === newUserSearch) || actionLoading === 'grant'} className="w-full md:w-auto">
|
||||
}} disabled={!newRole || !isValidRole(newRole) || !searchResults.find(p => (p.display_name || p.username) === newUserSearch) || actionLoading === 'grant'} className="w-full md:w-auto">
|
||||
{actionLoading === 'grant' ? 'Granting...' : 'Grant Role'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
|
||||
Reference in New Issue
Block a user