import { useState, useEffect } from 'react'; import { Search, Ban, Shield, UserCheck, UserX, AlertTriangle } from 'lucide-react'; import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/hooks/useAuth'; import { useUserRole, UserRole } from '@/hooks/useUserRole'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from '@/components/ui/alert-dialog'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { useToast } from '@/hooks/use-toast'; interface UserProfile { id: string; user_id: string; username: string; display_name?: string; avatar_url?: string; banned: boolean; created_at: string; roles: UserRole[]; } export function ProfileManager() { const { user } = useAuth(); const { permissions, loading: roleLoading } = useUserRole(); const { toast } = useToast(); const [profiles, setProfiles] = useState([]); const [loading, setLoading] = useState(true); const [searchTerm, setSearchTerm] = useState(''); const [statusFilter, setStatusFilter] = useState<'all' | 'active' | 'banned'>('all'); const [roleFilter, setRoleFilter] = useState<'all' | UserRole>('all'); const [actionLoading, setActionLoading] = useState(null); useEffect(() => { if (!roleLoading && permissions?.can_view_all_profiles) { fetchProfiles(); } }, [roleLoading, permissions]); const fetchProfiles = async () => { try { setLoading(true); // Fetch profiles with user roles const { data: profilesData, error: profilesError } = await supabase .from('profiles') .select('*') .order('created_at', { ascending: false }); if (profilesError) throw profilesError; // Fetch roles for each user const profilesWithRoles = await Promise.all( profilesData.map(async (profile) => { const { data: rolesData } = await supabase .from('user_roles') .select('role') .eq('user_id', profile.user_id); return { ...profile, roles: rolesData?.map(r => r.role as UserRole) || [] }; }) ); setProfiles(profilesWithRoles); } catch (error) { console.error('Error fetching profiles:', error); toast({ title: "Error", description: "Failed to fetch user profiles.", variant: "destructive", }); } finally { setLoading(false); } }; const handleBanUser = async (targetUserId: string, ban: boolean) => { if (!user || !permissions) return; setActionLoading(targetUserId); try { // Update banned status const { error: updateError } = await supabase .from('profiles') .update({ banned: ban }) .eq('user_id', targetUserId); if (updateError) throw updateError; // Log the action const { error: logError } = await supabase .rpc('log_admin_action', { _admin_user_id: user.id, _target_user_id: targetUserId, _action: ban ? 'ban_user' : 'unban_user', _details: { banned: ban } }); if (logError) console.error('Error logging action:', logError); toast({ title: "Success", description: `User ${ban ? 'banned' : 'unbanned'} successfully.`, }); // Refresh profiles fetchProfiles(); } catch (error) { console.error('Error updating user ban status:', error); toast({ title: "Error", description: `Failed to ${ban ? 'ban' : 'unban'} user.`, variant: "destructive", }); } finally { setActionLoading(null); } }; const handleRoleChange = async (targetUserId: string, newRole: UserRole | 'remove', currentRoles: UserRole[]) => { if (!user || !permissions) return; setActionLoading(targetUserId); try { if (newRole === 'remove') { // Remove all roles except superuser (which can't be removed via UI) const rolesToRemove = currentRoles.filter(role => role !== 'superuser'); for (const role of rolesToRemove) { const { error } = await supabase .from('user_roles') .delete() .eq('user_id', targetUserId) .eq('role', role); if (error) throw error; } } else { // Check permissions before allowing role assignment if (newRole === 'admin' && !permissions.can_manage_admin_roles) { toast({ title: "Access Denied", description: "You don't have permission to assign admin roles.", variant: "destructive", }); return; } if (newRole === 'superuser') { toast({ title: "Access Denied", description: "Superuser roles can only be assigned directly in the database.", variant: "destructive", }); return; } // Add new role const { error } = await supabase .from('user_roles') .upsert({ user_id: targetUserId, role: newRole, granted_by: user.id }); if (error) throw error; } // Log the action const { error: logError } = await supabase .rpc('log_admin_action', { _admin_user_id: user.id, _target_user_id: targetUserId, _action: newRole === 'remove' ? 'remove_roles' : 'assign_role', _details: { role: newRole, previous_roles: currentRoles } }); if (logError) console.error('Error logging action:', logError); toast({ title: "Success", description: `User role updated successfully.`, }); // Refresh profiles fetchProfiles(); } catch (error) { console.error('Error updating user role:', error); toast({ title: "Error", description: "Failed to update user role.", variant: "destructive", }); } finally { setActionLoading(null); } }; const canManageUser = (targetProfile: UserProfile) => { if (!permissions) return false; // Superuser can manage anyone except other superusers if (permissions.role_level === 'superuser') { return !targetProfile.roles.includes('superuser'); } // Admin can manage moderators and users, but not admins or superusers if (permissions.role_level === 'admin') { return !targetProfile.roles.some(role => ['admin', 'superuser'].includes(role)); } // Moderator can only ban users with no roles or user role if (permissions.role_level === 'moderator') { return targetProfile.roles.length === 0 || (targetProfile.roles.length === 1 && targetProfile.roles[0] === 'user'); } return false; }; const filteredProfiles = profiles.filter(profile => { const matchesSearch = profile.username.toLowerCase().includes(searchTerm.toLowerCase()) || profile.display_name?.toLowerCase().includes(searchTerm.toLowerCase()); const matchesStatus = statusFilter === 'all' || (statusFilter === 'banned' && profile.banned) || (statusFilter === 'active' && !profile.banned); const matchesRole = roleFilter === 'all' || profile.roles.includes(roleFilter as UserRole) || (roleFilter === 'user' && profile.roles.length === 0); return matchesSearch && matchesStatus && matchesRole; }); if (roleLoading) { return (

Loading permissions...

); } if (!permissions?.can_view_all_profiles) { return (

Access Denied

You don't have permission to manage user profiles.

); } return (
{/* Filters */}
setSearchTerm(e.target.value)} className="pl-10" />
{/* Users List */} {loading ? (
) : (
{filteredProfiles.map((profile) => (
{profile.display_name?.[0] || profile.username[0]}

{profile.display_name || profile.username}

{profile.banned && ( Banned )}

@{profile.username}

{profile.roles.length > 0 ? ( profile.roles.map((role) => ( {role} )) ) : ( User )}
{canManageUser(profile) && (
{/* Ban/Unban Button */} {permissions.can_ban_any_user && ( {profile.banned ? 'Unban' : 'Ban'} User Are you sure you want to {profile.banned ? 'unban' : 'ban'} {profile.username}? {!profile.banned && ' This will prevent them from accessing the application.'} Cancel handleBanUser(profile.user_id, !profile.banned)} className={profile.banned ? "" : "bg-destructive hover:bg-destructive/90"} > {profile.banned ? 'Unban' : 'Ban'} User )} {/* Role Management */} {(permissions.can_manage_moderator_roles || permissions.can_manage_admin_roles) && ( )}
)}
))} {filteredProfiles.length === 0 && (

No Users Found

No users match your current filters.

)}
)}
); }