import { useEffect } from 'react'; import { zodResolver } from '@hookform/resolvers/zod'; import { useLingui } from '@lingui/react/macro'; import { Trans } from '@lingui/react/macro'; import { DocumentVisibility, TeamMemberRole } from '@prisma/client'; import { DocumentDistributionMethod, type Field, type Recipient } from '@prisma/client'; import { InfoIcon } from 'lucide-react'; import { useForm } from 'react-hook-form'; import { match } from 'ts-pattern'; import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave'; import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation'; import { DATE_FORMATS, DEFAULT_DOCUMENT_DATE_FORMAT } from '@documenso/lib/constants/date-formats'; import { DOCUMENT_DISTRIBUTION_METHODS, DOCUMENT_SIGNATURE_TYPES, } from '@documenso/lib/constants/document'; import { SUPPORTED_LANGUAGES } from '@documenso/lib/constants/i18n'; import { DEFAULT_DOCUMENT_TIME_ZONE, TIME_ZONES } from '@documenso/lib/constants/time-zones'; import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email'; import type { TTemplate } from '@documenso/lib/types/template'; import { extractDocumentAuthMethods } from '@documenso/lib/utils/document-auth'; import { extractTeamSignatureSettings } from '@documenso/lib/utils/teams'; import { trpc } from '@documenso/trpc/react'; import type { TDocumentMetaDateFormat } from '@documenso/trpc/server/document-router/schema'; import { DocumentGlobalAuthAccessSelect, DocumentGlobalAuthAccessTooltip, } from '@documenso/ui/components/document/document-global-auth-access-select'; import { DocumentGlobalAuthActionSelect, DocumentGlobalAuthActionTooltip, } from '@documenso/ui/components/document/document-global-auth-action-select'; import { DocumentSendEmailMessageHelper } from '@documenso/ui/components/document/document-send-email-message-helper'; import { DocumentVisibilitySelect, DocumentVisibilityTooltip, } from '@documenso/ui/components/document/document-visibility-select'; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from '@documenso/ui/primitives/accordion'; import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from '@documenso/ui/primitives/form/form'; import { DocumentEmailCheckboxes } from '../../components/document/document-email-checkboxes'; import { DocumentReadOnlyFields, mapFieldsWithRecipients, } from '../../components/document/document-read-only-fields'; import { DocumentSignatureSettingsTooltip } from '../../components/document/document-signature-settings-tooltip'; import { Combobox } from '../combobox'; import { DocumentFlowFormContainerActions, DocumentFlowFormContainerContent, DocumentFlowFormContainerFooter, DocumentFlowFormContainerHeader, DocumentFlowFormContainerStep, } from '../document-flow/document-flow-root'; import type { DocumentFlowStep } from '../document-flow/types'; import { Input } from '../input'; import { MultiSelectCombobox } from '../multi-select-combobox'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../select'; import { useStep } from '../stepper'; import { Textarea } from '../textarea'; import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip'; import type { TAddTemplateSettingsFormSchema } from './add-template-settings.types'; import { ZAddTemplateSettingsFormSchema } from './add-template-settings.types'; export type AddTemplateSettingsFormProps = { documentFlow: DocumentFlowStep; recipients: Recipient[]; fields: Field[]; isDocumentPdfLoaded: boolean; template: TTemplate; currentTeamMemberRole?: TeamMemberRole; onSubmit: (_data: TAddTemplateSettingsFormSchema) => void; onAutoSave: (_data: TAddTemplateSettingsFormSchema) => Promise; }; export const AddTemplateSettingsFormPartial = ({ documentFlow, recipients, fields, isDocumentPdfLoaded, template, currentTeamMemberRole, onSubmit, onAutoSave, }: AddTemplateSettingsFormProps) => { const { t, i18n } = useLingui(); const organisation = useCurrentOrganisation(); const { documentAuthOption } = extractDocumentAuthMethods({ documentAuth: template.authOptions, }); const form = useForm({ resolver: zodResolver(ZAddTemplateSettingsFormSchema), defaultValues: { title: template.title, externalId: template.externalId || undefined, visibility: template.visibility || '', globalAccessAuth: documentAuthOption?.globalAccessAuth || [], globalActionAuth: documentAuthOption?.globalActionAuth || [], meta: { subject: template.templateMeta?.subject ?? '', message: template.templateMeta?.message ?? '', timezone: template.templateMeta?.timezone ?? DEFAULT_DOCUMENT_TIME_ZONE, // eslint-disable-next-line @typescript-eslint/consistent-type-assertions dateFormat: (template.templateMeta?.dateFormat ?? DEFAULT_DOCUMENT_DATE_FORMAT) as TDocumentMetaDateFormat, distributionMethod: template.templateMeta?.distributionMethod || DocumentDistributionMethod.EMAIL, redirectUrl: template.templateMeta?.redirectUrl ?? '', language: template.templateMeta?.language ?? 'en', emailId: template.templateMeta?.emailId ?? null, emailReplyTo: template.templateMeta?.emailReplyTo ?? undefined, emailSettings: ZDocumentEmailSettingsSchema.parse(template?.templateMeta?.emailSettings), signatureTypes: extractTeamSignatureSettings(template?.templateMeta), }, }, }); const { stepIndex, currentStep, totalSteps, previousStep } = useStep(); const distributionMethod = form.watch('meta.distributionMethod'); const emailSettings = form.watch('meta.emailSettings'); const { data: emailData, isLoading: isLoadingEmails } = trpc.enterprise.organisation.email.find.useQuery({ organisationId: organisation.id, perPage: 100, }); const emails = emailData?.data || []; const canUpdateVisibility = match(currentTeamMemberRole) .with(TeamMemberRole.ADMIN, () => true) .with( TeamMemberRole.MANAGER, () => template.visibility === DocumentVisibility.EVERYONE || template.visibility === DocumentVisibility.MANAGER_AND_ABOVE, ) .otherwise(() => false); // We almost always want to set the timezone to the user's local timezone to avoid confusion // when the document is signed. useEffect(() => { if (!form.formState.touchedFields.meta?.timezone && !template.templateMeta?.timezone) { form.setValue('meta.timezone', Intl.DateTimeFormat().resolvedOptions().timeZone); } }, [form, form.setValue, form.formState.touchedFields.meta?.timezone]); const { scheduleSave } = useAutoSave(onAutoSave); const handleAutoSave = async () => { const isFormValid = await form.trigger(); if (!isFormValid) { return; } const formData = form.getValues(); /* * Parse the form data through the Zod schema to handle transformations * (like -1 -> undefined for the Document Global Auth Access) */ const parseResult = ZAddTemplateSettingsFormSchema.safeParse(formData); if (parseResult.success) { scheduleSave(parseResult.data); } }; return ( <> {isDocumentPdfLoaded && ( recipient.id)} fields={mapFieldsWithRecipients(fields, recipients)} /> )}
( Template title )} /> ( Language Controls the language for the document, including the language to be used for email notifications, and the final certificate that is generated and attached to the document. )} /> ( Document access { field.onChange(value); void handleAutoSave(); }} value={field.value} disabled={field.disabled} /> )} /> {currentTeamMemberRole && ( ( Document visibility { field.onChange(value); void handleAutoSave(); }} /> )} /> )} ( Document Distribution Method

Document Distribution Method

This is how the document will reach the recipients once the document is ready for signing.

  • Email - The recipient will be emailed the document to sign, approve, etc.
  • None - We will generate links which you can send to the recipients manually.
Note - If you use Links in combination with direct templates, you will need to manually send the links to the remaining recipients.
)} /> ( Allowed Signature Types ({ label: t(option.label), value: option.value, }))} selectedValues={field.value} onChange={(value) => { field.onChange(value); void handleAutoSave(); }} className="bg-background w-full" emptySelectionPlaceholder="Select signature types" /> )} /> {organisation.organisationClaim.flags.cfr21 && ( ( Recipient action authentication { field.onChange(value); void handleAutoSave(); }} value={field.value} disabled={field.disabled} /> )} /> )} {distributionMethod === DocumentDistributionMethod.EMAIL && ( Email Options
{organisation.organisationClaim.flags.emailDomains && ( ( Email Sender )} /> )} ( Reply To Email{' '} (Optional) )} /> ( Subject (Optional) )} /> ( Message{' '} (Optional)