diff --git a/packages/features/ee/workflows/pages/workflow.tsx b/packages/features/ee/workflows/pages/workflow.tsx index d9f753ad22977b..677b94bef1ca7a 100644 --- a/packages/features/ee/workflows/pages/workflow.tsx +++ b/packages/features/ee/workflows/pages/workflow.tsx @@ -63,7 +63,7 @@ function WorkflowPage({ const [selectedOptions, setSelectedOptions] = useState([]); const [isAllDataLoaded, setIsAllDataLoaded] = useState(false); - const [isMixedEventType, setIsMixedEventType] = useState(false); //for old event types before team workflows existed + const [isMixedEventType, setIsMixedEventType] = useState(false); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [isEditingName, setIsEditingName] = useState(false); const [nameValue, setNameValue] = useState(""); @@ -86,9 +86,7 @@ function WorkflowPage({ isPending: _isPendingWorkflow, } = trpc.viewer.workflows.get.useQuery( { id: +workflowId }, - { - enabled: workflowDataProp ? false : !!workflowId, - } + { enabled: workflowDataProp ? false : !!workflowId } ); const workflow = workflowDataProp || workflowData; @@ -97,22 +95,17 @@ function WorkflowPage({ const { data: verifiedNumbersData } = trpc.viewer.workflows.getVerifiedNumbers.useQuery( { teamId: workflow?.team?.id }, - { - enabled: verifiedNumbersProp ? false : !!workflow?.id, - } + { enabled: verifiedNumbersProp ? false : !!workflow?.id } ); const verifiedNumbers = verifiedNumbersProp || verifiedNumbersData; const { data: verifiedEmailsData } = trpc.viewer.workflows.getVerifiedEmails.useQuery( - { - teamId: workflow?.team?.id, - }, + { teamId: workflow?.team?.id }, { enabled: !verifiedEmailsProp } ); const verifiedEmails = verifiedEmailsProp || verifiedEmailsData; const isOrg = workflow?.team?.isOrganization ?? false; - const teamId = workflow?.teamId ?? undefined; const { data, isPending: isPendingEventTypes } = trpc.viewer.eventTypes.getTeamAndEventTypeOptions.useQuery( @@ -121,7 +114,6 @@ function WorkflowPage({ ); const teamOptions = data?.teamOptions ?? []; - let allEventTypeOptions = data?.eventTypeOptions ?? []; const distinctEventTypes = new Set(); @@ -134,38 +126,24 @@ function WorkflowPage({ }); } - const hasPermissions = (w: typeof workflow): w is RouterOutputs["viewer"]["workflows"]["get"] => { - return w !== null && w !== undefined && "permissions" in w; - }; + const hasPermissions = (w: typeof workflow): w is RouterOutputs["viewer"]["workflows"]["get"] => + w !== null && w !== undefined && "permissions" in w; const permissions: WorkflowPermissions = workflow && hasPermissions(workflow) - ? workflow?.permissions - : { - canUpdate: !teamId, - canView: !teamId, - canDelete: !teamId, - readOnly: !!teamId, - }; - - // Watch for form name changes - const watchedName = form.watch("name"); + ? workflow.permissions + : { canUpdate: !teamId, canView: !teamId, canDelete: !teamId, readOnly: !!teamId }; - // Handler functions for editable name - const handleNameChange = (e: React.ChangeEvent) => { - setNameValue(e.target.value); - }; + const watchedName = form.watch("name"); + const handleNameChange = (e: React.ChangeEvent) => setNameValue(e.target.value); const handleNameSubmit = () => { form.setValue("name", nameValue); setIsEditingName(false); }; - const handleNameKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter") { - form.setValue("name", nameValue); - setIsEditingName(false); - } else if (e.key === "Escape") { + if (e.key === "Enter") handleNameSubmit(); + else if (e.key === "Escape") { setNameValue(watchedName || ""); setIsEditingName(false); } @@ -174,20 +152,14 @@ function WorkflowPage({ const isPending = isPendingWorkflow || isPendingEventTypes; useEffect(() => { - requestAnimationFrame(() => { - window.scrollTo({ - top: 0, - behavior: "smooth", - }); - }); + requestAnimationFrame(() => window.scrollTo({ top: 0, behavior: "smooth" })); }, [isPending]); useEffect(() => { if (!isPending) { - if (hasPermissions(workflow)) { - setFormData(workflow); - } else if (workflow) { - const workflowWithDefaults = { + if (hasPermissions(workflow)) setFormData(workflow); + else if (workflow) { + const readonlyWorkflow = { ...workflow, permissions: { canUpdate: false, @@ -198,108 +170,83 @@ function WorkflowPage({ }, readOnly: true, } as RouterOutputs["viewer"]["workflows"]["get"]; - setFormData(workflowWithDefaults); + setFormData(readonlyWorkflow); } } - // eslint-disable-next-line react-hooks/exhaustive-deps }, [isPending]); - // Update nameValue when workflow changes useEffect(() => { - if (workflow?.name) { - setNameValue(workflow.name); - } + if (workflow?.name) setNameValue(workflow.name); }, [workflow?.name]); function setFormData(workflowData: RouterOutputs["viewer"]["workflows"]["get"] | undefined) { - if (workflowData) { - if (workflowData.userId && workflowData.activeOn.find((active) => !!active.eventType.teamId)) { - setIsMixedEventType(true); - } - let activeOn; + if (!workflowData) return; + if (workflowData.userId && workflowData.activeOn.find((a) => !!a.eventType.teamId)) { + setIsMixedEventType(true); + } - if (workflowData.isActiveOnAll) { - activeOn = isOrg ? teamOptions : allEventTypeOptions; + let activeOn; + if (workflowData.isActiveOnAll) { + activeOn = isOrg ? teamOptions : allEventTypeOptions; + } else { + if (isOrg) { + activeOn = workflowData.activeOnTeams.map((a) => ({ + value: String(a.team.id), + label: a.team.slug || "", + })); + setSelectedOptions(activeOn); } else { - if (isOrg) { - activeOn = workflowData.activeOnTeams.flatMap((active) => { - return { - value: String(active.team.id) || "", - label: active.team.slug || "", - }; - }); - setSelectedOptions(activeOn || []); - } else { - setSelectedOptions( - workflowData.activeOn?.flatMap((active) => { - if (workflowData.teamId && active.eventType.parentId) return []; - return { - value: String(active.eventType.id), - label: active.eventType.title, - }; - }) || [] - ); - activeOn = workflowData.activeOn - ? workflowData.activeOn.map((active) => ({ - value: active.eventType.id.toString(), - label: active.eventType.slug, - })) - : undefined; - } + const options = + workflowData.activeOn?.flatMap((a) => + workflowData.teamId && a.eventType.parentId + ? [] + : { value: String(a.eventType.id), label: a.eventType.title } + ) || []; + setSelectedOptions(options); + activeOn = workflowData.activeOn?.map((a) => ({ + value: a.eventType.id.toString(), + label: a.eventType.slug, + })); } - //translate dynamic variables into local language - const steps = workflowData.steps?.map((step) => { - const updatedStep = { - ...step, - senderName: step.sender, - sender: isSMSAction(step.action) ? step.sender : SENDER_ID, - }; - if (step.reminderBody) { - updatedStep.reminderBody = getTranslatedText(step.reminderBody || "", { - locale: i18n.language, - t, - }); - } - if (step.emailSubject) { - updatedStep.emailSubject = getTranslatedText(step.emailSubject || "", { - locale: i18n.language, - t, - }); - } - return updatedStep; - }); - - form.setValue("name", workflowData.name); - form.setValue("steps", steps); - form.setValue("trigger", workflowData.trigger); - form.setValue("time", workflowData.time || undefined); - form.setValue("timeUnit", workflowData.timeUnit || undefined); - form.setValue("activeOn", activeOn || []); - form.setValue("selectAll", workflowData.isActiveOnAll ?? false); - setNameValue(workflowData.name); - setIsAllDataLoaded(true); } + + const steps = workflowData.steps?.map((step) => { + const updatedStep = { + ...step, + senderName: step.sender, + sender: isSMSAction(step.action) ? step.sender : SENDER_ID, + }; + if (step.reminderBody) { + updatedStep.reminderBody = getTranslatedText(step.reminderBody, { locale: i18n.language, t }); + } + if (step.emailSubject) { + updatedStep.emailSubject = getTranslatedText(step.emailSubject, { locale: i18n.language, t }); + } + return updatedStep; + }); + + form.setValue("name", workflowData.name); + form.setValue("steps", steps); + form.setValue("trigger", workflowData.trigger); + form.setValue("time", workflowData.time || undefined); + form.setValue("timeUnit", workflowData.timeUnit || undefined); + form.setValue("activeOn", activeOn || []); + form.setValue("selectAll", workflowData.isActiveOnAll ?? false); + setNameValue(workflowData.name); + setIsAllDataLoaded(true); } const updateMutation = trpc.viewer.workflows.update.useMutation({ onSuccess: async ({ workflow }) => { utils.viewer.workflows.get.setData({ id: +workflow.id }, workflow); setFormData(workflow); - - const autoCreateAgent = searchParams?.get("autoCreateAgent"); - if (!autoCreateAgent) { - showToast( - t("workflow_updated_successfully", { - workflowName: workflow.name, - }), - "success" - ); + if (!searchParams?.get("autoCreateAgent")) { + showToast(t("workflow_updated_successfully", { workflowName: workflow.name }), "success"); } }, onError: (err) => { if (err instanceof HttpError) { - const message = `${err.statusCode}: ${err.message}`; - showToast(message, "error"); + showToast(`${err.statusCode}: ${err.message}`, "error"); } }, }); @@ -310,10 +257,9 @@ function WorkflowPage({ let isVerified = true; values.steps.forEach((step) => { - const strippedHtml = step.reminderBody?.replace(/<[^>]+>/g, "") || ""; - + const stripped = step.reminderBody?.replace(/<[^>]+>/g, "") || ""; const isBodyEmpty = - !isSMSOrWhatsappAction(step.action) && !isCalAIAction(step.action) && strippedHtml.length <= 1; + !isSMSOrWhatsappAction(step.action) && !isCalAIAction(step.action) && stripped.length <= 1; if (isBodyEmpty) { form.setError(`steps.${step.stepNumber - 1}.reminderBody`, { @@ -322,53 +268,32 @@ function WorkflowPage({ }); } - if (step.reminderBody) { - step.reminderBody = translateVariablesToEnglish(step.reminderBody, { - locale: i18n.language, - t, - }); - } - if (step.emailSubject) { - step.emailSubject = translateVariablesToEnglish(step.emailSubject, { - locale: i18n.language, - t, - }); - } - isEmpty = !isEmpty ? isBodyEmpty : isEmpty; + if (step.reminderBody) + step.reminderBody = translateVariablesToEnglish(step.reminderBody, { locale: i18n.language, t }); + if (step.emailSubject) + step.emailSubject = translateVariablesToEnglish(step.emailSubject, { locale: i18n.language, t }); + + isEmpty ||= isBodyEmpty; - //check if phone number is verified if ( (step.action === WorkflowActions.SMS_NUMBER || step.action === WorkflowActions.WHATSAPP_NUMBER) && - !verifiedNumbers?.find((verifiedNumber) => verifiedNumber.phoneNumber === step.sendTo) + !verifiedNumbers?.find((v) => v.phoneNumber === step.sendTo) ) { isVerified = false; - - form.setError(`steps.${step.stepNumber - 1}.sendTo`, { - type: "custom", - message: t("not_verified"), - }); + form.setError(`steps.${step.stepNumber - 1}.sendTo`, { type: "custom", message: t("not_verified") }); } - if ( - step.action === WorkflowActions.EMAIL_ADDRESS && - !verifiedEmails?.find((verifiedEmail) => verifiedEmail === step.sendTo) - ) { + if (step.action === WorkflowActions.EMAIL_ADDRESS && !verifiedEmails?.find((v) => v === step.sendTo)) { isVerified = false; - - form.setError(`steps.${step.stepNumber - 1}.sendTo`, { - type: "custom", - message: t("not_verified"), - }); + form.setError(`steps.${step.stepNumber - 1}.sendTo`, { type: "custom", message: t("not_verified") }); } }); if (!isEmpty && isVerified) { if (values.activeOn) { activeOnIds = values.activeOn - .filter((option) => option.value !== "all") - .map((option) => { - return parseInt(option.value, 10); - }); + .filter((opt) => opt.value !== "all") + .map((opt) => parseInt(opt.value, 10)); } await updateMutation.mutateAsync({ @@ -381,28 +306,20 @@ function WorkflowPage({ timeUnit: values.timeUnit || null, isActiveOnAll: values.selectAll || false, }); - utils.viewer.workflows.getVerifiedNumbers.invalidate(); } else { - const validationErrors: string[] = []; - - if (isEmpty) { - validationErrors.push(t("workflow_validation_empty_fields")); - } - - if (!isVerified) { - validationErrors.push(t("workflow_validation_unverified_contacts")); - } - - throw new Error(`${t("workflow_validation_failed")}: ${validationErrors.join("; ")}`); + const errs: string[] = []; + if (isEmpty) errs.push(t("workflow_validation_empty_fields")); + if (!isVerified) errs.push(t("workflow_validation_unverified_contacts")); + throw new Error(`${t("workflow_validation_failed")}: ${errs.join("; ")}`); } }; const handleSaveWorkflow = async (): Promise => { - const values = form.getValues(); - await validateAndSubmitWorkflow(values); + await validateAndSubmitWorkflow(form.getValues()); }; + // ===================== RETURN ===================== return session.data ? (
-
-
+
+
+ {workflow && workflow.team && ( - + {workflow.team.name} )} {permissions.readOnly && ( - + {t("readonly")} )}
-
+
+
-
+
{!isError ? ( <> {isAllDataLoaded && user ? ( - <> - - + ) : ( )} @@ -522,12 +447,12 @@ function WorkflowPage({
+ { - // Navigate back to workflows list after deletion window.location.href = "/workflows"; }} />