diff --git a/apps/web/src/components/forms/edit-document/add-subject.action.ts b/apps/web/src/components/forms/edit-document/add-subject.action.ts index c0175b751..14ddef867 100644 --- a/apps/web/src/components/forms/edit-document/add-subject.action.ts +++ b/apps/web/src/components/forms/edit-document/add-subject.action.ts @@ -1,9 +1,8 @@ 'use server'; import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-session'; -import { createDocumentMeta } from '@documenso/lib/server-only/document-meta/create-document-meta'; +import { upsertDocumentMeta } from '@documenso/lib/server-only/document-meta/upsert-document-meta'; import { sendDocument } from '@documenso/lib/server-only/document/send-document'; -import { updateDocument } from '@documenso/lib/server-only/document/update-document'; import { TAddSubjectFormSchema } from '@documenso/ui/primitives/document-flow/add-subject.types'; export type CompleteDocumentActionInput = TAddSubjectFormSchema & { @@ -15,30 +14,15 @@ export const completeDocument = async ({ documentId, email }: CompleteDocumentAc const { id: userId } = await getRequiredServerComponentSession(); - if (!email.message && !email.subject) { - return await sendDocument({ - userId, + if (email.message || email.subject) { + await upsertDocumentMeta({ documentId, + subject: email.subject, + message: email.message, }); } - const createDocumentMetaResponse = await createDocumentMeta({ - emailBody: email.message, - emailSubject: email.subject, - }); - - await updateDocument({ - documentId, - data: { - DocumentMeta: { - connect: { - id: createDocumentMetaResponse.id, - }, - }, - }, - }); - - await sendDocument({ + return await sendDocument({ userId, documentId, }); diff --git a/packages/email/template-components/template-document-completed.tsx b/packages/email/template-components/template-document-completed.tsx index b64b13cff..91d8fa29d 100644 --- a/packages/email/template-components/template-document-completed.tsx +++ b/packages/email/template-components/template-document-completed.tsx @@ -1,4 +1,4 @@ -import { Button, Img, Section, Tailwind, Text } from '@react-email/components'; +import { Button, Column, Img, Row, Section, Tailwind, Text } from '@react-email/components'; import * as config from '@documenso/tailwind-config'; @@ -29,11 +29,23 @@ export const TemplateDocumentCompleted = ({ }, }} > -
-
- Documenso -
+
+ + + + Documenso + + + + +
+ +
Completed diff --git a/packages/email/template-components/template-document-invite.tsx b/packages/email/template-components/template-document-invite.tsx index bf2fb905e..fcfba406d 100644 --- a/packages/email/template-components/template-document-invite.tsx +++ b/packages/email/template-components/template-document-invite.tsx @@ -1,4 +1,4 @@ -import { Button, Img, Section, Tailwind, Text } from '@react-email/components'; +import { Button, Column, Img, Row, Section, Tailwind, Text } from '@react-email/components'; import * as config from '@documenso/tailwind-config'; @@ -30,13 +30,26 @@ export const TemplateDocumentInvite = ({ }, }} > -
-
- Documenso -
+
+ + + + Documenso + + + + +
+ +
- {inviterName} has invited you to sign "{documentName}" + {inviterName} has invited you to sign +
"{documentName}"
diff --git a/packages/email/template-components/template-document-pending.tsx b/packages/email/template-components/template-document-pending.tsx index 80387b783..f9fc8648a 100644 --- a/packages/email/template-components/template-document-pending.tsx +++ b/packages/email/template-components/template-document-pending.tsx @@ -1,4 +1,4 @@ -import { Img, Section, Tailwind, Text } from '@react-email/components'; +import { Column, Img, Row, Section, Tailwind, Text } from '@react-email/components'; import * as config from '@documenso/tailwind-config'; @@ -25,11 +25,23 @@ export const TemplateDocumentPending = ({ }, }} > -
-
- Documenso -
+
+ + + + Documenso + + + + +
+ +
Waiting for others diff --git a/packages/lib/server-only/document-meta/create-document-meta.ts b/packages/lib/server-only/document-meta/create-document-meta.ts deleted file mode 100644 index 68fa8ed06..000000000 --- a/packages/lib/server-only/document-meta/create-document-meta.ts +++ /dev/null @@ -1,33 +0,0 @@ -'use server'; - -import { prisma } from '@documenso/prisma'; - -export type CreateDocumentMetaOptions = { - emailSubject: string; - emailBody: string; -}; - -export const createDocumentMeta = async ({ - emailBody, - emailSubject, -}: CreateDocumentMetaOptions) => { - const emailData = { - customEmailBody: emailBody, - customEmailSubject: emailSubject, - }; - - const existingDocumentMeta = await prisma.documentMeta.findFirst({ - where: emailData, - }); - - if (existingDocumentMeta) { - return await prisma.documentMeta.update({ - where: { id: existingDocumentMeta.id }, - data: emailData, - }); - } else { - return await prisma.documentMeta.create({ - data: emailData, - }); - } -}; diff --git a/packages/lib/server-only/document-meta/upsert-document-meta.ts b/packages/lib/server-only/document-meta/upsert-document-meta.ts new file mode 100644 index 000000000..e3cce2ea2 --- /dev/null +++ b/packages/lib/server-only/document-meta/upsert-document-meta.ts @@ -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, + }, + }); +}; diff --git a/packages/lib/server-only/document/get-document-by-id.ts b/packages/lib/server-only/document/get-document-by-id.ts index 0fce1af4d..0b599a71c 100644 --- a/packages/lib/server-only/document/get-document-by-id.ts +++ b/packages/lib/server-only/document/get-document-by-id.ts @@ -13,6 +13,7 @@ export const getDocumentById = async ({ id, userId }: GetDocumentByIdOptions) => }, include: { documentData: true, + documentMeta: true, }, }); }; diff --git a/packages/lib/server-only/document/send-document.tsx b/packages/lib/server-only/document/send-document.tsx index b3eda2077..fcc0f829c 100644 --- a/packages/lib/server-only/document/send-document.tsx +++ b/packages/lib/server-only/document/send-document.tsx @@ -26,11 +26,11 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) }, include: { Recipient: true, - DocumentMeta: true, + documentMeta: true, }, }); - const customEmail = document?.DocumentMeta; + const customEmail = document?.documentMeta; if (!document) { throw new Error('Document not found'); @@ -67,10 +67,7 @@ export const sendDocument = async ({ documentId, userId }: SendDocumentOptions) inviterEmail: user.email, assetBaseUrl, signDocumentLink, - customBody: renderCustomEmailTemplate( - customEmail?.customEmailBody || '', - customEmailTemplate, - ), + customBody: renderCustomEmailTemplate(customEmail?.message || '', customEmailTemplate), }); await mailer.sendMail({ @@ -82,8 +79,8 @@ 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: customEmail?.customEmailSubject - ? renderCustomEmailTemplate(customEmail.customEmailSubject, customEmailTemplate) + subject: customEmail?.subject + ? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate) : 'Please sign this document', html: render(template), text: render(template, { plainText: true }), diff --git a/packages/lib/utils/render-custom-email-template.ts b/packages/lib/utils/render-custom-email-template.ts index 304298c86..e3fdf5c7b 100644 --- a/packages/lib/utils/render-custom-email-template.ts +++ b/packages/lib/utils/render-custom-email-template.ts @@ -2,19 +2,11 @@ export const renderCustomEmailTemplate = >( template: string, variables: T, ): string => { - let t = template; - - Object.entries(variables).forEach((entry) => { - const [key, value] = entry; - - const placeholder = `{${key}}`; - - const re = new RegExp(placeholder, 'g'); - - if (Object.prototype.hasOwnProperty.call(variables, key)) { - t = t.replace(re, String(value)); + return template.replace(/\{(\S+)\}/g, (_, key) => { + if (key in variables) { + return variables[key]; } - }); - return t; + return key; + }); }; diff --git a/packages/prisma/migrations/20230922121421_fix_document_meta_schema/migration.sql b/packages/prisma/migrations/20230922121421_fix_document_meta_schema/migration.sql new file mode 100644 index 000000000..42c20c112 --- /dev/null +++ b/packages/prisma/migrations/20230922121421_fix_document_meta_schema/migration.sql @@ -0,0 +1,52 @@ +/* + Warnings: + + - You are about to drop the column `documentMetaId` on the `Document` table. All the data in the column will be lost. + - You are about to drop the column `customEmailBody` on the `DocumentMeta` table. All the data in the column will be lost. + - You are about to drop the column `customEmailSubject` on the `DocumentMeta` table. All the data in the column will be lost. + - A unique constraint covering the columns `[documentId]` on the table `DocumentMeta` will be added. If there are existing duplicate values, this will fail. + - Added the required column `documentId` to the `DocumentMeta` table without a default value. This is not possible if the table is not empty. + +*/ +-- DropForeignKey +ALTER TABLE "Document" DROP CONSTRAINT "Document_documentMetaId_fkey"; + +-- DropIndex +DROP INDEX "Document_documentMetaId_key"; + +-- AlterTable +ALTER TABLE "DocumentMeta" +ADD COLUMN "documentId" INTEGER, +ADD COLUMN "message" TEXT, +ADD COLUMN "subject" TEXT; + +-- Migrate data +UPDATE "DocumentMeta" SET "documentId" = ( + SELECT "id" FROM "Document" WHERE "Document"."documentMetaId" = "DocumentMeta"."id" +); + +-- Migrate data +UPDATE "DocumentMeta" SET "message" = "customEmailBody"; + +-- Migrate data +UPDATE "DocumentMeta" SET "subject" = "customEmailSubject"; + +-- Prune data +DELETE FROM "DocumentMeta" WHERE "documentId" IS NULL; + +-- AlterTable +ALTER TABLE "Document" DROP COLUMN "documentMetaId"; + +-- AlterTable +ALTER TABLE "DocumentMeta" +DROP COLUMN "customEmailBody", +DROP COLUMN "customEmailSubject"; + +-- AlterColumn +ALTER TABLE "DocumentMeta" ALTER COLUMN "documentId" SET NOT NULL; + +-- CreateIndex +CREATE UNIQUE INDEX "DocumentMeta_documentId_key" ON "DocumentMeta"("documentId"); + +-- AddForeignKey +ALTER TABLE "DocumentMeta" ADD CONSTRAINT "DocumentMeta_documentId_fkey" FOREIGN KEY ("documentId") REFERENCES "Document"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 56c29a64e..c4f034ba2 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -110,15 +110,12 @@ model Document { Field Field[] ShareLink DocumentShareLink[] documentDataId String - documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) - createdAt DateTime @default(now()) - updatedAt DateTime @default(now()) @updatedAt - - documentMetaId String? - DocumentMeta DocumentMeta? @relation(fields: [documentMetaId], references: [id], onDelete: Cascade) + documentData DocumentData @relation(fields: [documentDataId], references: [id], onDelete: Cascade) + documentMeta DocumentMeta? + createdAt DateTime @default(now()) + updatedAt DateTime @default(now()) @updatedAt @@unique([documentDataId]) - @@unique([documentMetaId]) } enum DocumentDataType { @@ -136,10 +133,11 @@ model DocumentData { } model DocumentMeta { - id String @id @default(cuid()) - customEmailSubject String? - customEmailBody String? - Document Document? + id String @id @default(cuid()) + subject String? + message String? + documentId Int @unique + document Document @relation(fields: [documentId], references: [id], onDelete: Cascade) } enum ReadStatus { diff --git a/packages/prisma/types/document-with-data.ts b/packages/prisma/types/document-with-data.ts index d52987552..d8dd8a888 100644 --- a/packages/prisma/types/document-with-data.ts +++ b/packages/prisma/types/document-with-data.ts @@ -1,5 +1,6 @@ -import { Document, DocumentData } from '@documenso/prisma/client'; +import { Document, DocumentData, DocumentMeta } from '@documenso/prisma/client'; export type DocumentWithData = Document & { documentData?: DocumentData | null; + documentMeta?: DocumentMeta | null; }; diff --git a/packages/ui/primitives/document-flow/add-subject.tsx b/packages/ui/primitives/document-flow/add-subject.tsx index 312a4eea2..1bf3b2cb4 100644 --- a/packages/ui/primitives/document-flow/add-subject.tsx +++ b/packages/ui/primitives/document-flow/add-subject.tsx @@ -2,7 +2,8 @@ import { useForm } from 'react-hook-form'; -import { Document, DocumentStatus, Field, Recipient } from '@documenso/prisma/client'; +import { DocumentStatus, Field, Recipient } from '@documenso/prisma/client'; +import { DocumentWithData } from '@documenso/prisma/types/document-with-data'; import { FormErrorMessage } from '@documenso/ui/primitives/form/form-error-message'; import { Input } from '@documenso/ui/primitives/input'; import { Label } from '@documenso/ui/primitives/label'; @@ -21,7 +22,7 @@ export type AddSubjectFormProps = { documentFlow: DocumentFlowStep; recipients: Recipient[]; fields: Field[]; - document: Document; + document: DocumentWithData; numberOfSteps: number; onSubmit: (_data: TAddSubjectFormSchema) => void; }; @@ -41,8 +42,8 @@ export const AddSubjectFormPartial = ({ } = useForm({ defaultValues: { email: { - subject: '', - message: '', + subject: document.documentMeta?.subject ?? '', + message: document.documentMeta?.message ?? '', }, }, });