import { useState, useEffect, useCallback } from 'react'; import { supabase } from '@/config'; import { auditedSupabase } from '@/hooks/useAuditedSupabase'; import { useRealtimeSubscription } from '@/hooks/useRealtimeSubscription'; import { RealtimeStatusIndicator } from '@/components/RealtimeStatusIndicator'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Badge } from '@/components/ui/badge'; import { Skeleton } from '@/components/ui/skeleton'; import { PaginationControls } from '@/components/ui/pagination'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Checkbox } from '@/components/ui/checkbox'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Search, Shield, Trash2, UserPlus, Settings } from 'lucide-react'; import { format } from 'date-fns'; import { useAuth, AdminRole, PERMISSION_METADATA, PermissionKey } from '@/contexts/AuthContext'; import { toast } from 'sonner'; interface AdminUser { id: string; // admin_users PK user_id: string; role: AdminRole; permissions: string[]; created_at: string; profile?: { name: string | null; email: string | null; avatar_url: string | null; }; } interface Profile { id: string; name: string | null; email: string | null; avatar_url: string | null; } export function AdminsPage() { const { user: currentUser } = useAuth(); const [admins, setAdmins] = useState([]); const [loading, setLoading] = useState(true); const [search, setSearch] = useState(''); const [page, setPage] = useState(1); const [pageSize, setPageSize] = useState(25); // Add Admin State const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); const [userSearch, setUserSearch] = useState(''); const [searchResults, setSearchResults] = useState([]); const [searchingUsers, setSearchingUsers] = useState(false); const [selectedUser, setSelectedUser] = useState(null); const [isSuperAdminChecked, setIsSuperAdminChecked] = useState(false); const [selectedPermissions, setSelectedPermissions] = useState([]); const [isSubmitting, setIsSubmitting] = useState(false); // Edit Permissions State const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [editingAdmin, setEditingAdmin] = useState(null); const [editIsSuperAdmin, setEditIsSuperAdmin] = useState(false); const [editPermissions, setEditPermissions] = useState([]); const fetchAdmins = useCallback(async () => { setLoading(true); try { // First fetch admin users with permissions const { data: adminData, error: adminError } = await supabase .from('admin_users') .select('id, user_id, role, permissions, created_at') .order('created_at', { ascending: false }); if (adminError) throw adminError; // Then fetch profiles for these users // We do this manually to avoid relying on foreign key relationships if they aren't perfect if (adminData && adminData.length > 0) { const userIds = adminData.map(a => a.user_id); const { data: profileData, error: profileError } = await supabase .from('profiles') .select('id, name, email, avatar_url') .in('id', userIds); if (profileError) throw profileError; // Merge data const joinedAdmins = adminData.map(admin => ({ ...admin, permissions: (admin.permissions as string[]) || [], profile: profileData?.find(p => p.id === admin.user_id) })); setAdmins(joinedAdmins); } else { setAdmins([]); } } catch (error: unknown) { console.error('Failed to load admins:', error); toast.error("Failed to load admin users"); } finally { setLoading(false); } }, []); // Real-time subscription for admin_users table const { status: realtimeStatus } = useRealtimeSubscription({ table: 'admin_users', onInsert: fetchAdmins, onUpdate: fetchAdmins, onDelete: fetchAdmins, insertToast: { enabled: true, message: 'A new admin has been added', type: 'info', }, }); // Initial load useEffect(() => { fetchAdmins(); }, [fetchAdmins]); // Search users for adding useEffect(() => { const searchUsers = async () => { if (!userSearch.trim()) { setSearchResults([]); return; } setSearchingUsers(true); try { // Determine which users are already admins to exclude them const adminUserIds = new Set(admins.map(a => a.user_id)); const { data, error } = await supabase .from('profiles') .select('id, name, email, avatar_url') .or(`email.ilike.%${userSearch}%,name.ilike.%${userSearch}%`) .limit(5); if (error) throw error; // Filter out existing admins const availableUsers = (data || []).filter(u => !adminUserIds.has(u.id)); setSearchResults(availableUsers); } catch (error) { console.error('Search failed:', error); } finally { setSearchingUsers(false); } }; const debounce = setTimeout(searchUsers, 500); return () => clearTimeout(debounce); }, [userSearch, admins]); const handleAddAdmin = async () => { if (!selectedUser) return; setIsSubmitting(true); try { const role: AdminRole = isSuperAdminChecked ? 'super_admin' : 'pack_creator'; const permissions = isSuperAdminChecked ? [] : selectedPermissions; const { error } = await auditedSupabase.insert('admin_users', { user_id: selectedUser.id, role, permissions }); if (error) throw error; const roleLabel = isSuperAdminChecked ? 'Super Admin' : 'Admin'; toast.success(`${selectedUser.name || selectedUser.email} added as ${roleLabel}`); setIsAddDialogOpen(false); setSelectedUser(null); setUserSearch(''); setIsSuperAdminChecked(false); setSelectedPermissions([]); fetchAdmins(); } catch (error: any) { console.error('Failed to add admin:', error); toast.error(error.message || "Failed to add admin"); } finally { setIsSubmitting(false); } }; const handleEditPermissions = (admin: AdminUser) => { setEditingAdmin(admin); setEditIsSuperAdmin(admin.role === 'super_admin'); setEditPermissions(admin.permissions || []); setIsEditDialogOpen(true); }; const handleSavePermissions = async () => { if (!editingAdmin) return; setIsSubmitting(true); try { const role: AdminRole = editIsSuperAdmin ? 'super_admin' : 'pack_creator'; const permissions = editIsSuperAdmin ? [] : editPermissions; const { error } = await auditedSupabase.update('admin_users', editingAdmin.id, { role, permissions }); if (error) throw error; toast.success("Permissions updated successfully"); setIsEditDialogOpen(false); setEditingAdmin(null); fetchAdmins(); } catch (error: any) { console.error('Failed to update permissions:', error); toast.error(error.message || "Failed to update permissions"); } finally { setIsSubmitting(false); } }; const togglePermission = (permission: string, isEdit: boolean) => { if (isEdit) { setEditPermissions(prev => prev.includes(permission) ? prev.filter(p => p !== permission) : [...prev, permission] ); } else { setSelectedPermissions(prev => prev.includes(permission) ? prev.filter(p => p !== permission) : [...prev, permission] ); } }; const handleRemoveAdmin = async (admin: AdminUser) => { if (!confirm(`Are you sure you want to remove access for ${admin.profile?.name || admin.profile?.email || 'this user'}?`)) { return; } try { const { error } = await auditedSupabase.deleteBy('admin_users', 'user_id', admin.user_id); if (error) throw error; toast.success("Admin access revoked"); fetchAdmins(); } catch (error: any) { toast.error("Failed to remove admin"); } }; const filteredAdmins = admins.filter(admin => { if (!search) return true; const query = search.toLowerCase(); return ( admin.profile?.name?.toLowerCase().includes(query) || admin.profile?.email?.toLowerCase().includes(query) || admin.role.includes(query) ); }); const totalCount = filteredAdmins.length; const paginatedAdmins = filteredAdmins.slice( (page - 1) * pageSize, page * pageSize ); useEffect(() => { const totalPages = Math.max(1, Math.ceil(totalCount / pageSize)); if (page > totalPages) { setPage(totalPages); } }, [page, pageSize, totalCount]); useEffect(() => { setPage(1); }, [search]); return (

Admin Management

Manage system administrators and their roles

setSearch(e.target.value)} className="pl-9" />
Add Administrator Search for an existing user to grant admin access.
{selectedUser ? (
{selectedUser.name?.charAt(0) || 'U'}
{selectedUser.name || 'Unnamed'}
{selectedUser.email}
) : (
setUserSearch(e.target.value)} className="pl-8" /> {userSearch && (
{searchingUsers &&
Searching...
} {!searchingUsers && searchResults.length === 0 && (
No eligible users found
)} {searchResults.map(user => (
setSelectedUser(user)} > {user.name?.charAt(0) || 'U'}
{user.name || 'Unnamed'}
{user.email}
))}
)}
)}
setIsSuperAdminChecked(checked === true)} />
{!isSuperAdminChecked && ( <>
{Object.entries(PERMISSION_METADATA) .filter(([_, meta]) => meta.group === 'content') .map(([key, meta]) => (
togglePermission(key, false)} />

{meta.description}

))}
{Object.entries(PERMISSION_METADATA) .filter(([_, meta]) => meta.group === 'users') .map(([key, meta]) => (
togglePermission(key, false)} />

{meta.description}

))}
{Object.entries(PERMISSION_METADATA) .filter(([_, meta]) => meta.group === 'system') .map(([key, meta]) => (
togglePermission(key, false)} />

{meta.description}

))}
)}
Admin User Email Role / Permissions Added On Actions {loading ? (
) : filteredAdmins.length === 0 ? ( No admin users found. ) : ( paginatedAdmins.map((admin) => (
{admin.profile?.name?.charAt(0).toUpperCase() || 'A'} {admin.profile?.name || 'Unknown User'} {admin.user_id === currentUser?.id && ( You )}
{admin.profile?.email || '—'} {admin.role === 'super_admin' ? ( Super Admin ) : (
{admin.permissions.length === 0 ? ( No permissions ) : ( admin.permissions.slice(0, 3).map(perm => ( {PERMISSION_METADATA[perm as PermissionKey]?.label.replace('Can ', '') || perm} )) )} {admin.permissions.length > 3 && ( +{admin.permissions.length - 3} more )}
)}
{admin.created_at ? format(new Date(admin.created_at), 'MMM d, yyyy') : '—'}
{admin.user_id !== currentUser?.id && ( <> )}
)) )}
{filteredAdmins.length > 0 && ( { setPage(1); setPageSize(size); }} /> )} {/* Edit Permissions Dialog */} Edit Permissions Update permissions for {editingAdmin?.profile?.name || editingAdmin?.profile?.email || 'this admin'}.
setEditIsSuperAdmin(checked === true)} />
{!editIsSuperAdmin && ( <>
{Object.entries(PERMISSION_METADATA) .filter(([_, meta]) => meta.group === 'content') .map(([key, meta]) => (
togglePermission(key, true)} />

{meta.description}

))}
{Object.entries(PERMISSION_METADATA) .filter(([_, meta]) => meta.group === 'users') .map(([key, meta]) => (
togglePermission(key, true)} />

{meta.description}

))}
{Object.entries(PERMISSION_METADATA) .filter(([_, meta]) => meta.group === 'system') .map(([key, meta]) => (
togglePermission(key, true)} />

{meta.description}

))}
)}
); }