fix: disable custom templates for free plan (#18635)
* add upgrade badge * adjust update handler * code clean up * clean up template code * only handle email templates * fixes * code clean up * fix workflow template zod type --------- Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Alex van Andel <me@alexvanandel.com>
This commit is contained in:
co-authored by
CarinaWolli
Alex van Andel
parent
f137aee436
commit
36dd3e9e6c
@@ -12,7 +12,7 @@ import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import type { MultiSelectCheckboxesOptionType as Option } from "@calcom/ui";
|
||||
import { Button, Icon, Label, MultiSelectCheckboxes, TextField, CheckboxField, InfoBadge } from "@calcom/ui";
|
||||
|
||||
import { isSMSAction, isWhatsappAction } from "../lib/actionHelperFunctions";
|
||||
import { isSMSAction } from "../lib/actionHelperFunctions";
|
||||
import type { FormValues } from "../pages/workflow";
|
||||
import { AddActionDialog } from "./AddActionDialog";
|
||||
import { DeleteDialog } from "./DeleteDialog";
|
||||
@@ -83,7 +83,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
||||
workflowId: workflowId,
|
||||
reminderBody: null,
|
||||
emailSubject: null,
|
||||
template: isWhatsappAction(action) ? WorkflowTemplates.REMINDER : WorkflowTemplates.CUSTOM,
|
||||
template: WorkflowTemplates.REMINDER,
|
||||
numberRequired: numberRequired || false,
|
||||
sender: isSMSAction(action) ? sender || SENDER_ID : SENDER_ID,
|
||||
senderName: !isSMSAction(action) ? senderName || SENDER_NAME : SENDER_NAME,
|
||||
|
||||
@@ -8,6 +8,7 @@ import "react-phone-number-input/style.css";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { SENDER_ID, SENDER_NAME } from "@calcom/lib/constants";
|
||||
import useHasPaidPlan from "@calcom/lib/hooks/useHasPaidPlan";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
@@ -84,6 +85,8 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
const { t, i18n } = useLocale();
|
||||
const utils = trpc.useUtils();
|
||||
|
||||
const { hasPaidPlan } = useHasPaidPlan();
|
||||
|
||||
const { step, form, reload, setReload, teamId } = props;
|
||||
const { data: _verifiedNumbers } = trpc.viewer.workflows.getVerifiedNumbers.useQuery(
|
||||
{ teamId },
|
||||
@@ -129,7 +132,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
|
||||
const { data: actionOptions } = trpc.viewer.workflows.getWorkflowActionOptions.useQuery();
|
||||
const triggerOptions = getWorkflowTriggerOptions(t);
|
||||
const templateOptions = getWorkflowTemplateOptions(t, step?.action);
|
||||
const templateOptions = getWorkflowTemplateOptions(t, step?.action, hasPaidPlan);
|
||||
|
||||
if (step && form.getValues(`steps.${step.stepNumber - 1}.template`) === WorkflowTemplates.REMINDER) {
|
||||
if (!form.getValues(`steps.${step.stepNumber - 1}.reminderBody`)) {
|
||||
@@ -370,7 +373,11 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
needsTeamsUpgrade: false,
|
||||
};
|
||||
|
||||
const selectedTemplate = { label: t(`${step.template.toLowerCase()}`), value: step.template };
|
||||
const selectedTemplate = {
|
||||
label: t(`${step.template.toLowerCase()}`),
|
||||
value: step.template,
|
||||
needsTeamsUpgrade: false,
|
||||
};
|
||||
|
||||
const canRequirePhoneNumber = (workflowStep: string) => {
|
||||
return (
|
||||
@@ -879,6 +886,11 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
defaultValue={selectedTemplate}
|
||||
value={selectedTemplate}
|
||||
options={templateOptions}
|
||||
isOptionDisabled={(option: {
|
||||
label: string;
|
||||
value: any;
|
||||
needsTeamsUpgrade: boolean;
|
||||
}) => option.needsTeamsUpgrade}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
@@ -906,7 +918,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
refEmailSubject.current = e;
|
||||
}}
|
||||
rows={1}
|
||||
disabled={props.readOnly}
|
||||
disabled={props.readOnly || !hasPaidPlan}
|
||||
className="my-0 focus:ring-transparent"
|
||||
required
|
||||
{...restEmailSubjectForm}
|
||||
@@ -939,7 +951,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
updateTemplate={updateTemplate}
|
||||
firstRender={firstRender}
|
||||
setFirstRender={setFirstRender}
|
||||
editable={!props.readOnly && !isWhatsappAction(step.action)}
|
||||
editable={!props.readOnly && !isWhatsappAction(step.action) && hasPaidPlan}
|
||||
excludedToolbarItems={
|
||||
!isSMSAction(step.action) ? [] : ["blockType", "bold", "italic", "link"]
|
||||
}
|
||||
|
||||
@@ -30,6 +30,14 @@ export function isSMSOrWhatsappAction(action: WorkflowActions) {
|
||||
return isSMSAction(action) || isWhatsappAction(action);
|
||||
}
|
||||
|
||||
export function isEmailAction(action: WorkflowActions) {
|
||||
return (
|
||||
action === WorkflowActions.EMAIL_ADDRESS ||
|
||||
action === WorkflowActions.EMAIL_ATTENDEE ||
|
||||
action === WorkflowActions.EMAIL_HOST
|
||||
);
|
||||
}
|
||||
|
||||
export function isAttendeeAction(action: WorkflowActions) {
|
||||
return (
|
||||
action === WorkflowActions.SMS_ATTENDEE ||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type { TFunction } from "next-i18next";
|
||||
|
||||
import type { WorkflowActions } from "@calcom/prisma/enums";
|
||||
import { WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { WorkflowTemplates, WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
||||
|
||||
import { isSMSOrWhatsappAction, isWhatsappAction, isEmailToAttendeeAction } from "./actionHelperFunctions";
|
||||
import {
|
||||
@@ -39,7 +39,11 @@ export function getWorkflowTriggerOptions(t: TFunction) {
|
||||
});
|
||||
}
|
||||
|
||||
export function getWorkflowTemplateOptions(t: TFunction, action: WorkflowActions | undefined) {
|
||||
export function getWorkflowTemplateOptions(
|
||||
t: TFunction,
|
||||
action: WorkflowActions | undefined,
|
||||
hasPaidPlan: boolean
|
||||
) {
|
||||
const TEMPLATES =
|
||||
action && isWhatsappAction(action)
|
||||
? WHATSAPP_WORKFLOW_TEMPLATES
|
||||
@@ -47,6 +51,10 @@ export function getWorkflowTemplateOptions(t: TFunction, action: WorkflowActions
|
||||
? ATTENDEE_WORKFLOW_TEMPLATES
|
||||
: BASIC_WORKFLOW_TEMPLATES;
|
||||
return TEMPLATES.map((template) => {
|
||||
return { label: t(`${template.toLowerCase()}`), value: template };
|
||||
}) as { label: string; value: any }[];
|
||||
return {
|
||||
label: t(`${template.toLowerCase()}`),
|
||||
value: template,
|
||||
needsTeamsUpgrade: !hasPaidPlan && template == WorkflowTemplates.CUSTOM,
|
||||
};
|
||||
}) as { label: string; value: any; needsTeamsUpgrade: boolean }[];
|
||||
}
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { isSMSOrWhatsappAction } from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
|
||||
import {
|
||||
isEmailAction,
|
||||
isSMSOrWhatsappAction,
|
||||
} from "@calcom/features/ee/workflows/lib/actionHelperFunctions";
|
||||
import { IS_SELF_HOSTED } from "@calcom/lib/constants";
|
||||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||
import { WorkflowRepository } from "@calcom/lib/server/repository/workflow";
|
||||
import type { PrismaClient } from "@calcom/prisma";
|
||||
import { WorkflowActions } from "@calcom/prisma/enums";
|
||||
import { WorkflowActions, WorkflowTemplates } from "@calcom/prisma/enums";
|
||||
import type { TrpcSessionUser } from "@calcom/trpc/server/trpc";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
@@ -20,6 +23,7 @@ import {
|
||||
verifyEmailSender,
|
||||
removeSmsReminderFieldForEventTypes,
|
||||
isStepEdited,
|
||||
getEmailTemplateText,
|
||||
} from "./util";
|
||||
|
||||
type UpdateOptions = {
|
||||
@@ -333,8 +337,34 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||
});
|
||||
} else if (isStepEdited(oldStep, newStep)) {
|
||||
// check if step that require team plan already existed before
|
||||
if (!hasPaidPlan && !isSMSOrWhatsappAction(oldStep.action) && isSMSOrWhatsappAction(newStep.action)) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not available on free plan" });
|
||||
if (!hasPaidPlan) {
|
||||
const isChangingToSMSOrWhatsapp =
|
||||
!isSMSOrWhatsappAction(oldStep.action) && isSMSOrWhatsappAction(newStep.action);
|
||||
const isChangingToCustomTemplate =
|
||||
newStep.template === WorkflowTemplates.CUSTOM && oldStep.template !== WorkflowTemplates.CUSTOM;
|
||||
|
||||
if (isChangingToSMSOrWhatsapp || isChangingToCustomTemplate) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not available on free plan" });
|
||||
}
|
||||
|
||||
//if email body or subject was changed, change to predefined template
|
||||
if (newStep.emailSubject !== oldStep.emailSubject || newStep.reminderBody !== oldStep.reminderBody) {
|
||||
// already existing custom templates can't be updated
|
||||
if (newStep.template === WorkflowTemplates.CUSTOM) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not available on free plan" });
|
||||
}
|
||||
|
||||
if (isEmailAction(newStep.action)) {
|
||||
// on free plans always use predefined templates
|
||||
const { emailBody, emailSubject } = getEmailTemplateText(newStep.template, {
|
||||
locale: ctx.user.locale,
|
||||
action: newStep.action,
|
||||
timeFormat: ctx.user.timeFormat,
|
||||
});
|
||||
|
||||
newStep = { ...newStep, reminderBody: emailBody, emailSubject };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// update step
|
||||
@@ -388,8 +418,19 @@ export const updateHandler = async ({ ctx, input }: UpdateOptions) => {
|
||||
steps
|
||||
.filter((step) => step.id <= 0)
|
||||
.map(async (newStep) => {
|
||||
if (isSMSOrWhatsappAction(newStep.action) && !hasPaidPlan) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not available on free plan" });
|
||||
if (!hasPaidPlan) {
|
||||
if (isSMSOrWhatsappAction(newStep.action) || newStep.template === WorkflowTemplates.CUSTOM) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED", message: "Not available on free plan" });
|
||||
}
|
||||
|
||||
// on free plans always use predefined templates
|
||||
const { emailBody, emailSubject } = getEmailTemplateText(newStep.template, {
|
||||
locale: ctx.user.locale,
|
||||
action: newStep.action,
|
||||
timeFormat: ctx.user.timeFormat,
|
||||
});
|
||||
|
||||
newStep = { ...newStep, reminderBody: emailBody, emailSubject };
|
||||
}
|
||||
|
||||
if (newStep.action === WorkflowActions.EMAIL_ADDRESS) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import {
|
||||
WORKFLOW_TEMPLATES,
|
||||
TIME_UNIT,
|
||||
WORKFLOW_ACTIONS,
|
||||
WORKFLOW_TEMPLATES,
|
||||
WORKFLOW_TRIGGER_EVENTS,
|
||||
} from "@calcom/features/ee/workflows/lib/constants";
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ import { isSMSOrWhatsappAction } from "@calcom/ee/workflows/lib/actionHelperFunc
|
||||
import { getAllWorkflows } from "@calcom/ee/workflows/lib/getAllWorkflows";
|
||||
import { scheduleEmailReminder } from "@calcom/ee/workflows/lib/reminders/emailReminderManager";
|
||||
import { scheduleSMSReminder } from "@calcom/ee/workflows/lib/reminders/smsReminderManager";
|
||||
import emailRatingTemplate from "@calcom/ee/workflows/lib/reminders/templates/emailRatingTemplate";
|
||||
import emailReminderTemplate from "@calcom/ee/workflows/lib/reminders/templates/emailReminderTemplate";
|
||||
import { scheduleWhatsappReminder } from "@calcom/ee/workflows/lib/reminders/whatsappReminderManager";
|
||||
import type { Workflow as WorkflowType } from "@calcom/ee/workflows/lib/types";
|
||||
import { SMS_REMINDER_NUMBER_FIELD } from "@calcom/features/bookings/lib/SystemField";
|
||||
@@ -23,6 +25,7 @@ import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
import prisma from "@calcom/prisma";
|
||||
import type { Prisma, WorkflowStep } from "@calcom/prisma/client";
|
||||
import type { TimeUnit } from "@calcom/prisma/enums";
|
||||
import { WorkflowTemplates } from "@calcom/prisma/enums";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import { BookingStatus, MembershipRole, WorkflowActions, WorkflowTriggerEvents } from "@calcom/prisma/enums";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
@@ -848,3 +851,28 @@ export const getEventTypeWorkflows = async (
|
||||
|
||||
return workflows.map((workflow) => ({ workflow }));
|
||||
};
|
||||
|
||||
export function getEmailTemplateText(
|
||||
template: WorkflowTemplates,
|
||||
params: { locale: string; action: WorkflowActions; timeFormat: number | null }
|
||||
) {
|
||||
const { locale, action } = params;
|
||||
|
||||
const timeFormat = getTimeFormatStringFromUserTimeFormat(params.timeFormat);
|
||||
|
||||
let { emailBody, emailSubject } = emailReminderTemplate(true, locale, action, timeFormat);
|
||||
|
||||
if (template === WorkflowTemplates.RATING) {
|
||||
const ratingTemplate = emailRatingTemplate({
|
||||
isEditingMode: true,
|
||||
locale,
|
||||
action,
|
||||
timeFormat,
|
||||
});
|
||||
|
||||
emailBody = ratingTemplate.emailBody;
|
||||
emailSubject = ratingTemplate.emailSubject;
|
||||
}
|
||||
|
||||
return { emailBody, emailSubject };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user