feat: create document in a specific folder (#1965)

This commit is contained in:
Catalin Pit
2025-08-22 17:12:17 +03:00
committed by GitHub
parent 17b36ac8e4
commit 67501b45cf
8 changed files with 77 additions and 3 deletions

View File

@ -330,6 +330,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
userId: user.id, userId: user.id,
teamId: team?.id, teamId: team?.id,
formValues: body.formValues, formValues: body.formValues,
folderId: body.folderId,
documentDataId: documentData.id, documentDataId: documentData.id,
requestMetadata: metadata, requestMetadata: metadata,
}); });
@ -736,6 +737,7 @@ export const ApiContractV1Implementation = tsr.router(ApiContractV1, {
teamId: team?.id, teamId: team?.id,
recipients: body.recipients, recipients: body.recipients,
prefillFields: body.prefillFields, prefillFields: body.prefillFields,
folderId: body.folderId,
override: { override: {
title: body.title, title: body.title,
...body.meta, ...body.meta,

View File

@ -136,6 +136,12 @@ export type TUploadDocumentSuccessfulSchema = z.infer<typeof ZUploadDocumentSucc
export const ZCreateDocumentMutationSchema = z.object({ export const ZCreateDocumentMutationSchema = z.object({
title: z.string().min(1), title: z.string().min(1),
externalId: z.string().nullish(), externalId: z.string().nullish(),
folderId: z
.string()
.describe(
'The ID of the folder to create the document in. If not provided, the document will be created in the root folder.',
)
.optional(),
recipients: z.array( recipients: z.array(
z.object({ z.object({
name: z.string().min(1), name: z.string().min(1),
@ -287,6 +293,12 @@ export type TCreateDocumentFromTemplateMutationResponseSchema = z.infer<
export const ZGenerateDocumentFromTemplateMutationSchema = z.object({ export const ZGenerateDocumentFromTemplateMutationSchema = z.object({
title: z.string().optional(), title: z.string().optional(),
externalId: z.string().optional(), externalId: z.string().optional(),
folderId: z
.string()
.describe(
'The ID of the folder to create the document in. If not provided, the document will be created in the root folder.',
)
.optional(),
recipients: z recipients: z
.array( .array(
z.object({ z.object({

View File

@ -1,6 +1,7 @@
import type { DocumentVisibility, TemplateMeta } from '@prisma/client'; import type { DocumentVisibility, TemplateMeta } from '@prisma/client';
import { import {
DocumentSource, DocumentSource,
FolderType,
RecipientRole, RecipientRole,
SendStatus, SendStatus,
SigningStatus, SigningStatus,
@ -45,6 +46,7 @@ export type CreateDocumentOptions = {
globalActionAuth?: TDocumentActionAuthTypes[]; globalActionAuth?: TDocumentActionAuthTypes[];
formValues?: TDocumentFormValues; formValues?: TDocumentFormValues;
recipients: TCreateDocumentV2Request['recipients']; recipients: TCreateDocumentV2Request['recipients'];
folderId?: string;
}; };
meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>; meta?: Partial<Omit<TemplateMeta, 'id' | 'templateId'>>;
requestMetadata: ApiRequestMetadata; requestMetadata: ApiRequestMetadata;
@ -59,7 +61,7 @@ export const createDocumentV2 = async ({
meta, meta,
requestMetadata, requestMetadata,
}: CreateDocumentOptions) => { }: CreateDocumentOptions) => {
const { title, formValues } = data; const { title, formValues, folderId } = data;
const team = await prisma.team.findFirst({ const team = await prisma.team.findFirst({
where: buildTeamWhereQuery({ teamId, userId }), where: buildTeamWhereQuery({ teamId, userId }),
@ -78,6 +80,22 @@ export const createDocumentV2 = async ({
}); });
} }
if (folderId) {
const folder = await prisma.folder.findUnique({
where: {
id: folderId,
type: FolderType.DOCUMENT,
team: buildTeamWhereQuery({ teamId, userId }),
},
});
if (!folder) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Folder not found',
});
}
}
const settings = await getTeamSettings({ const settings = await getTeamSettings({
userId, userId,
teamId, teamId,
@ -164,6 +182,7 @@ export const createDocumentV2 = async ({
teamId, teamId,
authOptions, authOptions,
visibility, visibility,
folderId,
formValues, formValues,
source: DocumentSource.DOCUMENT, source: DocumentSource.DOCUMENT,
documentMeta: { documentMeta: {

View File

@ -2,6 +2,7 @@ import type { DocumentDistributionMethod, DocumentSigningOrder } from '@prisma/c
import { import {
DocumentSource, DocumentSource,
type Field, type Field,
FolderType,
type Recipient, type Recipient,
RecipientRole, RecipientRole,
SendStatus, SendStatus,
@ -69,6 +70,7 @@ export type CreateDocumentFromTemplateOptions = {
email: string; email: string;
signingOrder?: number | null; signingOrder?: number | null;
}[]; }[];
folderId?: string;
prefillFields?: TFieldMetaPrefillFieldsSchema[]; prefillFields?: TFieldMetaPrefillFieldsSchema[];
customDocumentDataId?: string; customDocumentDataId?: string;
@ -274,6 +276,7 @@ export const createDocumentFromTemplate = async ({
customDocumentDataId, customDocumentDataId,
override, override,
requestMetadata, requestMetadata,
folderId,
prefillFields, prefillFields,
}: CreateDocumentFromTemplateOptions) => { }: CreateDocumentFromTemplateOptions) => {
const template = await prisma.template.findUnique({ const template = await prisma.template.findUnique({
@ -298,6 +301,22 @@ export const createDocumentFromTemplate = async ({
}); });
} }
if (folderId) {
const folder = await prisma.folder.findUnique({
where: {
id: folderId,
type: FolderType.DOCUMENT,
team: buildTeamWhereQuery({ teamId, userId }),
},
});
if (!folder) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Folder not found',
});
}
}
const settings = await getTeamSettings({ const settings = await getTeamSettings({
userId, userId,
teamId, teamId,
@ -368,6 +387,7 @@ export const createDocumentFromTemplate = async ({
externalId: externalId || template.externalId, externalId: externalId || template.externalId,
templateId: template.id, templateId: template.id,
userId, userId,
folderId,
teamId: template.teamId, teamId: template.teamId,
title: override?.title || template.title, title: override?.title || template.title,
documentDataId: documentData.id, documentDataId: documentData.id,

View File

@ -284,6 +284,7 @@ export const documentRouter = router({
globalActionAuth, globalActionAuth,
recipients, recipients,
meta, meta,
folderId,
} = input; } = input;
const { remaining } = await getServerLimits({ userId: user.id, teamId }); const { remaining } = await getServerLimits({ userId: user.id, teamId });
@ -316,6 +317,7 @@ export const documentRouter = router({
globalAccessAuth, globalAccessAuth,
globalActionAuth, globalActionAuth,
recipients, recipients,
folderId,
}, },
meta, meta,
requestMetadata: ctx.metadata, requestMetadata: ctx.metadata,

View File

@ -209,6 +209,12 @@ export const ZCreateDocumentV2RequestSchema = z.object({
globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(), globalAccessAuth: z.array(ZDocumentAccessAuthTypesSchema).optional(),
globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional(), globalActionAuth: z.array(ZDocumentActionAuthTypesSchema).optional(),
formValues: ZDocumentFormValuesSchema.optional(), formValues: ZDocumentFormValuesSchema.optional(),
folderId: z
.string()
.describe(
'The ID of the folder to create the document in. If not provided, the document will be created in the root folder.',
)
.optional(),
recipients: z recipients: z
.array( .array(
ZCreateRecipientSchema.extend({ ZCreateRecipientSchema.extend({

View File

@ -339,8 +339,14 @@ export const templateRouter = router({
.output(ZCreateDocumentFromTemplateResponseSchema) .output(ZCreateDocumentFromTemplateResponseSchema)
.mutation(async ({ ctx, input }) => { .mutation(async ({ ctx, input }) => {
const { teamId } = ctx; const { teamId } = ctx;
const { templateId, recipients, distributeDocument, customDocumentDataId, prefillFields } = const {
input; templateId,
recipients,
distributeDocument,
customDocumentDataId,
prefillFields,
folderId,
} = input;
ctx.logger.info({ ctx.logger.info({
input: { input: {
@ -361,6 +367,7 @@ export const templateRouter = router({
recipients, recipients,
customDocumentDataId, customDocumentDataId,
requestMetadata: ctx.metadata, requestMetadata: ctx.metadata,
folderId,
prefillFields, prefillFields,
}); });

View File

@ -117,6 +117,12 @@ export const ZCreateDocumentFromTemplateRequestSchema = z.object({
'The data ID of an alternative PDF to use when creating the document. If not provided, the PDF attached to the template will be used.', 'The data ID of an alternative PDF to use when creating the document. If not provided, the PDF attached to the template will be used.',
) )
.optional(), .optional(),
folderId: z
.string()
.describe(
'The ID of the folder to create the document in. If not provided, the document will be created in the root folder.',
)
.optional(),
prefillFields: z prefillFields: z
.array(ZFieldMetaPrefillFieldsSchema) .array(ZFieldMetaPrefillFieldsSchema)
.describe( .describe(