From 1c8cf625825e6e49c9c1831981df0e8415307703 Mon Sep 17 00:00:00 2001 From: Keith Williams Date: Thu, 24 Oct 2024 23:00:37 -0300 Subject: [PATCH] feat: add more routing forms data (#17229) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Omar López --- .../playwright/tests/basic.e2e.ts | 11 ++- .../routing-forms/trpc/report.handler.ts | 85 ++++++++++++++++--- 2 files changed, 81 insertions(+), 15 deletions(-) diff --git a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts index f23e2c3461..de235a3c28 100644 --- a/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts +++ b/packages/app-store/routing-forms/playwright/tests/basic.e2e.ts @@ -323,11 +323,16 @@ test.describe("Routing Forms", () => { "Multi Select", "Legacy Select", "Select", + // TODO: Find a way to incorporate Routed To and Booked At into the report + // @see https://github.com/calcom/cal.com/pull/17229 + "Routed To", + "Booked At", ]); + /* Last two columns are "Routed To" and "Booked At" */ expect(responses).toEqual([ - ["event-routing", "Option-2", "Option-2", "Option-2", "Option-2"], - ["external-redirect", "Option-2", "Option-2", "Option-2", "Option-2"], - ["custom-page", "Option-2", "Option-2", "Option-2", "Option-2"], + ["event-routing", "Option-2", "Option-2", "Option-2", "Option-2", "", ""], + ["external-redirect", "Option-2", "Option-2", "Option-2", "Option-2", "", ""], + ["custom-page", "Option-2", "Option-2", "Option-2", "Option-2", "", ""], ]); await page.goto(`apps/routing-forms/route-builder/${routingForm.id}`); diff --git a/packages/app-store/routing-forms/trpc/report.handler.ts b/packages/app-store/routing-forms/trpc/report.handler.ts index efa48d8a0e..4cdd11f8e8 100644 --- a/packages/app-store/routing-forms/trpc/report.handler.ts +++ b/packages/app-store/routing-forms/trpc/report.handler.ts @@ -4,6 +4,7 @@ import logger from "@calcom/lib/logger"; import type { PrismaClient } from "@calcom/prisma"; import type { App_RoutingForms_FormResponse } from "@calcom/prisma/client"; import { TRPCError } from "@calcom/trpc/server"; +import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { jsonLogicToPrisma } from "../jsonLogicToPrisma"; import { getSerializableForm } from "../lib/getSerializableForm"; @@ -15,11 +16,23 @@ import type { TReportInputSchema } from "./report.schema"; interface ReportHandlerOptions { ctx: { prisma: PrismaClient; + user: NonNullable; }; input: TReportInputSchema; } -export const reportHandler = async ({ ctx: { prisma }, input }: ReportHandlerOptions) => { +const makeFormatDate = (locale: string, timeZone: string) => { + const formatDate = (date: Date): string => { + return new Intl.DateTimeFormat(locale, { + timeZone, + dateStyle: "medium", + timeStyle: "short", + }).format(date); + }; + return formatDate; +}; + +const getRows = async ({ ctx: { prisma }, input }: ReportHandlerOptions) => { // Can be any prisma `where` clause // eslint-disable-next-line @typescript-eslint/no-explicit-any const prismaWhere: Record = input.jsonLogicQuery @@ -32,6 +45,32 @@ export const reportHandler = async ({ ctx: { prisma }, input }: ReportHandlerOpt input.jsonLogicQuery )}` ); + const rows = await prisma.app_RoutingForms_FormResponse.findMany({ + where: { + formId: input.formId, + ...prismaWhere, + }, + include: { + routedToBooking: { + select: { + createdAt: true, + user: { + select: { id: true, name: true, email: true }, + }, + }, + }, + }, + take, + skip, + }); + return { skip, take, rows }; +}; + +export const reportHandler = async (options: ReportHandlerOptions) => { + const { + ctx: { prisma }, + input, + } = options; const form = await prisma.app_RoutingForms_Form.findUnique({ where: { id: input.formId, @@ -46,15 +85,7 @@ export const reportHandler = async ({ ctx: { prisma }, input }: ReportHandlerOpt } // TODO: Second argument is required to return deleted operators. const serializedForm = await getSerializableForm({ form, withDeletedFields: true }); - - const rows = await prisma.app_RoutingForms_FormResponse.findMany({ - where: { - formId: input.formId, - ...prismaWhere, - }, - take, - skip, - }); + const { rows, skip, take } = await getRows(options); const fields = serializedForm?.fields || []; const { responses, headers } = buildResponsesForReporting({ @@ -63,13 +94,43 @@ export const reportHandler = async ({ ctx: { prisma }, input }: ReportHandlerOpt }); const areThereNoResultsOrLessThanAskedFor = !rows.length || rows.length < take; - return { + return presenter({ + rows, + options, headers, responses, nextCursor: areThereNoResultsOrLessThanAskedFor ? null : skip + rows.length, - }; + }); }; +/** + * This is a temporary solution to make the report work. It should be incorporated into the report data itself. + * Right now we cannot filter by Routed To and Booked At because they are not part of the response data. + */ +function presenter(args: { + rows: Awaited>["rows"]; + options: ReportHandlerOptions; + headers: string[]; + responses: string[][]; + nextCursor: number | null; +}) { + const { headers, responses, nextCursor, options, rows } = args; + const { ctx } = options; + const formatDate = makeFormatDate(ctx.user.locale, ctx.user.timeZone); + return { + nextCursor, + headers: [...headers, "Routed To", "Booked At"], + responses: responses.map((r, i) => { + const currentRow = rows[i]; + return [ + ...r, + currentRow.routedToBooking?.user?.email || "", + currentRow.routedToBooking?.createdAt ? formatDate(currentRow.routedToBooking.createdAt) : "", + ]; + }), + }; +} + export default reportHandler; export function buildResponsesForReporting({