Compare commits

...

2 Commits

Author SHA1 Message Date
Carina Wollendorfer 84fb1e2a64
cancel and schedule reminders when workflow activated/deactivated on event type (#8757)
* add switches to advanced even type settings

* only show switches when workflow is enabled

* check in event type update handler if user is allowed to disable standard emails

* don't send emails if disabled

* remove  reminders when workflow is deactivated on event type

* make sure emails can't be disabled if no workflow exist

* send workflow emails to all attendees

* code clean up

* add translations

* always send to all attendees when scheduling reminders

* schedule reminders if event type gets activated on workflow

* schedule reminders (commented code)

* change text

* schedule reminders for all attendees not just first one

---------

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Joe Au-Yeung <65426560+joeauyeung@users.noreply.github.com>
Co-authored-by: zomars <zomars@me.com>
2023-05-12 17:31:01 +00:00
Ayush Mainali d0030ce3a3
chore: extended slugify function (#8740)
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Omar López <zomars@me.com>
2023-05-12 17:19:05 +00:00
3 changed files with 145 additions and 5 deletions

View File

@ -1,5 +1,13 @@
export const slugify = (str: string) => {
return str.replace(/[^a-zA-Z0-9-]/g, "-").toLowerCase();
return str
.toLowerCase() // Convert to lowercase
.trim() // Remove whitespace from both sides
.normalize("NFD") // Normalize to decomposed form for handling accents
.replace(/\p{Diacritic}/gu, "") // Remove any diacritics (accents) from characters
.replace(/[^\p{L}\p{N}\p{Zs}\p{Emoji}]+/gu, "-") // Replace any non-alphanumeric characters (including Unicode) with a dash
.replace(/[\s_]+/g, "-") // Replace whitespace and underscores with a single dash
.replace(/^-+/, "") // Remove dashes from start
.replace(/-+$/, ""); // Remove dashes from end
};
export default slugify;

View File

@ -1,5 +1,15 @@
import {
deleteScheduledEmailReminder,
scheduleEmailReminder,
} from "@calcom/features/ee/workflows/lib/reminders/emailReminderManager";
import {
deleteScheduledSMSReminder,
scheduleSMSReminder,
} from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
import { prisma } from "@calcom/prisma";
import { MembershipRole, WorkflowActions } from "@calcom/prisma/enums";
import { BookingStatus } from "@calcom/prisma/client";
import { MembershipRole, WorkflowActions, WorkflowMethods } from "@calcom/prisma/enums";
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
import { TRPCError } from "@trpc/server";
@ -17,7 +27,7 @@ type ActivateEventTypeOptions = {
export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventTypeOptions) => {
const { eventTypeId, workflowId } = input;
// Check that vent type belong to the user or team
// Check that event type belong to the user or team
const userEventType = await prisma.eventType.findFirst({
where: {
id: eventTypeId,
@ -76,6 +86,35 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
});
if (isActive) {
// disable workflow for this event type & delete all reminders
const remindersToDelete = await prisma.workflowReminder.findMany({
where: {
booking: {
eventTypeId: eventTypeId,
userId: ctx.user.id,
},
workflowStepId: {
in: eventTypeWorkflow.steps.map((step) => {
return step.id;
}),
},
},
select: {
id: true,
referenceId: true,
method: true,
scheduled: true,
},
});
remindersToDelete.forEach((reminder) => {
if (reminder.method === WorkflowMethods.EMAIL) {
deleteScheduledEmailReminder(reminder.id, reminder.referenceId);
} else if (reminder.method === WorkflowMethods.SMS) {
deleteScheduledSMSReminder(reminder.id, reminder.referenceId);
}
});
await prisma.workflowsOnEventTypes.deleteMany({
where: {
workflowId,
@ -88,6 +127,99 @@ export const activateEventTypeHandler = async ({ ctx, input }: ActivateEventType
eventTypeId,
});
} else {
// activate workflow and schedule reminders for existing bookings
const bookingsForReminders = await prisma.booking.findMany({
where: {
eventTypeId: eventTypeId,
status: BookingStatus.ACCEPTED,
startTime: {
gte: new Date(),
},
},
include: {
attendees: true,
eventType: true,
user: true,
},
});
for (const booking of bookingsForReminders) {
const bookingInfo = {
uid: booking.uid,
attendees: booking.attendees.map((attendee) => {
return {
name: attendee.name,
email: attendee.email,
timeZone: attendee.timeZone,
language: { locale: attendee.locale || "" },
};
}),
organizer: booking.user
? {
name: booking.user.name || "",
email: booking.user.email,
timeZone: booking.user.timeZone,
language: { locale: booking.user.locale || "" },
}
: { name: "", email: "", timeZone: "", language: { locale: "" } },
startTime: booking.startTime.toISOString(),
endTime: booking.endTime.toISOString(),
title: booking.title,
language: { locale: booking?.user?.locale || "" },
eventType: {
slug: booking.eventType?.slug,
},
};
for (const step of eventTypeWorkflow.steps) {
if (step.action === WorkflowActions.EMAIL_ATTENDEE || step.action === WorkflowActions.EMAIL_HOST) {
let sendTo: string[] = [];
switch (step.action) {
case WorkflowActions.EMAIL_HOST:
sendTo = [bookingInfo.organizer?.email];
break;
case WorkflowActions.EMAIL_ATTENDEE:
sendTo = bookingInfo.attendees.map((attendee) => attendee.email);
break;
}
await scheduleEmailReminder(
bookingInfo,
eventTypeWorkflow.trigger,
step.action,
{
time: eventTypeWorkflow.time,
timeUnit: eventTypeWorkflow.timeUnit,
},
sendTo,
step.emailSubject || "",
step.reminderBody || "",
step.id,
step.template,
step.sender || SENDER_NAME
);
} else if (step.action === WorkflowActions.SMS_NUMBER && step.sendTo) {
await scheduleSMSReminder(
bookingInfo,
step.sendTo,
eventTypeWorkflow.trigger,
step.action,
{
time: eventTypeWorkflow.time,
timeUnit: eventTypeWorkflow.timeUnit,
},
step.reminderBody || "",
step.id,
step.template,
step.sender || SENDER_ID,
booking.userId,
eventTypeWorkflow.teamId
);
}
}
}
await prisma.workflowsOnEventTypes.create({
data: {
workflowId,

View File

@ -532,7 +532,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
user: true,
},
});
bookingsForReminders.forEach(async (booking) => {
for (const booking of bookingsForReminders) {
const bookingInfo = {
uid: booking.uid,
attendees: booking.attendees.map((attendee) => {
@ -611,7 +611,7 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
userWorkflow.teamId
);
}
});
}
}
}
});