import React, { useEffect, useRef, useState } from "react"; // next import Image from "next/image"; import { useTheme } from "next-themes"; // headless ui import { Listbox, Transition } from "@headlessui/react"; // react-hook-form import { Control, Controller, FieldArrayWithId, UseFieldArrayRemove, useFieldArray, useForm } from "react-hook-form"; // services import { WorkspaceService } from "services/workspace.service"; // hooks import useToast from "hooks/use-toast"; // ui import { Button, Input } from "@plane/ui"; // components import OnboardingStepIndicator from "components/account/step-indicator"; // hooks import useDynamicDropdownPosition from "hooks/use-dynamic-dropdown"; // icons import { Check, ChevronDown, Plus, User2, XCircle } from "lucide-react"; // types import { IUser, IWorkspace, TOnboardingSteps, TUserWorkspaceRole } from "types"; // constants import { ROLE } from "constants/workspace"; // assets import user1 from "public/users/user-1.png"; import user2 from "public/users/user-2.png"; import userDark from "public/onboarding/user-dark.svg"; import userLight from "public/onboarding/user-light.svg"; type Props = { finishOnboarding: () => Promise; stepChange: (steps: Partial) => Promise; user: IUser | undefined; workspace: IWorkspace | undefined; }; type EmailRole = { email: string; role: TUserWorkspaceRole; }; type FormValues = { emails: EmailRole[]; }; type InviteMemberFormProps = { index: number; remove: UseFieldArrayRemove; control: Control; field: FieldArrayWithId; fields: FieldArrayWithId[]; errors: any; isInvitationDisabled: boolean; setIsInvitationDisabled: (value: boolean) => void; }; // services const workspaceService = new WorkspaceService(); const InviteMemberForm: React.FC = (props) => { const { control, index, fields, remove, errors, isInvitationDisabled, setIsInvitationDisabled } = props; const buttonRef = useRef(null); const dropdownRef = useRef(null); const [isDropdownOpen, setIsDropdownOpen] = useState(false); useDynamicDropdownPosition(isDropdownOpen, () => setIsDropdownOpen(false), buttonRef, dropdownRef); return (
( { if (event.target.value === "") { const validEmail = !fields .filter((ele) => { ele.id !== `emails.${index}.email`; }) .map((ele) => ele.email) .includes(""); if (validEmail) { setIsInvitationDisabled(false); } else { setIsInvitationDisabled(true); } } else if ( isInvitationDisabled && /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(event.target.value) ) { setIsInvitationDisabled(false); } else if ( !isInvitationDisabled && !/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i.test(event.target.value) ) { setIsInvitationDisabled(true); } onChange(event); }} ref={ref} hasError={Boolean(errors.emails?.[index]?.email)} placeholder="Enter their email..." className="text-xs sm:text-sm w-full h-12 placeholder:text-onboarding-text-400 border-onboarding-border-100" /> )} />
( { onChange(val); setIsDropdownOpen(false); }} className="flex-shrink-0 text-left w-full" > setIsDropdownOpen((prev) => !prev)} className="flex items-center px-2.5 h-11 py-2 text-xs justify-between gap-1 w-full rounded-md duration-300" > {ROLE[value]}
{Object.entries(ROLE).map(([key, value]) => ( `cursor-pointer select-none truncate rounded px-1 py-1.5 ${ active || selected ? "bg-onboarding-background-400/40" : "" } ${selected ? "text-onboarding-text-100" : "text-custom-text-200"}` } > {({ selected }) => (
{value}
{selected && }
)}
))}
)} />
{fields.length > 1 && ( )}
); }; export const InviteMembers: React.FC = (props) => { const { finishOnboarding, stepChange, workspace } = props; const [isInvitationDisabled, setIsInvitationDisabled] = useState(true); const { setToastAlert } = useToast(); const { resolvedTheme } = useTheme(); const { control, handleSubmit, formState: { isSubmitting, errors, isValid }, } = useForm(); const { fields, append, remove } = useFieldArray({ control, name: "emails", }); const nextStep = async () => { const payload: Partial = { workspace_invite: true, }; await stepChange(payload); await finishOnboarding(); }; const onSubmit = async (formData: FormValues) => { if (!workspace) return; let payload = { ...formData }; payload = { emails: payload.emails.filter((email) => email.email !== "") }; await workspaceService .inviteWorkspace(workspace.slug, payload) .then(async () => { setToastAlert({ type: "success", title: "Success!", message: "Invitations sent successfully.", }); await nextStep(); }) .catch((err) => setToastAlert({ type: "error", title: "Error!", message: err?.error, }) ); }; const appendField = () => { append({ email: "", role: 15 }); }; useEffect(() => { if (fields.length === 0) { append( [ { email: "", role: 15 }, { email: "", role: 15 }, { email: "", role: 15 }, ], { focusIndex: 0, } ); } }, [fields, append]); return (