Merge branch 'main' into feat/building-documenso-part-2
This commit is contained in:
@@ -82,7 +82,7 @@ Contact us if you are interested in our Enterprise plan for large organizations
|
|||||||
- [NextAuth.js](https://next-auth.js.org/) - Authentication
|
- [NextAuth.js](https://next-auth.js.org/) - Authentication
|
||||||
- [react-email](https://react.email/) - Email Templates
|
- [react-email](https://react.email/) - Email Templates
|
||||||
- [tRPC](https://trpc.io/) - API
|
- [tRPC](https://trpc.io/) - API
|
||||||
- [Node SignPDF](https://github.com/vbuch/node-signpdf) - Digital Signature
|
- [@documenso/pdf-sign](https://www.npmjs.com/package/@documenso/pdf-sign) - PDF Signatures
|
||||||
- [React-PDF](https://github.com/wojtekmaj/react-pdf) - Viewing PDFs
|
- [React-PDF](https://github.com/wojtekmaj/react-pdf) - Viewing PDFs
|
||||||
- [PDF-Lib](https://github.com/Hopding/pdf-lib) - PDF manipulation
|
- [PDF-Lib](https://github.com/Hopding/pdf-lib) - PDF manipulation
|
||||||
- [Stripe](https://stripe.com/) - Payments
|
- [Stripe](https://stripe.com/) - Payments
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ export const DocumentEditPageView = async ({ params, team }: DocumentEditPageVie
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<EditDocumentForm
|
<EditDocumentForm
|
||||||
className="mt-8"
|
className="mt-6"
|
||||||
initialDocument={document}
|
initialDocument={document}
|
||||||
documentRootPath={documentRootPath}
|
documentRootPath={documentRootPath}
|
||||||
isDocumentEnterprise={isDocumentEnterprise}
|
isDocumentEnterprise={isDocumentEnterprise}
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ import Link from 'next/link';
|
|||||||
|
|
||||||
import { ChevronLeft, Loader } from 'lucide-react';
|
import { ChevronLeft, Loader } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Skeleton } from '@documenso/ui/primitives/skeleton';
|
||||||
|
|
||||||
export default function Loading() {
|
export default function Loading() {
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto -mt-4 flex w-full max-w-screen-xl flex-col px-4 md:px-8">
|
<div className="mx-auto -mt-4 flex w-full max-w-screen-xl flex-col px-4 md:px-8">
|
||||||
@@ -13,7 +15,12 @@ export default function Loading() {
|
|||||||
<h1 className="mt-4 grow-0 truncate text-2xl font-semibold md:text-3xl">
|
<h1 className="mt-4 grow-0 truncate text-2xl font-semibold md:text-3xl">
|
||||||
Loading Document...
|
Loading Document...
|
||||||
</h1>
|
</h1>
|
||||||
<div className="mt-8 grid h-[80vh] max-h-[60rem] w-full grid-cols-12 gap-x-8">
|
|
||||||
|
<div className="flex h-10 items-center">
|
||||||
|
<Skeleton className="my-6 h-4 w-24 rounded-2xl" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 grid h-[80vh] max-h-[60rem] w-full grid-cols-12 gap-x-8">
|
||||||
<div className="dark:bg-background border-border col-span-12 rounded-xl border-2 bg-white/50 p-2 before:rounded-xl lg:col-span-6 xl:col-span-7">
|
<div className="dark:bg-background border-border col-span-12 rounded-xl border-2 bg-white/50 p-2 before:rounded-xl lg:col-span-6 xl:col-span-7">
|
||||||
<div className="flex h-[80vh] max-h-[60rem] flex-col items-center justify-center">
|
<div className="flex h-[80vh] max-h-[60rem] flex-col items-center justify-center">
|
||||||
<Loader className="text-documenso h-12 w-12 animate-spin" />
|
<Loader className="text-documenso h-12 w-12 animate-spin" />
|
||||||
|
|||||||
@@ -2,16 +2,21 @@ import Link from 'next/link';
|
|||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
import { ChevronLeft, DownloadIcon } from 'lucide-react';
|
import { ChevronLeft, DownloadIcon } from 'lucide-react';
|
||||||
|
import { DateTime } from 'luxon';
|
||||||
|
|
||||||
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session';
|
||||||
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
|
||||||
|
import { getLocale } from '@documenso/lib/server-only/headers/get-locale';
|
||||||
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
||||||
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
import { formatDocumentsPath } from '@documenso/lib/utils/teams';
|
||||||
import type { Recipient, Team } from '@documenso/prisma/client';
|
import type { Recipient, Team } from '@documenso/prisma/client';
|
||||||
import { Button } from '@documenso/ui/primitives/button';
|
import { Button } from '@documenso/ui/primitives/button';
|
||||||
import { Card } from '@documenso/ui/primitives/card';
|
import { Card } from '@documenso/ui/primitives/card';
|
||||||
|
|
||||||
import { FRIENDLY_STATUS_MAP } from '~/components/formatter/document-status';
|
import {
|
||||||
|
DocumentStatus as DocumentStatusComponent,
|
||||||
|
FRIENDLY_STATUS_MAP,
|
||||||
|
} from '~/components/formatter/document-status';
|
||||||
|
|
||||||
import { DocumentLogsDataTable } from './document-logs-data-table';
|
import { DocumentLogsDataTable } from './document-logs-data-table';
|
||||||
|
|
||||||
@@ -23,6 +28,8 @@ export type DocumentLogsPageViewProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageViewProps) => {
|
export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageViewProps) => {
|
||||||
|
const locale = getLocale();
|
||||||
|
|
||||||
const { id } = params;
|
const { id } = params;
|
||||||
|
|
||||||
const documentId = Number(id);
|
const documentId = Number(id);
|
||||||
@@ -67,15 +74,21 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Created by',
|
description: 'Created by',
|
||||||
value: document.User.name ?? document.User.email,
|
value: document.User.name
|
||||||
|
? `${document.User.name} (${document.User.email})`
|
||||||
|
: document.User.email,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Date created',
|
description: 'Date created',
|
||||||
value: document.createdAt.toISOString(),
|
value: DateTime.fromJSDate(document.createdAt)
|
||||||
|
.setLocale(locale)
|
||||||
|
.toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Last updated',
|
description: 'Last updated',
|
||||||
value: document.updatedAt.toISOString(),
|
value: DateTime.fromJSDate(document.updatedAt)
|
||||||
|
.setLocale(locale)
|
||||||
|
.toLocaleString(DateTime.DATETIME_MED_WITH_SECONDS),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: 'Time zone',
|
description: 'Time zone',
|
||||||
@@ -90,7 +103,7 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
|
|||||||
text = `${recipient.name} (${recipient.email})`;
|
text = `${recipient.name} (${recipient.email})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${text} - ${recipient.role}`;
|
return `[${recipient.role}] ${text}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -104,9 +117,19 @@ export const DocumentLogsPageView = async ({ params, team }: DocumentLogsPageVie
|
|||||||
</Link>
|
</Link>
|
||||||
|
|
||||||
<div className="flex flex-col justify-between sm:flex-row">
|
<div className="flex flex-col justify-between sm:flex-row">
|
||||||
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
<div>
|
||||||
{document.title}
|
<h1 className="mt-4 truncate text-2xl font-semibold md:text-3xl" title={document.title}>
|
||||||
</h1>
|
{document.title}
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="mt-2.5 flex items-center gap-x-6">
|
||||||
|
<DocumentStatusComponent
|
||||||
|
inheritColor
|
||||||
|
status={document.status}
|
||||||
|
className="text-muted-foreground"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="mt-4 flex w-full flex-row sm:mt-0 sm:w-auto sm:self-end">
|
<div className="mt-4 flex w-full flex-row sm:mt-0 sm:w-auto sm:self-end">
|
||||||
<Button variant="outline" className="mr-2 w-full sm:w-auto">
|
<Button variant="outline" className="mr-2 w-full sm:w-auto">
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { createField } from '@documenso/lib/server-only/field/create-field';
|
|||||||
import { deleteField } from '@documenso/lib/server-only/field/delete-field';
|
import { deleteField } from '@documenso/lib/server-only/field/delete-field';
|
||||||
import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
|
import { getFieldById } from '@documenso/lib/server-only/field/get-field-by-id';
|
||||||
import { updateField } from '@documenso/lib/server-only/field/update-field';
|
import { updateField } from '@documenso/lib/server-only/field/update-field';
|
||||||
|
import { insertFormValuesInPdf } from '@documenso/lib/server-only/pdf/insert-form-values-in-pdf';
|
||||||
import { deleteRecipient } from '@documenso/lib/server-only/recipient/delete-recipient';
|
import { deleteRecipient } from '@documenso/lib/server-only/recipient/delete-recipient';
|
||||||
import { getRecipientById } from '@documenso/lib/server-only/recipient/get-recipient-by-id';
|
import { getRecipientById } from '@documenso/lib/server-only/recipient/get-recipient-by-id';
|
||||||
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
import { getRecipientsForDocument } from '@documenso/lib/server-only/recipient/get-recipients-for-document';
|
||||||
@@ -20,6 +21,8 @@ import { setRecipientsForDocument } from '@documenso/lib/server-only/recipient/s
|
|||||||
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
|
import { updateRecipient } from '@documenso/lib/server-only/recipient/update-recipient';
|
||||||
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
import { createDocumentFromTemplate } from '@documenso/lib/server-only/template/create-document-from-template';
|
||||||
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import { extractNextApiRequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
|
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||||
|
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
||||||
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
|
import { getPresignPostUrl } from '@documenso/lib/universal/upload/server-actions';
|
||||||
import { DocumentDataType, DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentDataType, DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
@@ -156,6 +159,7 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
title: body.title,
|
title: body.title,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
|
formValues: body.formValues,
|
||||||
documentDataId: documentData.id,
|
documentDataId: documentData.id,
|
||||||
requestMetadata: extractNextApiRequestMetadata(args.req),
|
requestMetadata: extractNextApiRequestMetadata(args.req),
|
||||||
});
|
});
|
||||||
@@ -217,12 +221,37 @@ export const ApiContractV1Implementation = createNextRoute(ApiContractV1, {
|
|||||||
recipients: body.recipients,
|
recipients: body.recipients,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let documentDataId = document.documentDataId;
|
||||||
|
|
||||||
|
if (body.formValues) {
|
||||||
|
const pdf = await getFile(document.documentData);
|
||||||
|
|
||||||
|
const prefilled = await insertFormValuesInPdf({
|
||||||
|
pdf: Buffer.from(pdf),
|
||||||
|
formValues: body.formValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newDocumentData = await putFile({
|
||||||
|
name: fileName,
|
||||||
|
type: 'application/pdf',
|
||||||
|
arrayBuffer: async () => Promise.resolve(prefilled),
|
||||||
|
});
|
||||||
|
|
||||||
|
documentDataId = newDocumentData.id;
|
||||||
|
}
|
||||||
|
|
||||||
await updateDocument({
|
await updateDocument({
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
userId: user.id,
|
userId: user.id,
|
||||||
teamId: team?.id,
|
teamId: team?.id,
|
||||||
data: {
|
data: {
|
||||||
title: fileName,
|
title: fileName,
|
||||||
|
formValues: body.formValues,
|
||||||
|
documentData: {
|
||||||
|
connect: {
|
||||||
|
id: documentDataId,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ export const ZCreateDocumentMutationSchema = z.object({
|
|||||||
redirectUrl: z.string(),
|
redirectUrl: z.string(),
|
||||||
})
|
})
|
||||||
.partial(),
|
.partial(),
|
||||||
|
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
|
export type TCreateDocumentMutationSchema = z.infer<typeof ZCreateDocumentMutationSchema>;
|
||||||
@@ -112,6 +113,7 @@ export const ZCreateDocumentFromTemplateMutationSchema = z.object({
|
|||||||
})
|
})
|
||||||
.partial()
|
.partial()
|
||||||
.optional(),
|
.optional(),
|
||||||
|
formValues: z.record(z.string(), z.union([z.string(), z.boolean(), z.number()])).optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
|
export type TCreateDocumentFromTemplateMutationSchema = z.infer<
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ export type CreateDocumentOptions = {
|
|||||||
userId: number;
|
userId: number;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
documentDataId: string;
|
documentDataId: string;
|
||||||
|
formValues?: Record<string, string | number | boolean>;
|
||||||
requestMetadata?: RequestMetadata;
|
requestMetadata?: RequestMetadata;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -22,6 +23,7 @@ export const createDocument = async ({
|
|||||||
title,
|
title,
|
||||||
documentDataId,
|
documentDataId,
|
||||||
teamId,
|
teamId,
|
||||||
|
formValues,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
}: CreateDocumentOptions) => {
|
}: CreateDocumentOptions) => {
|
||||||
const user = await prisma.user.findFirstOrThrow({
|
const user = await prisma.user.findFirstOrThrow({
|
||||||
@@ -51,6 +53,7 @@ export const createDocument = async ({
|
|||||||
documentDataId,
|
documentDataId,
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
|
formValues,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -153,9 +153,19 @@ export const sealDocument = async ({
|
|||||||
await sendCompletedEmail({ documentId, requestMetadata });
|
await sendCompletedEmail({ documentId, requestMetadata });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const updatedDocument = await prisma.document.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: document.id,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
documentData: true,
|
||||||
|
Recipient: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
await triggerWebhook({
|
await triggerWebhook({
|
||||||
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
event: WebhookTriggerEvents.DOCUMENT_COMPLETED,
|
||||||
data: document,
|
data: updatedDocument,
|
||||||
userId: document.userId,
|
userId: document.userId,
|
||||||
teamId: document.teamId ?? undefined,
|
teamId: document.teamId ?? undefined,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ import {
|
|||||||
RECIPIENT_ROLES_DESCRIPTION,
|
RECIPIENT_ROLES_DESCRIPTION,
|
||||||
RECIPIENT_ROLE_TO_EMAIL_TYPE,
|
RECIPIENT_ROLE_TO_EMAIL_TYPE,
|
||||||
} from '../../constants/recipient-roles';
|
} from '../../constants/recipient-roles';
|
||||||
|
import { getFile } from '../../universal/upload/get-file';
|
||||||
|
import { putFile } from '../../universal/upload/put-file';
|
||||||
|
import { insertFormValuesInPdf } from '../pdf/insert-form-values-in-pdf';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||||
|
|
||||||
export type SendDocumentOptions = {
|
export type SendDocumentOptions = {
|
||||||
@@ -65,6 +68,7 @@ export const sendDocument = async ({
|
|||||||
include: {
|
include: {
|
||||||
Recipient: true,
|
Recipient: true,
|
||||||
documentMeta: true,
|
documentMeta: true,
|
||||||
|
documentData: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -82,6 +86,38 @@ export const sendDocument = async ({
|
|||||||
throw new Error('Can not send completed document');
|
throw new Error('Can not send completed document');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { documentData } = document;
|
||||||
|
|
||||||
|
if (!documentData.data) {
|
||||||
|
throw new Error('Document data not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.formValues) {
|
||||||
|
const file = await getFile(documentData);
|
||||||
|
|
||||||
|
const prefilled = await insertFormValuesInPdf({
|
||||||
|
pdf: Buffer.from(file),
|
||||||
|
formValues: document.formValues as Record<string, string | number | boolean>,
|
||||||
|
});
|
||||||
|
|
||||||
|
const newDocumentData = await putFile({
|
||||||
|
name: document.title,
|
||||||
|
type: 'application/pdf',
|
||||||
|
arrayBuffer: async () => Promise.resolve(prefilled),
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await prisma.document.update({
|
||||||
|
where: {
|
||||||
|
id: document.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
documentDataId: newDocumentData.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.assign(document, result);
|
||||||
|
}
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
document.Recipient.map(async (recipient) => {
|
document.Recipient.map(async (recipient) => {
|
||||||
if (recipient.sendStatus === SendStatus.SENT || recipient.role === RecipientRole.CC) {
|
if (recipient.sendStatus === SendStatus.SENT || recipient.role === RecipientRole.CC) {
|
||||||
|
|||||||
54
packages/lib/server-only/pdf/insert-form-values-in-pdf.ts
Normal file
54
packages/lib/server-only/pdf/insert-form-values-in-pdf.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { PDFCheckBox, PDFDocument, PDFDropdown, PDFRadioGroup, PDFTextField } from 'pdf-lib';
|
||||||
|
|
||||||
|
export type InsertFormValuesInPdfOptions = {
|
||||||
|
pdf: Buffer;
|
||||||
|
formValues: Record<string, string | boolean | number>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const insertFormValuesInPdf = async ({ pdf, formValues }: InsertFormValuesInPdfOptions) => {
|
||||||
|
const doc = await PDFDocument.load(pdf);
|
||||||
|
|
||||||
|
const form = doc.getForm();
|
||||||
|
|
||||||
|
if (!form) {
|
||||||
|
return pdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(formValues)) {
|
||||||
|
try {
|
||||||
|
const field = form.getField(key);
|
||||||
|
|
||||||
|
if (!field) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'boolean' && field instanceof PDFCheckBox) {
|
||||||
|
if (value) {
|
||||||
|
field.check();
|
||||||
|
} else {
|
||||||
|
field.uncheck();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field instanceof PDFTextField) {
|
||||||
|
field.setText(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field instanceof PDFDropdown) {
|
||||||
|
field.select(value.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field instanceof PDFRadioGroup) {
|
||||||
|
field.select(value.toString());
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
console.error(`Error setting value for field ${key}: ${err.message}`);
|
||||||
|
} else {
|
||||||
|
console.error(`Error setting value for field ${key}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return await doc.save().then((buf) => Buffer.from(buf));
|
||||||
|
};
|
||||||
@@ -79,6 +79,7 @@ export const createDocumentFromTemplate = async ({
|
|||||||
id: 'asc',
|
id: 'asc',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
documentData: true,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -236,11 +236,29 @@ export const ZDocumentAuditLogEventDocumentFieldInsertedSchema = z.object({
|
|||||||
data: z.string(),
|
data: z.string(),
|
||||||
}),
|
}),
|
||||||
]),
|
]),
|
||||||
fieldSecurity: z
|
fieldSecurity: z.preprocess(
|
||||||
.object({
|
(input) => {
|
||||||
type: ZRecipientActionAuthTypesSchema,
|
const legacyNoneSecurityType = JSON.stringify({
|
||||||
})
|
type: 'NONE',
|
||||||
.optional(),
|
});
|
||||||
|
|
||||||
|
// Replace legacy 'NONE' field security type with undefined.
|
||||||
|
if (
|
||||||
|
typeof input === 'object' &&
|
||||||
|
input !== null &&
|
||||||
|
JSON.stringify(input) === legacyNoneSecurityType
|
||||||
|
) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return input;
|
||||||
|
},
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
type: ZRecipientActionAuthTypesSchema,
|
||||||
|
})
|
||||||
|
.optional(),
|
||||||
|
),
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Document" ADD COLUMN "formValues" JSONB;
|
||||||
@@ -257,6 +257,7 @@ model Document {
|
|||||||
userId Int
|
userId Int
|
||||||
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||||
authOptions Json?
|
authOptions Json?
|
||||||
|
formValues Json?
|
||||||
title String
|
title String
|
||||||
status DocumentStatus @default(DRAFT)
|
status DocumentStatus @default(DRAFT)
|
||||||
Recipient Recipient[]
|
Recipient Recipient[]
|
||||||
|
|||||||
Reference in New Issue
Block a user