import { Dispatch, SetStateAction, useEffect, useMemo, useState } from "react"; import { Controller, useForm } from "react-hook-form"; import { useRouter } from "next/router"; import { faCheck, faCopy, faMagnifyingGlass, faPlus, faTrash, faUsers } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { yupResolver } from "@hookform/resolvers/yup"; import * as yup from "yup"; import { Button, DeleteActionModal, EmailServiceSetupModal, EmptyState, FormControl, IconButton, Input, Modal, ModalContent, Select, SelectItem, Table, TableContainer, TableSkeleton, Tag, TBody, Td, Th, THead, Tr, UpgradePlanModal} from "@app/components/v2"; import { usePopUp, useToggle } from "@app/hooks"; import { useFetchServerStatus } from "@app/hooks/api/serverDetails"; import { OrgUser, Workspace } from "@app/hooks/api/types"; type Props = { members?: OrgUser[]; workspaceMemberships?: Record; orgName: string; isLoading?: boolean; isMoreUserNotAllowed: boolean; onRemoveMember: (userId: string) => Promise; onInviteMember: (email: string) => Promise; onRoleChange: (membershipId: string, role: string) => Promise; onGrantAccess: (userId: string, publicKey: string) => Promise; // the current user id to block remove org button userId: string; completeInviteLink: string | undefined, setCompleteInviteLink: Dispatch> }; const addMemberFormSchema = yup.object({ email: yup.string().email().required().label("Email").trim() }); type TAddMemberForm = yup.InferType; export const OrgMembersTable = ({ members = [], workspaceMemberships = {}, orgName, isMoreUserNotAllowed, onRemoveMember, onInviteMember, onGrantAccess, onRoleChange, userId, isLoading, completeInviteLink, setCompleteInviteLink }: Props) => { const router = useRouter(); const [searchMemberFilter, setSearchMemberFilter] = useState(""); const {data: serverDetails } = useFetchServerStatus() const [isInviteLinkCopied, setInviteLinkCopied] = useToggle(false); const { handlePopUpToggle, popUp, handlePopUpOpen, handlePopUpClose } = usePopUp([ "addMember", "removeMember", "upgradePlan", "setUpEmail" ] as const); const { control, handleSubmit, reset, formState: { isSubmitting } } = useForm({ resolver: yupResolver(addMemberFormSchema) }); const onAddMember = async ({ email }: TAddMemberForm) => { await onInviteMember(email); if (serverDetails?.emailConfigured){ handlePopUpClose("addMember"); } reset(); }; const onRemoveOrgMemberApproved = async () => { const orgMembershipId = (popUp?.removeMember?.data as { id: string })?.id; await onRemoveMember(orgMembershipId); handlePopUpClose("removeMember"); }; const isIamOwner = useMemo( () => members.find(({ user }) => userId === user?._id)?.role === "owner", [userId, members] ); const filterdUser = useMemo( () => members.filter( ({ user, inviteEmail }) => user?.firstName?.toLowerCase().includes(searchMemberFilter) || user?.lastName?.toLowerCase().includes(searchMemberFilter) || user?.email?.toLowerCase().includes(searchMemberFilter) || inviteEmail?.includes(searchMemberFilter) ), [members, searchMemberFilter] ); useEffect(() => { let timer: NodeJS.Timeout; if (isInviteLinkCopied) { timer = setTimeout(() => setInviteLinkCopied.off(), 2000); } return () => clearTimeout(timer); }, [isInviteLinkCopied]); const copyTokenToClipboard = () => { navigator.clipboard.writeText(completeInviteLink as string); setInviteLinkCopied.on(); }; return (
setSearchMemberFilter(e.target.value)} leftIcon={} placeholder="Search members..." />
{isLoading && } {!isLoading && filterdUser.map(({ user, inviteEmail, role, _id: orgMembershipId, status }) => { const name = user ? `${user.firstName} ${user.lastName}` : "-"; const email = user?.email || inviteEmail; const userWs = workspaceMemberships?.[user?._id]; return ( ); })}
Name Email Role Projects
{name} {email} {status === "accepted" && ( )} {((status === "invited" || status === "verified") && serverDetails?.emailConfigured) && ( )} {status === "completed" && ( )} {userWs ? ( userWs?.map(({ name: wsName, _id }) => ( {wsName} )) ) : (
{((status === "invited" || status === "verified") && serverDetails?.emailConfigured) ? This user hasn't accepted the invite yet : This user isn't part of any projects yet} {router.query.id !== "undefined" && !((status === "invited" || status === "verified") && serverDetails?.emailConfigured) && }
)}
{userId !== user?._id && ( handlePopUpOpen("removeMember", { id: orgMembershipId })} > )}
{!isLoading && filterdUser?.length === 0 && ( )}
{ handlePopUpToggle("addMember", isOpen); setCompleteInviteLink(undefined) }} > {!completeInviteLink &&
An invite is specific to an email address and expires after 1 day.
For security reasons, you will need to separately add members to projects.
} {completeInviteLink && "This Infisical instance does not have a email provider setup. Please share this invite link with the invitee manually"}
} > {!completeInviteLink &&
( )} />
} { completeInviteLink &&

{completeInviteLink}

click to copy
} handlePopUpToggle("removeMember", isOpen)} onDeleteApproved={onRemoveOrgMemberApproved} /> handlePopUpToggle("upgradePlan", isOpen)} text="You can add custom environments if you switch to Infisical's Team plan." /> handlePopUpToggle("setUpEmail", isOpen)} /> ); };