diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index b943fd986b..424a7441a7 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -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?" } diff --git a/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx b/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx index 2b133b6b2b..c1931d85e5 100644 --- a/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx +++ b/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx @@ -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 && (
- +
)} diff --git a/packages/features/ee/workflows/lib/constants.ts b/packages/features/ee/workflows/lib/constants.ts index e496cfd9cc..2e99eed88d 100644 --- a/packages/features/ee/workflows/lib/constants.ts +++ b/packages/features/ee/workflows/lib/constants.ts @@ -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; diff --git a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts index 588962b4fa..369c3b0e98 100644 --- a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts @@ -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 ( diff --git a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts index ba0c452d3d..a7aec1c3bd 100644 --- a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts +++ b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts @@ -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) { diff --git a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts index ab3adf5656..eb315fe16b 100644 --- a/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts +++ b/packages/features/ee/workflows/lib/reminders/smsReminderManager.ts @@ -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")) && diff --git a/packages/prisma/migrations/20221006121954_add_after_event_ends_workflow_trigger/migration.sql b/packages/prisma/migrations/20221006121954_add_after_event_ends_workflow_trigger/migration.sql new file mode 100644 index 0000000000..ee6197e175 --- /dev/null +++ b/packages/prisma/migrations/20221006121954_add_after_event_ends_workflow_trigger/migration.sql @@ -0,0 +1,2 @@ +-- AlterEnum +ALTER TYPE "WorkflowTriggerEvents" ADD VALUE 'AFTER_EVENT'; diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma index c70bd81f1d..dc60065494 100644 --- a/packages/prisma/schema.prisma +++ b/packages/prisma/schema.prisma @@ -531,6 +531,7 @@ enum WorkflowTriggerEvents { BEFORE_EVENT EVENT_CANCELLED NEW_EVENT + AFTER_EVENT RESCHEDULE_EVENT } diff --git a/packages/trpc/server/routers/viewer/workflows.tsx b/packages/trpc/server/routers/viewer/workflows.tsx index 3f5f924e78..24a90e06a5 100644 --- a/packages/trpc/server/routers/viewer/workflows.tsx +++ b/packages/trpc/server/routers/viewer/workflows.tsx @@ -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,