Make SMS workflow actions only available for teams plan (#5330)
* fix issue when updating workflow * make SMS actions pro feature * Improved clean up on inline functions Co-authored-by: CarinaWolli <wollencarina@gmail.com> Co-authored-by: Peer Richelsen <peeroke@gmail.com> Co-authored-by: Alan <alannnc@gmail.com>
This commit is contained in:
4 files changed
+66
-25
No files matched your search
@@ -27,6 +27,12 @@ interface IAddActionDialog {
|
||||
isOpenDialog: boolean;
|
||||
setIsOpenDialog: Dispatch<SetStateAction<boolean>>;
|
||||
addAction: (action: WorkflowActions, sendTo?: string, numberRequired?: boolean) => void;
|
||||
isFreeUser: boolean;
|
||||
}
|
||||
|
||||
interface ISelectActionOption {
|
||||
label: string;
|
||||
value: WorkflowActions;
|
||||
}
|
||||
|
||||
type AddActionFormValues = {
|
||||
@@ -35,12 +41,19 @@ type AddActionFormValues = {
|
||||
numberRequired?: boolean;
|
||||
};
|
||||
|
||||
const cleanUpActionsForFreeUser = (actions: ISelectActionOption[]) => {
|
||||
return actions.filter(
|
||||
(item) => item.value !== WorkflowActions.SMS_ATTENDEE && item.value !== WorkflowActions.SMS_NUMBER
|
||||
);
|
||||
};
|
||||
|
||||
export const AddActionDialog = (props: IAddActionDialog) => {
|
||||
const { t } = useLocale();
|
||||
const { isOpenDialog, setIsOpenDialog, addAction } = props;
|
||||
const { isOpenDialog, setIsOpenDialog, addAction, isFreeUser } = props;
|
||||
const [isPhoneNumberNeeded, setIsPhoneNumberNeeded] = useState(false);
|
||||
const [isEmailAddressNeeded, setIsEmailAddressNeeded] = useState(false);
|
||||
const actionOptions = getWorkflowActionOptions(t);
|
||||
const workflowActions = getWorkflowActionOptions(t);
|
||||
const actionOptions = isFreeUser ? cleanUpActionsForFreeUser(workflowActions) : workflowActions;
|
||||
|
||||
const formSchema = z.object({
|
||||
action: z.enum(WORKFLOW_ACTIONS),
|
||||
@@ -59,6 +72,26 @@ export const AddActionDialog = (props: IAddActionDialog) => {
|
||||
resolver: zodResolver(formSchema),
|
||||
});
|
||||
|
||||
const handleSelectAction = (newValue: ISelectActionOption | null) => {
|
||||
if (newValue) {
|
||||
form.setValue("action", newValue.value);
|
||||
if (newValue.value === WorkflowActions.SMS_NUMBER) {
|
||||
setIsPhoneNumberNeeded(true);
|
||||
setIsEmailAddressNeeded(false);
|
||||
} else if (newValue.value === WorkflowActions.EMAIL_ADDRESS) {
|
||||
setIsEmailAddressNeeded(true);
|
||||
setIsPhoneNumberNeeded(false);
|
||||
} else {
|
||||
setIsEmailAddressNeeded(false);
|
||||
setIsPhoneNumberNeeded(false);
|
||||
}
|
||||
form.unregister("sendTo");
|
||||
form.unregister("numberRequired");
|
||||
form.clearErrors("action");
|
||||
form.clearErrors("sendTo");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpenDialog} onOpenChange={setIsOpenDialog}>
|
||||
<DialogContent type="creation" useOwnActionButtons={true} title={t("add_action")}>
|
||||
@@ -86,25 +119,7 @@ export const AddActionDialog = (props: IAddActionDialog) => {
|
||||
isSearchable={false}
|
||||
className="text-sm"
|
||||
defaultValue={actionOptions[0]}
|
||||
onChange={(val) => {
|
||||
if (val) {
|
||||
form.setValue("action", val.value);
|
||||
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("sendTo");
|
||||
form.unregister("numberRequired");
|
||||
form.clearErrors("action");
|
||||
form.clearErrors("sendTo");
|
||||
}
|
||||
}}
|
||||
onChange={handleSelectAction}
|
||||
options={actionOptions}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { Controller, UseFormReturn } from "react-hook-form";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
import { Label, TextField } from "@calcom/ui/v2";
|
||||
@@ -28,6 +29,9 @@ export default function WorkflowDetailsPage(props: Props) {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
|
||||
const me = useMeQuery();
|
||||
const isFreeUser = me.data?.plan === "FREE";
|
||||
|
||||
const [isAddActionDialogOpen, setIsAddActionDialogOpen] = useState(false);
|
||||
const [reload, setReload] = useState(false);
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
@@ -120,7 +124,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
||||
<div className="w-full rounded-md border border-gray-200 bg-gray-50 p-3 py-5 md:ml-3 md:p-8">
|
||||
{form.getValues("trigger") && (
|
||||
<div>
|
||||
<WorkflowStepContainer form={form} />
|
||||
<WorkflowStepContainer form={form} isFreeUser={isFreeUser} />
|
||||
</div>
|
||||
)}
|
||||
{form.getValues("steps") && (
|
||||
@@ -133,6 +137,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
||||
step={step}
|
||||
reload={reload}
|
||||
setReload={setReload}
|
||||
isFreeUser={isFreeUser}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -152,6 +157,7 @@ export default function WorkflowDetailsPage(props: Props) {
|
||||
isOpenDialog={isAddActionDialogOpen}
|
||||
setIsOpenDialog={setIsAddActionDialogOpen}
|
||||
addAction={addAction}
|
||||
isFreeUser={isFreeUser}
|
||||
/>
|
||||
<DeleteDialog
|
||||
isOpenDialog={deleteDialogOpen}
|
||||
|
||||
@@ -38,11 +38,12 @@ type WorkflowStepProps = {
|
||||
form: UseFormReturn<FormValues>;
|
||||
reload?: boolean;
|
||||
setReload?: Dispatch<SetStateAction<boolean>>;
|
||||
isFreeUser: boolean;
|
||||
};
|
||||
|
||||
export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
const { t, i18n } = useLocale();
|
||||
const { step, form, reload, setReload } = props;
|
||||
const { step, form, reload, setReload, isFreeUser } = props;
|
||||
const [isAdditionalInputsDialogOpen, setIsAdditionalInputsDialogOpen] = useState(false);
|
||||
const [confirmationDialogOpen, setConfirmationDialogOpen] = useState(false);
|
||||
|
||||
@@ -300,7 +301,15 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) {
|
||||
}
|
||||
}}
|
||||
defaultValue={selectedAction}
|
||||
options={actionOptions}
|
||||
options={
|
||||
isFreeUser
|
||||
? actionOptions.filter(
|
||||
(actionOption) =>
|
||||
actionOption.value !== WorkflowActions.SMS_ATTENDEE &&
|
||||
actionOption.value !== WorkflowActions.SMS_NUMBER
|
||||
)
|
||||
: actionOptions
|
||||
}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -27,12 +27,17 @@ import {
|
||||
scheduleSMSReminder,
|
||||
} from "@calcom/features/ee/workflows/lib/reminders/smsReminderManager";
|
||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { getTranslation } from "@calcom/lib/server/i18n";
|
||||
|
||||
import { TRPCError } from "@trpc/server";
|
||||
|
||||
import { createProtectedRouter } from "../../createRouter";
|
||||
|
||||
function isSMSAction(action: WorkflowActions) {
|
||||
if (action === WorkflowActions.SMS_ATTENDEE || action === WorkflowActions.SMS_NUMBER) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export const workflowsRouter = createProtectedRouter()
|
||||
.query("list", {
|
||||
async resolve({ ctx }) {
|
||||
@@ -515,6 +520,9 @@ export const workflowsRouter = createProtectedRouter()
|
||||
});
|
||||
//step was edited
|
||||
} else if (JSON.stringify(oldStep) !== JSON.stringify(newStep)) {
|
||||
if (user.plan === "FREE" && !isSMSAction(oldStep.action) && isSMSAction(newStep.action)) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
await ctx.prisma.workflowStep.update({
|
||||
where: {
|
||||
id: oldStep.id,
|
||||
@@ -652,6 +660,9 @@ export const workflowsRouter = createProtectedRouter()
|
||||
//added steps
|
||||
const addedSteps = steps.map((s) => {
|
||||
if (s.id <= 0) {
|
||||
if (user.plan === "FREE" && isSMSAction(s.action)) {
|
||||
throw new TRPCError({ code: "UNAUTHORIZED" });
|
||||
}
|
||||
const { id: stepId, ...stepToAdd } = s;
|
||||
return stepToAdd;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user