From dbbe17a0a8a662ff9ea43d0db9dc244134ea190c Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Thu, 7 Sep 2023 19:58:48 +0000 Subject: [PATCH 1/7] feat: send email when recipient is done signing --- .../document/complete-document-with-token.ts | 3 ++ .../document/send-recipient-signed-email.ts | 36 +++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 packages/lib/server-only/document/send-recipient-signed-email.ts diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index a013d5e69..4d4bacabf 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -4,6 +4,7 @@ import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { sealDocument } from './seal-document'; +import { sendPendingEmail } from './send-recipient-signed-email'; export type CompleteDocumentWithTokenOptions = { token: string; @@ -69,6 +70,8 @@ export const completeDocumentWithToken = async ({ }, }); + await sendPendingEmail({ document, recipient }); + const documents = await prisma.document.updateMany({ where: { id: document.id, diff --git a/packages/lib/server-only/document/send-recipient-signed-email.ts b/packages/lib/server-only/document/send-recipient-signed-email.ts new file mode 100644 index 000000000..5deed2343 --- /dev/null +++ b/packages/lib/server-only/document/send-recipient-signed-email.ts @@ -0,0 +1,36 @@ +import { createElement } from 'react'; + +import { mailer } from '@documenso/email/mailer'; +import { render } from '@documenso/email/render'; +import { DocumentPendingEmailTemplate } from '@documenso/email/templates/document-pending'; +import { Document, Recipient } from '@documenso/prisma/client'; + +export interface SendPendingEmailOptions { + document: Document; + recipient: Recipient; +} + +export const sendPendingEmail = async ({ document, recipient }: SendPendingEmailOptions) => { + const { email, name } = recipient; + + const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; + + const template = createElement(DocumentPendingEmailTemplate, { + documentName: document.title, + assetBaseUrl, + }); + + await mailer.sendMail({ + to: { + address: email, + name, + }, + from: { + name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso', + address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com', + }, + subject: 'You are done signing.', + html: render(template), + text: render(template, { plainText: true }), + }); +}; From da2033692c157f12f57832bb26287f8e49038569 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Thu, 7 Sep 2023 20:14:04 +0000 Subject: [PATCH 2/7] feat: send email when all recipients have signed --- .../document/complete-document-with-token.ts | 1 + .../lib/server-only/document/seal-document.ts | 3 + .../document/send-completed-email.ts | 57 +++++++++++++++++++ 3 files changed, 61 insertions(+) create mode 100644 packages/lib/server-only/document/send-completed-email.ts diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index 4d4bacabf..77ab33d04 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -70,6 +70,7 @@ export const completeDocumentWithToken = async ({ }, }); + // TODO: Send email to documents with two or more recipients await sendPendingEmail({ document, recipient }); const documents = await prisma.document.updateMany({ diff --git a/packages/lib/server-only/document/seal-document.ts b/packages/lib/server-only/document/seal-document.ts index 1a74cfaac..f0806919f 100644 --- a/packages/lib/server-only/document/seal-document.ts +++ b/packages/lib/server-only/document/seal-document.ts @@ -6,6 +6,7 @@ import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { insertFieldInPDF } from '../pdf/insert-field-in-pdf'; +import { sendCompletedEmail } from './send-completed-email'; export type SealDocumentOptions = { documentId: number; @@ -67,4 +68,6 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => { document: Buffer.from(pdfBytes).toString('base64'), }, }); + + await sendCompletedEmail({ documentId }); }; diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts new file mode 100644 index 000000000..b8d50dba4 --- /dev/null +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -0,0 +1,57 @@ +import { createElement } from 'react'; + +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'; + +export interface SendDocumentOptions { + documentId: number; +} + +export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => { + const document = await prisma.document.findUnique({ + where: { + id: documentId, + }, + include: { + Recipient: true, + }, + }); + + if (!document) { + throw new Error('Document not found'); + } + + if (document.Recipient.length === 0) { + throw new Error('Document has no recipients'); + } + + await Promise.all([ + document.Recipient.map(async (recipient) => { + const { email, name } = recipient; + + const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; + + const template = createElement(DocumentCompletedEmailTemplate, { + documentName: document.title, + assetBaseUrl, + downloadLink: 'https://documenso.com', + }); + + await mailer.sendMail({ + to: { + address: email, + name, + }, + from: { + name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso', + address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com', + }, + subject: 'Everyone has signed!', + html: render(template), + text: render(template, { plainText: true }), + }); + }), + ]); +}; From 863e53a2d5cc8077302ac7a37789f69e7163de92 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Thu, 7 Sep 2023 20:38:18 +0000 Subject: [PATCH 3/7] refactor: pass document id as arguments --- .../document/complete-document-with-token.ts | 2 +- .../document/send-recipient-signed-email.ts | 36 ++++++++++++++++--- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index 77ab33d04..ea440beb9 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -71,7 +71,7 @@ export const completeDocumentWithToken = async ({ }); // TODO: Send email to documents with two or more recipients - await sendPendingEmail({ document, recipient }); + await sendPendingEmail({ documentId, recipientId: recipient.id }); const documents = await prisma.document.updateMany({ where: { diff --git a/packages/lib/server-only/document/send-recipient-signed-email.ts b/packages/lib/server-only/document/send-recipient-signed-email.ts index 5deed2343..ece75caec 100644 --- a/packages/lib/server-only/document/send-recipient-signed-email.ts +++ b/packages/lib/server-only/document/send-recipient-signed-email.ts @@ -3,14 +3,42 @@ import { createElement } from 'react'; import { mailer } from '@documenso/email/mailer'; import { render } from '@documenso/email/render'; import { DocumentPendingEmailTemplate } from '@documenso/email/templates/document-pending'; -import { Document, Recipient } from '@documenso/prisma/client'; +import { prisma } from '@documenso/prisma'; export interface SendPendingEmailOptions { - document: Document; - recipient: Recipient; + documentId: number; + recipientId: number; } -export const sendPendingEmail = async ({ document, recipient }: SendPendingEmailOptions) => { +export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingEmailOptions) => { + const document = await prisma.document.findFirst({ + where: { + id: documentId, + Recipient: { + some: { + id: recipientId, + }, + }, + }, + include: { + Recipient: { + where: { + id: recipientId, + }, + }, + }, + }); + + if (!document) { + throw new Error('Document not found'); + } + + if (document.Recipient.length === 0) { + throw new Error('Document has no recipients'); + } + + const [recipient] = document.Recipient; + const { email, name } = recipient; const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; From 525ff215634c02fb9f456568795c2ed917266515 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Thu, 7 Sep 2023 20:52:18 +0000 Subject: [PATCH 4/7] feat: avoid sending pending email to document with 1 recipients --- .../document/complete-document-with-token.ts | 11 +++++++++-- .../lib/server-only/document/send-completed-email.ts | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index ea440beb9..464b7fe4f 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -70,8 +70,15 @@ export const completeDocumentWithToken = async ({ }, }); - // TODO: Send email to documents with two or more recipients - await sendPendingEmail({ documentId, recipientId: recipient.id }); + const numberOfRecipients = await prisma.recipient.count({ + where: { + documentId: document.id, + }, + }); + + if (numberOfRecipients > 1) { + await sendPendingEmail({ documentId, recipientId: recipient.id }); + } const documents = await prisma.document.updateMany({ where: { diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index b8d50dba4..0a1817964 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -48,7 +48,7 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso', address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com', }, - subject: 'Everyone has signed!', + subject: 'Signing Complete!', html: render(template), text: render(template, { plainText: true }), }); From 6f4c2805833f9ad90051b88796cf0591f8f0f7cc Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 17 Sep 2023 14:31:44 +0000 Subject: [PATCH 5/7] chore: match file name and method name --- .../lib/server-only/document/complete-document-with-token.ts | 2 +- .../{send-recipient-signed-email.ts => send-pending-email.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/lib/server-only/document/{send-recipient-signed-email.ts => send-pending-email.ts} (100%) diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index 464b7fe4f..e442f3061 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -4,7 +4,7 @@ import { prisma } from '@documenso/prisma'; import { DocumentStatus, SigningStatus } from '@documenso/prisma/client'; import { sealDocument } from './seal-document'; -import { sendPendingEmail } from './send-recipient-signed-email'; +import { sendPendingEmail } from './send-pending-email'; export type CompleteDocumentWithTokenOptions = { token: string; diff --git a/packages/lib/server-only/document/send-recipient-signed-email.ts b/packages/lib/server-only/document/send-pending-email.ts similarity index 100% rename from packages/lib/server-only/document/send-recipient-signed-email.ts rename to packages/lib/server-only/document/send-pending-email.ts From 776324c8750a409f366363fb27c1c60f65da30c3 Mon Sep 17 00:00:00 2001 From: Ephraim Atta-Duncan Date: Sun, 17 Sep 2023 14:38:39 +0000 Subject: [PATCH 6/7] fix: fitler only unsigned documents --- .../lib/server-only/document/complete-document-with-token.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index e442f3061..8b1b4d576 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -73,6 +73,9 @@ export const completeDocumentWithToken = async ({ const numberOfRecipients = await prisma.recipient.count({ where: { documentId: document.id, + signingStatus: { + not: SigningStatus.SIGNED, + }, }, }); From 0d130b17c8eb1967cd1cb79a196080f37fecd2fc Mon Sep 17 00:00:00 2001 From: Lucas Smith Date: Fri, 22 Sep 2023 23:22:48 +1000 Subject: [PATCH 7/7] fix: minor updates --- .../server-only/document/complete-document-with-token.ts | 4 ++-- packages/lib/server-only/document/send-completed-email.ts | 6 +++--- packages/lib/server-only/document/send-pending-email.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/lib/server-only/document/complete-document-with-token.ts b/packages/lib/server-only/document/complete-document-with-token.ts index 8b1b4d576..2712c56fa 100644 --- a/packages/lib/server-only/document/complete-document-with-token.ts +++ b/packages/lib/server-only/document/complete-document-with-token.ts @@ -70,7 +70,7 @@ export const completeDocumentWithToken = async ({ }, }); - const numberOfRecipients = await prisma.recipient.count({ + const pendingRecipients = await prisma.recipient.count({ where: { documentId: document.id, signingStatus: { @@ -79,7 +79,7 @@ export const completeDocumentWithToken = async ({ }, }); - if (numberOfRecipients > 1) { + if (pendingRecipients > 0) { await sendPendingEmail({ documentId, recipientId: recipient.id }); } diff --git a/packages/lib/server-only/document/send-completed-email.ts b/packages/lib/server-only/document/send-completed-email.ts index 0a1817964..9d0d2d499 100644 --- a/packages/lib/server-only/document/send-completed-email.ts +++ b/packages/lib/server-only/document/send-completed-email.ts @@ -29,14 +29,14 @@ export const sendCompletedEmail = async ({ documentId }: SendDocumentOptions) => await Promise.all([ document.Recipient.map(async (recipient) => { - const { email, name } = recipient; + const { email, name, token } = recipient; - const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; + const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const template = createElement(DocumentCompletedEmailTemplate, { documentName: document.title, assetBaseUrl, - downloadLink: 'https://documenso.com', + downloadLink: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${token}/complete`, }); await mailer.sendMail({ diff --git a/packages/lib/server-only/document/send-pending-email.ts b/packages/lib/server-only/document/send-pending-email.ts index ece75caec..75861be78 100644 --- a/packages/lib/server-only/document/send-pending-email.ts +++ b/packages/lib/server-only/document/send-pending-email.ts @@ -41,7 +41,7 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE const { email, name } = recipient; - const assetBaseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'; + const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000'; const template = createElement(DocumentPendingEmailTemplate, { documentName: document.title, @@ -57,7 +57,7 @@ export const sendPendingEmail = async ({ documentId, recipientId }: SendPendingE name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso', address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com', }, - subject: 'You are done signing.', + subject: 'Waiting for others to complete signing.', html: render(template), text: render(template, { plainText: true }), });