-
Notifications
You must be signed in to change notification settings - Fork 8.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Compare workflow reminder bodies to default template #19060
base: tasker-scan-workflow-templates
Are you sure you want to change the base?
Changes from all commits
7491884
0518177
8736701
dcdf238
a8f1601
e510870
574da22
5849851
f6e962e
2f1cecb
73ac145
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -43,18 +43,15 @@ import { | |
} from "@calcom/ui"; | ||
|
||
import { | ||
getWhatsappTemplateForAction, | ||
isAttendeeAction, | ||
isSMSAction, | ||
isSMSOrWhatsappAction, | ||
isWhatsappAction, | ||
getTemplateBodyForAction, | ||
shouldScheduleEmailReminder, | ||
} from "../lib/actionHelperFunctions"; | ||
import { DYNAMIC_TEXT_VARIABLES } from "../lib/constants"; | ||
import { getWorkflowTemplateOptions, getWorkflowTriggerOptions } from "../lib/getOptions"; | ||
import emailRatingTemplate from "../lib/reminders/templates/emailRatingTemplate"; | ||
import emailReminderTemplate from "../lib/reminders/templates/emailReminderTemplate"; | ||
import smsReminderTemplate from "../lib/reminders/templates/smsReminderTemplate"; | ||
import { whatsappReminderTemplate } from "../lib/reminders/templates/whatsapp"; | ||
import type { FormValues } from "../pages/workflow"; | ||
import { TimeTimeUnitInput } from "./TimeTimeUnitInput"; | ||
|
||
|
@@ -133,37 +130,25 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { | |
const { data: actionOptions } = trpc.viewer.workflows.getWorkflowActionOptions.useQuery(); | ||
const triggerOptions = getWorkflowTriggerOptions(t); | ||
const templateOptions = getWorkflowTemplateOptions(t, step?.action, hasActiveTeamPlan); | ||
if (step && !form.getValues(`steps.${step.stepNumber - 1}.reminderBody`)) { | ||
const action = form.getValues(`steps.${step.stepNumber - 1}.action`); | ||
const template = getTemplateBodyForAction({ | ||
action, | ||
locale: i18n.language, | ||
template: step.template ?? WorkflowTemplates.REMINDER, | ||
timeFormat, | ||
}); | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, template); | ||
} | ||
|
||
if (step && form.getValues(`steps.${step.stepNumber - 1}.template`) === WorkflowTemplates.REMINDER) { | ||
if (!form.getValues(`steps.${step.stepNumber - 1}.reminderBody`)) { | ||
const action = form.getValues(`steps.${step.stepNumber - 1}.action`); | ||
if (isSMSAction(action)) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
smsReminderTemplate(true, i18n.language, action, timeFormat) | ||
); | ||
} else if (isWhatsappAction(action)) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
whatsappReminderTemplate(true, i18n.language, action, timeFormat) | ||
); | ||
} else { | ||
const reminderBodyTemplate = emailReminderTemplate(true, i18n.language, action, timeFormat).emailBody; | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, reminderBodyTemplate); | ||
} | ||
} | ||
if (!form.getValues(`steps.${step.stepNumber - 1}.emailSubject`)) { | ||
const subjectTemplate = emailReminderTemplate( | ||
true, | ||
i18n.language, | ||
form.getValues(`steps.${step.stepNumber - 1}.action`), | ||
timeFormat | ||
).emailSubject; | ||
form.setValue(`steps.${step.stepNumber - 1}.emailSubject`, subjectTemplate); | ||
} | ||
} else if (step && isWhatsappAction(step.action)) { | ||
const templateBody = getWhatsappTemplateForAction(step.action, i18n.language, step.template, timeFormat); | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, templateBody); | ||
if (step && !form.getValues(`steps.${step.stepNumber - 1}.emailSubject`)) { | ||
const subjectTemplate = emailReminderTemplate({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Emails are the only one with subjects so keeping it as is |
||
isEditingMode: true, | ||
locale: i18n.language, | ||
action: form.getValues(`steps.${step.stepNumber - 1}.action`), | ||
timeFormat, | ||
}).emailSubject; | ||
form.setValue(`steps.${step.stepNumber - 1}.emailSubject`, subjectTemplate); | ||
} | ||
|
||
const { ref: emailSubjectFormRef, ...restEmailSubjectForm } = step | ||
|
@@ -457,6 +442,15 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { | |
if (val) { | ||
const oldValue = form.getValues(`steps.${step.stepNumber - 1}.action`); | ||
|
||
const template = getTemplateBodyForAction({ | ||
action: val.value, | ||
locale: i18n.language, | ||
template: WorkflowTemplates.REMINDER, | ||
timeFormat, | ||
}); | ||
|
||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, template); | ||
|
||
const setNumberRequiredConfigs = ( | ||
phoneNumberIsNeeded: boolean, | ||
senderNeeded = true | ||
|
@@ -471,7 +465,6 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { | |
setNumberRequiredConfigs(val.value === WorkflowActions.SMS_NUMBER); | ||
// email action changes to sms action | ||
if (!isSMSAction(oldValue)) { | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, ""); | ||
form.setValue(`steps.${step.stepNumber - 1}.sender`, SENDER_ID); | ||
} | ||
|
||
|
@@ -480,7 +473,6 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { | |
setNumberRequiredConfigs(val.value === WorkflowActions.WHATSAPP_NUMBER, false); | ||
|
||
if (!isWhatsappAction(oldValue)) { | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, ""); | ||
form.setValue(`steps.${step.stepNumber - 1}.sender`, ""); | ||
} | ||
|
||
|
@@ -492,65 +484,6 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { | |
setIsEmailSubjectNeeded(true); | ||
} | ||
|
||
if ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Handled with |
||
form.getValues(`steps.${step.stepNumber - 1}.template`) === | ||
WorkflowTemplates.REMINDER | ||
) { | ||
if (isSMSOrWhatsappAction(val.value) === isSMSOrWhatsappAction(oldValue)) { | ||
if (isAttendeeAction(oldValue) !== isAttendeeAction(val.value)) { | ||
const currentReminderBody = | ||
form.getValues(`steps.${step.stepNumber - 1}.reminderBody`) || ""; | ||
const newReminderBody = currentReminderBody | ||
.replaceAll("{ORGANIZER}", "{PLACEHOLDER}") | ||
.replaceAll("{ATTENDEE}", "{ORGANIZER}") | ||
.replaceAll("{PLACEHOLDER}", "{ATTENDEE}"); | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, newReminderBody); | ||
|
||
if (!isSMSOrWhatsappAction(val.value)) { | ||
const currentEmailSubject = | ||
form.getValues(`steps.${step.stepNumber - 1}.emailSubject`) || ""; | ||
const newEmailSubject = isAttendeeAction(val.value) | ||
? currentEmailSubject.replace("{ORGANIZER}", "{ATTENDEE}") | ||
: currentEmailSubject.replace("{ATTENDEE}", "{ORGANIZER}"); | ||
|
||
form.setValue( | ||
`steps.${step.stepNumber - 1}.emailSubject`, | ||
newEmailSubject || "" | ||
); | ||
} | ||
} | ||
} else { | ||
if (isSMSAction(val.value)) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
smsReminderTemplate(true, i18n.language, val.value, timeFormat) | ||
); | ||
} else if (isWhatsappAction(val.value)) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
whatsappReminderTemplate(true, i18n.language, val.value, timeFormat) | ||
); | ||
} else { | ||
const emailReminderBody = emailReminderTemplate( | ||
true, | ||
i18n.language, | ||
val.value, | ||
timeFormat | ||
); | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
emailReminderBody.emailBody | ||
); | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.emailSubject`, | ||
emailReminderBody.emailSubject | ||
); | ||
} | ||
} | ||
} else { | ||
const template = isWhatsappAction(val.value) ? "REMINDER" : "CUSTOM"; | ||
template && form.setValue(`steps.${step.stepNumber - 1}.template`, template); | ||
} | ||
form.unregister(`steps.${step.stepNumber - 1}.sendTo`); | ||
form.clearErrors(`steps.${step.stepNumber - 1}.sendTo`); | ||
form.setValue(`steps.${step.stepNumber - 1}.action`, val.value); | ||
|
@@ -827,55 +760,37 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { | |
onChange={(val) => { | ||
if (val) { | ||
const action = form.getValues(`steps.${step.stepNumber - 1}.action`); | ||
if (val.value === WorkflowTemplates.REMINDER) { | ||
if (isWhatsappAction(action)) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
whatsappReminderTemplate(true, i18n.language, action, timeFormat) | ||
); | ||
} else if (isSMSAction(action)) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
smsReminderTemplate(true, i18n.language, action, timeFormat) | ||
); | ||
} else { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
emailReminderTemplate(true, i18n.language, action, timeFormat).emailBody | ||
); | ||
|
||
const template = getTemplateBodyForAction({ | ||
action, | ||
locale: i18n.language, | ||
template: val.value ?? WorkflowTemplates.REMINDER, | ||
timeFormat, | ||
}); | ||
|
||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, template); | ||
|
||
if (shouldScheduleEmailReminder(action)) { | ||
if (val.value === WorkflowTemplates.REMINDER) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.emailSubject`, | ||
emailReminderTemplate(true, i18n.language, action, timeFormat).emailSubject | ||
emailReminderTemplate({ | ||
isEditingMode: true, | ||
locale: i18n.language, | ||
action, | ||
timeFormat, | ||
}).emailSubject | ||
); | ||
} | ||
} else if (val.value === WorkflowTemplates.RATING) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
emailRatingTemplate({ | ||
isEditingMode: true, | ||
locale: i18n.language, | ||
action, | ||
timeFormat, | ||
}).emailBody | ||
); | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.emailSubject`, | ||
emailRatingTemplate({ | ||
isEditingMode: true, | ||
locale: i18n.language, | ||
action, | ||
timeFormat, | ||
}).emailSubject | ||
); | ||
} else { | ||
if (isWhatsappAction(action)) { | ||
} else if (val.value === WorkflowTemplates.RATING) { | ||
form.setValue( | ||
`steps.${step.stepNumber - 1}.reminderBody`, | ||
getWhatsappTemplateForAction(action, i18n.language, val.value, timeFormat) | ||
`steps.${step.stepNumber - 1}.emailSubject`, | ||
emailRatingTemplate({ | ||
isEditingMode: true, | ||
locale: i18n.language, | ||
action, | ||
timeFormat, | ||
}).emailSubject | ||
); | ||
} else { | ||
form.setValue(`steps.${step.stepNumber - 1}.reminderBody`, ""); | ||
form.setValue(`steps.${step.stepNumber - 1}.emailSubject`, ""); | ||
} | ||
} | ||
field.onChange(val.value); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,9 @@ import { | |
whatsappEventRescheduledTemplate, | ||
whatsappReminderTemplate, | ||
} from "../lib/reminders/templates/whatsapp"; | ||
import emailRatingTemplate from "./reminders/templates/emailRatingTemplate"; | ||
import emailReminderTemplate from "./reminders/templates/emailReminderTemplate"; | ||
import smsReminderTemplate from "./reminders/templates/smsReminderTemplate"; | ||
|
||
export function shouldScheduleEmailReminder(action: WorkflowActions) { | ||
return action === WorkflowActions.EMAIL_ATTENDEE || action === WorkflowActions.EMAIL_HOST; | ||
|
@@ -86,6 +89,17 @@ export function getWhatsappTemplateFunction(template?: WorkflowTemplates): typeo | |
} | ||
} | ||
|
||
function getEmailTemplateFunction(template?: WorkflowTemplates) { | ||
switch (template) { | ||
case WorkflowTemplates.REMINDER: | ||
return emailReminderTemplate; | ||
case WorkflowTemplates.RATING: | ||
return emailRatingTemplate; | ||
default: | ||
return emailReminderTemplate; | ||
} | ||
} | ||
|
||
export function getWhatsappTemplateForAction( | ||
action: WorkflowActions, | ||
locale: string, | ||
|
@@ -95,3 +109,28 @@ export function getWhatsappTemplateForAction( | |
const templateFunction = getWhatsappTemplateFunction(template); | ||
return templateFunction(true, locale, action, timeFormat); | ||
} | ||
|
||
export function getTemplateBodyForAction({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function is used in |
||
action, | ||
locale, | ||
template, | ||
timeFormat, | ||
}: { | ||
action: WorkflowActions; | ||
locale: string; | ||
template: WorkflowTemplates; | ||
timeFormat: TimeFormat; | ||
}): string | null { | ||
if (isSMSAction(action)) { | ||
return smsReminderTemplate(true, locale, action, timeFormat); | ||
} | ||
|
||
if (isWhatsappAction(action)) { | ||
const templateFunction = getWhatsappTemplateFunction(template); | ||
return templateFunction(true, locale, action, timeFormat); | ||
} | ||
|
||
// If not a whatsapp action then it's an email action | ||
const templateFunction = getEmailTemplateFunction(template); | ||
return templateFunction({ isEditingMode: true, locale, action, timeFormat }).emailBody; | ||
} |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I found that the workflow editor was adding HTML tags that weren't in the standard template. I decided that to compare if the workflow body was the same as the template is to stripe all of the HTML tags out of the reminder body and template and compare the content. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
const compareReminderBodyToTemplate = ({ | ||
reminderBody, | ||
template, | ||
}: { | ||
reminderBody: string; | ||
template: string; | ||
}) => { | ||
const stripHTML = (html: string) => html.replace(/<[^>]+>/g, "").replace(/&/g, "&"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
const stripedReminderBody = stripHTML(reminderBody); | ||
const stripedTemplate = stripHTML(template); | ||
|
||
return stripedReminderBody === stripedTemplate; | ||
}; | ||
|
||
export default compareReminderBodyToTemplate; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,3 +79,6 @@ const emailRatingTemplate = ({ | |
}; | ||
|
||
export default emailRatingTemplate; | ||
|
||
export const plainTextTemplate = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For tests, it wouldn't make sense to strip the templates twice with the same function since it would always pass. By adding the plain text templates, we can detect whether the template was changed or something changed with the comparison function. |
||
"Hi {ORGANIZER},We're always looking to improve our customer's experience. How satisfied were you with your recent meeting?😠 🙁 😐 😄 😍{ORGANIZER} didn't join the meeting? Reschedule hereEvent: {EVENT_NAME}Date & Time: {EVENT_DATE_ddd, MMM D, YYYY h:mma} - {EVENT_END_TIME} ({TIMEZONE})Attendees: You & {ORGANIZER}This survey was triggered by a Workflow in Cal."; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,21 +4,35 @@ import { APP_NAME } from "@calcom/lib/constants"; | |
import { TimeFormat } from "@calcom/lib/timeFormat"; | ||
import { WorkflowActions } from "@calcom/prisma/enums"; | ||
|
||
const emailReminderTemplate = ( | ||
isEditingMode: boolean, | ||
locale: string, | ||
action?: WorkflowActions, | ||
timeFormat?: TimeFormat, | ||
startTime?: string, | ||
endTime?: string, | ||
eventName?: string, | ||
timeZone?: string, | ||
location?: string, | ||
meetingUrl?: string, | ||
otherPerson?: string, | ||
name?: string, | ||
isBrandingDisabled?: boolean | ||
) => { | ||
const emailReminderTemplate = ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Refactored this function to take in an object as a param for readability and to align with other template functions. |
||
isEditingMode, | ||
locale, | ||
action, | ||
timeFormat, | ||
startTime, | ||
endTime, | ||
eventName, | ||
timeZone, | ||
location, | ||
meetingUrl, | ||
otherPerson, | ||
name, | ||
isBrandingDisabled, | ||
}: { | ||
isEditingMode: boolean; | ||
locale: string; | ||
action?: WorkflowActions; | ||
timeFormat?: TimeFormat; | ||
startTime?: string; | ||
endTime?: string; | ||
eventName?: string; | ||
timeZone?: string; | ||
location?: string; | ||
meetingUrl?: string; | ||
otherPerson?: string; | ||
name?: string; | ||
isBrandingDisabled?: boolean; | ||
}) => { | ||
const currentTimeFormat = timeFormat || TimeFormat.TWELVE_HOUR; | ||
const dateTimeFormat = `ddd, MMM D, YYYY ${currentTimeFormat}`; | ||
|
||
|
@@ -63,3 +77,6 @@ const emailReminderTemplate = ( | |
}; | ||
|
||
export default emailReminderTemplate; | ||
|
||
export const plainTextTemplate = | ||
"Hi {ORGANIZER},This is a reminder about your upcoming event.Event: {EVENT_NAME}Date & Time: {EVENT_DATE_ddd, MMM D, YYYY h:mma} - {EVENT_END_TIME} ({TIMEZONE})Attendees: You & {ATTENDEE}Location: {LOCATION} {MEETING_URL}This reminder was triggered by a Workflow in Cal."; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type of logic was repeated through out the component. I decided to abstract this to
getTemplateBodyForAction
so it can be be used in other parts of the codebase