import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { apiFetch } from '@/lib/http' import { UserInvite } from '@/types/settings' import { LoadingSkeleton } from '../common/LoadingSkeleton' interface WorkspaceUser { id: string email: string name: string role: string is_active: boolean } export function AdminUserManagement() { const qc = useQueryClient() const [showAddForm, setShowAddForm] = useState(false) const [formData, setFormData] = useState({ email: '', name: '', role: 'engineer' }) const [inviteLink, setInviteLink] = useState<{ url: string; emailSent: boolean } | null>(null) const [error, setError] = useState(null) const { data, isLoading } = useQuery({ queryKey: ['workspace-users'], queryFn: async () => { const res = await apiFetch('/api/workspace/users') const json = await res.json() return (json.users ?? []) as WorkspaceUser[] }, }) const users = data ?? [] const { mutate: sendInvite, isPending: isSending } = useMutation({ mutationFn: async (input: UserInvite) => { const res = await apiFetch('/api/auth/invite', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: input.email, name: input.name, role: input.role }), }) if (!res.ok) { const body = await res.json().catch(() => ({})) throw new Error(body.detail ?? `Error ${res.status}`) } return res.json() as Promise<{ ok: boolean; email: string; invite_url: string; email_sent: boolean }> }, onSuccess: (data) => { setFormData({ email: '', name: '', role: 'engineer' }) setError(null) setInviteLink({ url: data.invite_url, emailSent: data.email_sent }) qc.invalidateQueries({ queryKey: ['workspace-users'] }) }, onError: (err: Error) => { setError(err.message) }, }) const { mutate: removeUser } = useMutation({ mutationFn: async (userId: string) => { const res = await apiFetch(`/api/workspace/users/${userId}`, { method: 'DELETE' }) if (!res.ok) throw new Error(`Error ${res.status}`) }, onSuccess: () => qc.invalidateQueries({ queryKey: ['workspace-users'] }), }) const handleInvite = () => { if (!formData.email.trim() || !formData.name.trim()) return setError(null) setInviteLink(null as null) sendInvite(formData) } return (
{/* Header */}

Workspace Users

{users.length} user{users.length !== 1 ? 's' : ''}

{/* Invite Form */} {showAddForm && (
setFormData({ ...formData, name: e.target.value })} placeholder="Jane Smith" className="mt-1 block w-full rounded border border-stone-300 bg-white px-3 py-2 text-sm text-stone-900 placeholder-stone-400 focus:border-brand focus:outline-none focus:ring-1 focus:ring-brand dark:border-stone-600 dark:bg-stone-800 dark:text-white" />
setFormData({ ...formData, email: e.target.value })} placeholder="jane@company.com" className="mt-1 block w-full rounded border border-stone-300 bg-white px-3 py-2 text-sm text-stone-900 placeholder-stone-400 focus:border-brand focus:outline-none focus:ring-1 focus:ring-brand dark:border-stone-600 dark:bg-stone-800 dark:text-white" />
{error && (

{error}

)}
{inviteLink && (

{inviteLink.emailSent ? 'Invite email sent! Share the link below as backup:' : 'Email not configured — share this link directly with the user:'}

Expires in 7 days.

)}
)} {/* Users Table */}
{isLoading ? ( ) : users.length > 0 ? (
{users.map((u) => ( ))}
Name Email Role Status Actions
{u.name} {u.email} {u.role} {u.is_active ? 'Active' : 'Inactive'}
) : (

No users yet

Click "Invite User" to add your first team member

)}
) }