feat: send custom email to signers of direct template documents (#1215)

Introduces customization options for the document completion email
template to allow for custom email bodies and subjects for documents
created from direct templates.


## Testing Performed
- Verified correct rendering of custom email subject and body for direct
template documents
- Verified the all other completed email types are sent correctly
This commit is contained in:
Ephraim Duncan
2024-07-05 03:03:22 +00:00
committed by GitHub
parent 06b1d4835e
commit 2eee2b4d2a
8 changed files with 51 additions and 9 deletions

View File

@ -76,8 +76,6 @@ export const moveDocumentToTeam = async ({
}),
});
console.log(log);
return updatedDocument;
});
};

View File

@ -4,12 +4,14 @@ import { mailer } from '@documenso/email/mailer';
import { render } from '@documenso/email/render';
import { DocumentCompletedEmailTemplate } from '@documenso/email/templates/document-completed';
import { prisma } from '@documenso/prisma';
import { DocumentSource } from '@documenso/prisma/client';
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
import type { RequestMetadata } from '../../universal/extract-request-metadata';
import { getFile } from '../../universal/upload/get-file';
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
import { renderCustomEmailTemplate } from '../../utils/render-custom-email-template';
export interface SendDocumentOptions {
documentId: number;
@ -23,6 +25,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
},
include: {
documentData: true,
documentMeta: true,
Recipient: true,
User: true,
team: {
@ -38,6 +41,8 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
throw new Error('Document not found');
}
const isDirectTemplate = document?.source === DocumentSource.TEMPLATE_DIRECT_LINK;
if (document.Recipient.length === 0) {
throw new Error('Document has no recipients');
}
@ -106,12 +111,22 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
await Promise.all(
document.Recipient.map(async (recipient) => {
const customEmailTemplate = {
'signer.name': recipient.name,
'signer.email': recipient.email,
'document.name': document.title,
};
const downloadLink = `${NEXT_PUBLIC_WEBAPP_URL()}/sign/${recipient.token}/complete`;
const template = createElement(DocumentCompletedEmailTemplate, {
documentName: document.title,
assetBaseUrl,
downloadLink: recipient.email === owner.email ? documentOwnerDownloadLink : downloadLink,
customBody:
isDirectTemplate && document.documentMeta?.message
? renderCustomEmailTemplate(document.documentMeta.message, customEmailTemplate)
: undefined,
});
await mailer.sendMail({
@ -125,7 +140,10 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
},
subject: 'Signing Complete!',
subject:
isDirectTemplate && document.documentMeta?.subject
? renderCustomEmailTemplate(document.documentMeta.subject, customEmailTemplate)
: 'Signing Complete!',
html: render(template),
text: render(template, { plainText: true }),
attachments: [

View File

@ -39,6 +39,7 @@ import { sendDocument } from '../document/send-document';
import { validateFieldAuth } from '../document/validate-field-auth';
export type CreateDocumentFromDirectTemplateOptions = {
directRecipientName?: string;
directRecipientEmail: string;
directTemplateToken: string;
signedFieldValues: TSignFieldWithTokenMutationSchema[];
@ -57,6 +58,7 @@ type CreatedDirectRecipientField = {
};
export const createDocumentFromDirectTemplate = async ({
directRecipientName: initialDirectRecipientName,
directRecipientEmail,
directTemplateToken,
signedFieldValues,
@ -110,7 +112,7 @@ export const createDocumentFromDirectTemplate = async ({
documentAuth: template.authOptions,
});
const directRecipientName = user?.name;
const directRecipientName = user?.name || initialDirectRecipientName;
// Ensure typesafety when we add more options.
const isAccessAuthValid = match(derivedRecipientAccessAuth)
@ -132,6 +134,8 @@ export const createDocumentFromDirectTemplate = async ({
const metaTimezone = template.templateMeta?.timezone || DEFAULT_DOCUMENT_TIME_ZONE;
const metaDateFormat = template.templateMeta?.dateFormat || DEFAULT_DOCUMENT_DATE_FORMAT;
const metaEmailMessage = template.templateMeta?.message || '';
const metaEmailSubject = template.templateMeta?.subject || '';
// Associate, validate and map to a query every direct template recipient field with the provided fields.
const createDirectRecipientFieldArgs = await Promise.all(
@ -250,6 +254,14 @@ export const createDocumentFromDirectTemplate = async ({
}),
},
},
documentMeta: {
create: {
timezone: metaTimezone,
dateFormat: metaDateFormat,
message: metaEmailMessage,
subject: metaEmailSubject,
},
},
},
include: {
Recipient: true,