mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 08:42:12 +10:00
feat: add support for attachments in template management
- Enhanced TemplateEditForm to include attachments in the template data. - Updated createDocumentFromTemplate to handle attachment creation. - Modified updateTemplate to manage attachment updates and deletions. - Integrated attachments into ZTemplateSchema and ZAddTemplateSettingsFormSchema for validation. - Improved getTemplateById to fetch attachments alongside other template data.
This commit is contained in:
@ -136,6 +136,7 @@ export const TemplateEditForm = ({
|
|||||||
visibility: data.visibility,
|
visibility: data.visibility,
|
||||||
globalAccessAuth: data.globalAccessAuth ?? null,
|
globalAccessAuth: data.globalAccessAuth ?? null,
|
||||||
globalActionAuth: data.globalActionAuth ?? null,
|
globalActionAuth: data.globalActionAuth ?? null,
|
||||||
|
attachments: data.attachments ?? [],
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
...data.meta,
|
...data.meta,
|
||||||
@ -165,6 +166,14 @@ export const TemplateEditForm = ({
|
|||||||
await Promise.all([
|
await Promise.all([
|
||||||
updateTemplateSettings({
|
updateTemplateSettings({
|
||||||
templateId: template.id,
|
templateId: template.id,
|
||||||
|
data: {
|
||||||
|
attachments: template.attachments?.map((attachment) => ({
|
||||||
|
id: attachment.id,
|
||||||
|
label: attachment.label,
|
||||||
|
url: attachment.url,
|
||||||
|
formId: attachment.id,
|
||||||
|
})),
|
||||||
|
},
|
||||||
meta: {
|
meta: {
|
||||||
signingOrder: data.signingOrder,
|
signingOrder: data.signingOrder,
|
||||||
allowDictateNextSigner: data.allowDictateNextSigner,
|
allowDictateNextSigner: data.allowDictateNextSigner,
|
||||||
|
|||||||
@ -296,6 +296,7 @@ export const createDocumentFromTemplate = async ({
|
|||||||
fields: true,
|
fields: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
attachments: true,
|
||||||
templateDocumentData: true,
|
templateDocumentData: true,
|
||||||
templateMeta: true,
|
templateMeta: true,
|
||||||
team: {
|
team: {
|
||||||
@ -385,6 +386,15 @@ export const createDocumentFromTemplate = async ({
|
|||||||
}),
|
}),
|
||||||
visibility: template.visibility || template.team?.teamGlobalSettings?.documentVisibility,
|
visibility: template.visibility || template.team?.teamGlobalSettings?.documentVisibility,
|
||||||
useLegacyFieldInsertion: template.useLegacyFieldInsertion ?? false,
|
useLegacyFieldInsertion: template.useLegacyFieldInsertion ?? false,
|
||||||
|
attachments: {
|
||||||
|
create: template.attachments.map((attachment) => ({
|
||||||
|
type: attachment.type,
|
||||||
|
label: attachment.label,
|
||||||
|
url: attachment.url,
|
||||||
|
createdAt: attachment.createdAt,
|
||||||
|
updatedAt: attachment.updatedAt,
|
||||||
|
})),
|
||||||
|
},
|
||||||
documentMeta: {
|
documentMeta: {
|
||||||
create: {
|
create: {
|
||||||
subject: override?.subject || template.templateMeta?.subject,
|
subject: override?.subject || template.templateMeta?.subject,
|
||||||
|
|||||||
@ -34,6 +34,7 @@ export const getTemplateById = async ({ id, userId, teamId }: GetTemplateByIdOpt
|
|||||||
templateMeta: true,
|
templateMeta: true,
|
||||||
recipients: true,
|
recipients: true,
|
||||||
fields: true,
|
fields: true,
|
||||||
|
attachments: true,
|
||||||
user: {
|
user: {
|
||||||
select: {
|
select: {
|
||||||
id: true,
|
id: true,
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import type { DocumentVisibility, Template, TemplateMeta } from '@prisma/client';
|
import type { Attachment, DocumentVisibility, Template, TemplateMeta } from '@prisma/client';
|
||||||
|
|
||||||
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
import { isUserEnterprise } from '@documenso/ee/server-only/util/is-document-enterprise';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
@ -21,6 +21,7 @@ export type UpdateTemplateOptions = {
|
|||||||
publicDescription?: string;
|
publicDescription?: string;
|
||||||
type?: Template['type'];
|
type?: Template['type'];
|
||||||
useLegacyFieldInsertion?: boolean;
|
useLegacyFieldInsertion?: boolean;
|
||||||
|
attachments?: Pick<Attachment, 'id' | 'label' | 'url'>[];
|
||||||
};
|
};
|
||||||
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
|
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
|
||||||
};
|
};
|
||||||
@ -53,6 +54,7 @@ export const updateTemplate = async ({
|
|||||||
},
|
},
|
||||||
include: {
|
include: {
|
||||||
templateMeta: true,
|
templateMeta: true,
|
||||||
|
attachments: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -104,6 +106,29 @@ export const updateTemplate = async ({
|
|||||||
publicDescription: data?.publicDescription,
|
publicDescription: data?.publicDescription,
|
||||||
publicTitle: data?.publicTitle,
|
publicTitle: data?.publicTitle,
|
||||||
useLegacyFieldInsertion: data?.useLegacyFieldInsertion,
|
useLegacyFieldInsertion: data?.useLegacyFieldInsertion,
|
||||||
|
attachments: {
|
||||||
|
deleteMany: {
|
||||||
|
templateId,
|
||||||
|
id: {
|
||||||
|
notIn: data?.attachments?.map((attachment) => attachment.id),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
upsert: data?.attachments?.map((attachment) => ({
|
||||||
|
where: {
|
||||||
|
id: attachment.id,
|
||||||
|
templateId,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
label: attachment.label,
|
||||||
|
url: attachment.url,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: attachment.id,
|
||||||
|
label: attachment.label,
|
||||||
|
url: attachment.url,
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
},
|
||||||
authOptions,
|
authOptions,
|
||||||
templateMeta: {
|
templateMeta: {
|
||||||
upsert: {
|
upsert: {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { z } from 'zod';
|
import type { z } from 'zod';
|
||||||
|
|
||||||
|
import { AttachmentSchema } from '@documenso/prisma/generated/zod/modelSchema/AttachmentSchema';
|
||||||
import { DocumentDataSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema';
|
import { DocumentDataSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema';
|
||||||
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
|
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
|
||||||
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
|
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
|
||||||
@ -62,6 +63,14 @@ export const ZTemplateSchema = TemplateSchema.pick({
|
|||||||
}),
|
}),
|
||||||
recipients: ZRecipientLiteSchema.array(),
|
recipients: ZRecipientLiteSchema.array(),
|
||||||
fields: ZFieldSchema.array(),
|
fields: ZFieldSchema.array(),
|
||||||
|
attachments: AttachmentSchema.pick({
|
||||||
|
id: true,
|
||||||
|
label: true,
|
||||||
|
url: true,
|
||||||
|
type: true,
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TTemplate = z.infer<typeof ZTemplateSchema>;
|
export type TTemplate = z.infer<typeof ZTemplateSchema>;
|
||||||
|
|||||||
@ -312,6 +312,8 @@ enum DocumentVisibility {
|
|||||||
ADMIN
|
ADMIN
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only "LINK" is supported for now.
|
||||||
|
// All other attachment types are not yet supported.
|
||||||
enum AttachmentType {
|
enum AttachmentType {
|
||||||
FILE
|
FILE
|
||||||
VIDEO
|
VIDEO
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import {
|
|||||||
ZTemplateManySchema,
|
ZTemplateManySchema,
|
||||||
ZTemplateSchema,
|
ZTemplateSchema,
|
||||||
} from '@documenso/lib/types/template';
|
} from '@documenso/lib/types/template';
|
||||||
|
import AttachmentSchema from '@documenso/prisma/generated/zod/modelSchema/AttachmentSchema';
|
||||||
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
|
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -154,6 +155,16 @@ export const ZUpdateTemplateRequestSchema = z.object({
|
|||||||
.optional(),
|
.optional(),
|
||||||
type: z.nativeEnum(TemplateType).optional(),
|
type: z.nativeEnum(TemplateType).optional(),
|
||||||
useLegacyFieldInsertion: z.boolean().optional(),
|
useLegacyFieldInsertion: z.boolean().optional(),
|
||||||
|
attachments: AttachmentSchema.pick({
|
||||||
|
id: true,
|
||||||
|
label: true,
|
||||||
|
url: true,
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
|
formId: z.string().min(1),
|
||||||
|
})
|
||||||
|
.array()
|
||||||
|
.optional(),
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
meta: z
|
meta: z
|
||||||
|
|||||||
@ -124,6 +124,11 @@ export const AddTemplateSettingsFormPartial = ({
|
|||||||
emailSettings: ZDocumentEmailSettingsSchema.parse(template?.templateMeta?.emailSettings),
|
emailSettings: ZDocumentEmailSettingsSchema.parse(template?.templateMeta?.emailSettings),
|
||||||
signatureTypes: extractTeamSignatureSettings(template?.templateMeta),
|
signatureTypes: extractTeamSignatureSettings(template?.templateMeta),
|
||||||
},
|
},
|
||||||
|
attachments: template.attachments?.map((attachment) => ({
|
||||||
|
...attachment,
|
||||||
|
id: String(attachment.id),
|
||||||
|
formId: String(attachment.id),
|
||||||
|
})),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -138,12 +143,20 @@ export const AddTemplateSettingsFormPartial = ({
|
|||||||
|
|
||||||
const onAddAttachment = () => {
|
const onAddAttachment = () => {
|
||||||
appendAttachment({
|
appendAttachment({
|
||||||
|
id: nanoid(12),
|
||||||
formId: nanoid(12),
|
formId: nanoid(12),
|
||||||
label: '',
|
label: '',
|
||||||
link: '',
|
url: '',
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onRemoveAttachment = (index: number) => {
|
||||||
|
removeAttachment(index);
|
||||||
|
|
||||||
|
const updatedAttachments = attachments.filter((_, idx) => idx !== index);
|
||||||
|
form.setValue('attachments', updatedAttachments);
|
||||||
|
};
|
||||||
|
|
||||||
const { stepIndex, currentStep, totalSteps, previousStep } = useStep();
|
const { stepIndex, currentStep, totalSteps, previousStep } = useStep();
|
||||||
|
|
||||||
const distributionMethod = form.watch('meta.distributionMethod');
|
const distributionMethod = form.watch('meta.distributionMethod');
|
||||||
@ -622,7 +635,7 @@ export const AddTemplateSettingsFormPartial = ({
|
|||||||
<div className="flex-1">
|
<div className="flex-1">
|
||||||
<FormField
|
<FormField
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name={`attachments.${index}.link`}
|
name={`attachments.${index}.url`}
|
||||||
render={({ field }) => (
|
render={({ field }) => (
|
||||||
<FormItem>
|
<FormItem>
|
||||||
<FormLabel className="flex flex-row items-center">
|
<FormLabel className="flex flex-row items-center">
|
||||||
@ -641,7 +654,7 @@ export const AddTemplateSettingsFormPartial = ({
|
|||||||
|
|
||||||
<div className="flex-none pt-8">
|
<div className="flex-none pt-8">
|
||||||
<button
|
<button
|
||||||
onClick={() => removeAttachment(index)}
|
onClick={() => onRemoveAttachment(index)}
|
||||||
className="hover:bg-muted rounded-md"
|
className="hover:bg-muted rounded-md"
|
||||||
>
|
>
|
||||||
<Trash className="h-4 w-4" />
|
<Trash className="h-4 w-4" />
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import {
|
|||||||
} from '@documenso/lib/types/document-auth';
|
} from '@documenso/lib/types/document-auth';
|
||||||
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';
|
||||||
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
|
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
|
||||||
|
import { AttachmentSchema } from '@documenso/prisma/generated/zod/modelSchema/AttachmentSchema';
|
||||||
import {
|
import {
|
||||||
ZDocumentMetaDateFormatSchema,
|
ZDocumentMetaDateFormatSchema,
|
||||||
ZDocumentMetaTimezoneSchema,
|
ZDocumentMetaTimezoneSchema,
|
||||||
@ -55,13 +56,16 @@ export const ZAddTemplateSettingsFormSchema = z.object({
|
|||||||
message: msg`At least one signature type must be enabled`.id,
|
message: msg`At least one signature type must be enabled`.id,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
attachments: z.array(
|
attachments: AttachmentSchema.pick({
|
||||||
z.object({
|
id: true,
|
||||||
|
label: true,
|
||||||
|
url: true,
|
||||||
|
})
|
||||||
|
.extend({
|
||||||
formId: z.string().min(1),
|
formId: z.string().min(1),
|
||||||
label: z.string(),
|
})
|
||||||
link: z.string(),
|
.array()
|
||||||
}),
|
.optional(),
|
||||||
),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TAddTemplateSettingsFormSchema = z.infer<typeof ZAddTemplateSettingsFormSchema>;
|
export type TAddTemplateSettingsFormSchema = z.infer<typeof ZAddTemplateSettingsFormSchema>;
|
||||||
|
|||||||
Reference in New Issue
Block a user