fix: merge conflicts

This commit is contained in:
Ephraim Atta-Duncan
2025-10-14 10:36:29 +00:00
341 changed files with 16004 additions and 3442 deletions

View File

@ -1,5 +1,6 @@
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
@ -21,6 +22,7 @@ import { useFieldArray, useForm } from 'react-hook-form';
import { useHotkeys } from 'react-hotkeys-hook';
import { getBoundingClientRect } from '@documenso/lib/client-only/get-bounding-client-rect';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import { useDocumentElement } from '@documenso/lib/client-only/hooks/use-document-element';
import { PDF_VIEWER_PAGE_SELECTOR } from '@documenso/lib/constants/pdf-viewer';
import { RECIPIENT_ROLES_DESCRIPTION } from '@documenso/lib/constants/recipient-roles';
@ -60,7 +62,10 @@ import type { FieldFormType } from '../document-flow/add-fields';
import { FieldAdvancedSettings } from '../document-flow/field-item-advanced-settings';
import { Form } from '../form/form';
import { useStep } from '../stepper';
import type { TAddTemplateFieldsFormSchema } from './add-template-fields.types';
import {
type TAddTemplateFieldsFormSchema,
ZAddTemplateFieldsFormSchema,
} from './add-template-fields.types';
const MIN_HEIGHT_PX = 12;
const MIN_WIDTH_PX = 36;
@ -73,6 +78,7 @@ export type AddTemplateFieldsFormProps = {
recipients: Recipient[];
fields: Field[];
onSubmit: (_data: TAddTemplateFieldsFormSchema) => void;
onAutoSave: (_data: TAddTemplateFieldsFormSchema) => Promise<void>;
teamId: number;
};
@ -81,6 +87,7 @@ export const AddTemplateFieldsFormPartial = ({
recipients,
fields,
onSubmit,
onAutoSave,
teamId,
}: AddTemplateFieldsFormProps) => {
const { _ } = useLingui();
@ -109,7 +116,7 @@ export const AddTemplateFieldsFormPartial = ({
pageY: Number(field.positionY),
pageWidth: Number(field.width),
pageHeight: Number(field.height),
signerId: field.recipientId ?? -1,
recipientId: field.recipientId ?? -1,
signerEmail:
recipients.find((recipient) => recipient.id === field.recipientId)?.email ?? '',
signerToken:
@ -117,10 +124,25 @@ export const AddTemplateFieldsFormPartial = ({
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined,
})),
},
resolver: zodResolver(ZAddTemplateFieldsFormSchema),
});
const onFormSubmit = form.handleSubmit(onSubmit);
const { scheduleSave } = useAutoSave(onAutoSave);
const handleAutoSave = async () => {
const isFormValid = await form.trigger();
if (!isFormValid) {
return;
}
const formData = form.getValues();
scheduleSave(formData);
};
const {
append,
remove,
@ -153,13 +175,14 @@ export const AddTemplateFieldsFormPartial = ({
nativeId: undefined,
formId: nanoid(12),
signerEmail: selectedSigner?.email ?? lastActiveField.signerEmail,
signerId: selectedSigner?.id ?? lastActiveField.signerId,
recipientId: selectedSigner?.id ?? lastActiveField.recipientId,
signerToken: selectedSigner?.token ?? lastActiveField.signerToken,
pageX: lastActiveField.pageX + 3,
pageY: lastActiveField.pageY + 3,
};
append(newField);
void handleAutoSave();
return;
}
@ -179,7 +202,7 @@ export const AddTemplateFieldsFormPartial = ({
nativeId: undefined,
formId: nanoid(12),
signerEmail: selectedSigner?.email ?? lastActiveField.signerEmail,
signerId: selectedSigner?.id ?? lastActiveField.signerId,
recipientId: selectedSigner?.id ?? lastActiveField.recipientId,
signerToken: selectedSigner?.token ?? lastActiveField.signerToken,
pageNumber,
};
@ -187,6 +210,7 @@ export const AddTemplateFieldsFormPartial = ({
append(newField);
});
void handleAutoSave();
return;
}
@ -198,7 +222,15 @@ export const AddTemplateFieldsFormPartial = ({
});
}
},
[append, lastActiveField, selectedSigner?.email, selectedSigner?.id, toast],
[
append,
lastActiveField,
selectedSigner?.email,
selectedSigner?.id,
selectedSigner?.token,
toast,
handleAutoSave,
],
);
const onFieldPaste = useCallback(
@ -213,14 +245,23 @@ export const AddTemplateFieldsFormPartial = ({
formId: nanoid(12),
nativeId: undefined,
signerEmail: selectedSigner?.email ?? copiedField.signerEmail,
signerId: selectedSigner?.id ?? copiedField.signerId,
recipientId: selectedSigner?.id ?? copiedField.recipientId,
signerToken: selectedSigner?.token ?? copiedField.signerToken,
pageX: copiedField.pageX + 3,
pageY: copiedField.pageY + 3,
});
void handleAutoSave();
}
},
[append, fieldClipboard, selectedSigner?.email, selectedSigner?.id, selectedSigner?.token],
[
append,
fieldClipboard,
selectedSigner?.email,
selectedSigner?.id,
selectedSigner?.token,
handleAutoSave,
],
);
useHotkeys(['ctrl+c', 'meta+c'], (evt) => onFieldCopy(evt));
@ -335,7 +376,7 @@ export const AddTemplateFieldsFormPartial = ({
pageWidth: fieldPageWidth,
pageHeight: fieldPageHeight,
signerEmail: selectedSigner.email,
signerId: selectedSigner.id,
recipientId: selectedSigner.id,
signerToken: selectedSigner.token ?? '',
fieldMeta: undefined,
};
@ -378,8 +419,10 @@ export const AddTemplateFieldsFormPartial = ({
pageWidth,
pageHeight,
});
void handleAutoSave();
},
[getFieldPosition, localFields, update],
[getFieldPosition, localFields, update, handleAutoSave],
);
const onFieldMove = useCallback(
@ -401,8 +444,10 @@ export const AddTemplateFieldsFormPartial = ({
pageX,
pageY,
});
void handleAutoSave();
},
[getFieldPosition, localFields, update],
[getFieldPosition, localFields, update, handleAutoSave],
);
useEffect(() => {
@ -504,6 +549,7 @@ export const AddTemplateFieldsFormPartial = ({
});
form.setValue('fields', updatedFields);
void handleAutoSave();
};
return (
@ -519,6 +565,10 @@ export const AddTemplateFieldsFormPartial = ({
fields={localFields}
onAdvancedSettings={handleAdvancedSettings}
onSave={handleSavedFieldSettings}
onAutoSave={async (fieldState) => {
handleSavedFieldSettings(fieldState);
await handleAutoSave();
}}
/>
) : (
<>
@ -552,26 +602,36 @@ export const AddTemplateFieldsFormPartial = ({
)}
{localFields.map((field, index) => {
const recipientIndex = recipients.findIndex((r) => r.email === field.signerEmail);
const recipientIndex = recipients.findIndex((r) => r.id === field.recipientId);
return (
<FieldItem
key={index}
recipientIndex={recipientIndex === -1 ? 0 : recipientIndex}
field={field}
disabled={selectedSigner?.email !== field.signerEmail}
disabled={selectedSigner?.id !== field.recipientId}
minHeight={MIN_HEIGHT_PX}
minWidth={MIN_WIDTH_PX}
defaultHeight={DEFAULT_HEIGHT_PX}
defaultWidth={DEFAULT_WIDTH_PX}
passive={isFieldWithinBounds && !!selectedField}
onFocus={() => setLastActiveField(field)}
onBlur={() => setLastActiveField(null)}
onBlur={() => {
setLastActiveField(null);
void handleAutoSave();
}}
onResize={(options) => onFieldResize(options, index)}
onMove={(options) => onFieldMove(options, index)}
onRemove={() => remove(index)}
onDuplicate={() => onFieldCopy(null, { duplicate: true })}
onDuplicateAllPages={() => onFieldCopy(null, { duplicateAll: true })}
onRemove={() => {
remove(index);
void handleAutoSave();
}}
onDuplicate={() => {
onFieldCopy(null, { duplicate: true });
}}
onDuplicateAllPages={() => {
onFieldCopy(null, { duplicateAll: true });
}}
onAdvancedSettings={() => {
setCurrentField(field);
handleAdvancedSettings();

View File

@ -10,8 +10,8 @@ export const ZAddTemplateFieldsFormSchema = z.object({
nativeId: z.number().optional(),
type: z.nativeEnum(FieldType),
signerEmail: z.string().min(1),
recipientId: z.number().min(1),
signerToken: z.string(),
signerId: z.number().optional(),
pageNumber: z.number().min(1),
pageX: z.number().min(0),
pageY: z.number().min(0),

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useId, useMemo, useRef, useState } from 'react';
import { useCallback, useId, useMemo, useRef, useState } from 'react';
import type { DropResult, SensorAPI } from '@hello-pangea/dnd';
import { DragDropContext, Draggable, Droppable } from '@hello-pangea/dnd';
@ -12,6 +12,7 @@ import { motion } from 'framer-motion';
import { GripVerticalIcon, HelpCircle, Link2Icon, Plus, Trash } from 'lucide-react';
import { useFieldArray, useForm } from 'react-hook-form';
import { useAutoSave } from '@documenso/lib/client-only/hooks/use-autosave';
import { useCurrentOrganisation } from '@documenso/lib/client-only/providers/organisation';
import { useSession } from '@documenso/lib/client-only/providers/session';
import { isTemplateRecipientEmailPlaceholder } from '@documenso/lib/constants/template';
@ -47,6 +48,10 @@ import { Tooltip, TooltipContent, TooltipTrigger } from '../tooltip';
import type { TAddTemplatePlacholderRecipientsFormSchema } from './add-template-placeholder-recipients.types';
import { ZAddTemplatePlacholderRecipientsFormSchema } from './add-template-placeholder-recipients.types';
type AutoSaveResponse = {
recipients: Recipient[];
};
export type AddTemplatePlaceholderRecipientsFormProps = {
documentFlow: DocumentFlowStep;
recipients: Recipient[];
@ -55,6 +60,7 @@ export type AddTemplatePlaceholderRecipientsFormProps = {
allowDictateNextSigner?: boolean;
templateDirectLink?: TemplateDirectLink | null;
onSubmit: (_data: TAddTemplatePlacholderRecipientsFormSchema) => void;
onAutoSave: (_data: TAddTemplatePlacholderRecipientsFormSchema) => Promise<AutoSaveResponse>;
isDocumentPdfLoaded: boolean;
};
@ -67,6 +73,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
allowDictateNextSigner,
isDocumentPdfLoaded,
onSubmit,
onAutoSave,
}: AddTemplatePlaceholderRecipientsFormProps) => {
const initialId = useId();
const $sensorApi = useRef<SensorAPI | null>(null);
@ -123,15 +130,75 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
},
});
useEffect(() => {
form.reset({
signers: generateDefaultFormSigners(),
signingOrder: signingOrder || DocumentSigningOrder.PARALLEL,
allowDictateNextSigner: allowDictateNextSigner ?? false,
});
const emptySigners = useCallback(
() => form.getValues('signers').filter((signer) => signer.email === ''),
[form],
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [recipients]);
const { scheduleSave } = useAutoSave(onAutoSave);
const handleAutoSave = async () => {
if (emptySigners().length > 0) {
return;
}
const isFormValid = await form.trigger();
if (!isFormValid) {
return;
}
const formData = form.getValues();
scheduleSave(formData, (response) => {
// Sync the response recipients back to form state to prevent duplicates
if (response?.recipients) {
const currentSigners = form.getValues('signers');
const updatedSigners = currentSigners.map((signer) => {
// Find the matching recipient from the response using nativeId
const matchingRecipient = response.recipients.find(
(recipient) => recipient.id === signer.nativeId,
);
if (matchingRecipient) {
// Update the signer with the server-returned data, especially the ID
return {
...signer,
nativeId: matchingRecipient.id,
};
}
// For new signers without nativeId, match by email and update with server ID
if (!signer.nativeId) {
const newRecipient = response.recipients.find(
(recipient) => recipient.email === signer.email,
);
if (newRecipient) {
return {
...signer,
nativeId: newRecipient.id,
};
}
}
return signer;
});
// Update the form state with the synced data
form.setValue('signers', updatedSigners, { shouldValidate: false });
}
});
};
// useEffect(() => {
// form.reset({
// signers: generateDefaultFormSigners(),
// signingOrder: signingOrder || DocumentSigningOrder.PARALLEL,
// allowDictateNextSigner: allowDictateNextSigner ?? false,
// });
// // eslint-disable-next-line react-hooks/exhaustive-deps
// }, [recipients]);
// Always show advanced settings if any recipient has auth options.
const alwaysShowAdvancedSettings = useMemo(() => {
@ -204,7 +271,12 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
const onRemoveSigner = (index: number) => {
removeSigner(index);
const updatedSigners = signers.filter((_, idx) => idx !== index);
form.setValue('signers', normalizeSigningOrders(updatedSigners));
form.setValue('signers', normalizeSigningOrders(updatedSigners), {
shouldValidate: true,
shouldDirty: true,
});
void handleAutoSave();
};
const isSignerDirectRecipient = (
@ -231,7 +303,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
signingOrder: index + 1,
}));
form.setValue('signers', updatedSigners);
form.setValue('signers', updatedSigners, {
shouldValidate: true,
shouldDirty: true,
});
const lastSigner = updatedSigners[updatedSigners.length - 1];
if (lastSigner.role === RecipientRole.ASSISTANT) {
@ -244,8 +319,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
}
await form.trigger('signers');
void handleAutoSave();
},
[form, watchedSigners, toast],
[form, watchedSigners, toast, handleAutoSave],
);
const handleSigningOrderChange = useCallback(
@ -273,7 +350,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
signingOrder: idx + 1,
}));
form.setValue('signers', updatedSigners);
form.setValue('signers', updatedSigners, {
shouldValidate: true,
shouldDirty: true,
});
if (signer.role === RecipientRole.ASSISTANT && newPosition === remainingSigners.length - 1) {
toast({
@ -283,8 +363,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
),
});
}
void handleAutoSave();
},
[form, toast],
[form, toast, handleAutoSave],
);
const handleRoleChange = useCallback(
@ -294,7 +376,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
// Handle parallel to sequential conversion for assistants
if (role === RecipientRole.ASSISTANT && signingOrder === DocumentSigningOrder.PARALLEL) {
form.setValue('signingOrder', DocumentSigningOrder.SEQUENTIAL);
form.setValue('signingOrder', DocumentSigningOrder.SEQUENTIAL, {
shouldValidate: true,
shouldDirty: true,
});
toast({
title: _(msg`Signing order is enabled.`),
description: _(msg`You cannot add assistants when signing order is disabled.`),
@ -309,7 +394,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
signingOrder: idx + 1,
}));
form.setValue('signers', updatedSigners);
form.setValue('signers', updatedSigners, {
shouldValidate: true,
shouldDirty: true,
});
if (role === RecipientRole.ASSISTANT && index === updatedSigners.length - 1) {
toast({
@ -319,8 +407,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
),
});
}
void handleAutoSave();
},
[form, toast],
[form, toast, handleAutoSave],
);
const [showSigningOrderConfirmation, setShowSigningOrderConfirmation] = useState(false);
@ -334,10 +424,21 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
role: signer.role === RecipientRole.ASSISTANT ? RecipientRole.SIGNER : signer.role,
}));
form.setValue('signers', updatedSigners);
form.setValue('signingOrder', DocumentSigningOrder.PARALLEL);
form.setValue('allowDictateNextSigner', false);
}, [form]);
form.setValue('signers', updatedSigners, {
shouldValidate: true,
shouldDirty: true,
});
form.setValue('signingOrder', DocumentSigningOrder.PARALLEL, {
shouldValidate: true,
shouldDirty: true,
});
form.setValue('allowDictateNextSigner', false, {
shouldValidate: true,
shouldDirty: true,
});
void handleAutoSave();
}, [form, handleAutoSave]);
return (
<>
@ -382,8 +483,13 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
// If sequential signing is turned off, disable dictate next signer
if (!checked) {
form.setValue('allowDictateNextSigner', false);
form.setValue('allowDictateNextSigner', false, {
shouldValidate: true,
shouldDirty: true,
});
}
void handleAutoSave();
}}
disabled={isSubmitting}
/>
@ -409,7 +515,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
{...field}
id="allowDictateNextSigner"
checked={value}
onCheckedChange={field.onChange}
onCheckedChange={(checked) => {
field.onChange(checked);
void handleAutoSave();
}}
disabled={isSubmitting || !isSigningOrderSequential}
/>
</FormControl>
@ -500,6 +609,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
<Input
type="number"
max={signers.length}
data-testid="placeholder-recipient-signing-order-input"
className={cn(
'w-full text-center',
'[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',
@ -558,6 +668,9 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
signers[index].email === user?.email ||
isSignerDirectRecipient(signer)
}
maxLength={254}
onBlur={handleAutoSave}
data-testid="placeholder-recipient-email-input"
/>
</FormControl>
@ -592,6 +705,9 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
signers[index].email === user?.email ||
isSignerDirectRecipient(signer)
}
maxLength={255}
onBlur={handleAutoSave}
data-testid="placeholder-recipient-name-input"
/>
</FormControl>
@ -633,10 +749,10 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
<FormControl>
<RecipientRoleSelect
{...field}
onValueChange={(value) =>
onValueChange={(value) => {
// eslint-disable-next-line @typescript-eslint/consistent-type-assertions
handleRoleChange(index, value as RecipientRole)
}
handleRoleChange(index, value as RecipientRole);
}}
disabled={isSubmitting}
hideCCRecipients={isSignerDirectRecipient(signer)}
/>
@ -672,6 +788,7 @@ export const AddTemplatePlaceholderRecipientsFormPartial = ({
className="col-span-1 mt-auto inline-flex h-10 w-10 items-center justify-center text-slate-500 hover:opacity-80 disabled:cursor-not-allowed disabled:opacity-50"
disabled={isSubmitting || signers.length === 1}
onClick={() => onRemoveSigner(index)}
data-testid="remove-placeholder-recipient-button"
>
<Trash className="h-5 w-5" />
</button>

View File

@ -1,7 +1,6 @@
import { DocumentSigningOrder, RecipientRole } from '@prisma/client';
import { z } from 'zod';
import { TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX } from '@documenso/lib/constants/template';
import { ZRecipientActionAuthTypesSchema } from '@documenso/lib/types/document-auth';
export const ZAddTemplatePlacholderRecipientsFormSchema = z
@ -20,17 +19,7 @@ export const ZAddTemplatePlacholderRecipientsFormSchema = z
signingOrder: z.nativeEnum(DocumentSigningOrder),
allowDictateNextSigner: z.boolean().default(false),
})
.refine(
(schema) => {
const nonPlaceholderEmails = schema.signers
.map((signer) => signer.email.toLowerCase())
.filter((email) => !TEMPLATE_RECIPIENT_EMAIL_PLACEHOLDER_REGEX.test(email));
return new Set(nonPlaceholderEmails).size === nonPlaceholderEmails.length;
},
// Dirty hack to handle errors when .root is populated for an array type
{ message: 'Signers must have unique emails', path: ['signers__root'] },
)
.refine(
/*
Since placeholder emails are empty, we need to check that the names are unique.

View File

@ -9,6 +9,7 @@ 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 {
@ -83,6 +84,7 @@ export type AddTemplateSettingsFormProps = {
template: TTemplate;
currentTeamMemberRole?: TeamMemberRole;
onSubmit: (_data: TAddTemplateSettingsFormSchema) => void;
onAutoSave: (_data: TAddTemplateSettingsFormSchema) => Promise<void>;
};
export const AddTemplateSettingsFormPartial = ({
@ -93,6 +95,7 @@ export const AddTemplateSettingsFormPartial = ({
template,
currentTeamMemberRole,
onSubmit,
onAutoSave,
}: AddTemplateSettingsFormProps) => {
const { t, i18n } = useLingui();
@ -160,6 +163,28 @@ export const AddTemplateSettingsFormPartial = ({
}
}, [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 (
<>
<DocumentFlowFormContainerHeader
@ -191,7 +216,12 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Input className="bg-background" {...field} />
<Input
className="bg-background"
{...field}
maxLength={255}
onBlur={handleAutoSave}
/>
</FormControl>
<FormMessage />
</FormItem>
@ -219,7 +249,13 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<Select
{...field}
onValueChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
>
<SelectTrigger className="bg-background">
<SelectValue />
</SelectTrigger>
@ -250,9 +286,13 @@ export const AddTemplateSettingsFormPartial = ({
<FormControl>
<DocumentGlobalAuthAccessSelect
{...field}
onValueChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
value={field.value}
disabled={field.disabled}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
@ -275,7 +315,10 @@ export const AddTemplateSettingsFormPartial = ({
canUpdateVisibility={canUpdateVisibility}
currentTeamMemberRole={currentTeamMemberRole}
{...field}
onValueChange={field.onChange}
onValueChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
/>
</FormControl>
</FormItem>
@ -334,7 +377,13 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<Select
{...field}
onValueChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
>
<SelectTrigger className="bg-background text-muted-foreground">
<SelectValue data-testid="documentDistributionMethodSelectValue" />
</SelectTrigger>
@ -371,7 +420,10 @@ export const AddTemplateSettingsFormPartial = ({
value: option.value,
}))}
selectedValues={field.value}
onChange={field.onChange}
onChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
className="bg-background w-full"
emptySelectionPlaceholder="Select signature types"
/>
@ -395,9 +447,13 @@ export const AddTemplateSettingsFormPartial = ({
<FormControl>
<DocumentGlobalAuthActionSelect
{...field}
onValueChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
value={field.value}
disabled={field.disabled}
onValueChange={field.onChange}
/>
</FormControl>
</FormItem>
@ -468,7 +524,7 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Input {...field} />
<Input {...field} maxLength={254} />
</FormControl>
<FormMessage />
@ -488,7 +544,7 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Input {...field} />
<Input {...field} maxLength={254} onBlur={handleAutoSave} />
</FormControl>
<FormMessage />
@ -515,7 +571,12 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Textarea className="bg-background h-16 resize-none" {...field} />
<Textarea
className="bg-background h-16 resize-none"
{...field}
maxLength={5000}
onBlur={handleAutoSave}
/>
</FormControl>
<FormMessage />
@ -525,7 +586,12 @@ export const AddTemplateSettingsFormPartial = ({
<DocumentEmailCheckboxes
value={emailSettings}
onChange={(value) => form.setValue('meta.emailSettings', value)}
onChange={(value) => {
form.setValue('meta.emailSettings', value, {
shouldDirty: true,
});
void handleAutoSave();
}}
/>
</div>
</AccordionContent>
@ -563,7 +629,12 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Input className="bg-background" {...field} />
<Input
className="bg-background"
{...field}
maxLength={255}
onBlur={handleAutoSave}
/>
</FormControl>
<FormMessage />
@ -581,7 +652,13 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<Select
{...field}
onValueChange={(value) => {
field.onChange(value);
void handleAutoSave();
}}
>
<SelectTrigger className="bg-background">
<SelectValue />
</SelectTrigger>
@ -615,7 +692,10 @@ export const AddTemplateSettingsFormPartial = ({
className="bg-background time-zone-field"
options={TIME_ZONES}
{...field}
onChange={(value) => value && field.onChange(value)}
onChange={(value) => {
value && field.onChange(value);
void handleAutoSave();
}}
/>
</FormControl>
@ -645,7 +725,12 @@ export const AddTemplateSettingsFormPartial = ({
</FormLabel>
<FormControl>
<Input className="bg-background" {...field} />
<Input
className="bg-background"
{...field}
maxLength={255}
onBlur={handleAutoSave}
/>
</FormControl>
<FormMessage />