import { useEffect, useMemo, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { Shield, ShieldCheck } from "lucide-react"; import { accessApi } from "@/api/access"; import { ApiError } from "@/api/client"; import { Button } from "@/components/ui/button"; import { Checkbox } from "@/components/ui/checkbox"; import { useBreadcrumbs } from "@/context/BreadcrumbContext"; import { useCompany } from "@/context/CompanyContext"; import { useToast } from "@/context/ToastContext"; import { queryKeys } from "@/lib/queryKeys"; export function InstanceAccess() { const { companies } = useCompany(); const { setBreadcrumbs } = useBreadcrumbs(); const { pushToast } = useToast(); const queryClient = useQueryClient(); const [search, setSearch] = useState(""); const [selectedUserId, setSelectedUserId] = useState(null); const [selectedCompanyIds, setSelectedCompanyIds] = useState>(new Set()); useEffect(() => { setBreadcrumbs([ { label: "Instance Settings", href: "/instance/settings/general" }, { label: "Access" }, ]); }, [setBreadcrumbs]); const usersQuery = useQuery({ queryKey: queryKeys.access.adminUsers(search), queryFn: () => accessApi.searchAdminUsers(search), }); const selectedUser = useMemo( () => usersQuery.data?.find((user) => user.id === selectedUserId) ?? null, [selectedUserId, usersQuery.data], ); const userAccessQuery = useQuery({ queryKey: queryKeys.access.userCompanyAccess(selectedUserId ?? ""), queryFn: () => accessApi.getUserCompanyAccess(selectedUserId!), enabled: !!selectedUserId, }); useEffect(() => { if (!selectedUserId && usersQuery.data?.[0]) { setSelectedUserId(usersQuery.data[0].id); } }, [selectedUserId, usersQuery.data]); useEffect(() => { if (!userAccessQuery.data) return; setSelectedCompanyIds( new Set( userAccessQuery.data.companyAccess .filter((membership) => membership.status === "active") .map((membership) => membership.companyId), ), ); }, [userAccessQuery.data]); const updateCompanyAccessMutation = useMutation({ mutationFn: () => accessApi.setUserCompanyAccess(selectedUserId!, [...selectedCompanyIds]), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: queryKeys.access.userCompanyAccess(selectedUserId!) }); await queryClient.invalidateQueries({ queryKey: queryKeys.access.adminUsers(search) }); pushToast({ title: "Company access updated", tone: "success" }); }, }); const setAdminMutation = useMutation({ mutationFn: async (makeAdmin: boolean) => { if (!selectedUserId) throw new Error("No user selected"); if (makeAdmin) return accessApi.promoteInstanceAdmin(selectedUserId); return accessApi.demoteInstanceAdmin(selectedUserId); }, onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: queryKeys.access.adminUsers(search) }); if (selectedUserId) { await queryClient.invalidateQueries({ queryKey: queryKeys.access.userCompanyAccess(selectedUserId) }); } pushToast({ title: "Instance role updated", tone: "success" }); }, }); if (usersQuery.isLoading) { return
Loading instance users…
; } if (usersQuery.error) { const message = usersQuery.error instanceof ApiError && usersQuery.error.status === 403 ? "Instance admin access is required to manage users." : usersQuery.error instanceof Error ? usersQuery.error.message : "Failed to load users."; return
{message}
; } return (

Instance Access

Search users, manage instance-admin status, and control which companies they can access.

{(usersQuery.data ?? []).map((user) => ( ))}
{!selectedUserId ? (
Select a user to inspect instance access.
) : userAccessQuery.isLoading ? (
Loading user access…
) : userAccessQuery.error ? (
{userAccessQuery.error instanceof Error ? userAccessQuery.error.message : "Failed to load user access."}
) : ( <>
{selectedUser?.name || selectedUser?.email || selectedUserId}
{selectedUser?.email || selectedUserId}

Company access

Toggle company membership for this user. New access defaults to an active operator membership.

{companies.map((company) => ( ))}

Current memberships

{(userAccessQuery.data?.companyAccess ?? []).map((membership) => (
{membership.companyName || membership.companyId}
{membership.membershipRole || "unset"} • {membership.status}
{new Date(membership.updatedAt).toLocaleDateString()}
))}
)}
); }