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 <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
This commit is contained in:
Udit Takkar
2025-02-07 12:33:38 +00:00
committed by GitHub
co-authored by Peer Richelsen Keith Williams
parent 6ca76707da
commit 808de26401
17 changed files with 83 additions and 30 deletions
@@ -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,
+1
View File
@@ -30,6 +30,7 @@ export const getEventTypesFromDB = async (id: number) => {
eventName: true,
recurringEvent: true,
requiresConfirmation: true,
canSendCalVideoTranscriptionEmails: true,
userId: true,
successRedirectUrl: true,
customInputs: true,
+1
View File
@@ -21,6 +21,7 @@ export const getBooking = async (bookingId: number) => {
select: {
teamId: true,
parentId: true,
canSendCalVideoTranscriptionEmails: true,
},
},
user: {
@@ -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)
@@ -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.",
@@ -64,6 +64,7 @@ type UserPageProps = {
| "hidden"
| "lockTimeZoneToggleOnBookingPage"
| "requiresConfirmation"
| "canSendCalVideoTranscriptionEmails"
| "requiresBookerEmailVerification"
| "price"
| "currency"
@@ -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}
/>
<Controller
name="canSendCalVideoTranscriptionEmails"
render={({ field: { value, onChange } }) => (
<SettingsToggle
labelClassName={classNames(
"text-sm",
customClassNames?.canSendCalVideoTranscriptionEmails?.label
)}
toggleSwitchAtTheEnd={true}
switchContainerClassName={classNames(
"border-subtle rounded-lg border py-6 px-4 sm:px-6",
customClassNames?.canSendCalVideoTranscriptionEmails?.container
)}
title={t("send_cal_video_transcription_emails")}
data-testid="send-cal-video-transcription-emails"
{...sendCalVideoTranscriptionEmailsProps}
description={t("description_send_cal_video_transcription_emails")}
descriptionClassName={customClassNames?.canSendCalVideoTranscriptionEmails?.description}
checked={value}
onCheckedChange={(e) => onChange(e)}
/>
)}
/>
<Controller
name="requiresBookerEmailVerification"
render={({ field: { value, onChange } }) => (
@@ -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 (
+1
View File
@@ -12,6 +12,7 @@ export const eventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
length: true,
title: true,
requiresConfirmation: true,
canSendCalVideoTranscriptionEmails: true,
requiresConfirmationForFreeEmail: true,
requiresConfirmationWillBlockSlot: true,
autoTranslateDescriptionEnabled: true,
@@ -515,6 +515,7 @@ export class EventTypeRepository {
lockTimeZoneToggleOnBookingPage: true,
requiresConfirmation: true,
requiresConfirmationForFreeEmail: true,
canSendCalVideoTranscriptionEmails: true,
requiresConfirmationWillBlockSlot: true,
requiresBookerEmailVerification: true,
autoTranslateDescriptionEnabled: true,
+1
View File
@@ -100,6 +100,7 @@ export const buildEventType = (eventType?: Partial<EventType>): EventType => {
userId: null,
teamId: null,
requiresBookerEmailVerification: false,
canSendCalVideoTranscriptionEmails: true,
useEventTypeDestinationCalendarEmail: false,
eventName: faker.lorem.words(),
timeZone: null,
@@ -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,
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "EventType" ADD COLUMN "canSendCalVideoTranscriptionEmails" BOOLEAN NOT NULL DEFAULT true;
+27 -25
View File
@@ -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?
+2
View File
@@ -14,6 +14,7 @@ export const baseEventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
lockTimeZoneToggleOnBookingPage: true,
requiresConfirmation: true,
requiresBookerEmailVerification: true,
canSendCalVideoTranscriptionEmails: true,
});
export const bookEventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
@@ -31,6 +32,7 @@ export const bookEventTypeSelect = Prisma.validator<Prisma.EventTypeSelect>()({
recurringEvent: true,
lockTimeZoneToggleOnBookingPage: true,
requiresConfirmation: true,
canSendCalVideoTranscriptionEmails: true,
requiresBookerEmailVerification: true,
metadata: true,
periodCountCalendarDays: true,
+1
View File
@@ -714,6 +714,7 @@ export const allManagedEventTypeProps: { [k in keyof Omit<Prisma.EventTypeSelect
customInputs: true,
disableGuests: true,
requiresConfirmation: true,
canSendCalVideoTranscriptionEmails: true,
requiresConfirmationForFreeEmail: true,
requiresConfirmationWillBlockSlot: true,
eventName: true,
@@ -30,7 +30,10 @@ type res = Awaited<
ReturnType<typeof MembershipRepository.findAllByUpIdIncludeMinimalEventTypes>
>[number]["team"]["eventTypes"][number];
type EventType = Omit<res, "forwardParamsSuccessRedirect"> & { children?: { id: number }[] };
type EventType = Omit<res, "forwardParamsSuccessRedirect"> & {
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(