mirror of
https://github.com/documenso/documenso.git
synced 2026-07-27 18:35:05 +10:00
feat: add signature configurations (#1710)
Add ability to enable or disable allowed signature types: - Drawn - Typed - Uploaded **Tabbed style signature dialog**  **Document settings**  **Team preferences**  - Add multiselect to select allowed signatures in document and templates settings tab - Add multiselect to select allowed signatures in teams preferences - Removed "Enable typed signatures" from document/template edit page - Refactored signature pad to use tabs instead of an all in one signature pad Added E2E tests to check settings are applied correctly for documents and templates
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "DocumentMeta" ADD COLUMN "drawSignatureEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN "uploadSignatureEnabled" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "TeamGlobalSettings" ADD COLUMN "drawSignatureEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN "uploadSignatureEnabled" BOOLEAN NOT NULL DEFAULT true;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "TemplateMeta" ADD COLUMN "drawSignatureEnabled" BOOLEAN NOT NULL DEFAULT true,
|
||||
ADD COLUMN "uploadSignatureEnabled" BOOLEAN NOT NULL DEFAULT true;
|
||||
@@ -0,0 +1,25 @@
|
||||
import type { PrismaClient } from '@prisma/client';
|
||||
|
||||
export function addPrismaMiddleware(prisma: PrismaClient) {
|
||||
prisma.$use(async (params, next) => {
|
||||
// Check if we're creating a new team
|
||||
if (params.model === 'Team' && params.action === 'create') {
|
||||
// Execute the team creation
|
||||
const result = await next(params);
|
||||
|
||||
// Create the TeamGlobalSettings
|
||||
await prisma.teamGlobalSettings.create({
|
||||
data: {
|
||||
teamId: result.id,
|
||||
},
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// For all other operations, just pass through
|
||||
return next(params);
|
||||
});
|
||||
|
||||
return prisma;
|
||||
}
|
||||
@@ -390,21 +390,25 @@ enum DocumentDistributionMethod {
|
||||
|
||||
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';"])
|
||||
model DocumentMeta {
|
||||
id String @id @default(cuid())
|
||||
id String @id @default(cuid())
|
||||
subject String?
|
||||
message String?
|
||||
timezone String? @default("Etc/UTC") @db.Text
|
||||
timezone String? @default("Etc/UTC") @db.Text
|
||||
password String?
|
||||
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
|
||||
documentId Int @unique
|
||||
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
|
||||
documentId Int @unique
|
||||
document Document @relation(fields: [documentId], references: [id], onDelete: Cascade)
|
||||
redirectUrl String?
|
||||
signingOrder DocumentSigningOrder @default(PARALLEL)
|
||||
allowDictateNextSigner Boolean @default(false)
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
language String @default("en")
|
||||
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
||||
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
|
||||
signingOrder DocumentSigningOrder @default(PARALLEL)
|
||||
allowDictateNextSigner Boolean @default(false)
|
||||
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
uploadSignatureEnabled Boolean @default(true)
|
||||
drawSignatureEnabled Boolean @default(true)
|
||||
|
||||
language String @default("en")
|
||||
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
||||
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
|
||||
}
|
||||
|
||||
enum ReadStatus {
|
||||
@@ -545,9 +549,12 @@ model TeamGlobalSettings {
|
||||
documentVisibility DocumentVisibility @default(EVERYONE)
|
||||
documentLanguage String @default("en")
|
||||
includeSenderDetails Boolean @default(true)
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
includeSigningCertificate Boolean @default(true)
|
||||
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
uploadSignatureEnabled Boolean @default(true)
|
||||
drawSignatureEnabled Boolean @default(true)
|
||||
|
||||
brandingEnabled Boolean @default(false)
|
||||
brandingLogo String @default("")
|
||||
brandingUrl String @default("")
|
||||
@@ -669,9 +676,12 @@ model TemplateMeta {
|
||||
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
|
||||
signingOrder DocumentSigningOrder? @default(PARALLEL)
|
||||
allowDictateNextSigner Boolean @default(false)
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
distributionMethod DocumentDistributionMethod @default(EMAIL)
|
||||
|
||||
typedSignatureEnabled Boolean @default(true)
|
||||
uploadSignatureEnabled Boolean @default(true)
|
||||
drawSignatureEnabled Boolean @default(true)
|
||||
|
||||
templateId Int @unique
|
||||
template Template @relation(fields: [templateId], references: [id], onDelete: Cascade)
|
||||
redirectUrl String?
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import type { Document, User } from '@prisma/client';
|
||||
import type { Document, Team, User } from '@prisma/client';
|
||||
import { nanoid } from 'nanoid';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { match } from 'ts-pattern';
|
||||
|
||||
import { createDocument } from '@documenso/lib/server-only/document/create-document';
|
||||
import { createTemplate } from '@documenso/lib/server-only/template/create-template';
|
||||
|
||||
import { prisma } from '..';
|
||||
import {
|
||||
DocumentDataType,
|
||||
@@ -87,6 +90,145 @@ export const unseedDocument = async (documentId: number) => {
|
||||
});
|
||||
};
|
||||
|
||||
export const seedTeamDocumentWithMeta = async (team: Team) => {
|
||||
const documentData = await prisma.documentData.create({
|
||||
data: {
|
||||
type: DocumentDataType.BYTES_64,
|
||||
data: examplePdf,
|
||||
initialData: examplePdf,
|
||||
},
|
||||
});
|
||||
|
||||
const document = await createDocument({
|
||||
userId: team.ownerUserId,
|
||||
teamId: team.id,
|
||||
title: `[TEST] Document ${nanoid(8)} - Draft`,
|
||||
documentDataId: documentData.id,
|
||||
normalizePdf: true,
|
||||
requestMetadata: {
|
||||
auth: null,
|
||||
requestMetadata: {},
|
||||
source: 'app',
|
||||
},
|
||||
});
|
||||
|
||||
const owner = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: team.ownerUserId,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.document.update({
|
||||
where: {
|
||||
id: document.id,
|
||||
},
|
||||
data: {
|
||||
status: DocumentStatus.PENDING,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.recipient.create({
|
||||
data: {
|
||||
email: owner.email,
|
||||
name: owner.name ?? '',
|
||||
token: nanoid(),
|
||||
readStatus: ReadStatus.OPENED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
signedAt: new Date(),
|
||||
document: {
|
||||
connect: {
|
||||
id: document.id,
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
create: {
|
||||
page: 1,
|
||||
type: FieldType.SIGNATURE,
|
||||
inserted: false,
|
||||
customText: '',
|
||||
positionX: new Prisma.Decimal(1),
|
||||
positionY: new Prisma.Decimal(1),
|
||||
width: new Prisma.Decimal(5),
|
||||
height: new Prisma.Decimal(5),
|
||||
documentId: document.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: document.id,
|
||||
},
|
||||
include: {
|
||||
recipients: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const seedTeamTemplateWithMeta = async (team: Team) => {
|
||||
const documentData = await prisma.documentData.create({
|
||||
data: {
|
||||
type: DocumentDataType.BYTES_64,
|
||||
data: examplePdf,
|
||||
initialData: examplePdf,
|
||||
},
|
||||
});
|
||||
|
||||
const template = await createTemplate({
|
||||
title: `[TEST] Template ${nanoid(8)} - Draft`,
|
||||
userId: team.ownerUserId,
|
||||
teamId: team.id,
|
||||
templateDocumentDataId: documentData.id,
|
||||
});
|
||||
|
||||
const owner = await prisma.user.findFirstOrThrow({
|
||||
where: {
|
||||
id: team.ownerUserId,
|
||||
},
|
||||
});
|
||||
|
||||
await prisma.recipient.create({
|
||||
data: {
|
||||
email: owner.email,
|
||||
name: owner.name ?? '',
|
||||
token: nanoid(),
|
||||
readStatus: ReadStatus.OPENED,
|
||||
sendStatus: SendStatus.SENT,
|
||||
signingStatus: SigningStatus.NOT_SIGNED,
|
||||
signedAt: new Date(),
|
||||
template: {
|
||||
connect: {
|
||||
id: template.id,
|
||||
},
|
||||
},
|
||||
fields: {
|
||||
create: {
|
||||
page: 1,
|
||||
type: FieldType.SIGNATURE,
|
||||
inserted: false,
|
||||
customText: '',
|
||||
positionX: new Prisma.Decimal(1),
|
||||
positionY: new Prisma.Decimal(1),
|
||||
width: new Prisma.Decimal(5),
|
||||
height: new Prisma.Decimal(5),
|
||||
templateId: template.id,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return await prisma.document.findFirstOrThrow({
|
||||
where: {
|
||||
id: template.id,
|
||||
},
|
||||
include: {
|
||||
recipients: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export const seedDraftDocument = async (
|
||||
sender: User,
|
||||
recipients: (User | string)[],
|
||||
|
||||
Reference in New Issue
Block a user