feat: add template page (#1395)

Add a template page view to allow users to see more details about a
template at a glance.
This commit is contained in:
David Nguyen
2024-11-05 17:36:30 +09:00
committed by GitHub
parent 32b65c4d49
commit 4fa6dc1e21
24 changed files with 1096 additions and 96 deletions

View File

@ -32,7 +32,7 @@ test.describe('[EE_ONLY]', () => {
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}`,
redirectPath: `/templates/${template.id}/edit`,
});
// Set EE action auth.
@ -74,7 +74,7 @@ test.describe('[EE_ONLY]', () => {
await apiSignin({
page,
email: teamMemberUser.email,
redirectPath: `/t/${team.url}/templates/${template.id}`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set EE action auth.
@ -110,7 +110,7 @@ test.describe('[EE_ONLY]', () => {
await apiSignin({
page,
email: teamMemberUser.email,
redirectPath: `/templates/${template.id}`,
redirectPath: `/templates/${template.id}/edit`,
});
// Global action auth should not be visible.
@ -132,7 +132,7 @@ test('[TEMPLATE_FLOW]: add settings', async ({ page }) => {
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}`,
redirectPath: `/templates/${template.id}/edit`,
});
// Set title.

View File

@ -31,7 +31,7 @@ test.describe('[EE_ONLY]', () => {
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}`,
redirectPath: `/templates/${template.id}/edit`,
});
// Save the settings by going to the next step.
@ -81,7 +81,7 @@ test('[TEMPLATE_FLOW]: add placeholder', async ({ page }) => {
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}`,
redirectPath: `/templates/${template.id}/edit`,
});
// Save the settings by going to the next step.

View File

@ -37,7 +37,7 @@ test('[TEMPLATE]: should create a document from a template', async ({ page }) =>
await apiSignin({
page,
email: user.email,
redirectPath: `/templates/${template.id}`,
redirectPath: `/templates/${template.id}/edit`,
});
// Set template title.
@ -172,7 +172,7 @@ test('[TEMPLATE]: should create a team document from a team template', async ({
await apiSignin({
page,
email: owner.email,
redirectPath: `/t/${team.url}/templates/${template.id}`,
redirectPath: `/t/${team.url}/templates/${template.id}/edit`,
});
// Set template title.

View File

@ -3,7 +3,14 @@ import { P, match } from 'ts-pattern';
import { prisma } from '@documenso/prisma';
import { RecipientRole, SigningStatus, TeamMemberRole } from '@documenso/prisma/client';
import type { Document, Prisma, Team, TeamEmail, User } from '@documenso/prisma/client';
import type {
Document,
DocumentSource,
Prisma,
Team,
TeamEmail,
User,
} from '@documenso/prisma/client';
import { ExtendedDocumentStatus } from '@documenso/prisma/types/extended-document-status';
import { DocumentVisibility } from '../../types/document-visibility';
@ -16,6 +23,8 @@ export type FindDocumentsOptions = {
userId: number;
teamId?: number;
term?: string;
templateId?: number;
source?: DocumentSource;
status?: ExtendedDocumentStatus;
page?: number;
perPage?: number;
@ -32,6 +41,8 @@ export const findDocuments = async ({
userId,
teamId,
term,
templateId,
source,
status = ExtendedDocumentStatus.ALL,
page = 1,
perPage = 10,
@ -40,44 +51,37 @@ export const findDocuments = async ({
senderIds,
search,
}: FindDocumentsOptions) => {
const { user, team } = await prisma.$transaction(async (tx) => {
const user = await tx.user.findFirstOrThrow({
const user = await prisma.user.findFirstOrThrow({
where: {
id: userId,
},
});
let team = null;
if (teamId !== undefined) {
team = await prisma.team.findFirstOrThrow({
where: {
id: userId,
id: teamId,
members: {
some: {
userId,
},
},
},
include: {
teamEmail: true,
members: {
where: {
userId,
},
select: {
role: true,
},
},
},
});
let team = null;
if (teamId !== undefined) {
team = await tx.team.findFirstOrThrow({
where: {
id: teamId,
members: {
some: {
userId,
},
},
},
include: {
teamEmail: true,
members: {
where: {
userId,
},
select: {
role: true,
},
},
},
});
}
return {
user,
team,
};
});
}
const orderByColumn = orderBy?.column ?? 'createdAt';
const orderByDirection = orderBy?.direction ?? 'desc';
@ -197,8 +201,27 @@ export const findDocuments = async ({
};
}
const whereAndClause: Prisma.DocumentWhereInput['AND'] = [
{ ...termFilters },
{ ...filters },
{ ...deletedFilter },
{ ...searchFilter },
];
if (templateId) {
whereAndClause.push({
templateId,
});
}
if (source) {
whereAndClause.push({
source,
});
}
const whereClause: Prisma.DocumentWhereInput = {
AND: [{ ...termFilters }, { ...filters }, { ...deletedFilter }, { ...searchFilter }],
AND: whereAndClause,
};
if (period) {

View File

@ -42,6 +42,13 @@ export const getTemplateById = async ({ id, userId, teamId }: GetTemplateByIdOpt
templateMeta: true,
Recipient: true,
Field: true,
User: {
select: {
id: true,
name: true,
email: true,
},
},
},
});

View File

@ -11,6 +11,7 @@ import { createDocument } from '@documenso/lib/server-only/document/create-docum
import { deleteDocument } from '@documenso/lib/server-only/document/delete-document';
import { duplicateDocumentById } from '@documenso/lib/server-only/document/duplicate-document-by-id';
import { findDocumentAuditLogs } from '@documenso/lib/server-only/document/find-document-audit-logs';
import { findDocuments } from '@documenso/lib/server-only/document/find-documents';
import { getDocumentById } from '@documenso/lib/server-only/document/get-document-by-id';
import { getDocumentAndSenderByToken } from '@documenso/lib/server-only/document/get-document-by-token';
import { getDocumentWithDetailsById } from '@documenso/lib/server-only/document/get-document-with-details-by-id';
@ -31,6 +32,7 @@ import {
ZDownloadAuditLogsMutationSchema,
ZDownloadCertificateMutationSchema,
ZFindDocumentAuditLogsQuerySchema,
ZFindDocumentsQuerySchema,
ZGetDocumentByIdQuerySchema,
ZGetDocumentByTokenQuerySchema,
ZGetDocumentWithDetailsByIdQuerySchema,
@ -190,6 +192,37 @@ export const documentRouter = router({
}
}),
findDocuments: authenticatedProcedure
.input(ZFindDocumentsQuerySchema)
.query(async ({ input, ctx }) => {
const { user } = ctx;
const { search, teamId, templateId, page, perPage, orderBy, source, status } = input;
try {
const documents = await findDocuments({
userId: user.id,
teamId,
templateId,
search,
source,
status,
page,
perPage,
orderBy,
});
return documents;
} catch (err) {
console.error(err);
throw new TRPCError({
code: 'BAD_REQUEST',
message: 'We are unable to search for documents. Please try again later.',
});
}
}),
findDocumentAuditLogs: authenticatedProcedure
.input(ZFindDocumentAuditLogsQuerySchema)
.query(async ({ input, ctx }) => {

View File

@ -7,7 +7,30 @@ import {
} from '@documenso/lib/types/document-auth';
import { ZBaseTableSearchParamsSchema } from '@documenso/lib/types/search-params';
import { isValidRedirectUrl } from '@documenso/lib/utils/is-valid-redirect-url';
import { DocumentSigningOrder, FieldType, RecipientRole } from '@documenso/prisma/client';
import {
DocumentSigningOrder,
DocumentSource,
DocumentStatus,
FieldType,
RecipientRole,
} from '@documenso/prisma/client';
export const ZFindDocumentsQuerySchema = ZBaseTableSearchParamsSchema.extend({
teamId: z.number().min(1).optional(),
templateId: z.number().min(1).optional(),
search: z
.string()
.optional()
.catch(() => undefined),
source: z.nativeEnum(DocumentSource).optional(),
status: z.nativeEnum(DocumentStatus).optional(),
orderBy: z
.object({
column: z.enum(['createdAt']),
direction: z.enum(['asc', 'desc']),
})
.optional(),
}).omit({ query: true });
export const ZFindDocumentAuditLogsQuerySchema = ZBaseTableSearchParamsSchema.extend({
documentId: z.number().min(1),