diff --git a/packages/features/ee/workflows/components/AddActionDialog.tsx b/packages/features/ee/workflows/components/AddActionDialog.tsx index f5ba024edf009c..e3318ccfd7f6ca 100644 --- a/packages/features/ee/workflows/components/AddActionDialog.tsx +++ b/packages/features/ee/workflows/components/AddActionDialog.tsx @@ -40,6 +40,7 @@ interface IAddActionDialog { needsCredits: boolean; creditsTeamId?: number; isOrganization?: boolean; + needsTeamsUpgrade?: boolean; }[]; } @@ -170,7 +171,16 @@ export const AddActionDialog = (props: IAddActionDialog) => { menuPlacement="bottom" defaultValue={actionOptions[0]} onChange={handleSelectAction} - options={actionOptions} + options={actionOptions.map((option) => ({ + label: option.label, + value: option.value, + needsTeamsUpgrade: option.needsTeamsUpgrade, + }))} + isOptionDisabled={(option: { + label: string; + value: WorkflowActions; + needsTeamsUpgrade?: boolean; + }) => !!option.needsTeamsUpgrade} /> ); }} diff --git a/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx b/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx index 7d44ca9c2129b5..edd0f863ab0b99 100644 --- a/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx +++ b/packages/features/ee/workflows/components/WorkflowDetailsPage.tsx @@ -3,6 +3,7 @@ import type { Dispatch, SetStateAction } from "react"; import { useState, useEffect } from "react"; import type { UseFormReturn } from "react-hook-form"; +import { useHasActiveTeamPlan } from "@calcom/features/billing/hooks/useHasPaidPlan"; import type { WorkflowPermissions } from "@calcom/features/workflows/repositories/WorkflowPermissionsRepository"; import { SENDER_ID, SENDER_NAME, SCANNING_WORKFLOW_STEPS } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; @@ -43,6 +44,7 @@ export default function WorkflowDetailsPage(props: Props) { const { form, workflowId, selectedOptions, setSelectedOptions, teamId, isOrg, allOptions, permissions } = props; const { t, i18n } = useLocale(); + const { hasActiveTeamPlan } = useHasActiveTeamPlan(); const [isAddActionDialogOpen, setIsAddActionDialogOpen] = useState(false); const [isDeleteStepDialogOpen, setIsDeleteStepDialogOpen] = useState(false); @@ -84,12 +86,15 @@ export default function WorkflowDetailsPage(props: Props) { } } + const needsTeamsUpgrade = isFormTrigger(form.getValues("trigger")) && !hasActiveTeamPlan; + return { ...option, label, creditsTeamId: teamId, isOrganization: isOrg, isCalAi: isCalAIAction(option.value), + needsTeamsUpgrade, }; }) : []; diff --git a/packages/features/ee/workflows/components/WorkflowStepContainer.tsx b/packages/features/ee/workflows/components/WorkflowStepContainer.tsx index 0eae491dcb5709..cef979e2c2a676 100644 --- a/packages/features/ee/workflows/components/WorkflowStepContainer.tsx +++ b/packages/features/ee/workflows/components/WorkflowStepContainer.tsx @@ -101,6 +101,7 @@ type WorkflowStepProps = { creditsTeamId?: number; isOrganization: boolean; isCalAi: boolean; + needsTeamsUpgrade?: boolean; }[]; updateTemplate: boolean; setUpdateTemplate: Dispatch>; @@ -371,7 +372,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { handleCreateAgent, ]); - const triggerOptions = getWorkflowTriggerOptions(t); + const triggerOptions = getWorkflowTriggerOptions(t, hasActiveTeamPlan); const templateOptions = getWorkflowTemplateOptions(t, step?.action, hasActiveTeamPlan, trigger); const steps = useWatch({ @@ -379,6 +380,11 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { name: "steps", }); + // Extract current step template from watched steps array + const currentStepTemplate = step ? steps?.[step.stepNumber - 1]?.template : null; + + const isReminderTemplate = currentStepTemplate === WorkflowTemplates.REMINDER; + const hasAiAction = hasCalAIAction(steps); const hasEmailToHostAction = steps.some((s) => s.action === WorkflowActions.EMAIL_HOST); const hasWhatsappAction = steps.some((s) => isWhatsappAction(s.action)); @@ -531,11 +537,12 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { isDisabled={props.readOnly} onChange={(val) => { if (val) { - const currentTrigger = form.getValues("trigger"); + const triggerValue = val.value as WorkflowTriggerEvents; + const currentTrigger = form.getValues("trigger") as WorkflowTriggerEvents; const isCurrentFormTrigger = isFormTrigger(currentTrigger); - const isNewFormTrigger = isFormTrigger(val.value); + const isNewFormTrigger = isFormTrigger(triggerValue); - form.setValue("trigger", val.value); + form.setValue("trigger", triggerValue); // Reset activeOn when switching between form and non-form triggers if (isCurrentFormTrigger !== isNewFormTrigger) { @@ -546,12 +553,12 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { form.setValue("selectAll", false); } - const newTimeSectionText = getTimeSectionText(val.value, t); + const newTimeSectionText = getTimeSectionText(triggerValue, t); if (newTimeSectionText) { setTimeSectionText(newTimeSectionText); if ( - val.value === WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW || - val.value === WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW + triggerValue === WorkflowTriggerEvents.AFTER_HOSTS_CAL_VIDEO_NO_SHOW || + triggerValue === WorkflowTriggerEvents.AFTER_GUESTS_CAL_VIDEO_NO_SHOW ) { form.setValue("time", 5); form.setValue("timeUnit", TimeUnit.MINUTE); @@ -564,7 +571,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { form.unregister("time"); form.unregister("timeUnit"); } - if (isFormTrigger(val.value)) { + if (isFormTrigger(triggerValue)) { const steps = form.getValues("steps"); if (steps?.length) { const updatedSteps = steps.map((step) => @@ -584,7 +591,14 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { } }} defaultValue={selectedTrigger} - options={filteredTriggerOptions} + options={filteredTriggerOptions.map((option) => ({ + label: option.label, + value: option.value, + needsTeamsUpgrade: option.needsTeamsUpgrade, + }))} + isOptionDisabled={(option: { label: string; value: string; needsTeamsUpgrade?: boolean }) => + !!option.needsTeamsUpgrade + } /> ); }} @@ -1321,7 +1335,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { refEmailSubject.current = e; }} rows={2} - disabled={props.readOnly || !hasActiveTeamPlan} + disabled={props.readOnly || (!hasActiveTeamPlan && !isReminderTemplate)} className="my-0 focus:ring-transparent" required {...restEmailSubjectForm} @@ -1354,7 +1368,7 @@ export default function WorkflowStepContainer(props: WorkflowStepProps) { editable={ !props.readOnly && !isWhatsappAction(step.action) && - (hasActiveTeamPlan || isSMSAction(step.action)) + (hasActiveTeamPlan || isSMSAction(step.action) || isReminderTemplate) } excludedToolbarItems={ !isSMSAction(step.action) ? [] : ["blockType", "bold", "italic", "link"] diff --git a/packages/features/ee/workflows/lib/getOptions.ts b/packages/features/ee/workflows/lib/getOptions.ts index e83b1dceb5c8c7..df73f475ccef76 100644 --- a/packages/features/ee/workflows/lib/getOptions.ts +++ b/packages/features/ee/workflows/lib/getOptions.ts @@ -31,7 +31,7 @@ export function getWorkflowActionOptions(t: TFunction, isOrgsPlan?: boolean) { }); } -export function getWorkflowTriggerOptions(t: TFunction) { +export function getWorkflowTriggerOptions(t: TFunction, hasPaidPlan: boolean = false) { // TODO: remove this after workflows are supported const filterdWorkflowTriggerEvents = WORKFLOW_TRIGGER_EVENTS.filter( (event) => @@ -41,8 +41,15 @@ export function getWorkflowTriggerOptions(t: TFunction) { return filterdWorkflowTriggerEvents.map((triggerEvent) => { const triggerString = t(`${triggerEvent.toLowerCase()}_trigger`); + const isFormSubmittedTrigger = + triggerEvent === WorkflowTriggerEvents.FORM_SUBMITTED || + triggerEvent === WorkflowTriggerEvents.FORM_SUBMITTED_NO_EVENT; - return { label: triggerString.charAt(0).toUpperCase() + triggerString.slice(1), value: triggerEvent }; + return { + label: triggerString.charAt(0).toUpperCase() + triggerString.slice(1), + value: triggerEvent, + needsTeamsUpgrade: !hasPaidPlan && isFormSubmittedTrigger, + }; }); } @@ -55,7 +62,7 @@ function convertToTemplateOptions( return { label: t(`${template.toLowerCase()}`), value: template, - needsTeamsUpgrade: !hasPaidPlan, + needsTeamsUpgrade: !hasPaidPlan && template !== WorkflowTemplates.REMINDER, } as { label: string; value: any; needsTeamsUpgrade: boolean }; }); }