import { useEffect, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { UserPlus2 } from "lucide-react"; import { accessApi } from "@/api/access"; import { ApiError } from "@/api/client"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { useBreadcrumbs } from "@/context/BreadcrumbContext"; import { useCompany } from "@/context/CompanyContext"; import { useToast } from "@/context/ToastContext"; import { queryKeys } from "@/lib/queryKeys"; export function JoinRequestQueue() { const { selectedCompany, selectedCompanyId } = useCompany(); const { setBreadcrumbs } = useBreadcrumbs(); const { pushToast } = useToast(); const queryClient = useQueryClient(); const [status, setStatus] = useState<"pending_approval" | "approved" | "rejected">("pending_approval"); const [requestType, setRequestType] = useState<"all" | "human" | "agent">("all"); useEffect(() => { setBreadcrumbs([ { label: selectedCompany?.name ?? "Company", href: "/dashboard" }, { label: "Inbox", href: "/inbox" }, { label: "Join Requests" }, ]); }, [selectedCompany?.name, setBreadcrumbs]); const requestsQuery = useQuery({ queryKey: queryKeys.access.joinRequests(selectedCompanyId ?? "", `${status}:${requestType}`), queryFn: () => accessApi.listJoinRequests( selectedCompanyId!, status, requestType === "all" ? undefined : requestType, ), enabled: !!selectedCompanyId, }); const approveMutation = useMutation({ mutationFn: (requestId: string) => accessApi.approveJoinRequest(selectedCompanyId!, requestId), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: queryKeys.access.joinRequests(selectedCompanyId!, `${status}:${requestType}`) }); await queryClient.invalidateQueries({ queryKey: queryKeys.access.companyMembers(selectedCompanyId!) }); await queryClient.invalidateQueries({ queryKey: queryKeys.access.companyUserDirectory(selectedCompanyId!) }); pushToast({ title: "Join request approved", tone: "success" }); }, }); const rejectMutation = useMutation({ mutationFn: (requestId: string) => accessApi.rejectJoinRequest(selectedCompanyId!, requestId), onSuccess: async () => { await queryClient.invalidateQueries({ queryKey: queryKeys.access.joinRequests(selectedCompanyId!, `${status}:${requestType}`) }); pushToast({ title: "Join request rejected", tone: "success" }); }, }); if (!selectedCompanyId) { return
Select a company to review join requests.
; } if (requestsQuery.isLoading) { return
Loading join requests…
; } if (requestsQuery.error) { const message = requestsQuery.error instanceof ApiError && requestsQuery.error.status === 403 ? "You do not have permission to review join requests for this company." : requestsQuery.error instanceof Error ? requestsQuery.error.message : "Failed to load join requests."; return
{message}
; } return (

Join Request Queue

Review human and agent join requests outside the mixed inbox feed. This queue uses the same approval mutations as the inline inbox cards.

{(requestsQuery.data ?? []).length === 0 ? (
No join requests match the current filters.
) : ( requestsQuery.data!.map((request) => (
{request.status.replace("_", " ")} {request.requestType} {request.adapterType ? {request.adapterType} : null}
{request.requestType === "human" ? request.requesterUser?.name || request.requestEmailSnapshot || request.requestingUserId || "Unknown human requester" : request.agentName || "Unknown agent requester"}
{request.requestType === "human" ? request.requesterUser?.email || request.requestEmailSnapshot || request.requestingUserId : request.capabilities || request.requestIp}
{request.status === "pending_approval" ? (
) : null}
Invite context
{request.invite ? `${request.invite.allowedJoinTypes} join invite${request.invite.humanRole ? ` • default role ${request.invite.humanRole}` : ""}` : "Invite metadata unavailable"}
{request.invite?.inviteMessage ? (
{request.invite.inviteMessage}
) : null}
Request details
Submitted {new Date(request.createdAt).toLocaleString()}
Source IP {request.requestIp}
{request.requestType === "agent" && request.capabilities ?
{request.capabilities}
: null}
)) )}
); }