Add new workflow action: Send email to specific email address (#4929)
Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com>
This commit is contained in:
parent
1b15b71a24
commit
2a0a293f8c
|
@ -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?"
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}}>
|
||||
<div className="mt-5 space-y-1">
|
||||
<Label htmlFor="label">{t("action")}:</Label>
|
||||
|
@ -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) => {
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isEmailAddressNeeded && (
|
||||
<div className="mt-5">
|
||||
<EmailField required label={t("email_address")} {...form.register("sendTo")} />
|
||||
</div>
|
||||
)}
|
||||
<DialogFooter>
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
|
@ -118,6 +141,7 @@ export const AddActionDialog = (props: IAddActionDialog) => {
|
|||
form.unregister("sendTo");
|
||||
form.unregister("action");
|
||||
setIsPhoneNumberNeeded(false);
|
||||
setIsEmailAddressNeeded(false);
|
||||
}}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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) {
|
|||
)}
|
||||
</div>
|
||||
)}
|
||||
{isEmailAddressNeeded && (
|
||||
<div className="mt-5 rounded-md bg-gray-50 p-4">
|
||||
<EmailField
|
||||
required
|
||||
label={t("email_address")}
|
||||
{...form.register(`steps.${step.stepNumber - 1}.sendTo`)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-5">
|
||||
<Label>{t("message_template")}</Label>
|
||||
<Controller
|
||||
|
@ -451,16 +474,16 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
|||
reminderBody,
|
||||
template: step.template,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
const isNumberValid =
|
||||
form.formState.errors.steps &&
|
||||
form.formState?.errors?.steps[step.stepNumber - 1]?.sendTo
|
||||
? false
|
||||
: true;
|
||||
|
||||
const isNumberValid =
|
||||
form.formState.errors.steps &&
|
||||
form.formState?.errors?.steps[step.stepNumber - 1]?.sendTo
|
||||
? false
|
||||
: true;
|
||||
|
||||
if (isPhoneNumberNeeded && isNumberValid && !isEmpty) {
|
||||
setConfirmationDialogOpen(true);
|
||||
if (isPhoneNumberNeeded && isNumberValid && !isEmpty) {
|
||||
setConfirmationDialogOpen(true);
|
||||
}
|
||||
}
|
||||
}}
|
||||
color="secondary">
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -18,7 +18,9 @@ const emailReminderTemplate = (
|
|||
.tz(timeZone)
|
||||
.format("YYYY MMM D")} at ${dayjs(startTime).tz(timeZone).format("h:mmA")} ${timeZone}.`;
|
||||
|
||||
const introHtml = `<body>Hi ${name},<br><br>This is a reminder about your upcoming event.<br><br>`;
|
||||
const introHtml = `<body>Hi${
|
||||
name ? " " + name : ""
|
||||
},<br><br>This is a reminder about your upcoming event.<br><br>`;
|
||||
|
||||
const eventHtml = `<div style="font-weight: bold;">Event:</div>${eventName}<br><br>`;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- AlterEnum
|
||||
ALTER TYPE "WorkflowActions" ADD VALUE 'EMAIL_ADDRESS';
|
|
@ -540,6 +540,7 @@ enum WorkflowActions {
|
|||
EMAIL_ATTENDEE
|
||||
SMS_ATTENDEE
|
||||
SMS_NUMBER
|
||||
EMAIL_ADDRESS
|
||||
}
|
||||
|
||||
model WorkflowStep {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue
Block a user