diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json
index 424a7441a7..890138ac50 100644
--- a/apps/web/public/static/locales/en/common.json
+++ b/apps/web/public/static/locales/en/common.json
@@ -1296,6 +1296,7 @@
"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",
+ "email_address_action": "send email to a specific email address",
"after_event_trigger": "after event ends",
"how_long_after": "How long after event ends?"
}
diff --git a/packages/features/ee/workflows/api/scheduleEmailReminders.ts b/packages/features/ee/workflows/api/scheduleEmailReminders.ts
index ddf4b21529..a52e59893f 100644
--- a/packages/features/ee/workflows/api/scheduleEmailReminders.ts
+++ b/packages/features/ee/workflows/api/scheduleEmailReminders.ts
@@ -66,10 +66,18 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
for (const reminder of unscheduledReminders) {
if (dayjs(reminder.scheduledDate).isBefore(dateInSeventyTwoHours)) {
try {
- const sendTo =
- reminder.workflowStep.action === WorkflowActions.EMAIL_HOST
- ? reminder.booking?.user?.email
- : reminder.booking?.attendees[0].email;
+ let sendTo;
+
+ switch (reminder.workflowStep.action) {
+ case WorkflowActions.EMAIL_HOST:
+ sendTo = reminder.booking?.user?.email;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ sendTo = reminder.booking?.attendees[0].email;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo = reminder.workflowStep.sendTo;
+ }
const name =
reminder.workflowStep.action === WorkflowActions.EMAIL_ATTENDEE
diff --git a/packages/features/ee/workflows/components/v2/AddActionDialog.tsx b/packages/features/ee/workflows/components/v2/AddActionDialog.tsx
index 29cd2e435c..0a0b01be66 100644
--- a/packages/features/ee/workflows/components/v2/AddActionDialog.tsx
+++ b/packages/features/ee/workflows/components/v2/AddActionDialog.tsx
@@ -7,7 +7,17 @@ import { z } from "zod";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
-import { Button, Dialog, DialogClose, DialogContent, DialogFooter, Form, Label, Select } from "@calcom/ui/v2";
+import {
+ Button,
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogFooter,
+ EmailField,
+ Form,
+ Label,
+ Select,
+} from "@calcom/ui/v2";
import { WORKFLOW_ACTIONS } from "../../lib/constants";
import { getWorkflowActionOptions } from "../../lib/getOptions";
@@ -27,13 +37,14 @@ export const AddActionDialog = (props: IAddActionDialog) => {
const { t } = useLocale();
const { isOpenDialog, setIsOpenDialog, addAction } = props;
const [isPhoneNumberNeeded, setIsPhoneNumberNeeded] = useState(false);
+ const [isEmailAddressNeeded, setIsEmailAddressNeeded] = useState(false);
const actionOptions = getWorkflowActionOptions(t);
const formSchema = z.object({
action: z.enum(WORKFLOW_ACTIONS),
sendTo: z
.string()
- .refine((val) => isValidPhoneNumber(val))
+ .refine((val) => isValidPhoneNumber(val) || val.includes("@"))
.optional(),
});
@@ -58,6 +69,7 @@ export const AddActionDialog = (props: IAddActionDialog) => {
form.unregister("action");
setIsOpenDialog(false);
setIsPhoneNumberNeeded(false);
+ setIsEmailAddressNeeded(false);
}}>
@@ -75,11 +87,17 @@ export const AddActionDialog = (props: IAddActionDialog) => {
form.setValue("action", val.value);
if (val.value === WorkflowActions.SMS_NUMBER) {
setIsPhoneNumberNeeded(true);
- } else {
+ setIsEmailAddressNeeded(false);
+ } else if (val.value === WorkflowActions.EMAIL_ADDRESS) {
+ setIsEmailAddressNeeded(true);
+ setIsPhoneNumberNeeded(false);
+ } else {
+ setIsEmailAddressNeeded(false);
setIsPhoneNumberNeeded(false);
- form.unregister("sendTo");
}
+ form.unregister("sendTo");
form.clearErrors("action");
+ form.clearErrors("sendTo");
}
}}
options={actionOptions}
@@ -109,6 +127,11 @@ export const AddActionDialog = (props: IAddActionDialog) => {
)}
+ {isEmailAddressNeeded && (
+
+
+
+ )}
diff --git a/packages/features/ee/workflows/components/v2/EventWorkflowsTab.tsx b/packages/features/ee/workflows/components/v2/EventWorkflowsTab.tsx
index 4a90910740..aecdb25c07 100644
--- a/packages/features/ee/workflows/components/v2/EventWorkflowsTab.tsx
+++ b/packages/features/ee/workflows/components/v2/EventWorkflowsTab.tsx
@@ -84,6 +84,9 @@ const WorkflowListItem = (props: ItemProps) => {
case WorkflowActions.SMS_NUMBER:
sendTo.add(step.sendTo || "");
break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo.add(step.sendTo || "");
+ break;
}
});
diff --git a/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx b/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx
index c1931d85e5..4211c9cf44 100644
--- a/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx
+++ b/packages/features/ee/workflows/components/v2/WorkflowStepContainer.tsx
@@ -20,7 +20,7 @@ import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
import { Button, DialogClose, DialogContent } from "@calcom/ui/v2";
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
import Select from "@calcom/ui/v2/core/form/Select";
-import { Label, TextArea } from "@calcom/ui/v2/core/form/fields";
+import { EmailField, Label, TextArea } from "@calcom/ui/v2/core/form/fields";
import { AddVariablesDropdown } from "../../components/v2/AddVariablesDropdown";
import {
@@ -49,12 +49,18 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
step?.action === WorkflowActions.SMS_NUMBER ? true : false
);
+ const [isEmailAddressNeeded, setIsEmailAddressNeeded] = useState(
+ step?.action === WorkflowActions.EMAIL_ADDRESS ? true : false
+ );
+
const [isCustomReminderBodyNeeded, setIsCustomReminderBodyNeeded] = useState(
step?.template === WorkflowTemplates.CUSTOM ? true : false
);
const [isEmailSubjectNeeded, setIsEmailSubjectNeeded] = useState(
- step?.action === WorkflowActions.EMAIL_ATTENDEE || step?.action === WorkflowActions.EMAIL_HOST
+ step?.action === WorkflowActions.EMAIL_ATTENDEE ||
+ step?.action === WorkflowActions.EMAIL_HOST ||
+ step?.action === WorkflowActions.EMAIL_ADDRESS
? true
: false
);
@@ -270,12 +276,20 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
if (val) {
if (val.value === WorkflowActions.SMS_NUMBER) {
setIsPhoneNumberNeeded(true);
+ setIsEmailAddressNeeded(false);
+ } else if (val.value === WorkflowActions.EMAIL_ADDRESS) {
+ setIsEmailAddressNeeded(true);
+ setIsPhoneNumberNeeded(false);
} else {
+ setIsEmailAddressNeeded(false);
setIsPhoneNumberNeeded(false);
}
+ form.unregister(`steps.${step.stepNumber - 1}.sendTo`);
+ form.clearErrors(`steps.${step.stepNumber - 1}.sendTo`);
if (
val.value === WorkflowActions.EMAIL_ATTENDEE ||
- val.value === WorkflowActions.EMAIL_HOST
+ val.value === WorkflowActions.EMAIL_HOST ||
+ val.value === WorkflowActions.EMAIL_ADDRESS
) {
setIsEmailSubjectNeeded(true);
} else {
@@ -316,6 +330,15 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
)}
)}
+ {isEmailAddressNeeded && (
+
+
+
+ )}
diff --git a/packages/features/ee/workflows/lib/constants.ts b/packages/features/ee/workflows/lib/constants.ts
index 2e99eed88d..07e43a397b 100644
--- a/packages/features/ee/workflows/lib/constants.ts
+++ b/packages/features/ee/workflows/lib/constants.ts
@@ -11,6 +11,7 @@ export const WORKFLOW_TRIGGER_EVENTS = [
export const WORKFLOW_ACTIONS = [
WorkflowActions.EMAIL_HOST,
WorkflowActions.EMAIL_ATTENDEE,
+ WorkflowActions.EMAIL_ADDRESS,
WorkflowActions.SMS_ATTENDEE,
WorkflowActions.SMS_NUMBER,
] as const;
diff --git a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts
index 369c3b0e98..cb332d3e05 100644
--- a/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts
+++ b/packages/features/ee/workflows/lib/reminders/emailReminderManager.ts
@@ -61,9 +61,26 @@ export const scheduleEmailReminder = async (
method: "POST",
});
- const name = action === WorkflowActions.EMAIL_HOST ? evt.organizer.name : evt.attendees[0].name;
- const attendeeName = action === WorkflowActions.EMAIL_HOST ? evt.attendees[0].name : evt.organizer.name;
- const timeZone = action === WorkflowActions.EMAIL_HOST ? evt.organizer.timeZone : evt.attendees[0].timeZone;
+ let name = "";
+ let attendeeName = "";
+ let timeZone = "";
+
+ switch (action) {
+ case WorkflowActions.EMAIL_HOST:
+ name = evt.organizer.name;
+ attendeeName = evt.attendees[0].name;
+ timeZone = evt.organizer.timeZone;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ name = evt.attendees[0].name;
+ attendeeName = evt.organizer.name;
+ timeZone = evt.attendees[0].timeZone;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ name = "";
+ attendeeName = evt.attendees[0].name;
+ timeZone = evt.organizer.timeZone;
+ }
let emailContent = {
emailSubject,
diff --git a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts
index a7aec1c3bd..48b2736574 100644
--- a/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts
+++ b/packages/features/ee/workflows/lib/reminders/reminderScheduler.ts
@@ -51,10 +51,21 @@ export const scheduleWorkflowReminders = async (
);
} else if (
step.action === WorkflowActions.EMAIL_ATTENDEE ||
- step.action === WorkflowActions.EMAIL_HOST
+ step.action === WorkflowActions.EMAIL_HOST ||
+ step.action === WorkflowActions.EMAIL_ADDRESS
) {
- const sendTo =
- step.action === WorkflowActions.EMAIL_HOST ? evt.organizer.email : evt.attendees[0].email;
+ let sendTo = "";
+
+ switch (step.action) {
+ case WorkflowActions.EMAIL_HOST:
+ sendTo = evt.organizer.email;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ sendTo = evt.attendees[0].email;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo = step.sendTo || "";
+ }
scheduleEmailReminder(
evt,
workflow.trigger,
@@ -108,10 +119,21 @@ export const sendCancelledReminders = async (
);
} else if (
step.action === WorkflowActions.EMAIL_ATTENDEE ||
- step.action === WorkflowActions.EMAIL_HOST
+ step.action === WorkflowActions.EMAIL_HOST ||
+ step.action === WorkflowActions.EMAIL_ADDRESS
) {
- const sendTo =
- step.action === WorkflowActions.EMAIL_HOST ? evt.organizer.email : evt.attendees[0].email;
+ let sendTo = "";
+
+ switch (step.action) {
+ case WorkflowActions.EMAIL_HOST:
+ sendTo = evt.organizer.email;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ sendTo = evt.attendees[0].email;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo = step.sendTo || "";
+ }
scheduleEmailReminder(
evt,
workflow.trigger,
diff --git a/packages/features/ee/workflows/lib/reminders/templates/emailReminderTemplate.ts b/packages/features/ee/workflows/lib/reminders/templates/emailReminderTemplate.ts
index 06f2405a3e..97371a798f 100644
--- a/packages/features/ee/workflows/lib/reminders/templates/emailReminderTemplate.ts
+++ b/packages/features/ee/workflows/lib/reminders/templates/emailReminderTemplate.ts
@@ -18,7 +18,9 @@ const emailReminderTemplate = (
.tz(timeZone)
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
- const introHtml = `Hi ${name},
This is a reminder about your upcoming event.
`;
+ const introHtml = `Hi${
+ name ? " " + name : ""
+ },
This is a reminder about your upcoming event.
`;
const eventHtml = `Event:
${eventName}
`;
diff --git a/packages/features/ee/workflows/pages/v2/workflow.tsx b/packages/features/ee/workflows/pages/v2/workflow.tsx
index e8cdff48b9..b9e4ebd91a 100644
--- a/packages/features/ee/workflows/pages/v2/workflow.tsx
+++ b/packages/features/ee/workflows/pages/v2/workflow.tsx
@@ -55,7 +55,7 @@ const formSchema = z.object({
template: z.nativeEnum(WorkflowTemplates),
sendTo: z
.string()
- .refine((val) => isValidPhoneNumber(val))
+ .refine((val) => isValidPhoneNumber(val) || val.includes("@"))
.nullable(),
})
.array(),
@@ -77,6 +77,7 @@ function WorkflowPage() {
mode: "onBlur",
resolver: zodResolver(formSchema),
});
+
const { workflow: workflowId } = router.isReady ? querySchema.parse(router.query) : { workflow: -1 };
const utils = trpc.useContext();
diff --git a/packages/prisma/migrations/20221007112203_add_email_address_workflow_action/migration.sql b/packages/prisma/migrations/20221007112203_add_email_address_workflow_action/migration.sql
new file mode 100644
index 0000000000..afd4c057f4
--- /dev/null
+++ b/packages/prisma/migrations/20221007112203_add_email_address_workflow_action/migration.sql
@@ -0,0 +1,2 @@
+-- AlterEnum
+ALTER TYPE "WorkflowActions" ADD VALUE 'EMAIL_ADDRESS';
diff --git a/packages/prisma/schema.prisma b/packages/prisma/schema.prisma
index dc60065494..1e98cd7c57 100644
--- a/packages/prisma/schema.prisma
+++ b/packages/prisma/schema.prisma
@@ -540,6 +540,7 @@ enum WorkflowActions {
EMAIL_ATTENDEE
SMS_ATTENDEE
SMS_NUMBER
+ EMAIL_ADDRESS
}
model WorkflowStep {
diff --git a/packages/trpc/server/routers/viewer/workflows.tsx b/packages/trpc/server/routers/viewer/workflows.tsx
index 24a90e06a5..b088ac4450 100644
--- a/packages/trpc/server/routers/viewer/workflows.tsx
+++ b/packages/trpc/server/routers/viewer/workflows.tsx
@@ -416,12 +416,22 @@ export const workflowsRouter = createProtectedRouter()
};
if (
step.action === WorkflowActions.EMAIL_HOST ||
- step.action === WorkflowActions.EMAIL_ATTENDEE
+ step.action === WorkflowActions.EMAIL_ATTENDEE ||
+ step.action === WorkflowActions.EMAIL_ADDRESS
) {
- const sendTo =
- step.action === WorkflowActions.EMAIL_HOST
- ? bookingInfo.organizer?.email
- : bookingInfo.attendees[0].email;
+ let sendTo = "";
+
+ switch (step.action) {
+ case WorkflowActions.EMAIL_HOST:
+ sendTo = bookingInfo.organizer?.email;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ sendTo = bookingInfo.attendees[0].email;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo = step.sendTo || "";
+ }
+
await scheduleEmailReminder(
bookingInfo,
trigger,
@@ -503,7 +513,11 @@ export const workflowsRouter = createProtectedRouter()
},
data: {
action: newStep.action,
- sendTo: newStep.action === WorkflowActions.SMS_NUMBER ? newStep.sendTo : null,
+ sendTo:
+ newStep.action === WorkflowActions.SMS_NUMBER ||
+ newStep.action === WorkflowActions.EMAIL_ADDRESS
+ ? newStep.sendTo
+ : null,
stepNumber: newStep.stepNumber,
workflowId: newStep.workflowId,
reminderBody: newStep.template === WorkflowTemplates.CUSTOM ? newStep.reminderBody : null,
@@ -577,12 +591,22 @@ export const workflowsRouter = createProtectedRouter()
};
if (
newStep.action === WorkflowActions.EMAIL_HOST ||
- newStep.action === WorkflowActions.EMAIL_ATTENDEE
+ newStep.action === WorkflowActions.EMAIL_ATTENDEE ||
+ newStep.action === WorkflowActions.EMAIL_ADDRESS
) {
- const sendTo =
- newStep.action === WorkflowActions.EMAIL_HOST
- ? bookingInfo.organizer?.email
- : bookingInfo.attendees[0].email;
+ let sendTo = "";
+
+ switch (newStep.action) {
+ case WorkflowActions.EMAIL_HOST:
+ sendTo = bookingInfo.organizer?.email;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ sendTo = bookingInfo.attendees[0].email;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo = newStep.sendTo || "";
+ }
+
await scheduleEmailReminder(
bookingInfo,
trigger,
@@ -681,12 +705,22 @@ export const workflowsRouter = createProtectedRouter()
if (
step.action === WorkflowActions.EMAIL_ATTENDEE ||
- step.action === WorkflowActions.EMAIL_HOST
+ step.action === WorkflowActions.EMAIL_HOST ||
+ step.action === WorkflowActions.EMAIL_ADDRESS
) {
- const sendTo =
- step.action === WorkflowActions.EMAIL_HOST
- ? bookingInfo.organizer?.email
- : bookingInfo.attendees[0].email;
+ let sendTo = "";
+
+ switch (step.action) {
+ case WorkflowActions.EMAIL_HOST:
+ sendTo = bookingInfo.organizer?.email;
+ break;
+ case WorkflowActions.EMAIL_ATTENDEE:
+ sendTo = bookingInfo.attendees[0].email;
+ break;
+ case WorkflowActions.EMAIL_ADDRESS:
+ sendTo = step.sendTo || "";
+ }
+
await scheduleEmailReminder(
bookingInfo,
trigger,
@@ -821,7 +855,11 @@ export const workflowsRouter = createProtectedRouter()
};
}
- if (action === WorkflowActions.EMAIL_ATTENDEE || action === WorkflowActions.EMAIL_HOST) {
+ if (
+ action === WorkflowActions.EMAIL_ATTENDEE ||
+ action === WorkflowActions.EMAIL_HOST ||
+ action === WorkflowActions.EMAIL_ADDRESS
+ ) {
scheduleEmailReminder(
evt,
WorkflowTriggerEvents.NEW_EVENT,