Compare commits

...

1 Commits

Author SHA1 Message Date
e7e2aa9bd8 fix: migrate template metadata 2025-08-21 17:56:04 +10:00
15 changed files with 93 additions and 70 deletions

View File

@ -3,7 +3,7 @@ import { useEffect, useLayoutEffect, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentMeta, Recipient, Signature, TemplateMeta } from '@prisma/client';
import type { DocumentMeta, Recipient, Signature } from '@prisma/client';
import { type DocumentData, type Field, FieldType } from '@prisma/client';
import { LucideChevronDown, LucideChevronUp } from 'lucide-react';
import { DateTime } from 'luxon';
@ -48,7 +48,7 @@ export type EmbedDirectTemplateClientPageProps = {
documentData: DocumentData;
recipient: Recipient;
fields: Field[];
metadata?: DocumentMeta | TemplateMeta | null;
metadata?: DocumentMeta | null;
hidePoweredBy?: boolean;
allowWhiteLabelling?: boolean;
};

View File

@ -1,4 +1,4 @@
import type { DocumentMeta, TemplateMeta } from '@prisma/client';
import type { DocumentMeta } from '@prisma/client';
import { type Field, FieldType } from '@prisma/client';
import { match } from 'ts-pattern';
@ -33,7 +33,7 @@ import { DocumentSigningTextField } from '~/components/general/document-signing/
export type EmbedDocumentFieldsProps = {
fields: Field[];
metadata?: Pick<
DocumentMeta | TemplateMeta,
DocumentMeta,
| 'timezone'
| 'dateFormat'
| 'typedSignatureEnabled'

View File

@ -3,7 +3,7 @@ import { useEffect, useId, useLayoutEffect, useMemo, useState } from 'react';
import { msg } from '@lingui/core/macro';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentMeta, TemplateMeta } from '@prisma/client';
import type { DocumentMeta } from '@prisma/client';
import {
type DocumentData,
type Field,
@ -50,7 +50,7 @@ export type EmbedSignDocumentClientPageProps = {
recipient: RecipientWithFields;
fields: Field[];
completedFields: DocumentField[];
metadata?: DocumentMeta | TemplateMeta | null;
metadata?: DocumentMeta | null;
isCompleted?: boolean;
hidePoweredBy?: boolean;
allowWhitelabelling?: boolean;

View File

@ -554,6 +554,12 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
status: 200,
body: {
...template,
templateMeta: template.templateMeta
? {
...template.templateMeta,
templateId: template.id,
}
: null,
Field: template.fields.map((field) => ({
...field,
fieldMeta: field.fieldMeta ? ZFieldMetaSchema.parse(field.fieldMeta) : null,

View File

@ -1,4 +1,4 @@
import type { DocumentVisibility, TemplateMeta } from '@prisma/client';
import type { DocumentMeta, DocumentVisibility } from '@prisma/client';
import {
DocumentSource,
RecipientRole,
@ -46,7 +46,7 @@ export type CreateDocumentOptions = {
formValues?: TDocumentFormValues;
recipients: TCreateDocumentV2Request['recipients'];
};
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
meta?: Partial<Omit<DocumentMeta, 'id' | 'templateId'>>;
requestMetadata: ApiRequestMetadata;
};

View File

@ -1,13 +0,0 @@
import { prisma } from '@documenso/prisma';
export interface GetDocumentMetaByDocumentIdOptions {
id: number;
}
export const getDocumentMetaByDocumentId = async ({ id }: GetDocumentMetaByDocumentIdOptions) => {
return await prisma.documentMeta.findFirstOrThrow({
where: {
documentId: id,
},
});
};

View File

@ -1,4 +1,4 @@
import type { DocumentVisibility, Template, TemplateMeta } from '@prisma/client';
import type { DocumentMeta, DocumentVisibility, Template } from '@prisma/client';
import type { z } from 'zod';
import { prisma } from '@documenso/prisma';
@ -26,7 +26,7 @@ export type CreateTemplateOptions = {
publicDescription?: string;
type?: Template['type'];
};
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
meta?: Partial<Omit<DocumentMeta, 'id' | 'templateId'>>;
};
export const ZCreateTemplateResponseSchema = TemplateSchema;

View File

@ -1,4 +1,4 @@
import type { DocumentVisibility, Template, TemplateMeta } from '@prisma/client';
import type { DocumentMeta, DocumentVisibility, Template } from '@prisma/client';
import { prisma } from '@documenso/prisma';
@ -22,7 +22,7 @@ export type UpdateTemplateOptions = {
type?: Template['type'];
useLegacyFieldInsertion?: boolean;
};
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
meta?: Partial<Omit<DocumentMeta, 'id' | 'templateId'>>;
};
export const updateTemplate = async ({

View File

@ -1,10 +1,10 @@
import type { z } from 'zod';
import { DocumentDataSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentDataSchema';
import { DocumentMetaSchema } from '@documenso/prisma/generated/zod/modelSchema/DocumentMetaSchema';
import { FolderSchema } from '@documenso/prisma/generated/zod/modelSchema/FolderSchema';
import TeamSchema from '@documenso/prisma/generated/zod/modelSchema/TeamSchema';
import { TemplateDirectLinkSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateDirectLinkSchema';
import { TemplateMetaSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateMetaSchema';
import { TemplateSchema } from '@documenso/prisma/generated/zod/modelSchema/TemplateSchema';
import { UserSchema } from '@documenso/prisma/generated/zod/modelSchema/UserSchema';
@ -39,7 +39,7 @@ export const ZTemplateSchema = TemplateSchema.pick({
data: true,
initialData: true,
}),
templateMeta: TemplateMetaSchema.pick({
templateMeta: DocumentMetaSchema.pick({
id: true,
subject: true,
message: true,
@ -129,7 +129,7 @@ export const ZTemplateManySchema = TemplateSchema.pick({
}).nullable(),
fields: ZFieldSchema.array(),
recipients: ZRecipientLiteSchema.array(),
templateMeta: TemplateMetaSchema.pick({
templateMeta: DocumentMetaSchema.pick({
signingOrder: true,
distributionMethod: true,
}).nullable(),

View File

@ -1,9 +1,4 @@
import type {
Document,
DocumentMeta,
OrganisationGlobalSettings,
TemplateMeta,
} from '@prisma/client';
import type { Document, DocumentMeta, OrganisationGlobalSettings } from '@prisma/client';
import { DocumentDistributionMethod, DocumentSigningOrder, DocumentStatus } from '@prisma/client';
import { DEFAULT_DOCUMENT_TIME_ZONE } from '../constants/time-zones';
@ -29,7 +24,7 @@ export const isDocumentCompleted = (document: Pick<Document, 'status'> | Documen
*/
export const extractDerivedDocumentMeta = (
settings: Omit<OrganisationGlobalSettings, 'id'>,
overrideMeta: Partial<DocumentMeta | TemplateMeta> | undefined | null,
overrideMeta: Partial<DocumentMeta> | undefined | null,
) => {
const meta = overrideMeta ?? {};
@ -58,5 +53,5 @@ export const extractDerivedDocumentMeta = (
emailReplyTo: meta.emailReplyTo ?? settings.emailReplyTo,
emailSettings:
meta.emailSettings || settings.emailDocumentSettings || DEFAULT_DOCUMENT_EMAIL_SETTINGS,
} satisfies Omit<DocumentMeta, 'id' | 'documentId'>;
} satisfies Omit<DocumentMeta, 'id' | 'documentId' | 'templateId'>;
};

View File

@ -0,0 +1,57 @@
-- DropForeignKey
ALTER TABLE "TemplateMeta" DROP CONSTRAINT "TemplateMeta_templateId_fkey";
-- AlterTable
ALTER TABLE "DocumentMeta" ADD COLUMN "templateId" INTEGER,
ALTER COLUMN "documentId" DROP NOT NULL;
-- [CUSTOM_CHANGE] Migrate existing TemplateMeta to DocumentMeta
INSERT INTO "DocumentMeta" (
"id",
"subject",
"message",
"timezone",
"password",
"dateFormat",
"redirectUrl",
"signingOrder",
"allowDictateNextSigner",
"typedSignatureEnabled",
"uploadSignatureEnabled",
"drawSignatureEnabled",
"language",
"distributionMethod",
"emailSettings",
"emailReplyTo",
"emailId",
"templateId"
)
SELECT
gen_random_uuid()::text, -- Generate new CUID-like IDs to avoid collisions
"subject",
"message",
"timezone",
"password",
"dateFormat",
"redirectUrl",
"signingOrder",
"allowDictateNextSigner",
"typedSignatureEnabled",
"uploadSignatureEnabled",
"drawSignatureEnabled",
"language",
"distributionMethod",
"emailSettings",
"emailReplyTo",
"emailId",
"templateId"
FROM "TemplateMeta";
-- DropTable
DROP TABLE "TemplateMeta";
-- CreateIndex
CREATE UNIQUE INDEX "DocumentMeta_templateId_key" ON "DocumentMeta"("templateId");
-- AddForeignKey
ALTER TABLE "DocumentMeta" ADD CONSTRAINT "DocumentMeta_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "Template"("id") ON DELETE CASCADE ON UPDATE CASCADE;

View File

@ -456,8 +456,6 @@ model DocumentMeta {
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)
redirectUrl String?
signingOrder DocumentSigningOrder @default(PARALLEL)
allowDictateNextSigner Boolean @default(false)
@ -472,6 +470,12 @@ model DocumentMeta {
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
emailReplyTo String?
emailId String?
documentId Int? @unique
document Document? @relation(fields: [documentId], references: [id], onDelete: Cascade)
templateId Int? @unique
template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade)
}
enum ReadStatus {
@ -842,32 +846,6 @@ enum TemplateType {
PRIVATE
}
/// @zod.import(["import { ZDocumentEmailSettingsSchema } from '@documenso/lib/types/document-email';"])
model TemplateMeta {
id String @id @default(cuid())
subject String?
message String?
timezone String? @default("Etc/UTC") @db.Text
password String?
dateFormat String? @default("yyyy-MM-dd hh:mm a") @db.Text
signingOrder DocumentSigningOrder? @default(PARALLEL)
allowDictateNextSigner Boolean @default(false)
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?
language String @default("en")
emailSettings Json? /// [DocumentEmailSettings] @zod.custom.use(ZDocumentEmailSettingsSchema)
emailReplyTo String?
emailId String?
}
/// @zod.import(["import { ZDocumentAuthOptionsSchema } from '@documenso/lib/types/document-auth';"])
model Template {
id Int @id @default(autoincrement())
@ -876,7 +854,7 @@ model Template {
title String
visibility DocumentVisibility @default(EVERYONE)
authOptions Json? /// [DocumentAuthOptions] @zod.custom.use(ZDocumentAuthOptionsSchema)
templateMeta TemplateMeta?
templateMeta DocumentMeta?
templateDocumentDataId String
createdAt DateTime @default(now())
updatedAt DateTime @default(now()) @updatedAt

View File

@ -94,7 +94,7 @@ export const createEmbeddingTemplateRoute = procedure
emailSettings: meta.emailSettings,
};
await prisma.templateMeta.upsert({
await prisma.documentMeta.upsert({
where: {
templateId: template.id,
},

View File

@ -2,7 +2,7 @@ import { useState } from 'react';
import { useLingui } from '@lingui/react';
import { Trans } from '@lingui/react/macro';
import type { DocumentMeta, Field, Recipient, TemplateMeta } from '@prisma/client';
import type { DocumentMeta, Field, Recipient } from '@prisma/client';
import { SigningStatus } from '@prisma/client';
import { Clock, EyeOffIcon } from 'lucide-react';
@ -36,7 +36,7 @@ const getRecipientDisplayText = (recipient: { name: string; email: string }) =>
export type DocumentReadOnlyFieldsProps = {
fields: DocumentField[];
documentMeta?: Pick<DocumentMeta | TemplateMeta, 'dateFormat'>;
documentMeta?: Pick<DocumentMeta, 'dateFormat'>;
showFieldStatus?: boolean;

View File

@ -1,5 +1,5 @@
import { useLingui } from '@lingui/react';
import type { DocumentMeta, Signature, TemplateMeta } from '@prisma/client';
import type { DocumentMeta, Signature } from '@prisma/client';
import { FieldType } from '@prisma/client';
import { ChevronDown } from 'lucide-react';
@ -27,7 +27,7 @@ type FieldIconProps = {
fieldMeta?: TFieldMetaSchema | null;
signature?: Signature | null;
};
documentMeta?: Pick<DocumentMeta | TemplateMeta, 'dateFormat'>;
documentMeta?: Pick<DocumentMeta, 'dateFormat'>;
};
/**