fix: envelope autosave (#2103)

This commit is contained in:
David Nguyen
2025-10-27 19:53:35 +11:00
committed by GitHub
parent 35250fa308
commit b0b07106b4
9 changed files with 107 additions and 33 deletions

View File

@ -50,6 +50,7 @@ type UseEditorFieldsResponse = {
// Field operations
addField: (field: Omit<TLocalField, 'formId'>) => TLocalField;
setFieldId: (formId: string, id: number) => void;
removeFieldsByFormId: (formIds: string[]) => void;
updateFieldByFormId: (formId: string, updates: Partial<TLocalField>) => void;
duplicateField: (field: TLocalField, recipientId?: number) => TLocalField;
@ -160,6 +161,17 @@ export const useEditorFields = ({
[localFields, remove, triggerFieldsUpdate],
);
const setFieldId = (formId: string, id: number) => {
const index = localFields.findIndex((field) => field.formId === formId);
if (index !== -1) {
update(index, {
...localFields[index],
id,
});
}
};
const updateFieldByFormId = useCallback(
(formId: string, updates: Partial<TLocalField>) => {
const index = localFields.findIndex((field) => field.formId === formId);
@ -269,6 +281,7 @@ export const useEditorFields = ({
// Field operations
addField,
setFieldId,
removeFieldsByFormId,
updateFieldByFormId,
duplicateField,

View File

@ -97,6 +97,11 @@ export const EnvelopeEditorProvider = ({
const [envelope, setEnvelope] = useState(initialEnvelope);
const [autosaveError, setAutosaveError] = useState<boolean>(false);
const editorFields = useEditorFields({
envelope,
handleFieldsUpdate: (fields) => setFieldsDebounced(fields),
});
const envelopeUpdateMutationQuery = trpc.envelope.update.useMutation({
onSuccess: (response, input) => {
setEnvelope({
@ -184,13 +189,24 @@ export const EnvelopeEditorProvider = ({
triggerSave: setFieldsDebounced,
flush: setFieldsAsync,
isPending: isFieldsMutationPending,
} = useEnvelopeAutosave(async (fields: TLocalField[]) => {
await envelopeFieldSetMutationQuery.mutateAsync({
} = useEnvelopeAutosave(async (localFields: TLocalField[]) => {
const envelopeFields = await envelopeFieldSetMutationQuery.mutateAsync({
envelopeId: envelope.id,
envelopeType: envelope.type,
fields,
fields: localFields,
});
}, 1000);
// Insert the IDs into the local fields.
envelopeFields.fields.forEach((field) => {
const localField = localFields.find((localField) => localField.formId === field.formId);
if (localField && !localField.id) {
localField.id = field.id;
editorFields.setFieldId(localField.formId, field.id);
}
});
}, 2000);
const {
triggerSave: setEnvelopeDebounced,
@ -221,11 +237,6 @@ export const EnvelopeEditorProvider = ({
setEnvelopeDebounced(envelopeUpdates);
};
const editorFields = useEditorFields({
envelope,
handleFieldsUpdate: (fields) => setFieldsDebounced(fields),
});
const getRecipientColorKey = useCallback(
(recipientId: number) => {
const recipientIndex = envelope.recipients.findIndex(

View File

@ -306,7 +306,10 @@ export const setFieldsForDocument = async ({
});
}
return upsertedField;
return {
...upsertedField,
formId: field.formId,
};
}),
);
});
@ -340,17 +343,25 @@ export const setFieldsForDocument = async ({
}
// Filter out fields that have been removed or have been updated.
const filteredFields = existingFields.filter((field) => {
const isRemoved = removedFields.find((removedField) => removedField.id === field.id);
const isUpdated = persistedFields.find((persistedField) => persistedField.id === field.id);
const mappedFilteredFields = existingFields
.filter((field) => {
const isRemoved = removedFields.find((removedField) => removedField.id === field.id);
const isUpdated = persistedFields.find((persistedField) => persistedField.id === field.id);
return !isRemoved && !isUpdated;
});
return !isRemoved && !isUpdated;
})
.map((field) => ({
...mapFieldToLegacyField(field, envelope),
formId: undefined,
}));
const mappedPersistentFields = persistedFields.map((field) => ({
...mapFieldToLegacyField(field, envelope),
formId: field?.formId,
}));
return {
fields: [...filteredFields, ...persistedFields].map((field) =>
mapFieldToLegacyField(field, envelope),
),
fields: [...mappedFilteredFields, ...mappedPersistentFields],
};
};
@ -359,6 +370,7 @@ export const setFieldsForDocument = async ({
*/
type FieldData = {
id?: number | null;
formId?: string;
envelopeItemId: string;
type: FieldType;
recipientId: number;

View File

@ -27,6 +27,7 @@ export type SetFieldsForTemplateOptions = {
id: EnvelopeIdOptions;
fields: {
id?: number | null;
formId?: string;
envelopeItemId: string;
type: FieldType;
recipientId: number;
@ -111,10 +112,10 @@ export const setFieldsForTemplate = async ({
};
});
const persistedFields = await prisma.$transaction(
const persistedFields = await Promise.all(
// Disabling as wrapping promises here causes type issues
// eslint-disable-next-line @typescript-eslint/promise-function-async
linkedFields.map((field) => {
linkedFields.map(async (field) => {
const parsedFieldMeta = field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : undefined;
if (field.type === FieldType.TEXT && field.fieldMeta) {
@ -176,7 +177,7 @@ export const setFieldsForTemplate = async ({
}
// Proceed with upsert operation
return prisma.field.upsert({
const upsertedField = await prisma.field.upsert({
where: {
id: field._persisted?.id ?? -1,
envelopeId: envelope.id,
@ -219,6 +220,11 @@ export const setFieldsForTemplate = async ({
},
},
});
return {
...upsertedField,
formId: field.formId,
};
}),
);
@ -240,9 +246,17 @@ export const setFieldsForTemplate = async ({
return !isRemoved && !isUpdated;
});
const mappedFilteredFields = filteredFields.map((field) => ({
...mapFieldToLegacyField(field, envelope),
formId: undefined,
}));
const mappedPersistentFields = persistedFields.map((field) => ({
...mapFieldToLegacyField(field, envelope),
formId: field?.formId,
}));
return {
fields: [...filteredFields, ...persistedFields].map((field) =>
mapFieldToLegacyField(field, envelope),
),
fields: [...mappedFilteredFields, ...mappedPersistentFields],
};
};