From e51cf607c96766d563a702d1aaea0ec2fa65a856 Mon Sep 17 00:00:00 2001 From: Abdul Rahman <81605929+abdulrahmancodes@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:58:29 +0530 Subject: [PATCH] Fix if-else node drag-to-create (#16944) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Félix Malfait --- .../CommandMenuWorkflowCreateStepContent.tsx | 40 ++++++++++++++- .../WorkflowDiagramCanvasEditable.tsx | 21 +++++++- .../WorkflowEditActionFilterBodyEffect.tsx | 51 +++++++++++++++++-- .../utils/prepareIfElseStepWithNewBranch.ts | 36 +++++++++++++ 4 files changed, 141 insertions(+), 7 deletions(-) create mode 100644 packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch.ts diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/create/components/CommandMenuWorkflowCreateStepContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/create/components/CommandMenuWorkflowCreateStepContent.tsx index 2f6aeb9cc7..2631905513 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/create/components/CommandMenuWorkflowCreateStepContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/create/components/CommandMenuWorkflowCreateStepContent.tsx @@ -3,11 +3,17 @@ import { CommandMenuWorkflowSelectAction } from '@/command-menu/pages/workflow/a import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; -import { type WorkflowActionType } from '@/workflow/types/Workflow'; +import { + type WorkflowActionType, + type WorkflowIfElseAction, +} from '@/workflow/types/Workflow'; import { useCloseRightClickMenu } from '@/workflow/workflow-diagram/hooks/useCloseRightClickMenu'; import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep'; +import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState'; +import { prepareIfElseStepWithNewBranch } from '@/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; @@ -20,6 +26,10 @@ export const CommandMenuWorkflowCreateStepContent = () => { ); const { createStep } = useCreateStep(); + const { updateStep } = useUpdateStep(); + const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( + workflowVisualizerWorkflowId, + ); const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu(); const { closeRightClickMenu } = useCloseRightClickMenu(); @@ -30,6 +40,21 @@ export const CommandMenuWorkflowCreateStepContent = () => { const [workflowInsertStepIds, setWorkflowInsertStepIds] = useRecoilComponentState(workflowInsertStepIdsComponentState); + const handleIfElseParentStep = async ({ + parentStep, + createdStepId, + }: { + parentStep: WorkflowIfElseAction; + createdStepId: string; + }) => { + const updatedStep = prepareIfElseStepWithNewBranch({ + parentStep, + targetStepId: createdStepId, + }); + + await updateStep(updatedStep); + }; + const handleCreateStep = async (actionType: WorkflowActionType) => { if (!isDefined(workflowVisualizerWorkflowId)) { throw new Error( @@ -52,6 +77,19 @@ export const CommandMenuWorkflowCreateStepContent = () => { return; } + const steps = workflowWithCurrentVersion?.currentVersion?.steps; + const parentStep = + isDefined(parentStepId) && isDefined(steps) && isDefined(position) + ? steps.find((step) => step.id === parentStepId) + : undefined; + + if (parentStep?.type === 'IF_ELSE') { + await handleIfElseParentStep({ + parentStep, + createdStepId: createdStep.id, + }); + } + setWorkflowInsertStepIds({ parentStepId: undefined, nextStepId: undefined, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx index 6cb128cb6d..23994314fb 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditable.tsx @@ -23,6 +23,7 @@ import { WorkflowDiagramStepNodeEditable } from '@/workflow/workflow-diagram/wor import { useCreateEdge } from '@/workflow/workflow-steps/hooks/useCreateEdge'; import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge'; import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; +import { prepareIfElseStepWithNewBranch } from '@/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; import { addEdge, @@ -60,7 +61,21 @@ export const WorkflowDiagramCanvasEditable = () => { const { startNodeCreation } = useStartNodeCreation(); - const onConnect = (edgeConnect: WorkflowConnection) => { + const onConnect = async (edgeConnect: WorkflowConnection) => { + const steps = workflowWithCurrentVersion?.currentVersion?.steps; + const sourceStep = isDefined(steps) + ? steps.find((step) => step.id === edgeConnect.source) + : undefined; + + if (sourceStep?.type === 'IF_ELSE') { + const updatedStep = prepareIfElseStepWithNewBranch({ + parentStep: sourceStep, + targetStepId: edgeConnect.target, + }); + + await updateStep(updatedStep); + } + setWorkflowDiagram((diagram) => { if (isDefined(diagram) === false) { throw new Error( @@ -74,6 +89,10 @@ export const WorkflowDiagramCanvasEditable = () => { }; }); + if (sourceStep?.type === 'IF_ELSE') { + return; + } + createEdge({ source: edgeConnect.source, target: edgeConnect.target, diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowEditActionFilterBodyEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowEditActionFilterBodyEffect.tsx index 23d25f9e6b..13267f0edd 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowEditActionFilterBodyEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/filters/components/WorkflowEditActionFilterBodyEffect.tsx @@ -1,4 +1,5 @@ import { useRecoilComponentFamilyState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { currentStepFilterGroupsComponentState } from '@/workflow/workflow-steps/filters/states/currentStepFilterGroupsComponentState'; import { currentStepFiltersComponentState } from '@/workflow/workflow-steps/filters/states/currentStepFiltersComponentState'; @@ -13,6 +14,7 @@ import { convertViewFilterOperandToCoreOperand, isDefined, } from 'twenty-shared/utils'; +import { isDeeplyEqual } from '~/utils/isDeeplyEqual'; type FilterSettingsWithPotentiallyDeprecatedOperand = { stepFilterGroups?: StepFilterGroup[]; @@ -42,6 +44,13 @@ export const WorkflowEditActionFilterBodyEffect = ({ { stepId }, ); + const currentStepFilters = useRecoilComponentValue( + currentStepFiltersComponentState, + ); + const currentStepFilterGroups = useRecoilComponentValue( + currentStepFilterGroupsComponentState, + ); + const setCurrentStepFilters = useSetRecoilComponentState( currentStepFiltersComponentState, ); @@ -58,8 +67,20 @@ export const WorkflowEditActionFilterBodyEffect = ({ }, [defaultValue?.stepFilters]); useEffect(() => { - if (!hasInitializedCurrentStepFilters && isDefined(stepFiltersConverted)) { - setCurrentStepFilters(stepFiltersConverted ?? []); + if (!isDefined(stepFiltersConverted)) { + return; + } + + if ( + hasInitializedCurrentStepFilters && + isDeeplyEqual(currentStepFilters, stepFiltersConverted) + ) { + return; + } + + setCurrentStepFilters(stepFiltersConverted ?? []); + + if (!hasInitializedCurrentStepFilters) { setHasInitializedCurrentStepFilters(true); } }, [ @@ -67,15 +88,34 @@ export const WorkflowEditActionFilterBodyEffect = ({ hasInitializedCurrentStepFilters, setHasInitializedCurrentStepFilters, stepFiltersConverted, + currentStepFilters, ]); useEffect(() => { + if (!isDefined(defaultValue?.stepFilterGroups)) { + return; + } + if ( !hasInitializedCurrentStepFilterGroups && - isDefined(defaultValue?.stepFilterGroups) && - defaultValue.stepFilterGroups.length > 0 + defaultValue.stepFilterGroups.length === 0 ) { - setCurrentStepFilterGroups(defaultValue.stepFilterGroups ?? []); + return; + } + + if ( + hasInitializedCurrentStepFilterGroups && + isDeeplyEqual( + currentStepFilterGroups, + defaultValue.stepFilterGroups ?? [], + ) + ) { + return; + } + + setCurrentStepFilterGroups(defaultValue.stepFilterGroups ?? []); + + if (!hasInitializedCurrentStepFilterGroups) { setHasInitializedCurrentStepFilterGroups(true); } }, [ @@ -83,6 +123,7 @@ export const WorkflowEditActionFilterBodyEffect = ({ hasInitializedCurrentStepFilterGroups, setHasInitializedCurrentStepFilterGroups, defaultValue?.stepFilterGroups, + currentStepFilterGroups, ]); return null; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch.ts new file mode 100644 index 0000000000..92be368e0a --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/if-else-action/utils/prepareIfElseStepWithNewBranch.ts @@ -0,0 +1,36 @@ +import { type WorkflowIfElseAction } from '@/workflow/types/Workflow'; +import { createElseIfBranch } from './createElseIfBranch'; + +export const prepareIfElseStepWithNewBranch = ({ + parentStep, + targetStepId, +}: { + parentStep: WorkflowIfElseAction; + targetStepId: string; +}): WorkflowIfElseAction => { + const branches = parentStep.settings.input.branches; + const stepFilterGroups = parentStep.settings.input.stepFilterGroups; + const stepFilters = parentStep.settings.input.stepFilters; + + const { filterGroup, filter, branchId, filterGroupId } = createElseIfBranch(); + + const updatedBranches = [...branches]; + updatedBranches.splice(branches.length - 1, 0, { + id: branchId, + filterGroupId, + nextStepIds: [targetStepId], + }); + + return { + ...parentStep, + settings: { + ...parentStep.settings, + input: { + ...parentStep.settings.input, + stepFilterGroups: [...stepFilterGroups, filterGroup], + stepFilters: [...stepFilters, filter], + branches: updatedBranches, + }, + }, + }; +};