mirror of
https://github.com/documenso/documenso.git
synced 2025-11-14 08:42:12 +10:00
feat: add folders (#1711)
This commit is contained in:
@ -107,8 +107,17 @@ export const documentRouter = router({
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { user, teamId } = ctx;
|
||||
|
||||
const { query, templateId, page, perPage, orderByDirection, orderByColumn, source, status } =
|
||||
input;
|
||||
const {
|
||||
query,
|
||||
templateId,
|
||||
page,
|
||||
perPage,
|
||||
orderByDirection,
|
||||
orderByColumn,
|
||||
source,
|
||||
status,
|
||||
folderId,
|
||||
} = input;
|
||||
|
||||
const documents = await findDocuments({
|
||||
userId: user.id,
|
||||
@ -119,6 +128,7 @@ export const documentRouter = router({
|
||||
status,
|
||||
page,
|
||||
perPage,
|
||||
folderId,
|
||||
orderBy: orderByColumn ? { column: orderByColumn, direction: orderByDirection } : undefined,
|
||||
});
|
||||
|
||||
@ -147,12 +157,14 @@ export const documentRouter = router({
|
||||
status,
|
||||
period,
|
||||
senderIds,
|
||||
folderId,
|
||||
} = input;
|
||||
|
||||
const getStatOptions: GetStatsInput = {
|
||||
user,
|
||||
period,
|
||||
search: query,
|
||||
folderId,
|
||||
};
|
||||
|
||||
if (teamId) {
|
||||
@ -181,6 +193,7 @@ export const documentRouter = router({
|
||||
status,
|
||||
period,
|
||||
senderIds,
|
||||
folderId,
|
||||
orderBy: orderByColumn
|
||||
? { column: orderByColumn, direction: orderByDirection }
|
||||
: undefined,
|
||||
@ -212,12 +225,13 @@ export const documentRouter = router({
|
||||
.output(ZGetDocumentWithDetailsByIdResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { documentId } = input;
|
||||
const { documentId, folderId } = input;
|
||||
|
||||
return await getDocumentWithDetailsById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
documentId,
|
||||
folderId,
|
||||
});
|
||||
}),
|
||||
|
||||
@ -290,6 +304,7 @@ export const documentRouter = router({
|
||||
|
||||
return {
|
||||
document: createdDocument,
|
||||
folder: createdDocument.folder,
|
||||
uploadUrl: url,
|
||||
};
|
||||
}),
|
||||
@ -311,7 +326,7 @@ export const documentRouter = router({
|
||||
.input(ZCreateDocumentRequestSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
const { title, documentDataId, timezone } = input;
|
||||
const { title, documentDataId, timezone, folderId } = input;
|
||||
|
||||
const { remaining } = await getServerLimits({ email: ctx.user.email, teamId });
|
||||
|
||||
@ -330,6 +345,7 @@ export const documentRouter = router({
|
||||
normalizePdf: true,
|
||||
timezone,
|
||||
requestMetadata: ctx.metadata,
|
||||
folderId,
|
||||
});
|
||||
}),
|
||||
|
||||
|
||||
@ -130,6 +130,7 @@ export const ZFindDocumentsRequestSchema = ZFindSearchParamsSchema.extend({
|
||||
.nativeEnum(DocumentStatus)
|
||||
.describe('Filter documents by the current status')
|
||||
.optional(),
|
||||
folderId: z.string().describe('Filter documents by folder ID').optional(),
|
||||
orderByColumn: z.enum(['createdAt']).optional(),
|
||||
orderByDirection: z.enum(['asc', 'desc']).describe('').default('desc'),
|
||||
});
|
||||
@ -144,6 +145,7 @@ export const ZFindDocumentsInternalRequestSchema = ZFindDocumentsRequestSchema.e
|
||||
period: z.enum(['7d', '14d', '30d']).optional(),
|
||||
senderIds: z.array(z.number()).optional(),
|
||||
status: z.nativeEnum(ExtendedDocumentStatus).optional(),
|
||||
folderId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const ZFindDocumentsInternalResponseSchema = ZFindResultResponse.extend({
|
||||
@ -188,6 +190,7 @@ export type TGetDocumentByTokenQuerySchema = z.infer<typeof ZGetDocumentByTokenQ
|
||||
|
||||
export const ZGetDocumentWithDetailsByIdRequestSchema = z.object({
|
||||
documentId: z.number(),
|
||||
folderId: z.string().describe('Filter documents by folder ID').optional(),
|
||||
});
|
||||
|
||||
export const ZGetDocumentWithDetailsByIdResponseSchema = ZDocumentSchema;
|
||||
@ -196,6 +199,7 @@ export const ZCreateDocumentRequestSchema = z.object({
|
||||
title: ZDocumentTitleSchema,
|
||||
documentDataId: z.string().min(1),
|
||||
timezone: ZDocumentMetaTimezoneSchema.optional(),
|
||||
folderId: z.string().describe('The ID of the folder to create the document in').optional(),
|
||||
});
|
||||
|
||||
export const ZCreateDocumentV2RequestSchema = z.object({
|
||||
|
||||
354
packages/trpc/server/folder-router/router.ts
Normal file
354
packages/trpc/server/folder-router/router.ts
Normal file
@ -0,0 +1,354 @@
|
||||
import { TRPCError } from '@trpc/server';
|
||||
|
||||
import { createFolder } from '@documenso/lib/server-only/folder/create-folder';
|
||||
import { deleteFolder } from '@documenso/lib/server-only/folder/delete-folder';
|
||||
import { findFolders } from '@documenso/lib/server-only/folder/find-folders';
|
||||
import { getFolderBreadcrumbs } from '@documenso/lib/server-only/folder/get-folder-breadcrumbs';
|
||||
import { getFolderById } from '@documenso/lib/server-only/folder/get-folder-by-id';
|
||||
import { moveDocumentToFolder } from '@documenso/lib/server-only/folder/move-document-to-folder';
|
||||
import { moveFolder } from '@documenso/lib/server-only/folder/move-folder';
|
||||
import { moveTemplateToFolder } from '@documenso/lib/server-only/folder/move-template-to-folder';
|
||||
import { pinFolder } from '@documenso/lib/server-only/folder/pin-folder';
|
||||
import { unpinFolder } from '@documenso/lib/server-only/folder/unpin-folder';
|
||||
import { updateFolder } from '@documenso/lib/server-only/folder/update-folder';
|
||||
import { FolderType } from '@documenso/lib/types/folder-type';
|
||||
|
||||
import { authenticatedProcedure, router } from '../trpc';
|
||||
import {
|
||||
ZCreateFolderSchema,
|
||||
ZDeleteFolderSchema,
|
||||
ZFindFoldersRequestSchema,
|
||||
ZFindFoldersResponseSchema,
|
||||
ZGenericSuccessResponse,
|
||||
ZGetFoldersResponseSchema,
|
||||
ZGetFoldersSchema,
|
||||
ZMoveDocumentToFolderSchema,
|
||||
ZMoveFolderSchema,
|
||||
ZMoveTemplateToFolderSchema,
|
||||
ZPinFolderSchema,
|
||||
ZSuccessResponseSchema,
|
||||
ZUnpinFolderSchema,
|
||||
ZUpdateFolderSchema,
|
||||
} from './schema';
|
||||
|
||||
export const folderRouter = router({
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
getFolders: authenticatedProcedure
|
||||
.input(ZGetFoldersSchema)
|
||||
.output(ZGetFoldersResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { parentId, type } = input;
|
||||
|
||||
const folders = await findFolders({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
parentId,
|
||||
type,
|
||||
});
|
||||
|
||||
const breadcrumbs = parentId
|
||||
? await getFolderBreadcrumbs({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: parentId,
|
||||
type,
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
folders,
|
||||
breadcrumbs,
|
||||
type,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
findFolders: authenticatedProcedure
|
||||
.input(ZFindFoldersRequestSchema)
|
||||
.output(ZFindFoldersResponseSchema)
|
||||
.query(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { parentId, type } = input;
|
||||
|
||||
const folders = await findFolders({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
parentId,
|
||||
type,
|
||||
});
|
||||
|
||||
const breadcrumbs = parentId
|
||||
? await getFolderBreadcrumbs({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: parentId,
|
||||
type,
|
||||
})
|
||||
: [];
|
||||
|
||||
return {
|
||||
data: folders,
|
||||
breadcrumbs,
|
||||
type,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
createFolder: authenticatedProcedure
|
||||
.input(ZCreateFolderSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { name, parentId, type } = input;
|
||||
|
||||
if (parentId) {
|
||||
try {
|
||||
await getFolderById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: parentId,
|
||||
type,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Parent folder not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result = await createFolder({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
name,
|
||||
parentId,
|
||||
type,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
updateFolder: authenticatedProcedure
|
||||
.input(ZUpdateFolderSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { id, name, visibility } = input;
|
||||
|
||||
const currentFolder = await getFolderById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: id,
|
||||
});
|
||||
|
||||
const result = await updateFolder({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: id,
|
||||
name,
|
||||
visibility,
|
||||
type: currentFolder.type,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type: currentFolder.type,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
deleteFolder: authenticatedProcedure
|
||||
.input(ZDeleteFolderSchema)
|
||||
.output(ZSuccessResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { id } = input;
|
||||
|
||||
await deleteFolder({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: id,
|
||||
});
|
||||
|
||||
return ZGenericSuccessResponse;
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
moveFolder: authenticatedProcedure.input(ZMoveFolderSchema).mutation(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { id, parentId } = input;
|
||||
|
||||
const currentFolder = await getFolderById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: id,
|
||||
});
|
||||
|
||||
if (parentId !== null) {
|
||||
try {
|
||||
await getFolderById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: parentId,
|
||||
type: currentFolder.type,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Parent folder not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result = await moveFolder({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId: id,
|
||||
parentId,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type: currentFolder.type,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
moveDocumentToFolder: authenticatedProcedure
|
||||
.input(ZMoveDocumentToFolderSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { documentId, folderId } = input;
|
||||
|
||||
if (folderId !== null) {
|
||||
try {
|
||||
await getFolderById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId,
|
||||
type: FolderType.DOCUMENT,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Folder not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result = await moveDocumentToFolder({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
documentId,
|
||||
folderId,
|
||||
requestMetadata: ctx.metadata,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type: FolderType.DOCUMENT,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
moveTemplateToFolder: authenticatedProcedure
|
||||
.input(ZMoveTemplateToFolderSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId, user } = ctx;
|
||||
const { templateId, folderId } = input;
|
||||
|
||||
if (folderId !== null) {
|
||||
try {
|
||||
await getFolderById({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
folderId,
|
||||
type: FolderType.TEMPLATE,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new TRPCError({
|
||||
code: 'NOT_FOUND',
|
||||
message: 'Folder not found',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const result = await moveTemplateToFolder({
|
||||
userId: user.id,
|
||||
teamId,
|
||||
templateId,
|
||||
folderId,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type: FolderType.TEMPLATE,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
pinFolder: authenticatedProcedure.input(ZPinFolderSchema).mutation(async ({ ctx, input }) => {
|
||||
const currentFolder = await getFolderById({
|
||||
userId: ctx.user.id,
|
||||
teamId: ctx.teamId,
|
||||
folderId: input.folderId,
|
||||
});
|
||||
|
||||
const result = await pinFolder({
|
||||
userId: ctx.user.id,
|
||||
teamId: ctx.teamId,
|
||||
folderId: input.folderId,
|
||||
type: currentFolder.type,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type: currentFolder.type,
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
unpinFolder: authenticatedProcedure.input(ZUnpinFolderSchema).mutation(async ({ ctx, input }) => {
|
||||
const currentFolder = await getFolderById({
|
||||
userId: ctx.user.id,
|
||||
teamId: ctx.teamId,
|
||||
folderId: input.folderId,
|
||||
});
|
||||
|
||||
const result = await unpinFolder({
|
||||
userId: ctx.user.id,
|
||||
teamId: ctx.teamId,
|
||||
folderId: input.folderId,
|
||||
type: currentFolder.type,
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
type: currentFolder.type,
|
||||
};
|
||||
}),
|
||||
});
|
||||
132
packages/trpc/server/folder-router/schema.ts
Normal file
132
packages/trpc/server/folder-router/schema.ts
Normal file
@ -0,0 +1,132 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import { ZFolderTypeSchema } from '@documenso/lib/types/folder-type';
|
||||
import { DocumentVisibility } from '@documenso/prisma/generated/types';
|
||||
|
||||
/**
|
||||
* Required for empty responses since we currently can't 201 requests for our openapi setup.
|
||||
*
|
||||
* Without this it will throw an error in Speakeasy SDK when it tries to parse an empty response.
|
||||
*/
|
||||
export const ZSuccessResponseSchema = z.object({
|
||||
success: z.boolean(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZGenericSuccessResponse = {
|
||||
success: true,
|
||||
} satisfies z.infer<typeof ZSuccessResponseSchema>;
|
||||
|
||||
export const ZFolderSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
userId: z.number(),
|
||||
teamId: z.number().nullable(),
|
||||
parentId: z.string().nullable(),
|
||||
pinned: z.boolean(),
|
||||
createdAt: z.date(),
|
||||
updatedAt: z.date(),
|
||||
visibility: z.nativeEnum(DocumentVisibility),
|
||||
type: ZFolderTypeSchema,
|
||||
});
|
||||
|
||||
export type TFolder = z.infer<typeof ZFolderSchema>;
|
||||
|
||||
const ZFolderCountSchema = z.object({
|
||||
documents: z.number(),
|
||||
templates: z.number(),
|
||||
subfolders: z.number(),
|
||||
});
|
||||
|
||||
const ZSubfolderSchema = ZFolderSchema.extend({
|
||||
subfolders: z.array(z.any()),
|
||||
_count: ZFolderCountSchema,
|
||||
});
|
||||
|
||||
export const ZFolderWithSubfoldersSchema = ZFolderSchema.extend({
|
||||
subfolders: z.array(ZSubfolderSchema),
|
||||
_count: ZFolderCountSchema,
|
||||
});
|
||||
|
||||
export type TFolderWithSubfolders = z.infer<typeof ZFolderWithSubfoldersSchema>;
|
||||
|
||||
export const ZCreateFolderSchema = z.object({
|
||||
name: z.string(),
|
||||
parentId: z.string().optional(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZUpdateFolderSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
visibility: z.nativeEnum(DocumentVisibility),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export type TUpdateFolderSchema = z.infer<typeof ZUpdateFolderSchema>;
|
||||
|
||||
export const ZDeleteFolderSchema = z.object({
|
||||
id: z.string(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZMoveFolderSchema = z.object({
|
||||
id: z.string(),
|
||||
parentId: z.string().nullable(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZMoveDocumentToFolderSchema = z.object({
|
||||
documentId: z.number(),
|
||||
folderId: z.string().nullable().optional(),
|
||||
type: z.enum(['DOCUMENT']).optional(),
|
||||
});
|
||||
|
||||
export const ZMoveTemplateToFolderSchema = z.object({
|
||||
templateId: z.number(),
|
||||
folderId: z.string().nullable().optional(),
|
||||
type: z.enum(['TEMPLATE']).optional(),
|
||||
});
|
||||
|
||||
export const ZPinFolderSchema = z.object({
|
||||
folderId: z.string(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZUnpinFolderSchema = z.object({
|
||||
folderId: z.string(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZGetFoldersSchema = z.object({
|
||||
parentId: z.string().nullable().optional(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZGetFoldersResponseSchema = z.object({
|
||||
folders: z.array(ZFolderWithSubfoldersSchema),
|
||||
breadcrumbs: z.array(ZFolderSchema),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export type TGetFoldersResponse = z.infer<typeof ZGetFoldersResponseSchema>;
|
||||
|
||||
export const ZFindSearchParamsSchema = z.object({
|
||||
query: z.string().optional(),
|
||||
page: z.number().optional(),
|
||||
perPage: z.number().optional(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZFindFoldersRequestSchema = ZFindSearchParamsSchema.extend({
|
||||
parentId: z.string().nullable().optional(),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export const ZFindFoldersResponseSchema = z.object({
|
||||
data: z.array(ZFolderWithSubfoldersSchema),
|
||||
breadcrumbs: z.array(ZFolderSchema),
|
||||
type: ZFolderTypeSchema.optional(),
|
||||
});
|
||||
|
||||
export type TFindFoldersResponse = z.infer<typeof ZFindFoldersResponseSchema>;
|
||||
@ -4,6 +4,7 @@ import { authRouter } from './auth-router/router';
|
||||
import { documentRouter } from './document-router/router';
|
||||
import { embeddingPresignRouter } from './embedding-router/_router';
|
||||
import { fieldRouter } from './field-router/router';
|
||||
import { folderRouter } from './folder-router/router';
|
||||
import { profileRouter } from './profile-router/router';
|
||||
import { recipientRouter } from './recipient-router/router';
|
||||
import { shareLinkRouter } from './share-link-router/router';
|
||||
@ -17,6 +18,7 @@ export const appRouter = router({
|
||||
profile: profileRouter,
|
||||
document: documentRouter,
|
||||
field: fieldRouter,
|
||||
folder: folderRouter,
|
||||
recipient: recipientRouter,
|
||||
admin: adminRouter,
|
||||
shareLink: shareLinkRouter,
|
||||
|
||||
@ -121,13 +121,14 @@ export const templateRouter = router({
|
||||
.output(ZCreateTemplateResponseSchema)
|
||||
.mutation(async ({ input, ctx }) => {
|
||||
const { teamId } = ctx;
|
||||
const { title, templateDocumentDataId } = input;
|
||||
const { title, templateDocumentDataId, folderId } = input;
|
||||
|
||||
return await createTemplate({
|
||||
userId: ctx.user.id,
|
||||
teamId,
|
||||
title,
|
||||
templateDocumentDataId,
|
||||
folderId,
|
||||
});
|
||||
}),
|
||||
|
||||
|
||||
@ -33,6 +33,7 @@ import { ZSignFieldWithTokenMutationSchema } from '../field-router/schema';
|
||||
export const ZCreateTemplateMutationSchema = z.object({
|
||||
title: z.string().min(1).trim(),
|
||||
templateDocumentDataId: z.string().min(1),
|
||||
folderId: z.string().optional(),
|
||||
});
|
||||
|
||||
export const ZCreateDocumentFromDirectTemplateRequestSchema = z.object({
|
||||
@ -179,6 +180,7 @@ export const ZUpdateTemplateResponseSchema = ZTemplateLiteSchema;
|
||||
|
||||
export const ZFindTemplatesRequestSchema = ZFindSearchParamsSchema.extend({
|
||||
type: z.nativeEnum(TemplateType).describe('Filter templates by type.').optional(),
|
||||
folderId: z.string().describe('The ID of the folder to filter templates by.').optional(),
|
||||
});
|
||||
|
||||
export const ZFindTemplatesResponseSchema = ZFindResultResponse.extend({
|
||||
|
||||
Reference in New Issue
Block a user