Merge branch 'feat/refresh' into feat/completed-share-link

This commit is contained in:
Lucas Smith
2023-09-23 21:07:31 +10:00
committed by GitHub
28 changed files with 607 additions and 52 deletions

View File

@ -0,0 +1,30 @@
'use server';
import { prisma } from '@documenso/prisma';
export type CreateDocumentMetaOptions = {
documentId: number;
subject: string;
message: string;
};
export const upsertDocumentMeta = async ({
subject,
message,
documentId,
}: CreateDocumentMetaOptions) => {
return await prisma.documentMeta.upsert({
where: {
documentId,
},
create: {
subject,
message,
documentId,
},
update: {
subject,
message,
},
});
};

View File

@ -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-pending-email';
export type CompleteDocumentWithTokenOptions = {
token: string;
@ -69,6 +70,19 @@ export const completeDocumentWithToken = async ({
},
});
const pendingRecipients = await prisma.recipient.count({
where: {
documentId: document.id,
signingStatus: {
not: SigningStatus.SIGNED,
},
},
});
if (pendingRecipients > 0) {
await sendPendingEmail({ documentId, recipientId: recipient.id });
}
const documents = await prisma.document.updateMany({
where: {
id: document.id,

View File

@ -13,6 +13,7 @@ export const getDocumentById = async ({ id, userId }: GetDocumentByIdOptions) =>
},
include: {
documentData: true,
documentMeta: true,
},
});
};

View File

@ -9,6 +9,7 @@ import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
import { getFile } from '../../universal/upload/get-file';
import { putFile } from '../../universal/upload/put-file';
import { insertFieldInPDF } from '../pdf/insert-field-in-pdf';
import { sendCompletedEmail } from './send-completed-email';
export type SealDocumentOptions = {
documentId: number;
@ -86,4 +87,6 @@ export const sealDocument = async ({ documentId }: SealDocumentOptions) => {
data: newData,
},
});
await sendCompletedEmail({ documentId });
};

View File

@ -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, token } = recipient;
const assetBaseUrl = process.env.NEXT_PUBLIC_WEBAPP_URL || 'http://localhost:3000';
const template = createElement(DocumentCompletedEmailTemplate, {
documentName: document.title,
assetBaseUrl,
downloadLink: `${process.env.NEXT_PUBLIC_WEBAPP_URL}/sign/${token}/complete`,
});
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: 'Signing Complete!',
html: render(template),
text: render(template, { plainText: true }),
});
}),
]);
};

View File

@ -3,13 +3,14 @@ import { createElement } from 'react';
import { mailer } from '@documenso/email/mailer';
import { render } from '@documenso/email/render';
import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite';
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
import { prisma } from '@documenso/prisma';
import { DocumentStatus, SendStatus } from '@documenso/prisma/client';
export interface SendDocumentOptions {
export type SendDocumentOptions = {
documentId: number;
userId: number;
}
};
export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) => {
const user = await prisma.user.findFirstOrThrow({
@ -25,9 +26,12 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions)
},
include: {
Recipient: true,
documentMeta: true,
},
});
const customEmail = document?.documentMeta;
if (!document) {
throw new Error('Document not found');
}
@ -44,6 +48,12 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions)
document.Recipient.map(async (recipient) => {
const { email, name } = recipient;
const customEmailTemplate = {
'signer.name': name,
'signer.email': email,
'document.name': document.title,
};
if (recipient.sendStatus === SendStatus.SENT) {
return;
}
@ -57,6 +67,7 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions)
inviterEmail: user.email,
assetBaseUrl,
signDocumentLink,
customBody: renderCustomEmailTemplate(customEmail?.message || '', customEmailTemplate),
});
await mailer.sendMail({
@ -68,7 +79,9 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions)
name: process.env.NEXT_PRIVATE_SMTP_FROM_NAME || 'Documenso',
address: process.env.NEXT_PRIVATE_SMTP_FROM_ADDRESS || 'noreply@documenso.com',
},
subject: 'Please sign this document',
subject: customEmail?.subject
? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate)
: 'Please sign this document',
html: render(template),
text: render(template, { plainText: true }),
});

View File

@ -0,0 +1,64 @@
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 { prisma } from '@documenso/prisma';
export interface SendPendingEmailOptions {
documentId: number;
recipientId: number;
}
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_WEBAPP_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: 'Waiting for others to complete signing.',
html: render(template),
text: render(template, { plainText: true }),
});
};

View File

@ -0,0 +1,21 @@
'use server';
import { Prisma } from '@prisma/client';
import { prisma } from '@documenso/prisma';
export type UpdateDocumentOptions = {
documentId: number;
data: Prisma.DocumentUpdateInput;
};
export const updateDocument = async ({ documentId, data }: UpdateDocumentOptions) => {
return await prisma.document.update({
where: {
id: documentId,
},
data: {
...data,
},
});
};

View File

@ -50,10 +50,10 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
let imageWidth = image.width;
let imageHeight = image.height;
const initialDimensions = {
width: imageWidth,
height: imageHeight,
};
// const initialDimensions = {
// width: imageWidth,
// height: imageHeight,
// };
const scalingFactor = Math.min(fieldWidth / imageWidth, fieldHeight / imageHeight, 1);
@ -76,10 +76,10 @@ export const insertFieldInPDF = async (pdf: PDFDocument, field: FieldWithSignatu
let textWidth = font.widthOfTextAtSize(field.customText, fontSize);
const textHeight = font.heightAtSize(fontSize);
const initialDimensions = {
width: textWidth,
height: textHeight,
};
// const initialDimensions = {
// width: textWidth,
// height: textHeight,
// };
const scalingFactor = Math.min(fieldWidth / textWidth, fieldHeight / textHeight, 1);