diff --git a/web/components/onboarding/invite-members.tsx b/web/components/onboarding/invite-members.tsx index 88361dd23..6b23f1b10 100644 --- a/web/components/onboarding/invite-members.tsx +++ b/web/components/onboarding/invite-members.tsx @@ -2,7 +2,17 @@ import React, { useEffect, useRef, useState } from "react"; import Image from "next/image"; import { useTheme } from "next-themes"; import { Listbox, Transition } from "@headlessui/react"; -import { Control, Controller, FieldArrayWithId, UseFieldArrayRemove, useFieldArray, useForm } from "react-hook-form"; +import { + Control, + Controller, + FieldArrayWithId, + UseFieldArrayRemove, + UseFormGetValues, + UseFormSetValue, + UseFormWatch, + useFieldArray, + useForm, +} from "react-hook-form"; import { Check, ChevronDown, Plus, XCircle } from "lucide-react"; // services import { WorkspaceService } from "services/workspace.service"; @@ -34,6 +44,7 @@ type Props = { type EmailRole = { email: string; role: TUserWorkspaceRole; + role_active: boolean; }; type FormValues = { @@ -44,6 +55,9 @@ type InviteMemberFormProps = { index: number; remove: UseFieldArrayRemove; control: Control; + setValue: UseFormSetValue; + getValues: UseFormGetValues; + watch: UseFormWatch; field: FieldArrayWithId; fields: FieldArrayWithId[]; errors: any; @@ -53,9 +67,33 @@ type InviteMemberFormProps = { // services const workspaceService = new WorkspaceService(); +const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; +const placeholderEmails = [ + "charlie.taylor@frstflit.com", + "octave.chanute@frstflit.com", + "george.spratt@frstflit.com", + "frank.coffyn@frstflit.com", + "amos.root@frstflit.com", + "edward.deeds@frstflit.com", + "charles.m.manly@frstflit.com", + "glenn.curtiss@frstflit.com", + "thomas.selfridge@frstflit.com", + "albert.zahm@frstflit.com", +]; const InviteMemberForm: React.FC = (props) => { - const { control, index, fields, remove, errors, isInvitationDisabled, setIsInvitationDisabled } = props; + const { + control, + index, + fields, + remove, + errors, + isInvitationDisabled, + setIsInvitationDisabled, + setValue, + getValues, + watch, + } = props; const buttonRef = useRef(null); const dropdownRef = useRef(null); @@ -64,131 +102,159 @@ const InviteMemberForm: React.FC = (props) => { useDynamicDropdownPosition(isDropdownOpen, () => setIsDropdownOpen(false), buttonRef, dropdownRef); + const email = watch(`emails.${index}.email`); + + const emailOnChange = (event: React.ChangeEvent) => { + if (event.target.value === "") { + const validEmail = fields.map((_, i) => emailRegex.test(getValues(`emails.${i}.email`))).includes(true); + if (validEmail) { + setIsInvitationDisabled(false); + } else { + setIsInvitationDisabled(true); + } + + if (getValues(`emails.${index}.role_active`)) { + setValue(`emails.${index}.role_active`, false); + } + } else { + if (!getValues(`emails.${index}.role_active`)) { + setValue(`emails.${index}.role_active`, true); + } + if (isInvitationDisabled && emailRegex.test(event.target.value)) { + setIsInvitationDisabled(false); + } else if (!isInvitationDisabled && !emailRegex.test(event.target.value)) { + setIsInvitationDisabled(true); + } + } + }; + 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" +
+
+
+ ( + { + emailOnChange(event); + onChange(event); + }} + ref={ref} + hasError={Boolean(errors.emails?.[index]?.email)} + placeholder={placeholderEmails[index % placeholderEmails.length]} + className="text-xs sm:text-sm w-full h-12 placeholder:text-onboarding-text-400 border-onboarding-border-100" + /> + )} + /> +
+
+ ( + { + onChange(val); + setIsDropdownOpen(false); + setValue(`emails.${index}.role_active`, true); + }} + className="flex-shrink-0 text-left w-full" > - {ROLE[value]} - - - - - - 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" > -
- {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 && } -
- )} -
- ))} -
-
-
-
- )} - /> + + {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 && ( + + )}
- {fields.length > 1 && ( - + {email && !emailRegex.test(email) && ( +
+ 🤥{" "} + That doesn{"'"}t look like an email address. +
)}
); @@ -204,6 +270,9 @@ export const InviteMembers: React.FC = (props) => { const { control, + watch, + getValues, + setValue, handleSubmit, formState: { isSubmitting, errors, isValid }, } = useForm(); @@ -229,7 +298,12 @@ export const InviteMembers: React.FC = (props) => { payload = { emails: payload.emails.filter((email) => email.email !== "") }; await workspaceService - .inviteWorkspace(workspace.slug, payload) + .inviteWorkspace(workspace.slug, { + emails: payload.emails.map((email) => ({ + email: email.email, + role: email.role, + })), + }) .then(async () => { setToastAlert({ type: "success", @@ -249,16 +323,16 @@ export const InviteMembers: React.FC = (props) => { }; const appendField = () => { - append({ email: "", role: 15 }); + append({ email: "", role: 15, role_active: false }); }; useEffect(() => { if (fields.length === 0) { append( [ - { email: "", role: 15 }, - { email: "", role: 15 }, - { email: "", role: 15 }, + { email: "", role: 15, role_active: false }, + { email: "", role: 15, role_active: false }, + { email: "", role: 15, role_active: false }, ], { focusIndex: 0, @@ -325,6 +399,9 @@ export const InviteMembers: React.FC = (props) => {
{fields.map((field, index) => ( setIsInvitationDisabled(value)} control={control}