From 7ec75bef4a0f0a9d07be0142da64c49d739439ea Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 00:50:00 -0500 Subject: [PATCH 01/44] Add booking incomplete actions table --- .../migration.sql | 15 +++++++ packages/prisma/schema.prisma | 43 ++++++++++++------- 2 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql diff --git a/packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql b/packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql new file mode 100644 index 00000000000000..7259ceb09e24e2 --- /dev/null +++ b/packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql @@ -0,0 +1,15 @@ +-- CreateEnum +CREATE TYPE "IncompleteBookingActionType" AS ENUM ('SALESFORCE'); + +-- CreateTable +CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( + "id" SERIAL NOT NULL, + "formId" TEXT NOT NULL, + "actionType" "IncompleteBookingActionType" NOT NULL, + "data" JSONB NOT NULL, + + CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 8b4fe4b31a7084..285307bd4666ae 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -979,24 +979,25 @@ model App { } model App_RoutingForms_Form { - id String @id @default(cuid()) - description String? - position Int @default(0) - routes Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - name String - fields Json? - user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + description String? + position Int @default(0) + routes Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + name String + fields Json? + user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) // This is the user who created the form and also the user who has read-write access to the form // If teamId is set, the members of the team would also have access to form readOnly or read-write depending on their permission level as team member. - userId Int - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) - teamId Int? - responses App_RoutingForms_FormResponse[] - disabled Boolean @default(false) + userId Int + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + teamId Int? + responses App_RoutingForms_FormResponse[] + disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) - settings Json? + settings Json? + incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] @@index([userId]) @@index([disabled]) @@ -1809,3 +1810,15 @@ model Watchlist { @@unique([type, value]) @@index([type, value]) } + +enum IncompleteBookingActionType { + SALESFORCE +} + +model App_RoutingForms_IncompleteBooking_Actions { + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json +} From 360b66958a684450cf19a9218608465846dfea51 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 00:50:23 -0500 Subject: [PATCH 02/44] Add incomplete booking page tab --- packages/app-store/routing-forms/components/RoutingNavBar.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/app-store/routing-forms/components/RoutingNavBar.tsx b/packages/app-store/routing-forms/components/RoutingNavBar.tsx index 3c02afd411f001..57276c8fd00edf 100644 --- a/packages/app-store/routing-forms/components/RoutingNavBar.tsx +++ b/packages/app-store/routing-forms/components/RoutingNavBar.tsx @@ -38,6 +38,10 @@ export default function RoutingNavBar({ target: "_blank", href: `${appUrl}/reporting/${form?.id}`, }, + { + name: "Incomplete Booking", + href: `${appUrl}/incomplete-booking/${form?.id}`, + }, ]; return (
From 8903e991e562841eb9cc86ca05eddbb43481192f Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 00:51:17 -0500 Subject: [PATCH 03/44] Add `getincompleteBookingSettings` trpc endpoints --- .../app-store/routing-forms/trpc/_router.ts | 11 +++ .../getIncompleteBookingSettings.handler.ts | 90 +++++++++++++++++++ .../getIncompleteBookingSettings.schema.ts | 9 ++ 3 files changed, 110 insertions(+) create mode 100644 packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts create mode 100644 packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.schema.ts diff --git a/packages/app-store/routing-forms/trpc/_router.ts b/packages/app-store/routing-forms/trpc/_router.ts index 2ccca208405192..82b551ce066461 100644 --- a/packages/app-store/routing-forms/trpc/_router.ts +++ b/packages/app-store/routing-forms/trpc/_router.ts @@ -7,6 +7,7 @@ import { ZDeleteFormInputSchema } from "./deleteForm.schema"; import { ZFormMutationInputSchema } from "./formMutation.schema"; import { ZFormQueryInputSchema } from "./formQuery.schema"; import { ZGetAttributesForTeamInputSchema } from "./getAttributesForTeam.schema"; +import { ZGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema"; import { forms } from "./procedures/forms"; import { ZReportInputSchema } from "./report.schema"; @@ -88,6 +89,16 @@ const appRoutingForms = router({ ); return handler({ ctx, input }); }), + + getIncompleteBookingSettings: authedProcedure + .input(ZGetIncompleteBookingSettingsInputSchema) + .query(async ({ ctx, input }) => { + const handler = await getHandler( + "getIncompleteBookingSettings", + () => import("./getIncompleteBookingSettings.handler") + ); + return handler({ ctx, input }); + }), }); export default appRoutingForms; diff --git a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts new file mode 100644 index 00000000000000..a259a5f34a5a88 --- /dev/null +++ b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts @@ -0,0 +1,90 @@ +import type { PrismaClient } from "@calcom/prisma"; +import { TRPCError } from "@calcom/trpc/server"; +import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; + +import type { TGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema"; + +interface GetInCompleteBookingSettingsOptions { + ctx: { + prisma: PrismaClient; + user: NonNullable; + }; + input: TGetIncompleteBookingSettingsInputSchema; +} + +const getInCompleteBookingSettingsHandler = async (options: GetInCompleteBookingSettingsOptions) => { + const { + ctx: { prisma }, + input, + } = options; + + const [incompleteBookingActions, form] = await Promise.all([ + prisma.app_RoutingForms_IncompleteBooking_Actions.findMany({ + where: { + formId: input.formId, + }, + }), + prisma.app_RoutingForms_Form.findFirst({ + where: { + id: input.formId, + }, + select: { + userId: true, + teamId: true, + }, + }), + ]); + + if (!form) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Form not found", + }); + } + + // See if the team has any eligible credentials + const enabledApps = ["salesforce"]; + + const teamId = form?.teamId; + const userId = form.userId; + + if (teamId) { + // Need to get the credentials for the team and org + const orgQuery = await prisma.team.findFirst({ + where: { + id: teamId, + }, + select: { + parentId: true, + }, + }); + + const credentials = await prisma.credential.findMany({ + where: { + appId: { + in: enabledApps, + }, + teamId: { + in: [teamId, ...(orgQuery?.parentId ? [orgQuery.parentId] : [])], + }, + }, + }); + + return { incompleteBookingActions, credentials }; + } + + if (userId) { + const credentials = await prisma.credential.findMany({ + where: { + appId: { + in: enabledApps, + }, + userId, + }, + }); + + return { incompleteBookingActions, credentials }; + } +}; + +export default getInCompleteBookingSettingsHandler; diff --git a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.schema.ts b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.schema.ts new file mode 100644 index 00000000000000..8f04950cd22f94 --- /dev/null +++ b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.schema.ts @@ -0,0 +1,9 @@ +import z from "zod"; + +export const ZGetIncompleteBookingSettingsInputSchema = z.object({ + formId: z.string(), +}); + +export type TGetIncompleteBookingSettingsInputSchema = z.infer< + typeof ZGetIncompleteBookingSettingsInputSchema +>; From b611c03392d1d0687d0d385a7a1b275e07c73a63 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 00:51:41 -0500 Subject: [PATCH 04/44] Add incomplete booking page --- .../pages/app-routing.config.tsx | 2 + .../incomplete-booking/[...appPages].tsx | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx diff --git a/packages/app-store/routing-forms/pages/app-routing.config.tsx b/packages/app-store/routing-forms/pages/app-routing.config.tsx index 65749ef1936970..f54eb9d1bef8a1 100644 --- a/packages/app-store/routing-forms/pages/app-routing.config.tsx +++ b/packages/app-store/routing-forms/pages/app-routing.config.tsx @@ -1,6 +1,7 @@ //TODO: Generate this file automatically so that like in Next.js file based routing can work automatically import * as formEdit from "./form-edit/[...appPages]"; import * as forms from "./forms/[...appPages]"; +import * as IncompleteBooking from "./incomplete-booking/[...appPages]"; import * as LayoutHandler from "./layout-handler/[...appPages]"; import * as Reporting from "./reporting/[...appPages]"; import * as RouteBuilder from "./route-builder/[...appPages]"; @@ -13,6 +14,7 @@ const routingConfig = { "routing-link": RoutingLink, reporting: Reporting, layoutHandler: LayoutHandler, + "incomplete-booking": IncompleteBooking, }; export default routingConfig; diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx new file mode 100644 index 00000000000000..fd26d72ebaef5b --- /dev/null +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -0,0 +1,44 @@ +import Shell from "@calcom/features/shell/Shell"; +import { trpc } from "@calcom/trpc/react"; +import type { inferSSRProps } from "@calcom/types/inferSSRProps"; + +import SingleForm, { + getServerSidePropsForSingleFormView as getServerSideProps, +} from "../../components/SingleForm"; +import type { RoutingFormWithResponseCount } from "../../components/SingleForm"; + +function Page({ form }: { form: RoutingFormWithResponseCount }) { + const { data, isLoading } = trpc.viewer.appRoutingForms.getIncompleteBookingSettings.useQuery({ + formId: form.id, + }); + + if (isLoading) { + return
Loading...
; + } + return

Incomplete Booking

; +} + +export default function IncompleteBookingPage({ + form, + appUrl, + enrichedWithUserProfileForm, +}: inferSSRProps & { appUrl: string }) { + return ( + + ); +} + +IncompleteBookingPage.getLayout = (page: React.ReactElement) => { + return ( + + {page} + + ); +}; + +export { getServerSideProps }; From 4787745acde314c42aa5b981e70419f4181d7e08 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 01:03:19 -0500 Subject: [PATCH 05/44] Abstract enabled apps array --- .../routing-forms/lib/enabledIncompleteBookingApps.ts | 1 + .../trpc/getIncompleteBookingSettings.handler.ts | 8 +++----- 2 files changed, 4 insertions(+), 5 deletions(-) create mode 100644 packages/app-store/routing-forms/lib/enabledIncompleteBookingApps.ts diff --git a/packages/app-store/routing-forms/lib/enabledIncompleteBookingApps.ts b/packages/app-store/routing-forms/lib/enabledIncompleteBookingApps.ts new file mode 100644 index 00000000000000..5aee6237c13504 --- /dev/null +++ b/packages/app-store/routing-forms/lib/enabledIncompleteBookingApps.ts @@ -0,0 +1 @@ +export const enabledIncompleteBookingApps = ["salesforce"]; diff --git a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts index a259a5f34a5a88..fcbf52c7a54ca1 100644 --- a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts +++ b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts @@ -2,6 +2,7 @@ import type { PrismaClient } from "@calcom/prisma"; import { TRPCError } from "@calcom/trpc/server"; import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; +import { enabledIncompleteBookingApps } from "../lib/enabledIncompleteBookingApps"; import type { TGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema"; interface GetInCompleteBookingSettingsOptions { @@ -42,9 +43,6 @@ const getInCompleteBookingSettingsHandler = async (options: GetInCompleteBooking }); } - // See if the team has any eligible credentials - const enabledApps = ["salesforce"]; - const teamId = form?.teamId; const userId = form.userId; @@ -62,7 +60,7 @@ const getInCompleteBookingSettingsHandler = async (options: GetInCompleteBooking const credentials = await prisma.credential.findMany({ where: { appId: { - in: enabledApps, + in: enabledIncompleteBookingApps, }, teamId: { in: [teamId, ...(orgQuery?.parentId ? [orgQuery.parentId] : [])], @@ -77,7 +75,7 @@ const getInCompleteBookingSettingsHandler = async (options: GetInCompleteBooking const credentials = await prisma.credential.findMany({ where: { appId: { - in: enabledApps, + in: enabledIncompleteBookingApps, }, userId, }, From 6281df0ed1fb8611b16fe2ce43772f477876d17a Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 01:03:48 -0500 Subject: [PATCH 06/44] Handle no enabled credentials and no actions --- .../incomplete-booking/[...appPages].tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index fd26d72ebaef5b..acb68f0c0facd4 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -1,11 +1,13 @@ import Shell from "@calcom/features/shell/Shell"; import { trpc } from "@calcom/trpc/react"; import type { inferSSRProps } from "@calcom/types/inferSSRProps"; +import { EmptyScreen } from "@calcom/ui"; import SingleForm, { getServerSidePropsForSingleFormView as getServerSideProps, } from "../../components/SingleForm"; import type { RoutingFormWithResponseCount } from "../../components/SingleForm"; +import { enabledIncompleteBookingApps } from "../../lib/enabledIncompleteBookingApps"; function Page({ form }: { form: RoutingFormWithResponseCount }) { const { data, isLoading } = trpc.viewer.appRoutingForms.getIncompleteBookingSettings.useQuery({ @@ -15,7 +17,23 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { if (isLoading) { return
Loading...
; } - return

Incomplete Booking

; + + // Check to see if the user has any compatable credentials + if ( + !data?.credentials.some((credential) => enabledIncompleteBookingApps.includes(credential?.appId ?? "")) + ) { + return
No apps installed that support this feature
; + } + + if (data?.incompleteBookingActions.length === 0) { + return ( + + ); + } } export default function IncompleteBookingPage({ From d279a1da05819eafa8fc5d664e3be733e0687e8d Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 20:49:56 -0500 Subject: [PATCH 07/44] Add enabled field to incomplete booking action db record --- .../migration.sql | 1 + packages/prisma/schema.prisma | 1 + 2 files changed, 2 insertions(+) rename packages/prisma/migrations/{20241225051513_add_routing_form_incomplete_booking_table => 20241225061556_add_routingform_incomplete_booking_action_table}/migration.sql (93%) diff --git a/packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql similarity index 93% rename from packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql rename to packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql index 7259ceb09e24e2..29f1a895a0cc74 100644 --- a/packages/prisma/migrations/20241225051513_add_routing_form_incomplete_booking_table/migration.sql +++ b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql @@ -7,6 +7,7 @@ CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( "formId" TEXT NOT NULL, "actionType" "IncompleteBookingActionType" NOT NULL, "data" JSONB NOT NULL, + "enabled" BOOLEAN NOT NULL DEFAULT true, CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") ); diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 285307bd4666ae..e23375fcca274c 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -1821,4 +1821,5 @@ model App_RoutingForms_IncompleteBooking_Actions { formId String actionType IncompleteBookingActionType data Json + enabled Boolean @default(true) } From 9065a4b73d8e63149f550a10b1650ff5e54b127d Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 22:51:20 -0500 Subject: [PATCH 08/44] Add new write record entries --- .../incomplete-booking/[...appPages].tsx | 224 +++++++++++++++++- packages/app-store/salesforce/zod.ts | 17 +- 2 files changed, 229 insertions(+), 12 deletions(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index acb68f0c0facd4..b765bd3fe2817f 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -1,7 +1,15 @@ +import { useState, useEffect } from "react"; +import type z from "zod"; + +import { WhenToWriteToRecord, SalesforceFieldType } from "@calcom/app-store/salesforce/lib/enums"; +import type { writeToRecordDataSchema as salesforceWriteToRecordDataSchema } from "@calcom/app-store/salesforce/zod"; +import { routingFormIncompleteBookingDataSchema as salesforceRoutingFormIncompleteBookingDataSchema } from "@calcom/app-store/salesforce/zod"; import Shell from "@calcom/features/shell/Shell"; +import { useLocale } from "@calcom/lib/hooks/useLocale"; +import { IncompleteBookingActionType } from "@calcom/prisma/enums"; import { trpc } from "@calcom/trpc/react"; import type { inferSSRProps } from "@calcom/types/inferSSRProps"; -import { EmptyScreen } from "@calcom/ui"; +import { Switch, InputField, Button, Select, showToast } from "@calcom/ui"; import SingleForm, { getServerSidePropsForSingleFormView as getServerSideProps, @@ -9,11 +17,61 @@ import SingleForm, { import type { RoutingFormWithResponseCount } from "../../components/SingleForm"; import { enabledIncompleteBookingApps } from "../../lib/enabledIncompleteBookingApps"; +interface IncompleteBookingActionData { + field: string; + fieldType: string; + value: string; + whenToWrite: WhenToWriteToRecord; +} + function Page({ form }: { form: RoutingFormWithResponseCount }) { + const { t } = useLocale(); const { data, isLoading } = trpc.viewer.appRoutingForms.getIncompleteBookingSettings.useQuery({ formId: form.id, }); + const [incompleteBookingWriteToRecordEntries, setIncompleteBookingWriteToRecordEntries] = useState< + z.infer | [] + >([]); + + // Handle just Salesforce for now but need to expand this to other apps + const [salesforceActionEnabled, setSalesforceActionEnabled] = useState(false); + + const fieldTypeOptions = [{ label: t("text"), value: SalesforceFieldType.TEXT }]; + + const [selectedFieldType, setSelectedFieldType] = useState(fieldTypeOptions[0]); + + const whenToWriteToRecordOptions = [ + { label: t("on_every_booking"), value: WhenToWriteToRecord.EVERY_BOOKING }, + { label: t("only_if_field_is_empty"), value: WhenToWriteToRecord.FIELD_EMPTY }, + ]; + + const [selectedWhenToWrite, setSelectedWhenToWrite] = useState(whenToWriteToRecordOptions[0]); + + const [newSalesforceAction, setNewSalesforceAction] = useState({ + field: "", + fieldType: selectedFieldType.value, + value: "", + whenToWrite: WhenToWriteToRecord.FIELD_EMPTY, + }); + + useEffect(() => { + const salesforceAction = data?.incompleteBookingActions.find( + (action) => action.actionType === IncompleteBookingActionType.SALESFORCE + ); + + if (salesforceAction) { + setSalesforceActionEnabled(salesforceAction.enabled); + + const parsedSalesforceActionData = salesforceRoutingFormIncompleteBookingDataSchema.safeParse( + salesforceAction.data + ); + if (parsedSalesforceActionData.success) { + setIncompleteBookingWriteToRecordEntries(parsedSalesforceActionData.data?.writeToRecordObject ?? []); + } + } + }, [data]); + if (isLoading) { return
Loading...
; } @@ -25,15 +83,161 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { return
No apps installed that support this feature
; } - if (data?.incompleteBookingActions.length === 0) { - return ( - - ); - } + return ( + <> +
+ { + setSalesforceActionEnabled(checked); + }} + /> + {salesforceActionEnabled ? ( +
+
+
{t("field_name")}
+
{t("field_type")}
+
{t("value")}
+
{t("when_to_write")}
+
+
+ {incompleteBookingWriteToRecordEntries.map((action) => ( +
+
+ +
+
+ option.value === action.whenToWrite)} + isDisabled={true} + /> +
+
+
+
+ ))} +
+
+ + setNewSalesforceAction({ + ...newSalesforceAction, + field: e.target.value, + }) + } + /> +
+
+ { + if (e) { + setSelectedWhenToWrite(e); + setNewSalesforceAction({ + ...newSalesforceAction, + whenToWrite: e.value, + }); + } + }} + /> +
+
+
+ +
+ ) : null} +
+ + ); } export default function IncompleteBookingPage({ diff --git a/packages/app-store/salesforce/zod.ts b/packages/app-store/salesforce/zod.ts index e5e3a59e884433..4a7cf084e43313 100644 --- a/packages/app-store/salesforce/zod.ts +++ b/packages/app-store/salesforce/zod.ts @@ -3,12 +3,21 @@ import { z } from "zod"; import { eventTypeAppCardZod } from "../eventTypeAppCardZod"; import { SalesforceRecordEnum, WhenToWriteToRecord, SalesforceFieldType } from "./lib/enums"; -const writeToRecordEntry = z.object({ +const writeToBookingEntry = z.object({ value: z.string(), fieldType: z.nativeEnum(SalesforceFieldType), whenToWrite: z.nativeEnum(WhenToWriteToRecord), }); +export const writeToRecordEntrySchema = z.object({ + field: z.string(), + fieldType: z.nativeEnum(SalesforceFieldType), + value: z.string(), + whenToWrite: z.nativeEnum(WhenToWriteToRecord), +}); + +export const writeToRecordDataSchema = z.array(writeToRecordEntrySchema); + export const routingFormOptions = z .object({ rrSkipToAccountLookupField: z.boolean().optional(), @@ -16,6 +25,10 @@ export const routingFormOptions = z }) .optional(); +export const routingFormIncompleteBookingDataSchema = z.object({ + writeToRecordObject: writeToRecordDataSchema.optional(), +}); + const optionalBooleanOnlyRunTimeValidation = z .any() .refine((val) => typeof val === "boolean" || val === undefined) @@ -40,7 +53,7 @@ export const appDataSchema = eventTypeAppCardZod.extend({ sendNoShowAttendeeData: z.boolean().optional(), sendNoShowAttendeeDataField: z.string().optional(), onBookingWriteToRecord: z.boolean().optional(), - onBookingWriteToRecordFields: z.record(z.string(), writeToRecordEntry).optional(), + onBookingWriteToRecordFields: z.record(z.string(), writeToBookingEntry).optional(), ignoreGuests: z.boolean().optional(), }); From b3d4cfffd9b08850e1514ff305cdc030cfca24b3 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Wed, 25 Dec 2024 23:33:17 -0500 Subject: [PATCH 09/44] UI add separation between switch and inputs --- .../incomplete-booking/[...appPages].tsx | 264 +++++++++--------- 1 file changed, 136 insertions(+), 128 deletions(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index b765bd3fe2817f..be9dbd2467dd7e 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -86,154 +86,162 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { return ( <>
- { - setSalesforceActionEnabled(checked); - }} - /> +
+ { + setSalesforceActionEnabled(checked); + }} + /> +
+ {salesforceActionEnabled ? ( -
-
-
{t("field_name")}
-
{t("field_type")}
-
{t("value")}
-
{t("when_to_write")}
-
-
- {incompleteBookingWriteToRecordEntries.map((action) => ( -
-
- + <> +
+
+
+
{t("field_name")}
+
{t("field_type")}
+
{t("value")}
+
{t("when_to_write")}
+
+
+ {incompleteBookingWriteToRecordEntries.map((action) => ( +
+
+ +
+
+ option.value === action.whenToWrite + )} + isDisabled={true} + /> +
+
+
+ ))} +
- { + if (e) { + setSelectedFieldType(e); + setNewSalesforceAction({ + ...newSalesforceAction, + fieldType: e.value, + }); + } + }} + />
- { + if (e) { + setSelectedWhenToWrite(e); + setNewSalesforceAction({ + ...newSalesforceAction, + whenToWrite: e.value, + }); + } }} />
- ))} -
-
- - setNewSalesforceAction({ - ...newSalesforceAction, - field: e.target.value, - }) - } - /> -
-
- { - if (e) { - setSelectedWhenToWrite(e); - setNewSalesforceAction({ - ...newSalesforceAction, - whenToWrite: e.value, - }); - } - }} - /> -
-
- -
+ setNewSalesforceAction({ + field: "", + fieldType: selectedFieldType.value, + value: "", + whenToWrite: WhenToWriteToRecord.FIELD_EMPTY, + }); + }}> + {t("add_new_field")} + +
+ ) : null}
From e00204e96c4f4ae12d966dd949af9123deb7eb3f Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 00:26:39 -0500 Subject: [PATCH 10/44] Fix typo --- .../trpc/getIncompleteBookingSettings.handler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts index fcbf52c7a54ca1..4f2eb766952d25 100644 --- a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts +++ b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts @@ -5,7 +5,7 @@ import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; import { enabledIncompleteBookingApps } from "../lib/enabledIncompleteBookingApps"; import type { TGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema"; -interface GetInCompleteBookingSettingsOptions { +interface GetIncompleteBookingSettingsOptions { ctx: { prisma: PrismaClient; user: NonNullable; @@ -13,7 +13,7 @@ interface GetInCompleteBookingSettingsOptions { input: TGetIncompleteBookingSettingsInputSchema; } -const getInCompleteBookingSettingsHandler = async (options: GetInCompleteBookingSettingsOptions) => { +const getInCompleteBookingSettingsHandler = async (options: GetIncompleteBookingSettingsOptions) => { const { ctx: { prisma }, input, From 04d5112093ca5cc8ae60895a301f3b1e3dabc5ab Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 01:24:03 -0500 Subject: [PATCH 11/44] clean up --- .../pages/incomplete-booking/[...appPages].tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index be9dbd2467dd7e..3cb408f7b8b249 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -17,13 +17,6 @@ import SingleForm, { import type { RoutingFormWithResponseCount } from "../../components/SingleForm"; import { enabledIncompleteBookingApps } from "../../lib/enabledIncompleteBookingApps"; -interface IncompleteBookingActionData { - field: string; - fieldType: string; - value: string; - whenToWrite: WhenToWriteToRecord; -} - function Page({ form }: { form: RoutingFormWithResponseCount }) { const { t } = useLocale(); const { data, isLoading } = trpc.viewer.appRoutingForms.getIncompleteBookingSettings.useQuery({ @@ -76,7 +69,7 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { return
Loading...
; } - // Check to see if the user has any compatable credentials + // Check to see if the user has any compatible credentials if ( !data?.credentials.some((credential) => enabledIncompleteBookingApps.includes(credential?.appId ?? "")) ) { From d4ae1dcaff95974c8600e9d5f1e89d19a61717c9 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 01:33:41 -0500 Subject: [PATCH 12/44] Add saveIncompleteBookingSettings endpoint --- .../incompleteBooking/actionDataSchemas.ts | 10 +++ .../app-store/routing-forms/trpc/_router.ts | 11 +++ .../saveIncompleteBookingSettings.handler.ts | 72 +++++++++++++++++++ .../saveIncompleteBookingSettings.schema.ts | 14 ++++ 4 files changed, 107 insertions(+) create mode 100644 packages/app-store/routing-forms/lib/incompleteBooking/actionDataSchemas.ts create mode 100644 packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts create mode 100644 packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts diff --git a/packages/app-store/routing-forms/lib/incompleteBooking/actionDataSchemas.ts b/packages/app-store/routing-forms/lib/incompleteBooking/actionDataSchemas.ts new file mode 100644 index 00000000000000..291e8e1d5fc0be --- /dev/null +++ b/packages/app-store/routing-forms/lib/incompleteBooking/actionDataSchemas.ts @@ -0,0 +1,10 @@ +import type { z } from "zod"; + +import { routingFormIncompleteBookingDataSchema as salesforceRoutingFormIncompleteBookingDataSchema } from "@calcom/app-store/salesforce/zod"; +import { IncompleteBookingActionType } from "@calcom/prisma/enums"; + +const incompleteBookingActionDataSchemas: Record> = { + [IncompleteBookingActionType.SALESFORCE]: salesforceRoutingFormIncompleteBookingDataSchema, +}; + +export default incompleteBookingActionDataSchemas; diff --git a/packages/app-store/routing-forms/trpc/_router.ts b/packages/app-store/routing-forms/trpc/_router.ts index 82b551ce066461..3b56fe38f926fb 100644 --- a/packages/app-store/routing-forms/trpc/_router.ts +++ b/packages/app-store/routing-forms/trpc/_router.ts @@ -10,6 +10,7 @@ import { ZGetAttributesForTeamInputSchema } from "./getAttributesForTeam.schema" import { ZGetIncompleteBookingSettingsInputSchema } from "./getIncompleteBookingSettings.schema"; import { forms } from "./procedures/forms"; import { ZReportInputSchema } from "./report.schema"; +import { ZSaveIncompleteBookingSettingsInputSchema } from "./saveIncompleteBookingSettings.schema"; // eslint-disable-next-line @typescript-eslint/ban-types const UNSTABLE_HANDLER_CACHE: Record = {}; @@ -99,6 +100,16 @@ const appRoutingForms = router({ ); return handler({ ctx, input }); }), + + saveIncompleteBookingSettings: authedProcedure + .input(ZSaveIncompleteBookingSettingsInputSchema) + .mutation(async ({ ctx, input }) => { + const handler = await getHandler( + "saveIncompleteBookingSettings", + () => import("./saveIncompleteBookingSettings.handler") + ); + return handler({ ctx, input }); + }), }); export default appRoutingForms; diff --git a/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts new file mode 100644 index 00000000000000..8df1990dff038b --- /dev/null +++ b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts @@ -0,0 +1,72 @@ +import type { PrismaClient } from "@calcom/prisma"; +import { TRPCError } from "@calcom/trpc/server"; +import type { TrpcSessionUser } from "@calcom/trpc/server/trpc"; + +import incompleteBookingActionDataSchemas from "../lib/incompleteBooking/actionDataSchemas"; +import type { TSaveIncompleteBookingSettingsInputSchema } from "./saveIncompleteBookingSettings.schema"; + +interface SaveIncompleteBookingSettingsOptions { + ctx: { + prisma: PrismaClient; + user: NonNullable; + }; + input: TSaveIncompleteBookingSettingsInputSchema; +} + +const saveIncompleteBookingSettingsHandler = async (options: SaveIncompleteBookingSettingsOptions) => { + const { + ctx: { prisma }, + input, + } = options; + + const { formId, actionType, data } = input; + + const dataSchema = incompleteBookingActionDataSchemas[actionType]; + + if (!dataSchema) { + throw new TRPCError({ + code: "NOT_FOUND", + message: "Action data schema not found", + }); + } + + const parsedData = dataSchema.safeParse(data); + + if (!parsedData.success) { + throw new TRPCError({ + code: "BAD_REQUEST", + message: "Data is not valid", + }); + } + + // Check to see if the action already exists + const existingAction = await prisma.app_RoutingForms_IncompleteBooking_Actions.findFirst({ + where: { + formId: formId, + actionType: actionType, + }, + }); + + if (existingAction) { + await prisma.app_RoutingForms_IncompleteBooking_Actions.update({ + where: { + id: existingAction.id, + }, + data: { + data: parsedData.data, + enabled: input.enabled, + }, + }); + } else { + await prisma.app_RoutingForms_IncompleteBooking_Actions.create({ + data: { + formId: formId, + actionType: actionType, + data: parsedData.data, + enabled: input.enabled, + }, + }); + } +}; + +export default saveIncompleteBookingSettingsHandler; diff --git a/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts new file mode 100644 index 00000000000000..93e03cf8211ece --- /dev/null +++ b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts @@ -0,0 +1,14 @@ +import z from "zod"; + +import { IncompleteBookingActionType } from "@calcom/prisma/enums"; + +export const ZSaveIncompleteBookingSettingsInputSchema = z.object({ + formId: z.string(), + actionType: z.nativeEnum(IncompleteBookingActionType), + data: z.record(z.any()).optional(), + enabled: z.boolean(), +}); + +export type TSaveIncompleteBookingSettingsInputSchema = z.infer< + typeof ZGetIncompleteBookingSettingsInputSchema +>; From a5c487742c009a3058c1d1ef19e2f009c1a9ed15 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 01:37:06 -0500 Subject: [PATCH 13/44] Save incomplete booking settings --- .../incomplete-booking/[...appPages].tsx | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index 3cb408f7b8b249..d29ae880befda9 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -23,7 +23,16 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { formId: form.id, }); - const [incompleteBookingWriteToRecordEntries, setIncompleteBookingWriteToRecordEntries] = useState< + const mutation = trpc.viewer.appRoutingForms.saveIncompleteBookingSettings.useMutation({ + onSuccess: () => { + showToast(t("success"), "success"); + }, + onError: (error) => { + showToast(t(`error: ${error.message}`), "error"); + }, + }); + + const [salesforceWriteToRecordObject, setSalesforceWriteToRecordObject] = useState< z.infer | [] >([]); @@ -60,7 +69,7 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { salesforceAction.data ); if (parsedSalesforceActionData.success) { - setIncompleteBookingWriteToRecordEntries(parsedSalesforceActionData.data?.writeToRecordObject ?? []); + setSalesforceWriteToRecordObject(parsedSalesforceActionData.data?.writeToRecordObject ?? []); } } }, [data]); @@ -101,7 +110,7 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) {
{t("when_to_write")}
- {incompleteBookingWriteToRecordEntries.map((action) => ( + {salesforceWriteToRecordObject.map((action) => (
@@ -129,10 +138,10 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { variant="icon" color="destructive" onClick={() => { - const newActions = incompleteBookingWriteToRecordEntries.filter( - (action) => action.field !== action.field + const newActions = salesforceWriteToRecordObject.filter( + (existingAction) => existingAction.field !== action.field ); - setIncompleteBookingWriteToRecordEntries(newActions); + setSalesforceWriteToRecordObject(newActions); }} />
@@ -205,17 +214,13 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { ) } onClick={() => { - if ( - Object.keys(incompleteBookingWriteToRecordEntries).includes( - newSalesforceAction.field.trim() - ) - ) { + if (Object.keys(salesforceWriteToRecordObject).includes(newSalesforceAction.field.trim())) { showToast("Field already exists", "error"); return; } - setIncompleteBookingWriteToRecordEntries([ - ...incompleteBookingWriteToRecordEntries, + setSalesforceWriteToRecordObject([ + ...salesforceWriteToRecordObject, { field: newSalesforceAction.field, fieldType: newSalesforceAction.fieldType, @@ -237,6 +242,23 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { ) : null}
+
+ +
); } From 80f66cf232c3cb01c15b44f0154c7e4a5d3c27f9 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 14:17:19 -0500 Subject: [PATCH 14/44] Fix language around when to write to field --- apps/web/public/static/locales/en/common.json | 1 + .../routing-forms/pages/incomplete-booking/[...appPages].tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 8133ae2c6e8178..51958ad44228d6 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -2926,5 +2926,6 @@ "managed_users": "Managed Users", "managed_users_description": "See all the managed users created by your OAuth client", "select_oAuth_client": "Select Oauth Client", + "on_every_instance": "On every instance", "ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑" } diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index d29ae880befda9..66059c7dd73165 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -44,7 +44,7 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { const [selectedFieldType, setSelectedFieldType] = useState(fieldTypeOptions[0]); const whenToWriteToRecordOptions = [ - { label: t("on_every_booking"), value: WhenToWriteToRecord.EVERY_BOOKING }, + { label: t("on_every_instance"), value: WhenToWriteToRecord.EVERY_BOOKING }, { label: t("only_if_field_is_empty"), value: WhenToWriteToRecord.FIELD_EMPTY }, ]; From 4dd40719ebed690903f09f307a9c68286ce82403 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 14:17:56 -0500 Subject: [PATCH 15/44] Add `credentialId` to action record --- .../migration.sql | 4 +++ packages/prisma/schema.prisma | 25 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) rename packages/prisma/migrations/{20241225061556_add_routingform_incomplete_booking_action_table => 20241226170740_add_routingforms_incompletebooking_action_table}/migration.sql (70%) diff --git a/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql b/packages/prisma/migrations/20241226170740_add_routingforms_incompletebooking_action_table/migration.sql similarity index 70% rename from packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql rename to packages/prisma/migrations/20241226170740_add_routingforms_incompletebooking_action_table/migration.sql index 29f1a895a0cc74..4d8247f5a53fe8 100644 --- a/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql +++ b/packages/prisma/migrations/20241226170740_add_routingforms_incompletebooking_action_table/migration.sql @@ -8,9 +8,13 @@ CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( "actionType" "IncompleteBookingActionType" NOT NULL, "data" JSONB NOT NULL, "enabled" BOOLEAN NOT NULL DEFAULT true, + "credentialId" INTEGER, CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") ); -- AddForeignKey ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "Credential"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index e23375fcca274c..252c0aa9e2a16d 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -197,11 +197,12 @@ model Credential { paymentStatus String? billingCycleStart Int? - destinationCalendars DestinationCalendar[] - selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) - CalendarCache CalendarCache[] - references BookingReference[] + destinationCalendars DestinationCalendar[] + selectedCalendars SelectedCalendar[] + invalid Boolean? @default(false) + CalendarCache CalendarCache[] + references BookingReference[] + incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] @@index([userId]) @@index([appId]) @@ -1816,10 +1817,12 @@ enum IncompleteBookingActionType { } model App_RoutingForms_IncompleteBooking_Actions { - id Int @id @default(autoincrement()) - form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) - formId String - actionType IncompleteBookingActionType - data Json - enabled Boolean @default(true) + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json + enabled Boolean @default(true) + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) + credentialId Int? } From f77d6dd4d4dc924c01cc79d5071b0e0920907e46 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Thu, 26 Dec 2024 14:18:59 -0500 Subject: [PATCH 16/44] Choose which credential to assign to action --- .../incomplete-booking/[...appPages].tsx | 36 +++++++++++++++++++ .../getIncompleteBookingSettings.handler.ts | 7 ++++ 2 files changed, 43 insertions(+) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index 66059c7dd73165..b5f74f80d9faea 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -57,6 +57,15 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { whenToWrite: WhenToWriteToRecord.FIELD_EMPTY, }); + const credentialOptions = data?.credentials.map((credential) => ({ + label: credential.team?.name, + value: credential.id, + })); + + const [selectedCredential, setSelectedCredential] = useState( + Array.isArray(credentialOptions) ? credentialOptions[0] : null + ); + useEffect(() => { const salesforceAction = data?.incompleteBookingActions.find( (action) => action.actionType === IncompleteBookingActionType.SALESFORCE @@ -71,6 +80,11 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { if (parsedSalesforceActionData.success) { setSalesforceWriteToRecordObject(parsedSalesforceActionData.data?.writeToRecordObject ?? []); } + + setSelectedCredential( + credentialOptions.find((option) => option.value === salesforceAction.data.credentialId) ?? + selectedCredential + ); } }, [data]); @@ -102,6 +116,27 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { {salesforceActionEnabled ? ( <>
+ + {form.team && ( + <> +
+

Credential to use

+ { - if (!option) { - return; - } - setSelectedCredential(option); - }} - /> -
- -
- - )} -
{t("field_name")}
@@ -289,7 +254,6 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { }, actionType: IncompleteBookingActionType.SALESFORCE, enabled: salesforceActionEnabled, - credentialId: selectedCredential?.value ?? data.credentials[0].id, }); }}> {t("save")} diff --git a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts index 9e3a41c1a58967..4f2eb766952d25 100644 --- a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts +++ b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts @@ -66,13 +66,6 @@ const getInCompleteBookingSettingsHandler = async (options: GetIncompleteBooking in: [teamId, ...(orgQuery?.parentId ? [orgQuery.parentId] : [])], }, }, - include: { - team: { - select: { - name: true, - }, - }, - }, }); return { incompleteBookingActions, credentials }; From 316a3662aa2f18d9f96dc6c1d8627b0997c79abb Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 12 Jan 2025 21:30:25 -0500 Subject: [PATCH 20/44] Revert "Add `credentialId` to action record" This reverts commit 579f9ff4167b65aa987659e4e8f8c26f9e773317. --- .../migration.sql | 4 --- packages/prisma/schema.prisma | 25 ++++++++----------- 2 files changed, 11 insertions(+), 18 deletions(-) rename packages/prisma/migrations/{20241226170740_add_routingforms_incompletebooking_action_table => 20241225061556_add_routingform_incomplete_booking_action_table}/migration.sql (70%) diff --git a/packages/prisma/migrations/20241226170740_add_routingforms_incompletebooking_action_table/migration.sql b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql similarity index 70% rename from packages/prisma/migrations/20241226170740_add_routingforms_incompletebooking_action_table/migration.sql rename to packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql index 4d8247f5a53fe8..29f1a895a0cc74 100644 --- a/packages/prisma/migrations/20241226170740_add_routingforms_incompletebooking_action_table/migration.sql +++ b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql @@ -8,13 +8,9 @@ CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( "actionType" "IncompleteBookingActionType" NOT NULL, "data" JSONB NOT NULL, "enabled" BOOLEAN NOT NULL DEFAULT true, - "credentialId" INTEGER, CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") ); -- AddForeignKey ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "Credential"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 252c0aa9e2a16d..e23375fcca274c 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -197,12 +197,11 @@ model Credential { paymentStatus String? billingCycleStart Int? - destinationCalendars DestinationCalendar[] - selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) - CalendarCache CalendarCache[] - references BookingReference[] - incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] + destinationCalendars DestinationCalendar[] + selectedCalendars SelectedCalendar[] + invalid Boolean? @default(false) + CalendarCache CalendarCache[] + references BookingReference[] @@index([userId]) @@index([appId]) @@ -1817,12 +1816,10 @@ enum IncompleteBookingActionType { } model App_RoutingForms_IncompleteBooking_Actions { - id Int @id @default(autoincrement()) - form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) - formId String - actionType IncompleteBookingActionType - data Json - enabled Boolean @default(true) - credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) - credentialId Int? + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json + enabled Boolean @default(true) } From 2843a92c61820e7f7a1614a557d3f7ea96342107 Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 12 Jan 2025 21:49:11 -0500 Subject: [PATCH 21/44] Add credentialId to action record - rewrite migration file --- .../migration.sql | 4 +++ packages/prisma/schema.prisma | 25 +++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) rename packages/prisma/migrations/{20241225061556_add_routingform_incomplete_booking_action_table => 20250113024840_add_routingforms_incompletebooking_action_table}/migration.sql (70%) diff --git a/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql b/packages/prisma/migrations/20250113024840_add_routingforms_incompletebooking_action_table/migration.sql similarity index 70% rename from packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql rename to packages/prisma/migrations/20250113024840_add_routingforms_incompletebooking_action_table/migration.sql index 29f1a895a0cc74..4d8247f5a53fe8 100644 --- a/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql +++ b/packages/prisma/migrations/20250113024840_add_routingforms_incompletebooking_action_table/migration.sql @@ -8,9 +8,13 @@ CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( "actionType" "IncompleteBookingActionType" NOT NULL, "data" JSONB NOT NULL, "enabled" BOOLEAN NOT NULL DEFAULT true, + "credentialId" INTEGER, CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") ); -- AddForeignKey ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "Credential"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index e23375fcca274c..252c0aa9e2a16d 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -197,11 +197,12 @@ model Credential { paymentStatus String? billingCycleStart Int? - destinationCalendars DestinationCalendar[] - selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) - CalendarCache CalendarCache[] - references BookingReference[] + destinationCalendars DestinationCalendar[] + selectedCalendars SelectedCalendar[] + invalid Boolean? @default(false) + CalendarCache CalendarCache[] + references BookingReference[] + incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] @@index([userId]) @@index([appId]) @@ -1816,10 +1817,12 @@ enum IncompleteBookingActionType { } model App_RoutingForms_IncompleteBooking_Actions { - id Int @id @default(autoincrement()) - form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) - formId String - actionType IncompleteBookingActionType - data Json - enabled Boolean @default(true) + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json + enabled Boolean @default(true) + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) + credentialId Int? } From 63199271857751f4a4a825a29fc016752138de8f Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 12 Jan 2025 21:59:56 -0500 Subject: [PATCH 22/44] Revert "Add credentialId to action record - rewrite migration file" This reverts commit 2843a92c61820e7f7a1614a557d3f7ea96342107. --- .../migration.sql | 4 --- packages/prisma/schema.prisma | 25 ++++++++----------- 2 files changed, 11 insertions(+), 18 deletions(-) rename packages/prisma/migrations/{20250113024840_add_routingforms_incompletebooking_action_table => 20241225061556_add_routingform_incomplete_booking_action_table}/migration.sql (70%) diff --git a/packages/prisma/migrations/20250113024840_add_routingforms_incompletebooking_action_table/migration.sql b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql similarity index 70% rename from packages/prisma/migrations/20250113024840_add_routingforms_incompletebooking_action_table/migration.sql rename to packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql index 4d8247f5a53fe8..29f1a895a0cc74 100644 --- a/packages/prisma/migrations/20250113024840_add_routingforms_incompletebooking_action_table/migration.sql +++ b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql @@ -8,13 +8,9 @@ CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( "actionType" "IncompleteBookingActionType" NOT NULL, "data" JSONB NOT NULL, "enabled" BOOLEAN NOT NULL DEFAULT true, - "credentialId" INTEGER, CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") ); -- AddForeignKey ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "Credential"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 252c0aa9e2a16d..e23375fcca274c 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -197,12 +197,11 @@ model Credential { paymentStatus String? billingCycleStart Int? - destinationCalendars DestinationCalendar[] - selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) - CalendarCache CalendarCache[] - references BookingReference[] - incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] + destinationCalendars DestinationCalendar[] + selectedCalendars SelectedCalendar[] + invalid Boolean? @default(false) + CalendarCache CalendarCache[] + references BookingReference[] @@index([userId]) @@index([appId]) @@ -1817,12 +1816,10 @@ enum IncompleteBookingActionType { } model App_RoutingForms_IncompleteBooking_Actions { - id Int @id @default(autoincrement()) - form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) - formId String - actionType IncompleteBookingActionType - data Json - enabled Boolean @default(true) - credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) - credentialId Int? + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json + enabled Boolean @default(true) } From f490b5196ab04511eb0b52172d83ff6a0ff5b320 Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 12 Jan 2025 22:00:40 -0500 Subject: [PATCH 23/44] Revert "Add booking incomplete actions table" This reverts commit 7ec75bef4a0f0a9d07be0142da64c49d739439ea. --- .../migration.sql | 16 ------- packages/prisma/schema.prisma | 44 +++++++------------ 2 files changed, 15 insertions(+), 45 deletions(-) delete mode 100644 packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql diff --git a/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql b/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql deleted file mode 100644 index 29f1a895a0cc74..00000000000000 --- a/packages/prisma/migrations/20241225061556_add_routingform_incomplete_booking_action_table/migration.sql +++ /dev/null @@ -1,16 +0,0 @@ --- CreateEnum -CREATE TYPE "IncompleteBookingActionType" AS ENUM ('SALESFORCE'); - --- CreateTable -CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( - "id" SERIAL NOT NULL, - "formId" TEXT NOT NULL, - "actionType" "IncompleteBookingActionType" NOT NULL, - "data" JSONB NOT NULL, - "enabled" BOOLEAN NOT NULL DEFAULT true, - - CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index e23375fcca274c..8b4fe4b31a7084 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -979,25 +979,24 @@ model App { } model App_RoutingForms_Form { - id String @id @default(cuid()) - description String? - position Int @default(0) - routes Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - name String - fields Json? - user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + description String? + position Int @default(0) + routes Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + name String + fields Json? + user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) // This is the user who created the form and also the user who has read-write access to the form // If teamId is set, the members of the team would also have access to form readOnly or read-write depending on their permission level as team member. - userId Int - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) - teamId Int? - responses App_RoutingForms_FormResponse[] - disabled Boolean @default(false) + userId Int + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + teamId Int? + responses App_RoutingForms_FormResponse[] + disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) - settings Json? - incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] + settings Json? @@index([userId]) @@index([disabled]) @@ -1810,16 +1809,3 @@ model Watchlist { @@unique([type, value]) @@index([type, value]) } - -enum IncompleteBookingActionType { - SALESFORCE -} - -model App_RoutingForms_IncompleteBooking_Actions { - id Int @id @default(autoincrement()) - form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) - formId String - actionType IncompleteBookingActionType - data Json - enabled Boolean @default(true) -} From b884958fa054306870998d5a46100bead44c0867 Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 12 Jan 2025 22:04:48 -0500 Subject: [PATCH 24/44] Revert "Add enabled field to incomplete booking action db record" This reverts commit d279a1da05819eafa8fc5d664e3be733e0687e8d. --- packages/prisma/schema.prisma | 43 +++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 8b4fe4b31a7084..285307bd4666ae 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -979,24 +979,25 @@ model App { } model App_RoutingForms_Form { - id String @id @default(cuid()) - description String? - position Int @default(0) - routes Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - name String - fields Json? - user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) + id String @id @default(cuid()) + description String? + position Int @default(0) + routes Json? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + name String + fields Json? + user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) // This is the user who created the form and also the user who has read-write access to the form // If teamId is set, the members of the team would also have access to form readOnly or read-write depending on their permission level as team member. - userId Int - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) - teamId Int? - responses App_RoutingForms_FormResponse[] - disabled Boolean @default(false) + userId Int + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + teamId Int? + responses App_RoutingForms_FormResponse[] + disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) - settings Json? + settings Json? + incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] @@index([userId]) @@index([disabled]) @@ -1809,3 +1810,15 @@ model Watchlist { @@unique([type, value]) @@index([type, value]) } + +enum IncompleteBookingActionType { + SALESFORCE +} + +model App_RoutingForms_IncompleteBooking_Actions { + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json +} From b145f55ac25e20619298684460d4e5bfba3965ac Mon Sep 17 00:00:00 2001 From: Joe Date: Sun, 12 Jan 2025 22:08:32 -0500 Subject: [PATCH 25/44] Write migration in single commit --- .../migration.sql | 20 ++++++++++++++++ packages/prisma/schema.prisma | 24 +++++++++++-------- 2 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql diff --git a/packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql b/packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql new file mode 100644 index 00000000000000..4d8247f5a53fe8 --- /dev/null +++ b/packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql @@ -0,0 +1,20 @@ +-- CreateEnum +CREATE TYPE "IncompleteBookingActionType" AS ENUM ('SALESFORCE'); + +-- CreateTable +CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( + "id" SERIAL NOT NULL, + "formId" TEXT NOT NULL, + "actionType" "IncompleteBookingActionType" NOT NULL, + "data" JSONB NOT NULL, + "enabled" BOOLEAN NOT NULL DEFAULT true, + "credentialId" INTEGER, + + CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "Credential"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 285307bd4666ae..252c0aa9e2a16d 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -197,11 +197,12 @@ model Credential { paymentStatus String? billingCycleStart Int? - destinationCalendars DestinationCalendar[] - selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) - CalendarCache CalendarCache[] - references BookingReference[] + destinationCalendars DestinationCalendar[] + selectedCalendars SelectedCalendar[] + invalid Boolean? @default(false) + CalendarCache CalendarCache[] + references BookingReference[] + incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] @@index([userId]) @@index([appId]) @@ -1816,9 +1817,12 @@ enum IncompleteBookingActionType { } model App_RoutingForms_IncompleteBooking_Actions { - id Int @id @default(autoincrement()) - form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) - formId String - actionType IncompleteBookingActionType - data Json + id Int @id @default(autoincrement()) + form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) + formId String + actionType IncompleteBookingActionType + data Json + enabled Boolean @default(true) + credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) + credentialId Int? } From 1ad2f5155869edaf27263a14b6f93567329ce8fc Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 11:40:15 -0500 Subject: [PATCH 26/44] Rename table --- packages/prisma/schema.prisma | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 252c0aa9e2a16d..0c2e50d926241a 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -199,10 +199,10 @@ model Credential { destinationCalendars DestinationCalendar[] selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) + invalid Boolean? @default(false) CalendarCache CalendarCache[] references BookingReference[] - incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] + incompleteBookingActions App_RoutingForms_IncompleteBookingActions[] @@index([userId]) @@index([appId]) @@ -980,25 +980,25 @@ model App { } model App_RoutingForms_Form { - id String @id @default(cuid()) + id String @id @default(cuid()) description String? - position Int @default(0) + position Int @default(0) routes Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt name String fields Json? - user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) + user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) // This is the user who created the form and also the user who has read-write access to the form // If teamId is set, the members of the team would also have access to form readOnly or read-write depending on their permission level as team member. userId Int - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) teamId Int? responses App_RoutingForms_FormResponse[] - disabled Boolean @default(false) + disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) settings Json? - incompleteBookingActions App_RoutingForms_IncompleteBooking_Actions[] + incompleteBookingActions App_RoutingForms_IncompleteBookingActions[] @@index([userId]) @@index([disabled]) @@ -1816,7 +1816,7 @@ enum IncompleteBookingActionType { SALESFORCE } -model App_RoutingForms_IncompleteBooking_Actions { +model App_RoutingForms_IncompleteBookingActions { id Int @id @default(autoincrement()) form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) formId String From d73089db5fc73fee4d277f84aef2b10670416189 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 11:49:44 -0500 Subject: [PATCH 27/44] Rename table - remove underscores --- packages/prisma/schema.prisma | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index 0c2e50d926241a..cfc5799f43848c 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -199,10 +199,10 @@ model Credential { destinationCalendars DestinationCalendar[] selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) + invalid Boolean? @default(false) CalendarCache CalendarCache[] references BookingReference[] - incompleteBookingActions App_RoutingForms_IncompleteBookingActions[] + incompleteBookingActions AppRoutingFormsIncompleteBookingActions[] @@index([userId]) @@index([appId]) @@ -980,25 +980,25 @@ model App { } model App_RoutingForms_Form { - id String @id @default(cuid()) + id String @id @default(cuid()) description String? - position Int @default(0) + position Int @default(0) routes Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt name String fields Json? - user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) + user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) // This is the user who created the form and also the user who has read-write access to the form // If teamId is set, the members of the team would also have access to form readOnly or read-write depending on their permission level as team member. userId Int - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) teamId Int? responses App_RoutingForms_FormResponse[] - disabled Boolean @default(false) + disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) settings Json? - incompleteBookingActions App_RoutingForms_IncompleteBookingActions[] + incompleteBookingActions AppRoutingFormsIncompleteBookingActions[] @@index([userId]) @@index([disabled]) @@ -1816,7 +1816,7 @@ enum IncompleteBookingActionType { SALESFORCE } -model App_RoutingForms_IncompleteBookingActions { +model AppRoutingFormsIncompleteBookingActions { id Int @id @default(autoincrement()) form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) formId String From 7b78f537b31c100676746711aec43a0cdd1bc6f9 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 12:08:00 -0500 Subject: [PATCH 28/44] Remove credential relationship --- .../migration.sql | 20 ------------- .../migration.sql | 17 +++++++++++ packages/prisma/schema.prisma | 30 +++++++++---------- 3 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql create mode 100644 packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql diff --git a/packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql b/packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql deleted file mode 100644 index 4d8247f5a53fe8..00000000000000 --- a/packages/prisma/migrations/20250113030734_add_routingforms_incompletebooking_action_table/migration.sql +++ /dev/null @@ -1,20 +0,0 @@ --- CreateEnum -CREATE TYPE "IncompleteBookingActionType" AS ENUM ('SALESFORCE'); - --- CreateTable -CREATE TABLE "App_RoutingForms_IncompleteBooking_Actions" ( - "id" SERIAL NOT NULL, - "formId" TEXT NOT NULL, - "actionType" "IncompleteBookingActionType" NOT NULL, - "data" JSONB NOT NULL, - "enabled" BOOLEAN NOT NULL DEFAULT true, - "credentialId" INTEGER, - - CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_pkey" PRIMARY KEY ("id") -); - --- AddForeignKey -ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "App_RoutingForms_IncompleteBooking_Actions" ADD CONSTRAINT "App_RoutingForms_IncompleteBooking_Actions_credentialId_fkey" FOREIGN KEY ("credentialId") REFERENCES "Credential"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql b/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql new file mode 100644 index 00000000000000..881f2a45287579 --- /dev/null +++ b/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql @@ -0,0 +1,17 @@ +-- CreateEnum +CREATE TYPE "IncompleteBookingActionType" AS ENUM ('SALESFORCE'); + +-- CreateTable +CREATE TABLE "AppRouting_Forms_IncompleteBookingActions" ( + "id" SERIAL NOT NULL, + "formId" TEXT NOT NULL, + "actionType" "IncompleteBookingActionType" NOT NULL, + "data" JSONB NOT NULL, + "enabled" BOOLEAN NOT NULL DEFAULT true, + "credentialId" INTEGER, + + CONSTRAINT "AppRouting_Forms_IncompleteBookingActions_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "AppRouting_Forms_IncompleteBookingActions" ADD CONSTRAINT "AppRouting_Forms_IncompleteBookingActions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index cfc5799f43848c..ca3e72a5887493 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -197,12 +197,11 @@ model Credential { paymentStatus String? billingCycleStart Int? - destinationCalendars DestinationCalendar[] - selectedCalendars SelectedCalendar[] - invalid Boolean? @default(false) - CalendarCache CalendarCache[] - references BookingReference[] - incompleteBookingActions AppRoutingFormsIncompleteBookingActions[] + destinationCalendars DestinationCalendar[] + selectedCalendars SelectedCalendar[] + invalid Boolean? @default(false) + CalendarCache CalendarCache[] + references BookingReference[] @@index([userId]) @@index([appId]) @@ -980,25 +979,25 @@ model App { } model App_RoutingForms_Form { - id String @id @default(cuid()) + id String @id @default(cuid()) description String? - position Int @default(0) + position Int @default(0) routes Json? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt name String fields Json? - user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) + user User @relation("routing-form", fields: [userId], references: [id], onDelete: Cascade) // This is the user who created the form and also the user who has read-write access to the form // If teamId is set, the members of the team would also have access to form readOnly or read-write depending on their permission level as team member. userId Int - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) teamId Int? responses App_RoutingForms_FormResponse[] - disabled Boolean @default(false) + disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) settings Json? - incompleteBookingActions AppRoutingFormsIncompleteBookingActions[] + incompleteBookingActions AppRouting_Forms_IncompleteBookingActions[] @@index([userId]) @@index([disabled]) @@ -1816,13 +1815,12 @@ enum IncompleteBookingActionType { SALESFORCE } -model AppRoutingFormsIncompleteBookingActions { +model AppRouting_Forms_IncompleteBookingActions { id Int @id @default(autoincrement()) form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) formId String actionType IncompleteBookingActionType data Json enabled Boolean @default(true) - credential Credential? @relation(fields: [credentialId], references: [id], onDelete: Cascade) credentialId Int? } From de62a1a0b5b561d6a562e4178b0d2411801e2356 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 14:13:34 -0500 Subject: [PATCH 29/44] Type fix - changing table name --- .../trpc/getIncompleteBookingSettings.handler.ts | 2 +- .../trpc/saveIncompleteBookingSettings.handler.ts | 6 +++--- .../trpc/saveIncompleteBookingSettings.schema.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts index 4f2eb766952d25..772666fa6f15a9 100644 --- a/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts +++ b/packages/app-store/routing-forms/trpc/getIncompleteBookingSettings.handler.ts @@ -20,7 +20,7 @@ const getInCompleteBookingSettingsHandler = async (options: GetIncompleteBooking } = options; const [incompleteBookingActions, form] = await Promise.all([ - prisma.app_RoutingForms_IncompleteBooking_Actions.findMany({ + prisma.app_RoutingForms_IncompleteBookingActions.findMany({ where: { formId: input.formId, }, diff --git a/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts index 8df1990dff038b..6c95c098f343c0 100644 --- a/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts +++ b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.handler.ts @@ -40,7 +40,7 @@ const saveIncompleteBookingSettingsHandler = async (options: SaveIncompleteBooki } // Check to see if the action already exists - const existingAction = await prisma.app_RoutingForms_IncompleteBooking_Actions.findFirst({ + const existingAction = await prisma.app_RoutingForms_IncompleteBookingActions.findFirst({ where: { formId: formId, actionType: actionType, @@ -48,7 +48,7 @@ const saveIncompleteBookingSettingsHandler = async (options: SaveIncompleteBooki }); if (existingAction) { - await prisma.app_RoutingForms_IncompleteBooking_Actions.update({ + await prisma.app_RoutingForms_IncompleteBookingActions.update({ where: { id: existingAction.id, }, @@ -58,7 +58,7 @@ const saveIncompleteBookingSettingsHandler = async (options: SaveIncompleteBooki }, }); } else { - await prisma.app_RoutingForms_IncompleteBooking_Actions.create({ + await prisma.app_RoutingForms_IncompleteBookingActions.create({ data: { formId: formId, actionType: actionType, diff --git a/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts index 93e03cf8211ece..09a8f1b9905a05 100644 --- a/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts +++ b/packages/app-store/routing-forms/trpc/saveIncompleteBookingSettings.schema.ts @@ -10,5 +10,5 @@ export const ZSaveIncompleteBookingSettingsInputSchema = z.object({ }); export type TSaveIncompleteBookingSettingsInputSchema = z.infer< - typeof ZGetIncompleteBookingSettingsInputSchema + typeof ZSaveIncompleteBookingSettingsInputSchema >; From b4e7a0293f3f77a1af67df5a1d1549254abcd580 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 14:22:35 -0500 Subject: [PATCH 30/44] Fix table name --- .../migration.sql | 6 +++--- packages/prisma/schema.prisma | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql b/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql index 881f2a45287579..1582d30b048b4a 100644 --- a/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql +++ b/packages/prisma/migrations/20250113170733_add_routingforms_incompletebooking_action_table/migration.sql @@ -2,7 +2,7 @@ CREATE TYPE "IncompleteBookingActionType" AS ENUM ('SALESFORCE'); -- CreateTable -CREATE TABLE "AppRouting_Forms_IncompleteBookingActions" ( +CREATE TABLE "App_RoutingForms_IncompleteBookingActions" ( "id" SERIAL NOT NULL, "formId" TEXT NOT NULL, "actionType" "IncompleteBookingActionType" NOT NULL, @@ -10,8 +10,8 @@ CREATE TABLE "AppRouting_Forms_IncompleteBookingActions" ( "enabled" BOOLEAN NOT NULL DEFAULT true, "credentialId" INTEGER, - CONSTRAINT "AppRouting_Forms_IncompleteBookingActions_pkey" PRIMARY KEY ("id") + CONSTRAINT "App_RoutingForms_IncompleteBookingActions_pkey" PRIMARY KEY ("id") ); -- AddForeignKey -ALTER TABLE "AppRouting_Forms_IncompleteBookingActions" ADD CONSTRAINT "AppRouting_Forms_IncompleteBookingActions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; +ALTER TABLE "App_RoutingForms_IncompleteBookingActions" ADD CONSTRAINT "App_RoutingForms_IncompleteBookingActions_formId_fkey" FOREIGN KEY ("formId") REFERENCES "App_RoutingForms_Form"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index ca3e72a5887493..f3b3a7a9a46455 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -997,7 +997,7 @@ model App_RoutingForms_Form { disabled Boolean @default(false) /// @zod.custom(imports.RoutingFormSettings) settings Json? - incompleteBookingActions AppRouting_Forms_IncompleteBookingActions[] + incompleteBookingActions App_RoutingForms_IncompleteBookingActions[] @@index([userId]) @@index([disabled]) @@ -1815,7 +1815,7 @@ enum IncompleteBookingActionType { SALESFORCE } -model AppRouting_Forms_IncompleteBookingActions { +model App_RoutingForms_IncompleteBookingActions { id Int @id @default(autoincrement()) form App_RoutingForms_Form @relation(fields: [formId], references: [id], onDelete: Cascade) formId String From 4231bd2ffdbf089be1225e271c59de644ce76364 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Fri, 27 Dec 2024 12:03:59 -0500 Subject: [PATCH 31/44] Change writeToRecordObject to object --- .../incomplete-booking/[...appPages].tsx | 92 ++++++++++--------- packages/app-store/salesforce/zod.ts | 2 +- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index 66059c7dd73165..0a5de0cd61a3b7 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -33,8 +33,8 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { }); const [salesforceWriteToRecordObject, setSalesforceWriteToRecordObject] = useState< - z.infer | [] - >([]); + z.infer + >({}); // Handle just Salesforce for now but need to expand this to other apps const [salesforceActionEnabled, setSalesforceActionEnabled] = useState(false); @@ -110,43 +110,52 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) {
{t("when_to_write")}
- {salesforceWriteToRecordObject.map((action) => ( -
-
- + {Object.keys(salesforceWriteToRecordObject).map((key) => { + const action = + salesforceWriteToRecordObject[key as keyof typeof salesforceWriteToRecordObject]; + return ( +
+
+ +
+
+ option.value === action.whenToWrite + )} + isDisabled={true} + /> +
+
+
-
- option.value === action.whenToWrite - )} - isDisabled={true} - /> -
-
-
-
- ))} + ); + })}
Date: Fri, 27 Dec 2024 12:09:47 -0500 Subject: [PATCH 32/44] Salesforce add incomplete booking, write to record --- .../app-store/salesforce/lib/CrmService.ts | 61 ++++++++++++++++++- .../routingForm/incompleteBookingAction.ts | 33 ++++++++++ 2 files changed, 92 insertions(+), 2 deletions(-) create mode 100644 packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts diff --git a/packages/app-store/salesforce/lib/CrmService.ts b/packages/app-store/salesforce/lib/CrmService.ts index 0427e6be0eea2b..a890842098c5bb 100644 --- a/packages/app-store/salesforce/lib/CrmService.ts +++ b/packages/app-store/salesforce/lib/CrmService.ts @@ -17,6 +17,7 @@ import type { CRM, Contact, CrmEvent } from "@calcom/types/CrmService"; import type { ParseRefreshTokenResponse } from "../../_utils/oauth/parseRefreshTokenResponse"; import parseRefreshTokenResponse from "../../_utils/oauth/parseRefreshTokenResponse"; import { default as appMeta } from "../config.json"; +import type { writeToRecordDataSchema } from "../zod"; import { SalesforceRecordEnum, SalesforceFieldType, @@ -962,7 +963,7 @@ export default class SalesforceCRMService implements CRM { existingFields: Field[]; personRecord: Record; onBookingWriteToRecordFields: Record; - startTime: string; + startTime?: string; bookingUid?: string | null; organizerEmail?: string; calEventResponses?: CalEventResponses | null; @@ -989,7 +990,7 @@ export default class SalesforceCRMService implements CRM { if (extractedText) { writeOnRecordBody[field.name] = extractedText; } - } else if (field.type === SalesforceFieldType.DATE) { + } else if (field.type === SalesforceFieldType.DATE && startTime && organizerEmail) { const dateValue = await this.getDateFieldValue( fieldConfig.value, startTime, @@ -1239,4 +1240,60 @@ export default class SalesforceCRMService implements CRM { const response = await checkIfFreeEmailDomain(attendeeEmail); return response; } + + async incompleteBookingWriteToRecord( + email: string, + writeToRecordObject: z.infer + ) { + const conn = await this.conn; + + let personRecord: { Id: string; Email: string; recordType: SalesforceRecordEnum } | null = null; + + // Prioritize contacts over leads + const contactsQuery = await conn.query( + `SELECT Id, Email FROM ${SalesforceRecordEnum.CONTACT} WHERE Email = '${email}'` + ); + + if (contactsQuery.records.length) { + personRecord = { + ...(contactsQuery.records[0] as { Id: string; Email: string }), + recordType: SalesforceRecordEnum.CONTACT, + }; + } + + const leadsQuery = await conn.query( + `SELECT Id, Email FROM ${SalesforceRecordEnum.LEAD} WHERE Email = '${email}'` + ); + + if (leadsQuery.records.length) { + personRecord = { + ...(leadsQuery.records[0] as { Id: string; Email: string }), + recordType: SalesforceRecordEnum.LEAD, + }; + } + + if (!personRecord) { + throw new Error(`No contact or lead found for email ${email}`); + } + // Ensure the fields exist on the record + const existingFields = await this.ensureFieldsExistOnObject( + Object.keys(writeToRecordObject), + personRecord.recordType + ); + + const writeOnRecordBody = await this.buildRecordUpdatePayload({ + existingFields, + personRecord, + onBookingWriteToRecordFields: writeToRecordObject, + }); + await conn + .sobject(personRecord.recordType) + .update({ + Id: personRecord.Id, + ...writeOnRecordBody, + }) + .catch((e) => { + this.log.error(`Error updating person record for contactId ${personRecord?.Id}`, e); + }); + } } diff --git a/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts b/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts new file mode 100644 index 00000000000000..445f5bcf559b8f --- /dev/null +++ b/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts @@ -0,0 +1,33 @@ +import logger from "@calcom/lib/logger"; + +import { CredentialRepository } from "../../../../lib/server/repository/credential"; +import { routingFormIncompleteBookingDataSchema } from "../../zod"; +import SalesforceCRMService from "../CrmService"; + +const log = logger.getSubLogger({ prefix: ["incomplete-booking: salesforce"] }); + +export const incompleteBookingAction = async (action: any, email: string) => { + console.log("🚀 ~ incompleteBookingAction ~ data:", action); + + const dataParse = routingFormIncompleteBookingDataSchema.safeParse(action.data); + + if (!dataParse.success) { + log.error(`Incomplete booking action data is not valid: ${dataParse.error}`); + return; + } + + const { writeToRecordObject } = dataParse.data; + + const credential = await CredentialRepository.findFirstByIdWithKeyAndUser({ id: action.credentialId }); + + if (!credential) { + log.error(`Credential with id ${action.credentialId} not found`); + return; + } + + const crm = new SalesforceCRMService(credential, {}); + + await crm.incompleteBookingWriteToRecord(email, writeToRecordObject); + + return; +}; From b8940bdc559945bd72f7bf3deef21578fdd49e7f Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Fri, 27 Dec 2024 12:11:18 -0500 Subject: [PATCH 33/44] Add incomplete booking actions to `triggerFormSubmittedNoEventWebhooks` --- .../lib/incompleteBooking/actionFunctions.ts | 13 +++++++++++ .../triggerFormSubmittedNoEventWebhook.ts | 22 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts diff --git a/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts b/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts new file mode 100644 index 00000000000000..827319ec7d50ab --- /dev/null +++ b/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts @@ -0,0 +1,13 @@ +import type { App_RoutingForms_IncompleteBooking_Actions } from "@prisma/client"; + +import { incompleteBookingAction as salesforceIncompleteBookingAction } from "@calcom/app-store/salesforce/lib/routingForm/incompleteBookingAction"; +import { IncompleteBookingActionType } from "@calcom/prisma/enums"; + +const incompleteBookingActionFunctions: Record< + IncompleteBookingActionType, + (action: App_RoutingForms_IncompleteBooking_Actions, email: string) => void +> = { + [IncompleteBookingActionType.SALESFORCE]: salesforceIncompleteBookingAction, +}; + +export default incompleteBookingActionFunctions; diff --git a/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts b/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts index 56ef23dd0cc29e..95adc57f273500 100644 --- a/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts +++ b/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts @@ -1,5 +1,6 @@ import { z } from "zod"; +import incompleteBookingActionFunctions from "@calcom/app-store/routing-forms/lib/incompleteBooking/actionFunctions"; import type { FORM_SUBMITTED_WEBHOOK_RESPONSES } from "@calcom/app-store/routing-forms/trpc/utils"; import { sendGenericWebhookPayload } from "@calcom/features/webhooks/lib/sendPayload"; import prisma from "@calcom/prisma"; @@ -41,8 +42,10 @@ export const ZTriggerFormSubmittedNoEventWebhookPayloadSchema = z.object({ }); export async function triggerFormSubmittedNoEventWebhook(payload: string): Promise { + console.log("🚀 ~ triggerFormSubmittedNoEventWebhook ~ payload:", payload); const { webhook, responseId, form, redirect, responses } = ZTriggerFormSubmittedNoEventWebhookPayloadSchema.parse(JSON.parse(payload)); + console.log("🚀 ~ triggerFormSubmittedNoEventWebhook ~ responseId:", responseId); const bookingFromResponse = await prisma.booking.findFirst({ where: { @@ -81,6 +84,7 @@ export async function triggerFormSubmittedNoEventWebhook(payload: string): Promi return typeof value === "string" && value.includes("@"); } )?.value; + console.log("🚀 ~ triggerFormSubmittedNoEventWebhook ~ emailValue:", emailValue); // Check for duplicate email in recent responses const hasDuplicate = @@ -114,4 +118,22 @@ export async function triggerFormSubmittedNoEventWebhook(payload: string): Promi }).catch((e) => { console.error(`Error executing FORM_SUBMITTED_NO_EVENT webhook`, webhook, e); }); + + // See if there are other incomplete booking actions + const incompleteBookingActions = await prisma.app_RoutingForms_IncompleteBooking_Actions.findMany({ + where: { + formId: form.id, + }, + }); + + for (const incompleteBookingAction of incompleteBookingActions) { + const actionType = incompleteBookingAction.actionType; + + // Get action function + const bookingActionFunction = incompleteBookingActionFunctions[actionType]; + + if (emailValue) { + await bookingActionFunction(incompleteBookingAction, emailValue); + } + } } From 53b8cbe3b08ec170c207f624c7867c25a55353e0 Mon Sep 17 00:00:00 2001 From: Joe Au-Yeung Date: Fri, 27 Dec 2024 12:15:44 -0500 Subject: [PATCH 34/44] Remove console.log --- .../salesforce/lib/routingForm/incompleteBookingAction.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts b/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts index 445f5bcf559b8f..b2baa9d494164b 100644 --- a/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts +++ b/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts @@ -7,8 +7,6 @@ import SalesforceCRMService from "../CrmService"; const log = logger.getSubLogger({ prefix: ["incomplete-booking: salesforce"] }); export const incompleteBookingAction = async (action: any, email: string) => { - console.log("🚀 ~ incompleteBookingAction ~ data:", action); - const dataParse = routingFormIncompleteBookingDataSchema.safeParse(action.data); if (!dataParse.success) { From fd092fdd11ce8f1a2ed1394c42d07d1b88aae343 Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 15:01:07 -0500 Subject: [PATCH 35/44] Type fixes --- .../routing-forms/lib/incompleteBooking/actionFunctions.ts | 4 ++-- .../salesforce/lib/routingForm/incompleteBookingAction.ts | 6 ++++-- .../triggerFormSubmittedNoEventWebhook.ts | 7 +------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts b/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts index 827319ec7d50ab..7758b68dbacb21 100644 --- a/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts +++ b/packages/app-store/routing-forms/lib/incompleteBooking/actionFunctions.ts @@ -1,11 +1,11 @@ -import type { App_RoutingForms_IncompleteBooking_Actions } from "@prisma/client"; +import type { App_RoutingForms_IncompleteBookingActions } from "@prisma/client"; import { incompleteBookingAction as salesforceIncompleteBookingAction } from "@calcom/app-store/salesforce/lib/routingForm/incompleteBookingAction"; import { IncompleteBookingActionType } from "@calcom/prisma/enums"; const incompleteBookingActionFunctions: Record< IncompleteBookingActionType, - (action: App_RoutingForms_IncompleteBooking_Actions, email: string) => void + (action: App_RoutingForms_IncompleteBookingActions, email: string) => void > = { [IncompleteBookingActionType.SALESFORCE]: salesforceIncompleteBookingAction, }; diff --git a/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts b/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts index b2baa9d494164b..2bc3c21838416b 100644 --- a/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts +++ b/packages/app-store/salesforce/lib/routingForm/incompleteBookingAction.ts @@ -23,9 +23,11 @@ export const incompleteBookingAction = async (action: any, email: string) => { return; } - const crm = new SalesforceCRMService(credential, {}); + if (writeToRecordObject) { + const crm = new SalesforceCRMService(credential, {}); - await crm.incompleteBookingWriteToRecord(email, writeToRecordObject); + await crm.incompleteBookingWriteToRecord(email, writeToRecordObject); + } return; }; diff --git a/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts b/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts index 95adc57f273500..e5cd4c8849eec1 100644 --- a/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts +++ b/packages/features/tasker/tasks/triggerFormSubmittedNoEvent/triggerFormSubmittedNoEventWebhook.ts @@ -42,11 +42,8 @@ export const ZTriggerFormSubmittedNoEventWebhookPayloadSchema = z.object({ }); export async function triggerFormSubmittedNoEventWebhook(payload: string): Promise { - console.log("🚀 ~ triggerFormSubmittedNoEventWebhook ~ payload:", payload); const { webhook, responseId, form, redirect, responses } = ZTriggerFormSubmittedNoEventWebhookPayloadSchema.parse(JSON.parse(payload)); - console.log("🚀 ~ triggerFormSubmittedNoEventWebhook ~ responseId:", responseId); - const bookingFromResponse = await prisma.booking.findFirst({ where: { routedFromRoutingFormReponse: { @@ -84,8 +81,6 @@ export async function triggerFormSubmittedNoEventWebhook(payload: string): Promi return typeof value === "string" && value.includes("@"); } )?.value; - console.log("🚀 ~ triggerFormSubmittedNoEventWebhook ~ emailValue:", emailValue); - // Check for duplicate email in recent responses const hasDuplicate = emailValue && @@ -120,7 +115,7 @@ export async function triggerFormSubmittedNoEventWebhook(payload: string): Promi }); // See if there are other incomplete booking actions - const incompleteBookingActions = await prisma.app_RoutingForms_IncompleteBooking_Actions.findMany({ + const incompleteBookingActions = await prisma.app_RoutingForms_IncompleteBookingActions.findMany({ where: { formId: form.id, }, From 09e7f60e6188271722a0c5f4bc47f9d00afbf54d Mon Sep 17 00:00:00 2001 From: Joe Date: Mon, 13 Jan 2025 15:24:33 -0500 Subject: [PATCH 36/44] Type fixes --- .../routing-forms/pages/incomplete-booking/[...appPages].tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx index 0a5de0cd61a3b7..3c04713d9dbbc9 100644 --- a/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx +++ b/packages/app-store/routing-forms/pages/incomplete-booking/[...appPages].tsx @@ -69,7 +69,7 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) { salesforceAction.data ); if (parsedSalesforceActionData.success) { - setSalesforceWriteToRecordObject(parsedSalesforceActionData.data?.writeToRecordObject ?? []); + setSalesforceWriteToRecordObject(parsedSalesforceActionData.data?.writeToRecordObject ?? {}); } } }, [data]); @@ -253,7 +253,7 @@ function Page({ form }: { form: RoutingFormWithResponseCount }) {