import { isCalAIAction, isFormTrigger, isSMSAction, } from "@calcom/features/ee/workflows/lib/actionHelperFunctions"; import { ALLOWED_FORM_WORKFLOW_ACTIONS } from "@calcom/features/ee/workflows/lib/constants"; import emailReminderTemplate from "@calcom/features/ee/workflows/lib/reminders/templates/emailReminderTemplate"; import type { FormValues } from "@calcom/features/ee/workflows/lib/types"; import type { WorkflowPermissions } from "@calcom/features/workflows/repositories/WorkflowPermissionsRepository"; import { SCANNING_WORKFLOW_STEPS, SENDER_ID, SENDER_NAME } from "@calcom/lib/constants"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { getTimeFormatStringFromUserTimeFormat } from "@calcom/lib/timeFormat"; import { WorkflowActions, WorkflowTemplates } from "@calcom/prisma/enums"; import { type RouterOutputs, trpc } from "@calcom/trpc/react"; import { Button } from "@calcom/ui/components/button"; import { FormCard, FormCardBody } from "@calcom/ui/components/card"; import type { MultiSelectCheckboxesOptionType as Option } from "@calcom/ui/components/form"; import { useHasPaidPlan, useHasActiveTeamPlan } from "@calcom/web/modules/billing/hooks/useHasPaidPlan"; import { useAgentsData } from "@calcom/web/modules/ee/workflows/hooks/useAgentsData"; import { ArrowRightIcon, ZapIcon } from "@coss/ui/icons"; import { useSearchParams } from "next/navigation"; import type { Dispatch, SetStateAction } from "react"; import { useEffect, useState } from "react"; import type { UseFormReturn } from "react-hook-form"; import { AddActionDialog } from "./AddActionDialog"; import WorkflowStepContainer from "./WorkflowStepContainer"; type User = RouterOutputs["viewer"]["me"]["get"]; interface Props { form: UseFormReturn; workflowId: number; selectedOptions: Option[]; setSelectedOptions: Dispatch>; teamId?: number; user: User; isOrg: boolean; allOptions: Option[]; eventTypeOptions: Option[]; onSaveWorkflow?: () => Promise; permissions: WorkflowPermissions; } export default function WorkflowDetailsPage(props: Props) { const { form, workflowId, selectedOptions, setSelectedOptions, teamId, isOrg, allOptions, eventTypeOptions, permissions, } = props; const { t, i18n } = useLocale(); const { hasPaidPlan } = useHasPaidPlan(); const { hasActiveTeamPlan, isTrial } = useHasActiveTeamPlan(); const [isAddActionDialogOpen, setIsAddActionDialogOpen] = useState(false); const [isDeleteStepDialogOpen, setIsDeleteStepDialogOpen] = useState(false); const [reload, setReload] = useState(false); const [updateTemplate, setUpdateTemplate] = useState(false); const searchParams = useSearchParams(); const eventTypeId = searchParams?.get("eventTypeId"); // Get base action options and transform them for form triggers const { data: baseActionOptions } = trpc.viewer.workflows.getWorkflowActionOptions.useQuery(); const transformedActionOptions = baseActionOptions ? baseActionOptions .filter((option) => { const isFormWorkflowWithInvalidSteps = isFormTrigger(form.getValues("trigger")) && !ALLOWED_FORM_WORKFLOW_ACTIONS.some((action) => action === option.value); const isSelectAllCalAiAction = isCalAIAction(option.value) && form.watch("selectAll"); const isOrgCalAiAction = isCalAIAction(option.value) && isOrg; if (isFormWorkflowWithInvalidSteps || isSelectAllCalAiAction || isOrgCalAiAction) { return false; } return true; }) .map((option) => { let label = option.label; // Transform labels for form triggers if (isFormTrigger(form.getValues("trigger"))) { if (option.value === WorkflowActions.EMAIL_ATTENDEE) { label = t("email_attendee_action_form"); } else if (option.value === WorkflowActions.SMS_ATTENDEE) { label = t("sms_attendee_action_form"); } } const needsTeamsUpgrade = isFormTrigger(form.getValues("trigger")) && !hasActiveTeamPlan; return { ...option, label, creditsTeamId: teamId, isOrganization: isOrg, isCalAi: isCalAIAction(option.value), needsTeamsUpgrade, upgradeTeamsBadgeProps: needsTeamsUpgrade ? { hasPaidPlan, hasActiveTeamPlan, isTrial } : undefined, }; }) : []; useEffect(() => { const matchingOption = allOptions.find((option) => option.value === eventTypeId); if (matchingOption && !selectedOptions.find((option) => option.value === eventTypeId)) { const newOptions = [...selectedOptions, matchingOption]; setSelectedOptions(newOptions); form.setValue("activeOn", newOptions); } // eslint-disable-next-line react-hooks/exhaustive-deps -- intentionally only run when eventTypeId changes }, [eventTypeId]); const addAction = ( action: WorkflowActions, sendTo?: string, numberRequired?: boolean, sender?: string, senderName?: string ) => { const steps = form.getValues("steps"); const id = steps?.length > 0 ? steps.sort((a, b) => { return a.id - b.id; })[0].id - 1 : 0; const timeFormat = getTimeFormatStringFromUserTimeFormat(props.user.timeFormat); const template = isFormTrigger(form.getValues("trigger")) ? WorkflowTemplates.CUSTOM : WorkflowTemplates.REMINDER; const { emailBody: reminderBody, emailSubject } = template !== WorkflowTemplates.CUSTOM ? emailReminderTemplate({ isEditingMode: true, locale: i18n.language, t, action, timeFormat, }) : { emailBody: null, emailSubject: null }; const step = { id: id > 0 ? 0 : id, //id of new steps always <= 0 action, stepNumber: steps && steps.length > 0 ? steps.sort((a, b) => { return a.stepNumber - b.stepNumber; })[steps.length - 1].stepNumber + 1 : 1, sendTo: sendTo || null, workflowId: workflowId, reminderBody, emailSubject, template, numberRequired: numberRequired || false, sender: isSMSAction(action) ? sender || SENDER_ID : SENDER_ID, senderName: !isSMSAction(action) ? senderName || SENDER_NAME : SENDER_NAME, numberVerificationPending: false, includeCalendarEvent: false, verifiedAt: SCANNING_WORKFLOW_STEPS ? null : new Date(), agentId: null, inboundAgentId: null, autoTranslateEnabled: false, sourceLocale: null, }; steps?.push(step); form.setValue("steps", steps); }; const { outboundAgentQueries: agentQueriesTrpc, inboundAgentQueries: inboundAgentQueriesTrpc } = useAgentsData(form); return ( <>
{t("trigger")}
}>
{form.getValues("steps") && (
{form.getValues("steps")?.map((step, index) => { const agentData = agentQueriesTrpc[index]?.data; const isAgentLoading = agentQueriesTrpc[index]?.isPending; const inboundAgentData = inboundAgentQueriesTrpc[index]?.data; const isInboundAgentLoading = inboundAgentQueriesTrpc[index]?.isPending; return (
{t("action")}
} deleteField={ !permissions.readOnly ? { color: "destructive", check: () => true, disabled: !permissions.canUpdate, fn: () => { if ( isCalAIAction(step.action) && agentData?.outboundPhoneNumbers && agentData.outboundPhoneNumbers.length > 0 ) { setIsDeleteStepDialogOpen(true); } else { const steps = form.getValues("steps"); const updatedSteps = steps ?.filter((currStep) => currStep.id !== step.id) .map((s) => { const updatedStep = s; if (step.stepNumber < updatedStep.stepNumber) { updatedStep.stepNumber = updatedStep.stepNumber - 1; } return updatedStep; }); form.setValue("steps", updatedSteps); if (setReload) { setReload(!reload); } } }, } : null }> {index !== form.getValues("steps").length - 1 && (
)}
); })}
)} {!permissions.readOnly && ( <>
)}
); }