From 808de26401a5b12b5444fc01c1dfa76440bbbbc8 Mon Sep 17 00:00:00 2001 From: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com> Date: Fri, 7 Feb 2025 18:03:38 +0530 Subject: [PATCH] feat: add option to disable sending transcription emails (#19065) * feat: add option to disable sending transcription emails * fix: types and tests * fix: type error * fix: type err * fix: type err --------- Co-authored-by: Peer Richelsen Co-authored-by: Keith Williams --- .../[[...step]]/getServerSideProps.ts | 1 + apps/web/lib/booking.ts | 1 + apps/web/lib/daily-webhook/getBooking.ts | 1 + apps/web/pages/api/recorded-daily-video.ts | 6 +++ apps/web/public/static/locales/en/common.json | 2 + .../server/lib/[user]/getServerSideProps.ts | 1 + .../tabs/advanced/EventAdvancedTab.tsx | 25 +++++++++ .../lib/event-types/getEventTypesPublic.ts | 8 +-- packages/lib/server/eventTypeSelect.ts | 1 + packages/lib/server/repository/eventType.ts | 1 + packages/lib/test/builder.ts | 1 + .../event-types/hooks/useEventTypeForm.ts | 1 + .../migration.sql | 2 + packages/prisma/schema.prisma | 52 ++++++++++--------- packages/prisma/selects/event-types.ts | 2 + packages/prisma/zod-utils.ts | 1 + .../getTeamAndEventTypeOptions.handler.ts | 7 ++- 17 files changed, 83 insertions(+), 30 deletions(-) create mode 100644 packages/prisma/migrations/20250203180603_add_can_send_transcription_email/migration.sql diff --git a/apps/web/lib/apps/installation/[[...step]]/getServerSideProps.ts b/apps/web/lib/apps/installation/[[...step]]/getServerSideProps.ts index 5a8056a189..43b0745fd1 100644 --- a/apps/web/lib/apps/installation/[[...step]]/getServerSideProps.ts +++ b/apps/web/lib/apps/installation/[[...step]]/getServerSideProps.ts @@ -88,6 +88,7 @@ const getEventTypes = async (userId: number, teamIds?: number[]) => { position: true, recurringEvent: true, requiresConfirmation: true, + canSendCalVideoTranscriptionEmails: true, team: { select: { slug: true } }, schedulingType: true, teamId: true, diff --git a/apps/web/lib/booking.ts b/apps/web/lib/booking.ts index 13a95ac662..2212c122f6 100644 --- a/apps/web/lib/booking.ts +++ b/apps/web/lib/booking.ts @@ -30,6 +30,7 @@ export const getEventTypesFromDB = async (id: number) => { eventName: true, recurringEvent: true, requiresConfirmation: true, + canSendCalVideoTranscriptionEmails: true, userId: true, successRedirectUrl: true, customInputs: true, diff --git a/apps/web/lib/daily-webhook/getBooking.ts b/apps/web/lib/daily-webhook/getBooking.ts index e7f7da2144..f2c7c06b4c 100644 --- a/apps/web/lib/daily-webhook/getBooking.ts +++ b/apps/web/lib/daily-webhook/getBooking.ts @@ -21,6 +21,7 @@ export const getBooking = async (bookingId: number) => { select: { teamId: true, parentId: true, + canSendCalVideoTranscriptionEmails: true, }, }, user: { diff --git a/apps/web/pages/api/recorded-daily-video.ts b/apps/web/pages/api/recorded-daily-video.ts index 54e8d5adf6..a565961593 100644 --- a/apps/web/pages/api/recorded-daily-video.ts +++ b/apps/web/pages/api/recorded-daily-video.ts @@ -158,6 +158,12 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { const bookingReference = await getBookingReference(room); const booking = await getBooking(bookingReference.bookingId as number); + if (!booking.eventType?.canSendCalVideoTranscriptionEmails) { + return res.status(200).json({ + message: `Transcription emails are disabled for this event type ${booking.eventTypeId}`, + }); + } + const transcripts = await getAllTranscriptsAccessLinkFromMeetingId(meeting_id); if (!transcripts || !transcripts.length) diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index 8338ae45b5..7e83722812 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -24,6 +24,8 @@ "verify_email_email_header": "Verify your email address", "verify_email_email_button": "Verify email", "cal_ai_assistant": "Cal AI Assistant", + "send_cal_video_transcription_emails": "Send Cal Video Transcription Emails", + "description_send_cal_video_transcription_emails": "Send emails with the transcription of the Cal Video after the meeting ends. (Requires a paid plan)", "verify_email_change_description": "You have recently requested to change the email address you use to log into your {{appName}} account. Please click the button below to confirm your new email address.", "verify_email_change_success_toast": "Updated your email to {{email}}", "verify_email_change_failure_toast": "Failed to update email.", diff --git a/apps/web/server/lib/[user]/getServerSideProps.ts b/apps/web/server/lib/[user]/getServerSideProps.ts index e9468f80a6..d59545fb01 100644 --- a/apps/web/server/lib/[user]/getServerSideProps.ts +++ b/apps/web/server/lib/[user]/getServerSideProps.ts @@ -64,6 +64,7 @@ type UserPageProps = { | "hidden" | "lockTimeZoneToggleOnBookingPage" | "requiresConfirmation" + | "canSendCalVideoTranscriptionEmails" | "requiresBookerEmailVerification" | "price" | "currency" diff --git a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx index fd07df2763..6ac7161750 100644 --- a/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx +++ b/packages/features/eventtypes/components/tabs/advanced/EventAdvancedTab.tsx @@ -74,6 +74,7 @@ export type EventAdvancedTabCustomClassNames = { }; requiresConfirmation?: RequiresConfirmationCustomClassNames; bookerEmailVerification?: SettingsToggleClassNames; + canSendCalVideoTranscriptionEmails?: SettingsToggleClassNames; calendarNotes?: SettingsToggleClassNames; eventDetailsVisibility?: SettingsToggleClassNames; bookingRedirect?: SettingsToggleClassNames & { @@ -471,6 +472,7 @@ export const EventAdvancedTab = ({ const successRedirectUrlLocked = shouldLockDisableProps("successRedirectUrl"); const seatsLocked = shouldLockDisableProps("seatsPerTimeSlotEnabled"); const requiresBookerEmailVerificationProps = shouldLockDisableProps("requiresBookerEmailVerification"); + const sendCalVideoTranscriptionEmailsProps = shouldLockDisableProps("canSendCalVideoTranscriptionEmails"); const hideCalendarNotesLocked = shouldLockDisableProps("hideCalendarNotes"); const hideCalendarEventDetailsLocked = shouldLockDisableProps("hideCalendarEventDetails"); const eventTypeColorLocked = shouldLockDisableProps("eventTypeColor"); @@ -574,6 +576,29 @@ export const EventAdvancedTab = ({ onRequiresConfirmation={setRequiresConfirmation} customClassNames={customClassNames?.requiresConfirmation} /> + ( + onChange(e)} + /> + )} + /> ( diff --git a/packages/lib/event-types/getEventTypesPublic.ts b/packages/lib/event-types/getEventTypesPublic.ts index 3e786c20d7..a128fc009d 100644 --- a/packages/lib/event-types/getEventTypesPublic.ts +++ b/packages/lib/event-types/getEventTypesPublic.ts @@ -36,14 +36,15 @@ const getEventTypesWithHiddenFromDB = async (userId: number) => { SELECT data."id", data."title", data."description", data."length", data."schedulingType"::text, data."recurringEvent", data."slug", data."hidden", data."price", data."currency", data."lockTimeZoneToggleOnBookingPage", data."requiresConfirmation", data."requiresBookerEmailVerification", - data."metadata" + data."metadata", data."canSendCalVideoTranscriptionEmails" FROM ( SELECT "EventType"."id", "EventType"."title", "EventType"."description", "EventType"."position", "EventType"."length", "EventType"."schedulingType"::text, "EventType"."recurringEvent", "EventType"."slug", "EventType"."hidden", "EventType"."price", "EventType"."currency", "EventType"."lockTimeZoneToggleOnBookingPage", "EventType"."requiresConfirmation", - "EventType"."requiresBookerEmailVerification", "EventType"."metadata" + "EventType"."requiresBookerEmailVerification", "EventType"."metadata", + "EventType"."canSendCalVideoTranscriptionEmails" FROM "EventType" WHERE "EventType"."teamId" IS NULL AND "EventType"."userId" = ${userId} UNION @@ -52,7 +53,8 @@ const getEventTypesWithHiddenFromDB = async (userId: number) => { "EventType"."recurringEvent", "EventType"."slug", "EventType"."hidden", "EventType"."price", "EventType"."currency", "EventType"."lockTimeZoneToggleOnBookingPage", "EventType"."requiresConfirmation", - "EventType"."requiresBookerEmailVerification", "EventType"."metadata" + "EventType"."requiresBookerEmailVerification", "EventType"."metadata", + "EventType"."canSendCalVideoTranscriptionEmails" FROM "EventType" WHERE "EventType"."teamId" IS NULL AND "EventType"."id" IN ( diff --git a/packages/lib/server/eventTypeSelect.ts b/packages/lib/server/eventTypeSelect.ts index 3c65195ff0..0395e2a68b 100644 --- a/packages/lib/server/eventTypeSelect.ts +++ b/packages/lib/server/eventTypeSelect.ts @@ -12,6 +12,7 @@ export const eventTypeSelect = Prisma.validator()({ length: true, title: true, requiresConfirmation: true, + canSendCalVideoTranscriptionEmails: true, requiresConfirmationForFreeEmail: true, requiresConfirmationWillBlockSlot: true, autoTranslateDescriptionEnabled: true, diff --git a/packages/lib/server/repository/eventType.ts b/packages/lib/server/repository/eventType.ts index 71f972466a..389b312203 100644 --- a/packages/lib/server/repository/eventType.ts +++ b/packages/lib/server/repository/eventType.ts @@ -515,6 +515,7 @@ export class EventTypeRepository { lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresConfirmationForFreeEmail: true, + canSendCalVideoTranscriptionEmails: true, requiresConfirmationWillBlockSlot: true, requiresBookerEmailVerification: true, autoTranslateDescriptionEnabled: true, diff --git a/packages/lib/test/builder.ts b/packages/lib/test/builder.ts index c2df9e83d0..fb2759a900 100644 --- a/packages/lib/test/builder.ts +++ b/packages/lib/test/builder.ts @@ -100,6 +100,7 @@ export const buildEventType = (eventType?: Partial): EventType => { userId: null, teamId: null, requiresBookerEmailVerification: false, + canSendCalVideoTranscriptionEmails: true, useEventTypeDestinationCalendarEmail: false, eventName: faker.lorem.words(), timeZone: null, diff --git a/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts b/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts index cb01a90f45..4c5f22bced 100644 --- a/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts +++ b/packages/platform/atoms/event-types/hooks/useEventTypeForm.ts @@ -86,6 +86,7 @@ export const useEventTypeForm = ({ periodCountCalendarDays: eventType.periodCountCalendarDays ? true : false, schedulingType: eventType.schedulingType, requiresConfirmation: eventType.requiresConfirmation, + canSendCalVideoTranscriptionEmails: eventType.canSendCalVideoTranscriptionEmails, requiresConfirmationWillBlockSlot: eventType.requiresConfirmationWillBlockSlot, requiresConfirmationForFreeEmail: eventType.requiresConfirmationForFreeEmail, slotInterval: eventType.slotInterval, diff --git a/packages/prisma/migrations/20250203180603_add_can_send_transcription_email/migration.sql b/packages/prisma/migrations/20250203180603_add_can_send_transcription_email/migration.sql new file mode 100644 index 0000000000..019266302d --- /dev/null +++ b/packages/prisma/migrations/20250203180603_add_can_send_transcription_email/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "EventType" ADD COLUMN "canSendCalVideoTranscriptionEmails" BOOLEAN NOT NULL DEFAULT true; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index e69f513399..3fef44bf5d 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -90,34 +90,36 @@ model EventType { profileId Int? profile Profile? @relation(fields: [profileId], references: [id], onDelete: Cascade) - team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) - teamId Int? - hashedLink HashedLink[] - bookings Booking[] - availability Availability[] - webhooks Webhook[] - destinationCalendar DestinationCalendar? - useEventLevelSelectedCalendars Boolean @default(false) - eventName String? - customInputs EventTypeCustomInput[] - parentId Int? - parent EventType? @relation("managed_eventtype", fields: [parentId], references: [id], onDelete: Cascade) - children EventType[] @relation("managed_eventtype") + team Team? @relation(fields: [teamId], references: [id], onDelete: Cascade) + teamId Int? + hashedLink HashedLink[] + bookings Booking[] + availability Availability[] + webhooks Webhook[] + destinationCalendar DestinationCalendar? + useEventLevelSelectedCalendars Boolean @default(false) + eventName String? + customInputs EventTypeCustomInput[] + parentId Int? + parent EventType? @relation("managed_eventtype", fields: [parentId], references: [id], onDelete: Cascade) + children EventType[] @relation("managed_eventtype") /// @zod.custom(imports.eventTypeBookingFields) - bookingFields Json? - timeZone String? - periodType PeriodType @default(UNLIMITED) + bookingFields Json? + timeZone String? + periodType PeriodType @default(UNLIMITED) /// @zod.custom(imports.coerceToDate) - periodStartDate DateTime? + periodStartDate DateTime? /// @zod.custom(imports.coerceToDate) - periodEndDate DateTime? - periodDays Int? - periodCountCalendarDays Boolean? - lockTimeZoneToggleOnBookingPage Boolean @default(false) - requiresConfirmation Boolean @default(false) - requiresConfirmationWillBlockSlot Boolean @default(false) - requiresConfirmationForFreeEmail Boolean @default(false) - requiresBookerEmailVerification Boolean @default(false) + periodEndDate DateTime? + periodDays Int? + periodCountCalendarDays Boolean? + lockTimeZoneToggleOnBookingPage Boolean @default(false) + requiresConfirmation Boolean @default(false) + requiresConfirmationWillBlockSlot Boolean @default(false) + requiresConfirmationForFreeEmail Boolean @default(false) + requiresBookerEmailVerification Boolean @default(false) + canSendCalVideoTranscriptionEmails Boolean @default(true) + autoTranslateDescriptionEnabled Boolean @default(false) /// @zod.custom(imports.recurringEventType) recurringEvent Json? diff --git a/packages/prisma/selects/event-types.ts b/packages/prisma/selects/event-types.ts index 9551466f7a..75c2dd83fa 100644 --- a/packages/prisma/selects/event-types.ts +++ b/packages/prisma/selects/event-types.ts @@ -14,6 +14,7 @@ export const baseEventTypeSelect = Prisma.validator()({ lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, requiresBookerEmailVerification: true, + canSendCalVideoTranscriptionEmails: true, }); export const bookEventTypeSelect = Prisma.validator()({ @@ -31,6 +32,7 @@ export const bookEventTypeSelect = Prisma.validator()({ recurringEvent: true, lockTimeZoneToggleOnBookingPage: true, requiresConfirmation: true, + canSendCalVideoTranscriptionEmails: true, requiresBookerEmailVerification: true, metadata: true, periodCountCalendarDays: true, diff --git a/packages/prisma/zod-utils.ts b/packages/prisma/zod-utils.ts index df77a72447..099c99e63c 100644 --- a/packages/prisma/zod-utils.ts +++ b/packages/prisma/zod-utils.ts @@ -714,6 +714,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit >[number]["team"]["eventTypes"][number]; -type EventType = Omit & { children?: { id: number }[] }; +type EventType = Omit & { + children?: { id: number }[]; + canSendCalVideoTranscriptionEmails?: boolean; +}; export const getTeamAndEventTypeOptions = async ({ ctx, input }: GetTeamAndEventTypeOptions) => { await checkRateLimitAndThrowError({ @@ -119,7 +122,7 @@ export const getTeamAndEventTypeOptions = async ({ ctx, input }: GetTeamAndEvent name: profile.name, eventTypesLockedByOrg: parentOrgHasLockedEventTypes, }, - eventTypes: profileEventTypes, + eventTypes: profileEventTypes as EventType[], }); eventTypeGroups = ([] as EventTypeGroup[]).concat(