import { useEffect, useState } from "react"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { UserPlus2 } from "lucide-react"; import { useTranslation } from "react-i18next"; 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 { t } = useTranslation(); 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 ?? t("Company", { defaultValue: "Company" }), href: "/dashboard" }, { label: t("Inbox", { defaultValue: "Inbox" }), href: "/inbox" }, { label: t("Join Requests", { defaultValue: "Join Requests" }) }, ]); }, [selectedCompany?.name, setBreadcrumbs, t]); 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: t("Join request approved", { defaultValue: "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: t("Join request rejected", { defaultValue: "Join request rejected" }), tone: "success" }); }, }); if (!selectedCompanyId) { return
{t("Select a company to review join requests.", { defaultValue: "Select a company to review join requests.", })}
; } if (requestsQuery.isLoading) { return
{t("Loading join requests…", { defaultValue: "Loading join requests…", })}
; } if (requestsQuery.error) { const message = requestsQuery.error instanceof ApiError && requestsQuery.error.status === 403 ? t("You do not have permission to review join requests for this company.", { defaultValue: "You do not have permission to review join requests for this company.", }) : requestsQuery.error instanceof Error ? requestsQuery.error.message : t("Failed to load join requests.", { defaultValue: "Failed to load join requests." }); return
{message}
; } return (

{t("Join Request Queue", { defaultValue: "Join Request Queue" })}

{t("Review human and agent join requests outside the mixed inbox feed. This queue uses the same approval mutations as the inline inbox cards.", { defaultValue: "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 ? (
{t("No join requests match the current filters.", { defaultValue: "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 || t("Unknown human requester", { defaultValue: "Unknown human requester" }) : request.agentName || t("Unknown agent requester", { defaultValue: "Unknown agent requester" })}
{request.requestType === "human" ? request.requesterUser?.email || request.requestEmailSnapshot || request.requestingUserId : request.capabilities || request.requestIp}
{request.status === "pending_approval" ? (
) : null}
{t("Invite context", { defaultValue: "Invite context" })}
{request.invite ? t("{{type}} join invite{{suffix}}", { defaultValue: "{{type}} join invite{{suffix}}", type: request.invite.allowedJoinTypes, suffix: request.invite.humanRole ? ` • ${t("default role {{role}}", { defaultValue: "default role {{role}}", role: request.invite.humanRole, })}` : "", }) : t("Invite metadata unavailable", { defaultValue: "Invite metadata unavailable" })}
{request.invite?.inviteMessage ? (
{request.invite.inviteMessage}
) : null}
{t("Request details", { defaultValue: "Request details" })}
{t("Submitted {{date}}", { defaultValue: "Submitted {{date}}", date: new Date(request.createdAt).toLocaleString(), })}
{t("Source IP {{ip}}", { defaultValue: "Source IP {{ip}}", ip: request.requestIp, })}
{request.requestType === "agent" && request.capabilities ?
{request.capabilities}
: null}
)) )}
); }