Add 'after event ends' trigger to workflows (#4882)

* add after event ends workflow trigger to enum

* add after meetings ends trigger

* fix label for time span

* add deleted label for message template

* add lost changes from merge conflict

* set reminder for already existing bookings

* fix label 'How long before event starts/ends'

Co-authored-by: CarinaWolli <wollencarina@gmail.com>
This commit is contained in:
Carina Wollendorfer 2022-10-07 20:18:28 +02:00 committed by GitHub
parent 50ac5f2892
commit 7d77132102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 67 additions and 26 deletions

View File

@ -1295,5 +1295,7 @@
"update_timezone": "Update timezone",
"update_timezone_question": "Update Timezone?",
"update_timezone_description": "It seems like your local timezone has changed to {{formattedCurrentTz}}. It's very important to have the correct timezone to prevent bookings at undesired times. Do you want to update it?",
"dont_update": "Don't update"
"dont_update": "Don't update",
"after_event_trigger": "after event ends",
"how_long_after": "How long after event ends?"
}

View File

@ -60,7 +60,12 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
);
const [showTimeSection, setShowTimeSection] = useState(
form.getValues("trigger") === WorkflowTriggerEvents.BEFORE_EVENT ? true : false
form.getValues("trigger") === WorkflowTriggerEvents.BEFORE_EVENT ||
form.getValues("trigger") === WorkflowTriggerEvents.AFTER_EVENT
);
const [showTimeSectionAfter, setShowTimeSectionAfter] = useState(
form.getValues("trigger") === WorkflowTriggerEvents.AFTER_EVENT
);
const actionOptions = getWorkflowActionOptions(t);
@ -147,12 +152,21 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
onChange={(val) => {
if (val) {
form.setValue("trigger", val.value);
if (val.value === WorkflowTriggerEvents.BEFORE_EVENT) {
if (
val.value === WorkflowTriggerEvents.BEFORE_EVENT ||
val.value === WorkflowTriggerEvents.AFTER_EVENT
) {
setShowTimeSection(true);
if (val.value === WorkflowTriggerEvents.AFTER_EVENT) {
setShowTimeSectionAfter(true);
} else {
setShowTimeSectionAfter(false);
}
form.setValue("time", 24);
form.setValue("timeUnit", TimeUnit.HOUR);
} else {
setShowTimeSection(false);
setShowTimeSectionAfter(false);
form.unregister("time");
form.unregister("timeUnit");
}
@ -166,7 +180,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
/>
{showTimeSection && (
<div className="mt-5">
<Label>{t("how_long_before")}</Label>
<Label>{showTimeSectionAfter ? t("how_long_after") : t("how_long_before")}</Label>
<TimeTimeUnitInput form={form} />
</div>
)}

View File

@ -4,6 +4,7 @@ export const WORKFLOW_TRIGGER_EVENTS = [
WorkflowTriggerEvents.BEFORE_EVENT,
WorkflowTriggerEvents.EVENT_CANCELLED,
WorkflowTriggerEvents.NEW_EVENT,
WorkflowTriggerEvents.AFTER_EVENT,
WorkflowTriggerEvents.RESCHEDULE_EVENT,
] as const;

View File

@ -29,7 +29,7 @@ export const scheduleEmailReminder = async (
evt: BookingInfo,
triggerEvent: WorkflowTriggerEvents,
action: WorkflowActions,
timeBefore: {
timeSpan: {
time: number | null;
timeUnit: TimeUnit | null;
},
@ -42,11 +42,15 @@ export const scheduleEmailReminder = async (
const { startTime, endTime } = evt;
const uid = evt.uid as string;
const currentDate = dayjs();
const timeUnit: timeUnitLowerCase | undefined =
timeBefore.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
const scheduledDate =
timeBefore.time && timeUnit ? dayjs(startTime).subtract(timeBefore.time, timeUnit) : null;
const timeUnit: timeUnitLowerCase | undefined = timeSpan.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
let scheduledDate = null;
if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT) {
scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
} else if (triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) {
scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
}
if (!process.env.SENDGRID_API_KEY || !process.env.SENDGRID_EMAIL) {
console.error("Sendgrid credentials are missing from the .env file");
return;
@ -113,7 +117,11 @@ export const scheduleEmailReminder = async (
} catch (error) {
console.log("Error sending Email");
}
} else if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT && scheduledDate) {
} else if (
(triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT ||
triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) &&
scheduledDate
) {
// Sendgrid to schedule emails
// Can only schedule at least 60 minutes and at most 72 hours in advance
if (

View File

@ -30,7 +30,8 @@ export const scheduleWorkflowReminders = async (
if (
workflow.trigger === WorkflowTriggerEvents.BEFORE_EVENT ||
(workflow.trigger === WorkflowTriggerEvents.NEW_EVENT && !isRescheduleEvent) ||
(workflow.trigger === WorkflowTriggerEvents.RESCHEDULE_EVENT && isRescheduleEvent)
(workflow.trigger === WorkflowTriggerEvents.RESCHEDULE_EVENT && isRescheduleEvent) ||
workflow.trigger === WorkflowTriggerEvents.AFTER_EVENT
) {
workflow.steps.forEach(async (step) => {
if (step.action === WorkflowActions.SMS_ATTENDEE || step.action === WorkflowActions.SMS_NUMBER) {

View File

@ -42,7 +42,7 @@ export const scheduleSMSReminder = async (
reminderPhone: string | null,
triggerEvent: WorkflowTriggerEvents,
action: WorkflowActions,
timeBefore: {
timeSpan: {
time: number | null;
timeUnit: TimeUnit | null;
},
@ -50,13 +50,17 @@ export const scheduleSMSReminder = async (
workflowStepId: number,
template: WorkflowTemplates
) => {
const { startTime } = evt;
const { startTime, endTime } = evt;
const uid = evt.uid as string;
const currentDate = dayjs();
const timeUnit: timeUnitLowerCase | undefined =
timeBefore.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
const scheduledDate =
timeBefore.time && timeUnit ? dayjs(startTime).subtract(timeBefore.time, timeUnit) : null;
const timeUnit: timeUnitLowerCase | undefined = timeSpan.timeUnit?.toLocaleLowerCase() as timeUnitLowerCase;
let scheduledDate = null;
if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT) {
scheduledDate = timeSpan.time && timeUnit ? dayjs(startTime).subtract(timeSpan.time, timeUnit) : null;
} else if (triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) {
scheduledDate = timeSpan.time && timeUnit ? dayjs(endTime).add(timeSpan.time, timeUnit) : null;
}
const name = action === WorkflowActions.SMS_ATTENDEE ? evt.attendees[0].name : "";
const attendeeName = action === WorkflowActions.SMS_ATTENDEE ? evt.organizer.name : evt.attendees[0].name;
@ -96,7 +100,11 @@ export const scheduleSMSReminder = async (
} catch (error) {
console.log(`Error sending SMS with error ${error}`);
}
} else if (triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT && scheduledDate) {
} else if (
(triggerEvent === WorkflowTriggerEvents.BEFORE_EVENT ||
triggerEvent === WorkflowTriggerEvents.AFTER_EVENT) &&
scheduledDate
) {
// Can only schedule at least 60 minutes in advance and at most 7 days in advance
if (
currentDate.isBefore(scheduledDate.subtract(1, "hour")) &&

View File

@ -0,0 +1,2 @@
-- AlterEnum
ALTER TYPE "WorkflowTriggerEvents" ADD VALUE 'AFTER_EVENT';

View File

@ -531,6 +531,7 @@ enum WorkflowTriggerEvents {
BEFORE_EVENT
EVENT_CANCELLED
NEW_EVENT
AFTER_EVENT
RESCHEDULE_EVENT
}

View File

@ -372,7 +372,7 @@ export const workflowsRouter = createProtectedRouter()
let newEventTypes: number[] = [];
if (activeOn.length) {
if (trigger === WorkflowTriggerEvents.BEFORE_EVENT) {
if (trigger === WorkflowTriggerEvents.BEFORE_EVENT || trigger === WorkflowTriggerEvents.AFTER_EVENT) {
newEventTypes = newActiveEventTypes;
}
if (newEventTypes.length > 0) {
@ -424,7 +424,7 @@ export const workflowsRouter = createProtectedRouter()
: bookingInfo.attendees[0].email;
await scheduleEmailReminder(
bookingInfo,
WorkflowTriggerEvents.BEFORE_EVENT,
trigger,
step.action,
{
time,
@ -440,7 +440,7 @@ export const workflowsRouter = createProtectedRouter()
await scheduleSMSReminder(
bookingInfo,
step.sendTo || "",
WorkflowTriggerEvents.BEFORE_EVENT,
trigger,
step.action,
{
time,
@ -536,7 +536,10 @@ export const workflowsRouter = createProtectedRouter()
return eventTypeId;
}
});
if (eventTypesToUpdateReminders && trigger === WorkflowTriggerEvents.BEFORE_EVENT) {
if (
eventTypesToUpdateReminders &&
(trigger === WorkflowTriggerEvents.BEFORE_EVENT || trigger === WorkflowTriggerEvents.AFTER_EVENT)
) {
const bookingsOfEventTypes = await ctx.prisma.booking.findMany({
where: {
eventTypeId: {
@ -582,7 +585,7 @@ export const workflowsRouter = createProtectedRouter()
: bookingInfo.attendees[0].email;
await scheduleEmailReminder(
bookingInfo,
WorkflowTriggerEvents.BEFORE_EVENT,
trigger,
newStep.action,
{
time,
@ -598,7 +601,7 @@ export const workflowsRouter = createProtectedRouter()
await scheduleSMSReminder(
bookingInfo,
newStep.sendTo || "",
WorkflowTriggerEvents.BEFORE_EVENT,
trigger,
newStep.action,
{
time,
@ -637,7 +640,8 @@ export const workflowsRouter = createProtectedRouter()
data: step,
});
if (
trigger === WorkflowTriggerEvents.BEFORE_EVENT &&
(trigger === WorkflowTriggerEvents.BEFORE_EVENT ||
trigger === WorkflowTriggerEvents.AFTER_EVENT) &&
eventTypesToCreateReminders &&
step.action !== WorkflowActions.SMS_ATTENDEE
) {
@ -701,7 +705,7 @@ export const workflowsRouter = createProtectedRouter()
await scheduleSMSReminder(
bookingInfo,
step.sendTo,
WorkflowTriggerEvents.BEFORE_EVENT,
trigger,
step.action,
{
time,