"use client"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { useState } from "react"; import { ApiError, api, type InstanceRole } from "@/lib/api"; import { useSession } from "@/lib/session"; import { Button } from "@/components/Button"; const ROLES: InstanceRole[] = ["owner", "admin", "member"]; /* * Absent entirely for self-hosted instances the backend refuses those, and a * panel that renders controls the server will reject is a panel that lies. */ export function MembersPanel({ instanceId }: { instanceId: string }) { const qc = useQueryClient(); const { session } = useSession(); const [selected, setSelected] = useState(""); const [role, setRole] = useState("member"); const [error, setError] = useState(null); const members = useQuery({ queryKey: ["members", instanceId], queryFn: () => api.members(instanceId), }); const people = useQuery({ queryKey: ["account-users"], queryFn: api.accountUsers }); const refresh = () => qc.invalidateQueries({ queryKey: ["members", instanceId] }); const fail = (e: unknown) => setError(e instanceof ApiError ? e.message : "Something went wrong. Try again."); const grant = useMutation({ mutationFn: () => api.grantMember(instanceId, selected, role), onSuccess: () => { setSelected(""); setRole("member"); refresh(); }, onError: fail, }); const changeRole = useMutation({ mutationFn: (v: { uid: string; role: InstanceRole }) => api.setMemberRole(instanceId, v.uid, v.role), onSuccess: refresh, onError: fail, }); const revoke = useMutation({ mutationFn: (uid: string) => api.revokeMember(instanceId, uid), onSuccess: refresh, onError: fail, }); const myRole = session?.account_role; const canManage = myRole === "owner" || myRole === "admin"; const granted = new Set((members.data ?? []).map((m) => m.customer_user_id)); const candidates = (people.data ?? []).filter((p) => !granted.has(p.user_id) && p.verified_at); const pending = (people.data ?? []).filter((p) => !p.verified_at).length; return (

Who can sign in

Each person here has a real user inside this instance and signs in with their Vantage HQ password.

{error &&

{error}

}
    {(members.data ?? []).map((m) => (
  • {m.email} {canManage ? ( ) : ( {m.role} )} {canManage && ( )}
  • ))} {members.data?.length === 0 &&
  • Nobody has been added yet.
  • }
{canManage && (
{ e.preventDefault(); setError(null); if (selected) grant.mutate(); }} >
)} {canManage && pending > 0 && (

{pending} invited {pending === 1 ? "person has" : "people have"} not accepted yet and cannot be added until they do.

)}
); }