import { useState } from 'react'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; import { Crown, Trash2, Loader2 } from 'lucide-react'; import { useClient } from 'hooks/useClient'; import { Button } from '@/components/ui/button'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; type ManagedUser = { id: number; email: string; name: string | null; username: string | null; status: string; role: string; createdAt: string; isOwner: boolean; }; type UsersResponse = { users: ManagedUser[]; roles: string[]; ownerId: number }; const USERS_KEY = ['MANAGED_USERS']; export const UsersSection = () => { const client = useClient(); const queryClient = useQueryClient(); const [pendingId, setPendingId] = useState(null); const [confirmDelete, setConfirmDelete] = useState(null); const { data, isLoading, isError } = useQuery({ queryKey: USERS_KEY, queryFn: () => client.get('/users'), }); const changeRole = async (user: ManagedUser, role: string) => { if (role === user.role) return; setPendingId(user.id); try { await client.patch(`/users/${user.id}/role`, { role }); await queryClient.invalidateQueries({ queryKey: USERS_KEY }); toast.success(`${user.email} is now ${role}`); } catch (ex) { toast.error(ex instanceof Error ? ex.message : 'Could not change the role'); } finally { setPendingId(null); } }; const remove = async (user: ManagedUser) => { setPendingId(user.id); setConfirmDelete(null); try { await client.delete(`/users/${user.id}`); await queryClient.invalidateQueries({ queryKey: USERS_KEY }); toast.success(`${user.email} removed`); } catch (ex) { toast.error(ex instanceof Error ? ex.message : 'Could not remove the account'); } finally { setPendingId(null); } }; if (isLoading) { return (
Loading accounts…
); } if (isError || !data) { return
Could not load the accounts.
; } return (

Every account on this server. The owner is fixed — the database itself refuses to demote or remove it — so that row cannot be changed from here.

{data.users.map((user) => { const busy = pendingId === user.id; return (
{user.name || user.username || user.email} {user.isOwner && }
{user.email} {user.status !== 'Active' && ` · ${user.status}`}
); })}
{/* Removal cascades — passkeys, dashboards, screens, email accounts, playlists — and there is no undo, so it is worth one deliberate confirmation. */} !open && setConfirmDelete(null)}> Remove {confirmDelete?.email}? This deletes the account and everything belonging to it — passkeys, dashboards, saved layouts, email accounts and playlists. It cannot be undone. Cancel confirmDelete && void remove(confirmDelete)} > Remove account
); };