🚸validate emails using react form hook

This commit is contained in:
Timur Ercan
2023-03-06 18:06:22 +01:00
parent dc333a5fc8
commit facbe60b50

View File

@ -12,6 +12,7 @@ import {
PencilSquareIcon, PencilSquareIcon,
TrashIcon, TrashIcon,
UserPlusIcon, UserPlusIcon,
XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { getUserFromToken } from "@documenso/lib/server"; import { getUserFromToken } from "@documenso/lib/server";
import { getDocument } from "@documenso/lib/query"; import { getDocument } from "@documenso/lib/query";
@ -23,6 +24,16 @@ import {
deleteRecipient, deleteRecipient,
sendSigningRequests, sendSigningRequests,
} from "@documenso/lib/api"; } from "@documenso/lib/api";
import {
FormProvider,
useFieldArray,
useForm,
useWatch,
} from "react-hook-form";
type FormValues = {
signers: { id: number; email: string; name: string }[];
};
const RecipientsPage: NextPageWithLayout = (props: any) => { const RecipientsPage: NextPageWithLayout = (props: any) => {
const title: string = const title: string =
@ -46,11 +57,28 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
}, },
]; ];
const [signers, setSigners] = useState(props?.document?.Recipient);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const form = useForm<FormValues>({
defaultValues: { signers: props?.document?.Recipient },
});
const {
register,
trigger,
control,
formState: { errors },
} = form;
const { fields, append, remove } = useFieldArray({
keyName: "dieldArrayId",
name: "signers",
control,
});
const formValues = useWatch({ control, name: "signers" });
const cancelButtonRef = useRef(null); const cancelButtonRef = useRef(null);
const hasEmailError = (formValue: any): boolean => {
const index = formValues.findIndex((e) => e.id === formValue.id);
return !!errors?.signers?.[index]?.email;
};
return ( return (
<> <>
@ -93,9 +121,10 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
setOpen(true); setOpen(true);
}} }}
disabled={ disabled={
(signers.length || 0) === 0 || (formValues.length || 0) === 0 ||
!signers.some( !formValues.some(
(r: any) => r.email && r.sendStatus === "NOT_SENT" (r: any) =>
r.email && !hasEmailError(r) && r.sendStatus === "NOT_SENT"
) || ) ||
loading loading
} }
@ -113,8 +142,14 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
The people who will sign the document. The people who will sign the document.
</p> </p>
</div> </div>
<FormProvider {...form}>
<form
onChange={() => {
trigger();
}}
>
<ul role="list" className="divide-y divide-gray-200"> <ul role="list" className="divide-y divide-gray-200">
{signers.map((item: any, index: number) => ( {fields.map((item: any, index: number) => (
<li <li
key={index} key={index}
className="px-0 py-4 w-full hover:bg-green-50 border-0 group" className="px-0 py-4 w-full hover:bg-green-50 border-0 group"
@ -134,25 +169,39 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
</label> </label>
<input <input
type="email" type="email"
name="email" {...register(`signers.${index}.email`, {
value={item.email} pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i,
})}
defaultValue={item.email}
disabled={item.sendStatus === "SENT" || loading} disabled={item.sendStatus === "SENT" || loading}
onChange={(e) => {
const updatedSigners = [...signers];
updatedSigners[index].email = e.target.value;
setSigners(updatedSigners);
}}
onBlur={() => { onBlur={() => {
item.documentId = props.document.id; if (!errors?.signers?.[index])
createOrUpdateRecipient(item); createOrUpdateRecipient({
...formValues[index],
documentId: props.document.id,
});
}} }}
onKeyDown={(event: any) => { onKeyDown={(event: any) => {
if (event.key === "Enter") if (event.key === "Enter")
createOrUpdateRecipient(item); if (!errors?.signers?.[index])
createOrUpdateRecipient({
...formValues[index],
documentId: props.document.id,
});
}} }}
className="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 sm:text-sm outline-none bg-inherit" className="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 sm:text-sm outline-none bg-inherit"
placeholder="john.dorian@loremipsum.com" placeholder="john.dorian@loremipsum.com"
/> />
{errors?.signers?.[index] ? (
<p
className="mt-2 text-sm text-red-600"
id="email-error"
>
<XMarkIcon className="inline h-5" /> Invalid Email
</p>
) : (
""
)}
</div> </div>
<div <div
className={classNames( className={classNames(
@ -167,22 +216,26 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
Name (optional) Name (optional)
</label> </label>
<input <input
type="email" type="text"
name="name" {...register(`signers.${index}.name`)}
value={item.name} defaultValue={item.name}
disabled={item.sendStatus === "SENT" || loading} disabled={item.sendStatus === "SENT" || loading}
onChange={(e) => {
const updatedSigners = [...signers];
updatedSigners[index].name = e.target.value;
setSigners(updatedSigners);
}}
onBlur={() => { onBlur={() => {
item.documentId = props.document.id; if (!errors?.signers?.[index])
createOrUpdateRecipient(item); createOrUpdateRecipient({
...formValues[index],
documentId: props.document.id,
});
}} }}
onKeyDown={(event: any) => { onKeyDown={(event: any) => {
if (event.key === "Enter") if (
createOrUpdateRecipient(item); event.key === "Enter" &&
!errors?.signers?.[index]
)
createOrUpdateRecipient({
...formValues[index],
documentId: props.document.id,
});
}} }}
className="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 sm:text-sm outline-none bg-inherit" className="block w-full border-0 p-0 text-gray-900 placeholder-gray-500 sm:text-sm outline-none bg-inherit"
placeholder="John Dorian" placeholder="John Dorian"
@ -193,7 +246,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
{item.sendStatus === "NOT_SENT" ? ( {item.sendStatus === "NOT_SENT" ? (
<span <span
id="sent_icon" id="sent_icon"
className="inline-block flex-shrink-0 rounded-full bg-gray-200 px-2 py-0.5 text-xs font-medium text-gray-800" className="inline-block mt-3 flex-shrink-0 rounded-full bg-gray-200 px-2 py-0.5 text-xs font-medium text-gray-800"
> >
Not Sent Not Sent
</span> </span>
@ -205,7 +258,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
<span id="sent_icon"> <span id="sent_icon">
<span <span
id="sent_icon" id="sent_icon"
className="inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800" className="inline-block mt-3 flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800"
> >
<CheckIcon className="inline h-5 mr-1"></CheckIcon>{" "} <CheckIcon className="inline h-5 mr-1"></CheckIcon>{" "}
Sent Sent
@ -219,7 +272,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
<span id="read_icon"> <span id="read_icon">
<span <span
id="sent_icon" id="sent_icon"
className="inline-block flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800" className="inline-block mt-3 flex-shrink-0 rounded-full bg-yellow-200 px-2 py-0.5 text-xs font-medium text-gray-800"
> >
<CheckIcon className="inline h-5 -mr-2"></CheckIcon> <CheckIcon className="inline h-5 -mr-2"></CheckIcon>
<CheckIcon className="inline h-5 mr-1"></CheckIcon> <CheckIcon className="inline h-5 mr-1"></CheckIcon>
@ -233,7 +286,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
<span id="signed_icon"> <span id="signed_icon">
<span <span
id="sent_icon" id="sent_icon"
className="inline-block flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800" className="inline-block mt-3 flex-shrink-0 rounded-full bg-green-100 px-2 py-0.5 text-xs font-medium text-green-800"
> >
<CheckBadgeIcon className="inline h-5 mr-1"></CheckBadgeIcon> <CheckBadgeIcon className="inline h-5 mr-1"></CheckBadgeIcon>
Signed Signed
@ -274,14 +327,10 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
!item.id || item.sendStatus === "SENT" || loading !item.id || item.sendStatus === "SENT" || loading
} }
onClick={() => { onClick={() => {
const signersWithoutIndex = [...signers]; const removedItem = { ...fields }[index];
const removedItem = signersWithoutIndex.splice( remove(index);
index,
1
);
setSigners(signersWithoutIndex);
deleteRecipient(item)?.catch((err) => { deleteRecipient(item)?.catch((err) => {
setSigners(signersWithoutIndex.concat(removedItem)); append(removedItem);
}); });
}} }}
className="group-hover:text-neon-dark group-hover:disabled:text-gray-400" className="group-hover:text-neon-dark group-hover:disabled:text-gray-400"
@ -301,12 +350,14 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
name: "", name: "",
documentId: props.document.id, documentId: props.document.id,
}).then((res) => { }).then((res) => {
setSigners(signers.concat(res)); append(res);
}); });
}} }}
> >
Add Signer Add Signer
</Button> </Button>
</form>
</FormProvider>
</div> </div>
</div> </div>
<Transition.Root show={open} as={Fragment}> <Transition.Root show={open} as={Fragment}>
@ -357,7 +408,7 @@ const RecipientsPage: NextPageWithLayout = (props: any) => {
<div className="mt-2"> <div className="mt-2">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
{`"${props.document.title}" will be sent to ${ {`"${props.document.title}" will be sent to ${
signers.filter( formValues.filter(
(s: any) => s.email && s.sendStatus != "SENT" (s: any) => s.email && s.sendStatus != "SENT"
).length ).length
} recipients.`} } recipients.`}