import { useEffect, useState } from 'react'; import { useRouter } from 'next/navigation'; import { zodResolver } from '@hookform/resolvers/zod'; import { Trans, msg } from '@lingui/macro'; import { useLingui } from '@lingui/react'; import { InfoIcon, Plus } from 'lucide-react'; import { useFieldArray, useForm } from 'react-hook-form'; import * as z from 'zod'; import { TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX, TEMPLATE_RECIPIENT_NAME_PLACEHOLDER_REGEX, } from '@documenso/lib/constants/template'; import { AppError } from '@documenso/lib/errors/app-error'; import type { Recipient } from '@documenso/prisma/client'; import { trpc } from '@documenso/trpc/react'; import { Button } from '@documenso/ui/primitives/button'; import { Checkbox } from '@documenso/ui/primitives/checkbox'; import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from '@documenso/ui/primitives/dialog'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { Input } from '@documenso/ui/primitives/input'; import { Tooltip, TooltipContent, TooltipTrigger } from '@documenso/ui/primitives/tooltip'; import type { Toast } from '@documenso/ui/primitives/use-toast'; import { useToast } from '@documenso/ui/primitives/use-toast'; import { useOptionalCurrentTeam } from '~/providers/team'; const ZAddRecipientsForNewDocumentSchema = z .object({ sendDocument: z.boolean(), recipients: z.array( z.object({ id: z.number(), email: z.string().email(), name: z.string(), }), ), }) // Display exactly which rows are duplicates. .superRefine((items, ctx) => { const uniqueEmails = new Map(); for (const [index, recipients] of items.recipients.entries()) { const email = recipients.email.toLowerCase(); const firstFoundIndex = uniqueEmails.get(email); if (firstFoundIndex === undefined) { uniqueEmails.set(email, index); continue; } ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Emails must be unique', path: ['recipients', index, 'email'], }); ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Emails must be unique', path: ['recipients', firstFoundIndex, 'email'], }); } }); type TAddRecipientsForNewDocumentSchema = z.infer; export type UseTemplateDialogProps = { templateId: number; recipients: Recipient[]; documentRootPath: string; }; export function UseTemplateDialog({ recipients, documentRootPath, templateId, }: UseTemplateDialogProps) { const router = useRouter(); const { toast } = useToast(); const { _ } = useLingui(); const [open, setOpen] = useState(false); const team = useOptionalCurrentTeam(); const form = useForm({ resolver: zodResolver(ZAddRecipientsForNewDocumentSchema), defaultValues: { sendDocument: false, recipients: recipients.map((recipient) => { const isRecipientEmailPlaceholder = recipient.email.match( TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX, ); const isRecipientNamePlaceholder = recipient.name.match( TEMPLATE_RECIPIENT_NAME_PLACEHOLDER_REGEX, ); return { id: recipient.id, name: !isRecipientNamePlaceholder ? recipient.name : '', email: !isRecipientEmailPlaceholder ? recipient.email : '', }; }), }, }); const { mutateAsync: createDocumentFromTemplate } = trpc.template.createDocumentFromTemplate.useMutation(); const onSubmit = async (data: TAddRecipientsForNewDocumentSchema) => { try { const { id } = await createDocumentFromTemplate({ templateId, teamId: team?.id, recipients: data.recipients, sendDocument: data.sendDocument, }); toast({ title: _(msg`Document created`), description: _(msg`Your document has been created from the template successfully.`), duration: 5000, }); router.push(`${documentRootPath}/${id}`); } catch (err) { const error = AppError.parseError(err); const toastPayload: Toast = { title: _(msg`Error`), description: _(msg`An error occurred while creating document from template.`), variant: 'destructive', }; if (error.code === 'DOCUMENT_SEND_FAILED') { toastPayload.description = _( msg`The document was created but could not be sent to recipients.`, ); } toast(toastPayload); } }; const { fields: formRecipients } = useFieldArray({ control: form.control, name: 'recipients', }); useEffect(() => { if (!open) { form.reset(); } }, [open, form]); return ( !form.formState.isSubmitting && setOpen(value)}> Create document from template {recipients.length === 0 ? ( A draft document will be created ) : ( Add the recipients to create the document with )}
{formRecipients.map((recipient, index) => (
( {index === 0 && ( Email )} )} /> ( {index === 0 && ( Name )} )} />
))}
{recipients.length > 0 && (
(
)} />
)}
); }