diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx
index 88ea2896ce7..bc63b9cccca 100644
--- a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx
+++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx
@@ -4,8 +4,8 @@ import { RightDrawerStepListContainer } from '@/workflow/workflow-steps/componen
import { RightDrawerWorkflowSelectStepTitle } from '@/workflow/workflow-steps/components/RightDrawerWorkflowSelectStepTitle';
import { AI_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/AiActions';
import { CORE_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/CoreActions';
+import { FLOW_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/FlowActions';
import { HUMAN_INPUT_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/HumanInputActions';
-import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions';
import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { useLingui } from '@lingui/react/macro';
@@ -45,6 +45,16 @@ export const CommandMenuWorkflowSelectAction = ({
>
)}
+
+ {t`Flow`}
+
+ action.type !== 'ITERATOR' || isIteratorEnabled,
+ )}
+ onClick={onActionSelected}
+ />
+
{t`Core`}
@@ -60,18 +70,6 @@ export const CommandMenuWorkflowSelectAction = ({
actions={HUMAN_INPUT_ACTIONS}
onClick={onActionSelected}
/>
-
- {isIteratorEnabled && (
- <>
-
- {t`Others`}
-
-
- >
- )}
);
};
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 a241edb86aa..9baba86534a 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
@@ -14,7 +14,6 @@ import {
import { getWorkflowVersionStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowVersionStatusTagProps';
import { WorkflowDiagramBlankEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBlankEdge';
import { WorkflowDiagramDefaultEdgeEditable } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable';
-import { WorkflowDiagramFilterEdgeEditable } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable';
import { WorkflowDiagramEmptyTriggerEditable } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerEditable';
import { WorkflowDiagramStepNodeEditable } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditable';
import { useCreateEdge } from '@/workflow/workflow-steps/hooks/useCreateEdge';
@@ -128,8 +127,7 @@ export const WorkflowDiagramCanvasEditable = () => {
}}
edgeTypes={{
blank: WorkflowDiagramBlankEdge,
- 'empty-filter--editable': WorkflowDiagramDefaultEdgeEditable,
- 'filter--editable': WorkflowDiagramFilterEdgeEditable,
+ editable: WorkflowDiagramDefaultEdgeEditable,
}}
tagContainerTestId="workflow-visualizer-status"
tagColor={tagProps.color}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx
index 682179c74b3..36dc41be4ca 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonly.tsx
@@ -4,7 +4,6 @@ import { WorkflowDiagramDefaultEdgeReadonly } from '@/workflow/workflow-diagram/
import { getWorkflowVersionStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowVersionStatusTagProps';
import { WorkflowDiagramEmptyTriggerReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerReadonly';
-import { WorkflowDiagramFilterEdgeReadonly } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly';
import { WorkflowDiagramStepNodeReadonly } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeReadonly';
import { ReactFlowProvider } from '@xyflow/react';
@@ -25,8 +24,7 @@ export const WorkflowDiagramCanvasReadonly = ({
'empty-trigger': WorkflowDiagramEmptyTriggerReadonly,
}}
edgeTypes={{
- 'empty-filter--readonly': WorkflowDiagramDefaultEdgeReadonly,
- 'filter--readonly': WorkflowDiagramFilterEdgeReadonly,
+ readonly: WorkflowDiagramDefaultEdgeReadonly,
}}
tagContainerTestId="workflow-visualizer-status"
tagColor={tagProps.color}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx
index 86cd7978005..b6073244ad6 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas.tsx
@@ -1,7 +1,6 @@
import { type WorkflowRunStatus } from '@/workflow/types/Workflow';
import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase';
import { WorkflowDiagramDefaultEdgeRun } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeRun';
-import { WorkflowDiagramFilterEdgeRun } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun';
import { getWorkflowRunStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowRunStatusTagProps';
import { WorkflowRunDiagramStepNode } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode';
@@ -23,8 +22,7 @@ export const WorkflowRunDiagramCanvas = ({
default: WorkflowRunDiagramStepNode,
}}
edgeTypes={{
- 'empty-filter--run': WorkflowDiagramDefaultEdgeRun,
- 'filter--run': WorkflowDiagramFilterEdgeRun,
+ run: WorkflowDiagramDefaultEdgeRun,
}}
tagContainerTestId="workflow-run-status"
tagColor={tagProps.color}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts
deleted file mode 100644
index e27b4ecf5ee..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts
+++ /dev/null
@@ -1,65 +0,0 @@
-import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
-import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
-import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
-import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
-import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState';
-import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
-import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
-import { isDefined } from 'twenty-shared/utils';
-import { useIcons } from 'twenty-ui/display';
-
-export const useOpenWorkflowEditFilterInCommandMenu = () => {
- const { getIcon } = useIcons();
-
- const workflowVisualizerWorkflowId = useRecoilComponentValue(
- workflowVisualizerWorkflowIdComponentState,
- );
- const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu();
-
- const setWorkflowSelectedNode = useSetRecoilComponentState(
- workflowSelectedNodeComponentState,
- );
- const setWorkflowDiagram = useSetRecoilComponentState(
- workflowDiagramComponentState,
- );
-
- const openWorkflowEditFilterInCommandMenu = ({
- stepId,
- stepName,
- }: {
- stepId: string;
- stepName: string;
- }) => {
- if (!isDefined(workflowVisualizerWorkflowId)) {
- throw new Error(
- 'Workflow ID must be configured for the edge when opening a filter in command menu',
- );
- }
-
- setWorkflowSelectedNode(stepId);
-
- setWorkflowDiagram((diagram) => {
- if (!isDefined(diagram)) {
- throw new Error('Workflow diagram must be defined');
- }
-
- return {
- ...diagram,
- nodes: diagram.nodes.map((node) => ({
- ...node,
- selected: node.id === stepId,
- })),
- };
- });
-
- openWorkflowEditStepInCommandMenu(
- workflowVisualizerWorkflowId,
- stepName,
- getIcon(getActionIcon('FILTER')),
- );
- };
-
- return {
- openWorkflowEditFilterInCommandMenu,
- };
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu.ts
deleted file mode 100644
index 3f2b0b76629..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
-import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
-import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
-import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
-import { workflowVisualizerWorkflowRunIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowRunIdComponentState';
-import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState';
-import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
-import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
-import { isDefined } from 'twenty-shared/utils';
-import { type StepStatus } from 'twenty-shared/workflow';
-import { useIcons } from 'twenty-ui/display';
-
-export const useOpenWorkflowRunFilterInCommandMenu = () => {
- const { getIcon } = useIcons();
-
- const workflowVisualizerWorkflowId = useRecoilComponentValue(
- workflowVisualizerWorkflowIdComponentState,
- );
- const workflowVisualizerWorkflowRunId = useRecoilComponentValue(
- workflowVisualizerWorkflowRunIdComponentState,
- );
-
- const setWorkflowSelectedNode = useSetRecoilComponentState(
- workflowSelectedNodeComponentState,
- );
- const setWorkflowDiagram = useSetRecoilComponentState(
- workflowDiagramComponentState,
- );
-
- const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu();
-
- const openWorkflowRunFilterInCommandMenu = ({
- stepId,
- stepName,
- stepExecutionStatus,
- }: {
- stepId: string;
- stepName: string;
- stepExecutionStatus: StepStatus;
- }) => {
- if (!isDefined(workflowVisualizerWorkflowId)) {
- throw new Error('Workflow ID is required');
- }
-
- if (!isDefined(workflowVisualizerWorkflowRunId)) {
- throw new Error('Workflow run ID is required');
- }
-
- setWorkflowSelectedNode(stepId);
-
- setWorkflowDiagram((diagram) => {
- if (!isDefined(diagram)) {
- throw new Error('Workflow diagram must be defined');
- }
-
- return {
- ...diagram,
- nodes: diagram.nodes.map((node) => ({
- ...node,
- selected: false,
- })),
- };
- });
-
- openWorkflowRunViewStepInCommandMenu({
- workflowId: workflowVisualizerWorkflowId,
- workflowRunId: workflowVisualizerWorkflowRunId,
- title: stepName,
- icon: getIcon(getActionIcon('FILTER')),
- workflowSelectedNode: stepId,
- stepExecutionStatus,
- });
- };
-
- return {
- openWorkflowRunFilterInCommandMenu,
- };
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu.ts
deleted file mode 100644
index 1decb7c1a31..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
-import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
-import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState';
-import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState';
-import { workflowVisualizerWorkflowVersionIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowVersionIdComponentState';
-import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState';
-import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
-import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
-import { isDefined } from 'twenty-shared/utils';
-import { useIcons } from 'twenty-ui/display';
-
-export const useOpenWorkflowViewFilterInCommandMenu = () => {
- const { getIcon } = useIcons();
-
- const workflowVisualizerWorkflowId = useRecoilComponentValue(
- workflowVisualizerWorkflowIdComponentState,
- );
- const workflowVisualizerWorkflowVersionId = useRecoilComponentValue(
- workflowVisualizerWorkflowVersionIdComponentState,
- );
- const { openWorkflowViewStepInCommandMenu } = useWorkflowCommandMenu();
-
- const setWorkflowSelectedNode = useSetRecoilComponentState(
- workflowSelectedNodeComponentState,
- );
- const setWorkflowDiagram = useSetRecoilComponentState(
- workflowDiagramComponentState,
- );
-
- const openWorkflowViewFilterInCommandMenu = ({
- stepId,
- stepName,
- }: {
- stepId: string;
- stepName: string;
- }) => {
- if (!workflowVisualizerWorkflowId) {
- throw new Error('Workflow ID is required');
- }
-
- if (!workflowVisualizerWorkflowVersionId) {
- throw new Error('Workflow version ID is required');
- }
-
- setWorkflowSelectedNode(stepId);
-
- setWorkflowDiagram((diagram) => {
- if (!isDefined(diagram)) {
- throw new Error('Workflow diagram must be defined');
- }
-
- return {
- ...diagram,
- nodes: diagram.nodes.map((node) => ({
- ...node,
- selected: false,
- })),
- };
- });
-
- openWorkflowViewStepInCommandMenu({
- workflowId: workflowVisualizerWorkflowId,
- workflowVersionId: workflowVisualizerWorkflowVersionId,
- title: stepName,
- icon: getIcon(getActionIcon('FILTER')),
- });
- };
-
- return {
- openWorkflowViewFilterInCommandMenu,
- };
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagram.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagram.ts
index 25eb8618e11..73ec0d284e4 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagram.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagram.ts
@@ -3,7 +3,6 @@ import {
type WorkflowRunStepStatus,
type WorkflowTriggerType,
} from '@/workflow/types/Workflow';
-import { type FilterSettings } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowEditActionFilter';
import { type MessageDescriptor } from '@lingui/core';
import {
type Connection,
@@ -105,33 +104,14 @@ export type WorkflowDiagramEdgeLabelOptions = {
label: MessageDescriptor;
};
-export type WorkflowDiagramFilterEdgeData = {
- edgeType: 'filter';
- stepId: string;
- filterSettings: FilterSettings;
- name: string;
- runStatus?: WorkflowRunStepStatus;
- edgeExecutionStatus?: StepStatus;
- labelOptions?: WorkflowDiagramEdgeLabelOptions;
-};
-
export type WorkflowDiagramDefaultEdgeData = {
edgeType: 'default';
edgeExecutionStatus?: StepStatus;
labelOptions?: WorkflowDiagramEdgeLabelOptions;
};
-export type WorkflowDiagramEdgeData =
- | WorkflowDiagramFilterEdgeData
- | WorkflowDiagramDefaultEdgeData;
+export type WorkflowDiagramEdgeData = WorkflowDiagramDefaultEdgeData;
export type WorkflowDiagramNodeType = 'default' | 'empty-trigger';
-export type WorkflowDiagramEdgeType =
- | 'blank'
- | 'empty-filter--editable'
- | 'empty-filter--readonly'
- | 'empty-filter--run'
- | 'filter--editable'
- | 'filter--readonly'
- | 'filter--run';
+export type WorkflowDiagramEdgeType = 'blank' | 'editable' | 'readonly' | 'run';
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts
index a2ea74ad897..3cb6cc27d00 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/generateWorkflowRunDiagram.test.ts
@@ -124,7 +124,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step1",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -141,7 +141,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step2",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -158,7 +158,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step3",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
],
@@ -361,7 +361,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step1",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -378,7 +378,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step2",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -395,7 +395,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step3",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
],
@@ -598,7 +598,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step1",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -615,7 +615,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step2",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -632,7 +632,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step3",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
],
@@ -854,7 +854,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step1",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -871,7 +871,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step2",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -888,7 +888,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step3",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
{
@@ -905,7 +905,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step4",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
],
@@ -1093,7 +1093,7 @@ describe('generateWorkflowRunDiagram', () => {
"sourceHandle": "default",
"target": "step1",
"targetHandle": "default",
- "type": "empty-filter--run",
+ "type": "run",
"zIndex": -2,
},
],
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts
index 61b7175090b..217d23cbf12 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getWorkflowVersionDiagram.test.ts
@@ -164,7 +164,7 @@ describe('getWorkflowVersionDiagram', () => {
"sourceHandle": "default",
"target": "step-1",
"targetHandle": "default",
- "type": "empty-filter--editable",
+ "type": "editable",
"zIndex": -2,
},
],
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/transformFilterNodesAsEdges.test.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/transformFilterNodesAsEdges.test.ts
deleted file mode 100644
index 112bc58533a..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/transformFilterNodesAsEdges.test.ts
+++ /dev/null
@@ -1,511 +0,0 @@
-import { type WorkflowDiagram } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
-import { transformFilterNodesAsEdges } from '../transformFilterNodesAsEdges';
-
-describe('transformFilterNodesAsEdges', () => {
- it('should return the original diagram when there are no filter nodes', () => {
- const diagram: WorkflowDiagram = {
- nodes: [
- {
- id: 'A',
- data: {
- nodeType: 'action',
- actionType: 'CODE',
- name: 'Step A',
- hasNextStepIds: false,
- position: { x: 0, y: 0 },
- stepId: 'A',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'C',
- data: {
- nodeType: 'action',
- actionType: 'SEND_EMAIL',
- name: 'Step C',
- hasNextStepIds: false,
- position: { x: 0, y: 0 },
- stepId: 'C',
- },
- position: { x: 0, y: 300 },
- },
- ],
- edges: [
- {
- id: 'A-C',
- source: 'A',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- data: {
- edgeType: 'default',
- },
- },
- ],
- };
-
- const result = transformFilterNodesAsEdges({
- nodes: diagram.nodes,
- edges: diagram.edges,
- workflowContext: 'workflow',
- });
-
- expect(result.nodes).toEqual(diagram.nodes);
- expect(result.edges).toEqual(diagram.edges);
- });
-
- it('should transform A->B->C where B is a FILTER step', () => {
- const diagram: WorkflowDiagram = {
- nodes: [
- {
- id: 'A',
- data: {
- nodeType: 'action',
- actionType: 'CODE',
- name: 'Step A',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'A',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'B',
- data: {
- nodeType: 'action',
- actionType: 'FILTER',
- name: 'Filter B',
- hasNextStepIds: true,
- position: { x: 0, y: 150 },
- stepId: 'B',
- },
- position: { x: 0, y: 150 },
- },
- {
- id: 'C',
- data: {
- nodeType: 'action',
- actionType: 'SEND_EMAIL',
- name: 'Step C',
- hasNextStepIds: false,
- position: { x: 0, y: 300 },
- stepId: 'C',
- },
- position: { x: 0, y: 300 },
- },
- ],
- edges: [
- {
- id: 'A-B',
- source: 'A',
- sourceHandle: 'default',
- target: 'B',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- {
- id: 'B-C',
- source: 'B',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- ],
- };
-
- const result = transformFilterNodesAsEdges({
- nodes: diagram.nodes,
- edges: diagram.edges,
- workflowContext: 'workflow',
- });
-
- // Should only have nodes A and C
- expect(result.nodes).toEqual([
- {
- id: 'A',
- data: {
- nodeType: 'action',
- actionType: 'CODE',
- name: 'Step A',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'A',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'C',
- data: {
- nodeType: 'action',
- actionType: 'SEND_EMAIL',
- name: 'Step C',
- hasNextStepIds: false,
- position: { x: 0, y: 300 },
- stepId: 'C',
- },
- position: { x: 0, y: 300 },
- },
- ]);
-
- // Should have one edge with filter data
- expect(result.edges).toHaveLength(1);
- expect(result.edges[0]).toEqual({
- id: 'A-C-filter-B',
- type: 'filter--editable',
- source: 'A',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- selectable: true,
- deletable: true,
- data: {
- edgeType: 'filter',
- stepId: 'B',
- name: 'Filter B',
- runStatus: undefined,
- filterSettings: {},
- },
- });
- });
-
- it('should handle multiple filter nodes', () => {
- const diagram: WorkflowDiagram = {
- nodes: [
- {
- id: 'A',
- data: {
- nodeType: 'action',
- actionType: 'CODE',
- name: 'Step A',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'A',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'B1',
- data: {
- nodeType: 'action',
- actionType: 'FILTER',
- name: 'Filter B1',
- hasNextStepIds: true,
- position: { x: 0, y: 150 },
- stepId: 'B1',
- },
- position: { x: 0, y: 150 },
- },
- {
- id: 'C',
- data: {
- nodeType: 'action',
- actionType: 'SEND_EMAIL',
- name: 'Step C',
- hasNextStepIds: true,
- position: { x: 0, y: 300 },
- stepId: 'C',
- },
- position: { x: 0, y: 300 },
- },
- {
- id: 'B2',
- data: {
- nodeType: 'action',
- actionType: 'FILTER',
- name: 'Filter B2',
- hasNextStepIds: true,
- position: { x: 0, y: 450 },
- stepId: 'B2',
- },
- position: { x: 0, y: 450 },
- },
- {
- id: 'D',
- data: {
- nodeType: 'action',
- actionType: 'CREATE_RECORD',
- name: 'Step D',
- hasNextStepIds: true,
- position: { x: 0, y: 600 },
- stepId: 'D',
- },
- position: { x: 0, y: 600 },
- },
- ],
- edges: [
- {
- id: 'A-B1',
- source: 'A',
- sourceHandle: 'default',
- target: 'B1',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- {
- id: 'B1-C',
- source: 'B1',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- {
- id: 'C-B2',
- source: 'C',
- sourceHandle: 'default',
- target: 'B2',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- {
- id: 'B2-D',
- source: 'B2',
- sourceHandle: 'default',
- target: 'D',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- ],
- };
-
- const result = transformFilterNodesAsEdges({
- nodes: diagram.nodes,
- edges: diagram.edges,
- workflowContext: 'workflow',
- });
-
- // Should only have nodes A, C, and D
- expect(result.nodes).toHaveLength(3);
- expect(result.nodes.map((n) => n.id)).toEqual(
- expect.arrayContaining(['A', 'C', 'D']),
- );
-
- // Should have two edges with filter data
- expect(result.edges).toHaveLength(2);
-
- const edgeAC = result.edges.find(
- (e) => e.source === 'A' && e.target === 'C',
- );
- expect(edgeAC).toEqual({
- id: 'A-C-filter-B1',
- type: 'filter--editable',
- source: 'A',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- selectable: true,
- deletable: true,
- data: {
- edgeType: 'filter',
- name: 'Filter B1',
- runStatus: undefined,
- stepId: 'B1',
- filterSettings: {},
- },
- });
-
- const edgeCD = result.edges.find(
- (e) => e.source === 'C' && e.target === 'D',
- );
- expect(edgeCD).toEqual({
- id: 'C-D-filter-B2',
- type: 'filter--editable',
- source: 'C',
- sourceHandle: 'default',
- target: 'D',
- targetHandle: 'default',
- selectable: true,
- deletable: true,
- data: {
- edgeType: 'filter',
- name: 'Filter B2',
- runStatus: undefined,
- stepId: 'B2',
- filterSettings: {},
- },
- });
- });
-
- it('should handle filter nodes that are not part of a chain', () => {
- const diagram: WorkflowDiagram = {
- nodes: [
- {
- id: 'A',
- data: {
- nodeType: 'action',
- actionType: 'CODE',
- name: 'Step A',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'A',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'B',
- data: {
- nodeType: 'action',
- actionType: 'FILTER',
- name: 'Filter B',
- hasNextStepIds: true,
- position: { x: 0, y: 150 },
- stepId: 'B',
- },
- position: { x: 0, y: 150 },
- },
- ],
- edges: [
- {
- id: 'A-B',
- source: 'A',
- sourceHandle: 'default',
- target: 'B',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- ],
- };
-
- const result = transformFilterNodesAsEdges({
- nodes: diagram.nodes,
- edges: diagram.edges,
- workflowContext: 'workflow',
- });
-
- // Should only have node A (filter node B is removed)
- expect(result.nodes).toEqual([
- {
- id: 'A',
- data: {
- nodeType: 'action',
- actionType: 'CODE',
- name: 'Step A',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'A',
- },
- position: { x: 0, y: 0 },
- },
- ]);
-
- // Should have no edges (original edge A-B is removed, no new edges created)
- expect(result.edges).toEqual([]);
- });
-
- it('should preserve trigger nodes', () => {
- const diagram: WorkflowDiagram = {
- nodes: [
- {
- id: 'trigger',
- data: {
- nodeType: 'trigger',
- triggerType: 'DATABASE_EVENT',
- name: 'Trigger',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'trigger',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'B',
- data: {
- nodeType: 'action',
- actionType: 'FILTER',
- name: 'Filter B',
- hasNextStepIds: true,
- position: { x: 0, y: 150 },
- stepId: 'B',
- },
- position: { x: 0, y: 150 },
- },
- {
- id: 'C',
- data: {
- nodeType: 'action',
- actionType: 'SEND_EMAIL',
- name: 'Step C',
- hasNextStepIds: true,
- position: { x: 0, y: 300 },
- stepId: 'C',
- },
- position: { x: 0, y: 300 },
- },
- ],
- edges: [
- {
- id: 'trigger-B',
- source: 'trigger',
- sourceHandle: 'default',
- target: 'B',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- {
- id: 'B-C',
- source: 'B',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- data: { edgeType: 'default' },
- },
- ],
- };
-
- const result = transformFilterNodesAsEdges({
- nodes: diagram.nodes,
- edges: diagram.edges,
- workflowContext: 'workflow',
- });
-
- // Should have trigger and C nodes
- expect(result.nodes).toEqual([
- {
- id: 'trigger',
- data: {
- nodeType: 'trigger',
- triggerType: 'DATABASE_EVENT',
- name: 'Trigger',
- hasNextStepIds: true,
- position: { x: 0, y: 0 },
- stepId: 'trigger',
- },
- position: { x: 0, y: 0 },
- },
- {
- id: 'C',
- data: {
- nodeType: 'action',
- actionType: 'SEND_EMAIL',
- name: 'Step C',
- hasNextStepIds: true,
- position: { x: 0, y: 300 },
- stepId: 'C',
- },
- position: { x: 0, y: 300 },
- },
- ]);
-
- // Should have one edge with filter data
- expect(result.edges).toEqual([
- {
- id: 'trigger-C-filter-B',
- type: 'filter--editable',
- source: 'trigger',
- sourceHandle: 'default',
- target: 'C',
- targetHandle: 'default',
- selectable: true,
- deletable: true,
- data: {
- edgeType: 'filter',
- name: 'Filter B',
- runStatus: undefined,
- stepId: 'B',
- filterSettings: {},
- },
- },
- ]);
- });
-});
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowRunDiagram.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowRunDiagram.ts
index a14e47d1a2b..d418d5027c1 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowRunDiagram.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/generateWorkflowRunDiagram.ts
@@ -4,14 +4,12 @@ import {
} from '@/workflow/types/Workflow';
import {
type WorkflowDiagramEdgeData,
- type WorkflowDiagramEdgeType,
type WorkflowRunDiagram,
type WorkflowRunDiagramNode,
type WorkflowRunDiagramStepNodeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { generateWorkflowDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowDiagram';
import { isStepNode } from '@/workflow/workflow-diagram/utils/isStepNode';
-import { transformFilterNodesAsEdges } from '@/workflow/workflow-diagram/utils/transformFilterNodesAsEdges';
import { isDefined } from 'twenty-shared/utils';
import { StepStatus, type WorkflowRunStepInfos } from 'twenty-shared/workflow';
@@ -87,11 +85,9 @@ export const generateWorkflowRunDiagram = ({
const stepInfo = stepInfos?.[parentNode.id];
- const edgeType: WorkflowDiagramEdgeType = 'empty-filter--run';
-
return {
...edge,
- type: edgeType,
+ type: 'run',
data: {
...edge.data,
edgeType: 'default',
@@ -101,11 +97,10 @@ export const generateWorkflowRunDiagram = ({
});
return {
- diagram: transformFilterNodesAsEdges({
+ diagram: {
nodes: workflowRunDiagramNodes,
edges: workflowRunDiagramEdges,
- workflowContext: 'workflow-run',
- }),
+ },
stepToOpenByDefault,
};
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getEdgeTypeBetweenTwoNodes.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getEdgeTypeBetweenTwoNodes.ts
index f488162a4e4..575028b982f 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getEdgeTypeBetweenTwoNodes.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getEdgeTypeBetweenTwoNodes.ts
@@ -8,11 +8,11 @@ export const getEdgeTypeBetweenTwoNodes = ({
}): WorkflowDiagramEdgeType => {
switch (workflowContext) {
case 'workflow': {
- return 'empty-filter--editable';
+ return 'editable';
}
case 'workflow-version':
case 'workflow-run': {
- return 'empty-filter--readonly';
+ return 'readonly';
}
}
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getFilterEdgeType.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getFilterEdgeType.ts
deleted file mode 100644
index 5502336cc8f..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getFilterEdgeType.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { type WorkflowContext } from '@/workflow/workflow-diagram/types/WorkflowContext';
-import { type WorkflowDiagramEdgeType } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
-
-export const getFilterEdgeType = ({
- workflowContext,
-}: {
- workflowContext: WorkflowContext;
-}): WorkflowDiagramEdgeType => {
- switch (workflowContext) {
- case 'workflow': {
- return 'filter--editable';
- }
- case 'workflow-version': {
- return 'filter--readonly';
- }
- case 'workflow-run': {
- return 'filter--run';
- }
- }
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts
index f02ca4c1e65..738391f873b 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts
@@ -41,8 +41,7 @@ export const getWorkflowDiagramColors = ({
},
};
}
- case 'FAILED':
- case 'STOPPED': {
+ case 'FAILED': {
return {
selected: {
background: theme.adaptiveColors.red1,
@@ -60,6 +59,7 @@ export const getWorkflowDiagramColors = ({
},
};
}
+ case 'STOPPED':
case 'SUCCESS': {
return {
selected: {
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowVersionDiagram.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowVersionDiagram.ts
index b4bded07366..00866cdf8e7 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowVersionDiagram.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowVersionDiagram.ts
@@ -2,7 +2,6 @@ import { type WorkflowVersion } from '@/workflow/types/Workflow';
import { type WorkflowContext } from '@/workflow/workflow-diagram/types/WorkflowContext';
import { type WorkflowDiagram } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { generateWorkflowDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowDiagram';
-import { transformFilterNodesAsEdges } from '@/workflow/workflow-diagram/utils/transformFilterNodesAsEdges';
import { isDefined } from 'twenty-shared/utils';
const EMPTY_DIAGRAM: WorkflowDiagram = {
@@ -21,15 +20,9 @@ export const getWorkflowVersionDiagram = ({
return EMPTY_DIAGRAM;
}
- const diagram = generateWorkflowDiagram({
+ return generateWorkflowDiagram({
trigger: workflowVersion.trigger ?? undefined,
steps: workflowVersion.steps ?? [],
workflowContext,
});
-
- return transformFilterNodesAsEdges({
- nodes: diagram.nodes,
- edges: diagram.edges,
- workflowContext,
- });
};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/transformFilterNodesAsEdges.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/transformFilterNodesAsEdges.ts
deleted file mode 100644
index da433156989..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/transformFilterNodesAsEdges.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { type WorkflowContext } from '@/workflow/workflow-diagram/types/WorkflowContext';
-import {
- type WorkflowDiagramEdge,
- type WorkflowDiagramNode,
-} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
-import { getFilterEdgeType } from '@/workflow/workflow-diagram/utils/getFilterEdgeType';
-import { isDefined } from 'twenty-shared/utils';
-
-export const transformFilterNodesAsEdges = <
- T extends WorkflowDiagramNode,
- U extends WorkflowDiagramEdge,
->({
- nodes,
- edges,
- workflowContext,
-}: {
- nodes: T[];
- edges: U[];
- workflowContext: WorkflowContext;
-}): { nodes: T[]; edges: U[] } => {
- const filterNodes = nodes.filter(
- (node) =>
- node.data.nodeType === 'action' &&
- 'actionType' in node.data &&
- node.data.actionType === 'FILTER',
- );
-
- if (filterNodes.length === 0) {
- return { nodes, edges };
- }
-
- const resultNodes = nodes.filter(
- (node) => !filterNodes.some((filterNode) => filterNode.id === node.id),
- );
-
- const resultEdges = [...edges];
- const edgesToRemove = new Set();
- const edgesToAdd: typeof edges = [];
-
- for (const filterNode of filterNodes) {
- const incomingEdge = edges.find((edge) => edge.target === filterNode.id);
- const outgoingEdge = edges.find((edge) => edge.source === filterNode.id);
-
- if (isDefined(incomingEdge) && isDefined(outgoingEdge)) {
- if (
- filterNode.data.nodeType !== 'action' ||
- filterNode.data.actionType !== 'FILTER'
- ) {
- throw new Error('Expected the filter node to be of action type');
- }
-
- const filterEdgeType = getFilterEdgeType({ workflowContext });
-
- const newEdge: U = {
- ...incomingEdge,
- type: filterEdgeType,
- id: `${incomingEdge.source}-${outgoingEdge.target}-filter-${filterNode.id}`,
- target: outgoingEdge.target,
- selectable: true,
- deletable: true,
- data: {
- ...incomingEdge.data,
- edgeType: 'filter',
- stepId: filterNode.id,
- // TODO: Get the filter settings from the filter node
- filterSettings: {},
- name: filterNode.data.name,
- runStatus: filterNode.data.runStatus,
- },
- };
-
- edgesToAdd.push(newEdge);
- edgesToRemove.add(incomingEdge.id);
- edgesToRemove.add(outgoingEdge.id);
- } else {
- if (isDefined(incomingEdge)) {
- edgesToRemove.add(incomingEdge.id);
- }
-
- if (isDefined(outgoingEdge)) {
- edgesToRemove.add(outgoingEdge.id);
- }
- }
- }
-
- const finalEdges = [
- ...resultEdges.filter((edge) => !edgesToRemove.has(edge.id)),
- ...edgesToAdd,
- ];
-
- return {
- nodes: resultNodes,
- edges: finalEdges,
- };
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable.tsx
index 568fd5d4229..160ca1c8fe5 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramDefaultEdgeEditable.tsx
@@ -1,6 +1,3 @@
-import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
-import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
-import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu';
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramBaseEdge';
import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup';
@@ -12,14 +9,12 @@ import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workf
import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState';
import { type WorkflowDiagramEdgeComponentProps } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdgeComponentProps';
import { getConnectionOptionsForSourceHandle } from '@/workflow/workflow-diagram/workflow-edges/utils/getConnectionOptionsForSourceHandle';
-import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep';
import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge';
import { useLingui } from '@lingui/react/macro';
import { EdgeLabelRenderer, getBezierPath } from '@xyflow/react';
-import { type MouseEvent, useContext } from 'react';
-import { useSetRecoilState } from 'recoil';
+import { type MouseEvent } from 'react';
import { isDefined } from 'twenty-shared/utils';
-import { IconFilter, IconPlus, IconTrash } from 'twenty-ui/display';
+import { IconPlus, IconTrash } from 'twenty-ui/display';
type WorkflowDiagramDefaultEdgeEditableProps =
WorkflowDiagramEdgeComponentProps;
@@ -39,8 +34,6 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
}: WorkflowDiagramDefaultEdgeEditableProps) => {
const { i18n } = useLingui();
- const { isInRightDrawer } = useContext(ActionMenuContext);
-
const { isEdgeHovered } = useEdgeState();
const [edgePath, labelX, labelY] = getBezierPath({
@@ -50,8 +43,6 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
targetY,
});
- const { createStep } = useCreateStep();
-
const { deleteEdge } = useDeleteEdge();
const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation();
@@ -61,34 +52,6 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
nextStepId: target,
});
- const setCommandMenuNavigationStack = useSetRecoilState(
- commandMenuNavigationStackState,
- );
-
- const { openWorkflowEditFilterInCommandMenu } =
- useOpenWorkflowEditFilterInCommandMenu();
-
- const handleCreateFilter = async () => {
- const createdStep = await createStep({
- newStepType: 'FILTER',
- parentStepId: source,
- nextStepId: target,
- });
-
- if (!isDefined(createdStep)) {
- return;
- }
-
- if (!isInRightDrawer) {
- setCommandMenuNavigationStack([]);
- }
-
- openWorkflowEditFilterInCommandMenu({
- stepId: createdStep.id,
- stepName: createdStep.name,
- });
- };
-
const handleNodeButtonClick = () => {
startNodeCreation({
parentStepId: source,
@@ -153,10 +116,6 @@ export const WorkflowDiagramDefaultEdgeEditable = ({
>
asserts data is WorkflowDiagramEdgeData & { edgeType: 'filter' } = (
- data: WorkflowDiagramEdgeData | undefined,
-) => {
- if (data?.edgeType !== 'filter') {
- throw new Error('Edge data must be of type "filter"');
- }
-};
-
-const StyledConfiguredFilterContainer = styled.div`
- height: 26px;
- width: 26px;
- position: relative;
-`;
-
-const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>`
- pointer-events: all;
-
- ${({ selected, theme }) => {
- if (!selected) return '';
- const colors = getWorkflowDiagramColors({ theme });
- return css`
- background-color: ${colors.selected.background};
- border: 1px solid ${colors.selected.borderColor};
- `;
- }}
-`;
-
-export const WorkflowDiagramFilterEdgeEditable = ({
- source,
- sourceHandleId,
- target,
- targetHandleId,
- sourceY,
- sourceX,
- targetY,
- targetX,
- markerStart,
- markerEnd,
- data,
-}: WorkflowDiagramFilterEdgeEditableProps) => {
- assertFilterEdgeDataOrThrow(data);
-
- const { t, i18n } = useLingui();
-
- const theme = useTheme();
-
- const [edgePath, labelX, labelY] = getBezierPath({
- sourceX,
- sourceY,
- targetX,
- targetY,
- });
-
- const { isInRightDrawer } = useContext(ActionMenuContext);
-
- const { deleteStep } = useDeleteStep();
- const { deleteEdge } = useDeleteEdge();
- const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation();
-
- const setCommandMenuNavigationStack = useSetRecoilState(
- commandMenuNavigationStackState,
- );
-
- const { openDropdown } = useOpenDropdown();
- const { closeDropdown } = useCloseDropdown();
-
- const { isEdgeHovered } = useEdgeState();
-
- const setWorkflowDiagramPanOnDrag = useSetRecoilComponentState(
- workflowDiagramPanOnDragComponentState,
- );
-
- const nodeCreationStarted = isNodeCreationStarted({
- parentStepId: data.stepId,
- nextStepId: target,
- });
-
- const workflowSelectedNode = useRecoilComponentValue(
- workflowSelectedNodeComponentState,
- );
-
- const isFilterNodeSelected =
- isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId;
-
- const dropdownId = `${WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID}-${source}-${target}`;
-
- const isDropdownOpen = useRecoilComponentValue(
- isDropdownOpenComponentState,
- dropdownId,
- );
-
- const { openWorkflowEditFilterInCommandMenu } =
- useOpenWorkflowEditFilterInCommandMenu();
-
- const handleFilterButtonClick = () => {
- if (!isInRightDrawer) {
- setCommandMenuNavigationStack([]);
- }
-
- openWorkflowEditFilterInCommandMenu({
- stepId: data.stepId,
- stepName: data.name,
- });
- };
-
- const handleRemoveFilterButtonClick = async () => {
- closeDropdown(dropdownId);
-
- await deleteStep(data.stepId);
- };
-
- const handleAddNodeButtonClick = () => {
- closeDropdown(dropdownId);
-
- startNodeCreation({
- parentStepId: data.stepId,
- nextStepId: target,
- position: { x: labelX, y: labelY },
- });
- };
-
- const handleDeleteBranchClick = async () => {
- closeDropdown(dropdownId);
-
- await deleteEdge({
- source,
- target,
- });
- };
-
- const { filterCounter } = useFilterCounter({ stepId: data.stepId });
- const { unselected } = getWorkflowDiagramColors({ theme });
-
- return (
- <>
-
-
-
- {isDefined(data?.labelOptions) && (
-
-
-
- )}
-
-
-
-
-
- {isEdgeHovered({
- source,
- target,
- sourceHandle: sourceHandleId,
- targetHandle: targetHandleId,
- }) ||
- isDropdownOpen ||
- nodeCreationStarted ? (
- {
- openDropdown({
- dropdownComponentInstanceIdFromProps: dropdownId,
- });
- },
- },
- ]}
- selected={isFilterNodeSelected}
- />
- ) : (
-
- )}
-
-
- }
- data-select-disable
- dropdownPlacement="bottom-start"
- dropdownStrategy="absolute"
- dropdownOffset={{
- x: 24,
- y: 4,
- }}
- onOpen={() => {
- setWorkflowDiagramPanOnDrag(false);
- }}
- onClose={() => {
- setWorkflowDiagramPanOnDrag(true);
- }}
- dropdownComponents={
-
-
-
-
-
-
-
- }
- />
-
-
-
- >
- );
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx
deleted file mode 100644
index 04769a474d3..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx
+++ /dev/null
@@ -1,143 +0,0 @@
-import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
-import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
-import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
-import { useOpenWorkflowViewFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu';
-import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
-import { type WorkflowDiagramEdgeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
-import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
-import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container';
-import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer';
-import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId';
-import { type WorkflowDiagramEdgeComponentProps } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdgeComponentProps';
-import { WorkflowStepFilterCounter } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterCounter';
-import { useFilterCounter } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterCounter';
-import { css, useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
-import { isNonEmptyString } from '@sniptt/guards';
-import { BaseEdge, EdgeLabelRenderer, getBezierPath } from '@xyflow/react';
-import { useContext } from 'react';
-import { useSetRecoilState } from 'recoil';
-import { IconFilter } from 'twenty-ui/display';
-import { IconButtonGroup } from 'twenty-ui/input';
-
-type WorkflowDiagramFilterEdgeReadonlyProps = WorkflowDiagramEdgeComponentProps;
-
-const assertFilterEdgeDataOrThrow: (
- data: WorkflowDiagramEdgeData | undefined,
-) => asserts data is WorkflowDiagramEdgeData & { edgeType: 'filter' } = (
- data: WorkflowDiagramEdgeData | undefined,
-) => {
- if (data?.edgeType !== 'filter') {
- throw new Error('Edge data must be of type "filter"');
- }
-};
-
-const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>`
- pointer-events: all;
-
- ${({ selected, theme }) => {
- if (!selected) return '';
- const colors = getWorkflowDiagramColors({ theme });
- return css`
- background-color: ${colors.selected.background};
- border: 1px solid ${colors.selected.borderColor};
- `;
- }}
-`;
-
-const StyledConfiguredFilterContainer = styled.div`
- height: 26px;
- width: 26px;
-`;
-
-export const WorkflowDiagramFilterEdgeReadonly = ({
- sourceX,
- sourceY,
- targetX,
- targetY,
- markerStart,
- markerEnd,
- data,
-}: WorkflowDiagramFilterEdgeReadonlyProps) => {
- assertFilterEdgeDataOrThrow(data);
-
- const { isInRightDrawer } = useContext(ActionMenuContext);
-
- const theme = useTheme();
-
- const [edgePath, labelX, labelY] = getBezierPath({
- sourceX,
- sourceY,
- targetX,
- targetY,
- });
-
- const workflowSelectedNode = useRecoilComponentValue(
- workflowSelectedNodeComponentState,
- );
-
- const isFilterNodeSelected =
- isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId;
-
- const { openWorkflowViewFilterInCommandMenu } =
- useOpenWorkflowViewFilterInCommandMenu();
-
- const setCommandMenuNavigationStack = useSetRecoilState(
- commandMenuNavigationStackState,
- );
-
- const handleFilterButtonClick = () => {
- if (!isInRightDrawer) {
- setCommandMenuNavigationStack([]);
- }
-
- openWorkflowViewFilterInCommandMenu({
- stepId: data.stepId,
- stepName: data.name,
- });
- };
-
- const { filterCounter } = useFilterCounter({
- stepId: data.stepId,
- });
- const { unselected } = getWorkflowDiagramColors({ theme });
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx
deleted file mode 100644
index 452c847a94a..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
-import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow';
-import { useOpenWorkflowRunFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu';
-import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState';
-import { type WorkflowDiagramEdgeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
-import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors';
-import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2Container';
-import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeV2VisibilityContainer';
-import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/workflow-edges/components/WorkflowRunDiagramBaseEdge';
-import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/workflow-edges/constants/WorkflowDiagramEdgeOptionsClickOutsideId';
-import { type WorkflowDiagramEdgeComponentProps } from '@/workflow/workflow-diagram/workflow-edges/types/WorkflowDiagramEdgeComponentProps';
-import { WorkflowStepFilterCounter } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterCounter';
-import { useFilterCounter } from '@/workflow/workflow-steps/workflow-actions/filter-action/hooks/useFilterCounter';
-import { css, useTheme } from '@emotion/react';
-import styled from '@emotion/styled';
-import { isNonEmptyString } from '@sniptt/guards';
-import { EdgeLabelRenderer, getBezierPath } from '@xyflow/react';
-import { isDefined } from 'twenty-shared/utils';
-import { IconFilter } from 'twenty-ui/display';
-import { IconButtonGroup } from 'twenty-ui/input';
-
-type WorkflowDiagramFilterEdgeRunProps = WorkflowDiagramEdgeComponentProps;
-
-const assertFilterEdgeDataOrThrow: (
- data: WorkflowDiagramEdgeData | undefined,
-) => asserts data is WorkflowDiagramEdgeData & { edgeType: 'filter' } = (
- data: WorkflowDiagramEdgeData | undefined,
-) => {
- if (data?.edgeType !== 'filter') {
- throw new Error('Edge data must be of type "filter"');
- }
-};
-
-const StyledIconButtonGroup = styled(IconButtonGroup)<{
- selected?: boolean;
- runStatus?: WorkflowRunStepStatus;
-}>`
- pointer-events: all;
-
- ${({ selected, runStatus, theme }) => {
- if (!selected) return '';
- const colors = getWorkflowDiagramColors({ runStatus, theme });
- return css`
- background-color: ${colors.selected.background};
- border: 1px solid ${colors.selected.borderColor};
- `;
- }}
-`;
-
-const StyledConfiguredFilterContainer = styled.div`
- height: 26px;
- width: 26px;
-`;
-
-export const WorkflowDiagramFilterEdgeRun = ({
- sourceX,
- sourceY,
- targetX,
- targetY,
- data,
- markerStart,
- markerEnd,
-}: WorkflowDiagramFilterEdgeRunProps) => {
- assertFilterEdgeDataOrThrow(data);
-
- const theme = useTheme();
-
- const [edgePath, labelX, labelY] = getBezierPath({
- sourceX,
- sourceY,
- targetX,
- targetY,
- });
-
- const workflowSelectedNode = useRecoilComponentValue(
- workflowSelectedNodeComponentState,
- );
-
- const isFilterNodeSelected =
- isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId;
-
- const { openWorkflowRunFilterInCommandMenu } =
- useOpenWorkflowRunFilterInCommandMenu();
-
- const handleFilterButtonClick = () => {
- if (!isDefined(data.runStatus)) {
- throw new Error('Run status must be set on edge data for workflow runs');
- }
-
- openWorkflowRunFilterInCommandMenu({
- stepId: data.stepId,
- stepName: data.name,
- stepExecutionStatus: data.runStatus,
- });
- };
-
- const { filterCounter } = useFilterCounter({ stepId: data.stepId });
- const { selected } = getWorkflowDiagramColors({
- theme,
- runStatus: data.runStatus,
- });
-
- return (
- <>
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts
index 67b9ab23193..4d3743c329f 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/constants/WorkflowVisualizerEdgeDefaultConfiguration.ts
@@ -1,11 +1,11 @@
-import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker';
import {
type WorkflowDiagramEdge,
type WorkflowDiagramEdgeType,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
+import { EDGE_BRANCH_ARROW_MARKER } from '@/workflow/workflow-diagram/workflow-edges/constants/EdgeBranchArrowMarker';
export const WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION = {
- type: 'empty-filter--readonly' satisfies WorkflowDiagramEdgeType,
+ type: 'readonly' satisfies WorkflowDiagramEdgeType,
markerStart: undefined,
...EDGE_BRANCH_ARROW_MARKER.Default,
deletable: false,
diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx
index 31450bf7a90..dd019adffa1 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx
@@ -122,7 +122,8 @@ export const WorkflowRunDiagramStepNode = ({
{capitalize(data.nodeType)}
- {data.runStatus === StepStatus.SUCCESS && (
+ {(data.runStatus === StepStatus.SUCCESS ||
+ data.runStatus === StepStatus.STOPPED) && (
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/FlowActions.ts
similarity index 53%
rename from packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts
rename to packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/FlowActions.ts
index 6791bd27dd2..b13f8f591b4 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/FlowActions.ts
@@ -1,8 +1,8 @@
import { type WorkflowActionType } from '@/workflow/types/Workflow';
-export const OTHER_ACTIONS: Array<{
+export const FLOW_ACTIONS: Array<{
label: string;
- type: Extract;
+ type: Extract;
icon: string;
}> = [
{
@@ -10,4 +10,9 @@ export const OTHER_ACTIONS: Array<{
type: 'ITERATOR',
icon: 'IconRepeat',
},
+ {
+ label: 'Filter',
+ type: 'FILTER',
+ icon: 'IconFilter',
+ },
];
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx
index 967c2c5ac7b..581d38dd9cb 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowDropdownStepOutputItems.tsx
@@ -18,6 +18,7 @@ import { type StepOutputSchemaV2 } from '@/workflow/workflow-variables/types/Ste
import { extractRawVariableNamePart } from '@/workflow/workflow-variables/utils/extractRawVariableNamePart';
import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath';
import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel';
+import { getStepItemIcon } from '@/workflow/workflow-variables/utils/getStepItemIcon';
import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath';
import { searchVariableThroughOutputSchemaV2 } from '@/workflow/workflow-variables/utils/searchVariableThroughOutputSchemaV2';
import { useLingui } from '@lingui/react/macro';
@@ -185,7 +186,7 @@ export const WorkflowDropdownStepOutputItems = ({
subStepObjectMetadataItem.labelSingular
.toLowerCase()
.includes(searchInputValue.toLowerCase())
- : true;
+ : isDefined(displayedSubStepObject);
const objectLabel = subStepObjectMetadataItem?.labelSingular;
@@ -236,7 +237,15 @@ export const WorkflowDropdownStepOutputItems = ({
onClick={() => handleSelectField(key)}
text={subStep.label || key}
hasSubMenu={!subStep.isLeaf}
- LeftIcon={subStep.icon ? getIcon(subStep.icon) : undefined}
+ LeftIcon={
+ subStep.icon
+ ? getIcon(subStep.icon)
+ : getIcon(
+ getStepItemIcon({
+ itemType: subStep.type,
+ }),
+ )
+ }
contextualText={
subStep.isLeaf ? subStep?.value?.toString() : undefined
}
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterCounter.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterCounter.tsx
deleted file mode 100644
index b97f99ec5ee..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterCounter.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import styled from '@emotion/styled';
-
-const StyledFilterCounter = styled.div<{
- backgroundColor: string;
- textColor: string;
-}>`
- align-items: center;
- background: ${({ backgroundColor }) => backgroundColor};
- border-radius: 50%;
- color: ${({ textColor }) => textColor};
- display: flex;
- font-size: ${({ theme }) => theme.font.size.xxs};
- font-weight: ${({ theme }) => theme.font.weight.semiBold};
- height: 12px;
- justify-content: center;
- width: 12px;
- left: -3px;
- position: absolute;
- top: -3px;
- z-index: 1;
-`;
-
-type WorkflowStepFilterCounterProps = {
- backgroundColor: string;
- textColor: string;
- counter: number;
-};
-
-export const WorkflowStepFilterCounter = ({
- backgroundColor,
- textColor,
- counter,
-}: WorkflowStepFilterCounterProps) => (
-
- {counter}
-
-);
diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/__stories__/WorkflowStepFilterCounter.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/__stories__/WorkflowStepFilterCounter.stories.tsx
deleted file mode 100644
index 1ca44ccf46b..00000000000
--- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/filter-action/components/__stories__/WorkflowStepFilterCounter.stories.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { WorkflowStepFilterCounter } from '@/workflow/workflow-steps/workflow-actions/filter-action/components/WorkflowStepFilterCounter';
-import { type Meta, type StoryObj } from '@storybook/react';
-import { expect, within } from '@storybook/test';
-import { ComponentDecorator } from 'twenty-ui/testing';
-import { THEME_LIGHT } from 'twenty-ui/theme';
-
-const meta: Meta = {
- title: 'Modules/Workflow/Actions/Filter/WorkflowStepFilterCounter',
- component: WorkflowStepFilterCounter,
- decorators: [ComponentDecorator],
-};
-
-export default meta;
-
-type Story = StoryObj;
-
-export const Default: Story = {
- args: {
- counter: 1,
- backgroundColor: THEME_LIGHT.border.color.strong,
- textColor: THEME_LIGHT.font.color.inverted,
- },
- play: async ({ canvasElement }) => {
- const canvas = within(canvasElement);
-
- expect(await canvas.findByText('1')).toBeVisible();
- },
-};
diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx
index f63191ac2fb..8b40cff6fab 100644
--- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx
+++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownStepItems.tsx
@@ -93,11 +93,10 @@ export const WorkflowVariablesDropdownStepItems = ({
.includes(searchInputValue.toLowerCase())
: true;
+ const objectLabel = displayedSubStepObjectMetadata?.labelSingular;
const shouldDisplaySubStepObject =
shouldDisplayRecordObjects && isObjectFoundThroughSearch;
- const objectLabel = displayedSubStepObjectMetadata?.labelSingular;
-
return (
{
});
});
});
-
- describe('with filter steps', () => {
- it('should delete the filter step when deleting edge from trigger to target through filter', async () => {
- const mockStepsWithFilter = [
- {
- id: 'step-1',
- type: WorkflowActionType.FORM,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: ['step-2'],
- },
- {
- id: 'step-2',
- type: WorkflowActionType.SEND_EMAIL,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: [],
- },
- {
- id: 'filter-step',
- type: WorkflowActionType.FILTER,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: ['step-2'],
- },
- ] as WorkflowAction[];
-
- const mockTriggerWithFilter = {
- type: WorkflowTriggerType.MANUAL,
- settings: {},
- nextStepIds: ['step-1', 'filter-step'],
- };
-
- const mockWorkflowVersionWithFilter = {
- id: mockWorkflowVersionId,
- trigger: mockTriggerWithFilter,
- steps: mockStepsWithFilter,
- status: 'DRAFT',
- } as WorkflowVersionWorkspaceEntity;
-
- workflowCommonWorkspaceService.getWorkflowVersionOrFail.mockResolvedValue(
- mockWorkflowVersionWithFilter,
- );
-
- const result = await service.deleteWorkflowVersionEdge({
- source: TRIGGER_STEP_ID,
- target: 'step-2',
- workflowVersionId: mockWorkflowVersionId,
- workspaceId: mockWorkspaceId,
- });
-
- expect(
- workflowCommonWorkspaceService.getWorkflowVersionOrFail,
- ).toHaveBeenCalledWith({
- workflowVersionId: mockWorkflowVersionId,
- workspaceId: mockWorkspaceId,
- });
-
- expect(
- mockWorkflowVersionWorkspaceRepository.update,
- ).toHaveBeenCalledWith(mockWorkflowVersionId, {
- trigger: {
- ...mockTriggerWithFilter,
- nextStepIds: ['step-1'],
- },
- steps: mockStepsWithFilter.filter(
- (step) => step.id !== 'filter-step',
- ),
- });
-
- expect(result).toEqual({
- triggerNextStepIds: ['step-1'],
- stepsNextStepIds: {
- 'step-1': ['step-2'],
- 'step-2': [],
- },
- });
- });
- it('should delete the filter step when deleting edge from step to target through filter', async () => {
- const mockStepsWithFilter = [
- {
- id: 'step-1',
- type: WorkflowActionType.FORM,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: ['step-2', 'filter-step'],
- },
- {
- id: 'step-2',
- type: WorkflowActionType.SEND_EMAIL,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: [],
- },
- {
- id: 'step-3',
- type: WorkflowActionType.SEND_EMAIL,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: [],
- },
- {
- id: 'filter-step',
- type: WorkflowActionType.FILTER,
- settings: {
- errorHandlingOptions: {
- continueOnFailure: { value: false },
- retryOnFailure: { value: false },
- },
- },
- nextStepIds: ['step-3'],
- },
- ] as WorkflowAction[];
-
- const mockWorkflowVersionWithFilter = {
- id: mockWorkflowVersionId,
- trigger: mockTrigger,
- steps: mockStepsWithFilter,
- status: 'DRAFT',
- } as WorkflowVersionWorkspaceEntity;
-
- workflowCommonWorkspaceService.getWorkflowVersionOrFail.mockResolvedValue(
- mockWorkflowVersionWithFilter,
- );
-
- const result = await service.deleteWorkflowVersionEdge({
- source: 'step-1',
- target: 'step-3',
- workflowVersionId: mockWorkflowVersionId,
- workspaceId: mockWorkspaceId,
- });
-
- expect(
- workflowCommonWorkspaceService.getWorkflowVersionOrFail,
- ).toHaveBeenCalledWith({
- workflowVersionId: mockWorkflowVersionId,
- workspaceId: mockWorkspaceId,
- });
-
- expect(
- mockWorkflowVersionWorkspaceRepository.update,
- ).toHaveBeenCalledWith(mockWorkflowVersionId, {
- steps: mockStepsWithFilter
- .map((step) => {
- if (step.id === 'step-1') {
- return {
- ...step,
- nextStepIds: ['step-2'],
- };
- }
-
- return step;
- })
- .filter((step) => step.id !== 'filter-step'),
- });
-
- expect(result).toEqual({
- triggerNextStepIds: ['step-1'],
- stepsNextStepIds: {
- 'step-1': ['step-2'],
- 'step-2': [],
- 'step-3': [],
- },
- });
- });
- });
});
});
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts
index 4f6243366a4..5347101a46f 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-edge/workflow-version-edge.workspace-service.ts
@@ -222,6 +222,16 @@ export class WorkflowVersionEdgeWorkspaceService {
);
}
+ if (
+ sourceStep.nextStepIds?.includes(target) &&
+ !isDefined(sourceConnectionOptions)
+ ) {
+ return computeWorkflowVersionStepChanges({
+ trigger,
+ steps,
+ });
+ }
+
const { updatedSourceStep, shouldPersist } = isDefined(
sourceConnectionOptions,
)
@@ -364,12 +374,9 @@ export class WorkflowVersionEdgeWorkspaceService {
}
if (!trigger.nextStepIds?.includes(target)) {
- return this.handleFilterBetweenTriggerAndTarget({
+ return computeWorkflowVersionStepChanges({
trigger,
steps,
- target,
- workflowVersionId: workflowVersion.id,
- workflowVersionRepository,
});
}
@@ -416,24 +423,13 @@ export class WorkflowVersionEdgeWorkspaceService {
);
}
- // TODO: Remove this once we start using filters as regular steps
- const isIteratorWithLoopTarget =
- isDefined(sourceConnectionOptions) &&
- sourceConnectionOptions.connectedStepType ===
- WorkflowActionType.ITERATOR &&
- sourceConnectionOptions.settings.isConnectedToLoop;
-
if (
!sourceStep.nextStepIds?.includes(target) &&
- !isIteratorWithLoopTarget
+ !isDefined(sourceConnectionOptions)
) {
- return await this.handleFilterBetweenSourceAndTarget({
+ return computeWorkflowVersionStepChanges({
trigger,
steps,
- sourceStep,
- target,
- workflowVersionId: workflowVersion.id,
- workflowVersionRepository,
});
}
@@ -480,109 +476,6 @@ export class WorkflowVersionEdgeWorkspaceService {
});
}
- private async handleFilterBetweenTriggerAndTarget({
- trigger,
- steps,
- target,
- workflowVersionId,
- workflowVersionRepository,
- }: {
- trigger: WorkflowTrigger;
- steps: WorkflowAction[];
- target: string;
- workflowVersionId: string;
- workflowVersionRepository: WorkspaceRepository;
- }): Promise {
- const filterBetweenTriggerAndTarget = this.findFilterBetweenNodes({
- steps,
- sourceNextStepIds: trigger.nextStepIds,
- target,
- });
-
- if (!isDefined(filterBetweenTriggerAndTarget)) {
- return computeWorkflowVersionStepChanges({
- trigger,
- steps,
- });
- }
-
- const updatedTrigger = {
- ...trigger,
- nextStepIds: trigger.nextStepIds?.filter(
- (nextStepId: string) => nextStepId !== filterBetweenTriggerAndTarget.id,
- ),
- };
-
- const updatedSteps = steps.filter(
- (step) => step.id !== filterBetweenTriggerAndTarget.id,
- );
-
- await workflowVersionRepository.update(workflowVersionId, {
- trigger: updatedTrigger,
- steps: updatedSteps,
- });
-
- return computeWorkflowVersionStepChanges({
- trigger: updatedTrigger,
- steps: updatedSteps,
- });
- }
-
- private async handleFilterBetweenSourceAndTarget({
- trigger,
- steps,
- sourceStep,
- target,
- workflowVersionRepository,
- workflowVersionId,
- }: {
- trigger: WorkflowTrigger | null;
- steps: WorkflowAction[];
- sourceStep: WorkflowAction;
- target: string;
- workflowVersionRepository: WorkspaceRepository;
- workflowVersionId: string;
- }): Promise {
- const filterBetweenSourceAndTarget = this.findFilterBetweenNodes({
- steps,
- sourceNextStepIds: sourceStep.nextStepIds,
- target,
- });
-
- if (!isDefined(filterBetweenSourceAndTarget)) {
- return computeWorkflowVersionStepChanges({
- trigger,
- steps,
- });
- }
-
- const updatedSourceStep = {
- ...sourceStep,
- nextStepIds: sourceStep.nextStepIds?.filter(
- (nextStepId: string) => nextStepId !== filterBetweenSourceAndTarget.id,
- ),
- };
-
- const updatedSteps = steps
- .map((step) => {
- if (step.id === sourceStep.id) {
- return updatedSourceStep;
- }
-
- return step;
- })
- .filter((step) => step.id !== filterBetweenSourceAndTarget.id);
-
- await workflowVersionRepository.update(workflowVersionId, {
- steps: updatedSteps,
- });
-
- return computeWorkflowVersionStepChanges({
- trigger,
- steps: updatedSteps,
- });
- }
-
private buildUpdatedSourceStepWithOptions({
sourceStep,
target,
@@ -650,22 +543,4 @@ export class WorkflowVersionEdgeWorkspaceService {
};
}
}
-
- private findFilterBetweenNodes({
- steps,
- sourceNextStepIds,
- target,
- }: {
- steps: WorkflowAction[];
- sourceNextStepIds: string[] | undefined;
- target: string;
- }) {
- const nextStepFilters = steps.filter(
- (step) =>
- sourceNextStepIds?.includes(step.id) &&
- step.type === WorkflowActionType.FILTER,
- );
-
- return nextStepFilters.find((step) => step.nextStepIds?.includes(target));
- }
}
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts
index 09f0f3b8a10..7651493ed35 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/__tests__/remove-step.spec.ts
@@ -151,100 +151,6 @@ describe('removeStep', () => {
});
});
- it('should remove step child that is a filter', () => {
- const step1 = createMockAction('1', ['2']);
- const step2 = createMockAction('2', ['3']);
- const step3 = {
- id: '3',
- name: 'Step 3',
- type: WorkflowActionType.FILTER,
- nextStepIds: ['4'],
- } as WorkflowAction;
- const step4 = createMockAction('4');
-
- const result = removeStep({
- existingTrigger: mockTrigger,
- existingSteps: [step1, step2, step3, step4],
- stepIdToDelete: '2',
- stepToDeleteChildrenIds: ['3'],
- });
-
- expect(result.updatedTrigger).toEqual(mockTrigger);
- expect(result.updatedSteps).toEqual([
- { ...step1, nextStepIds: ['4'] },
- step4,
- ]);
- });
-
- it('should remove trigger children that is a filter', () => {
- const step1 = {
- id: '1',
- name: 'Step 1',
- type: WorkflowActionType.FILTER,
- nextStepIds: ['2'],
- } as WorkflowAction;
- const step2 = createMockAction('2', ['3']);
- const step3 = createMockAction('3');
-
- const result = removeStep({
- existingTrigger: mockTrigger,
- existingSteps: [step1, step2, step3],
- stepIdToDelete: TRIGGER_STEP_ID,
- stepToDeleteChildrenIds: ['1'],
- });
-
- expect(result.updatedTrigger).toEqual(null);
- expect(result.updatedSteps).toEqual([step2, step3]);
- });
-
- it('should remove filter step if it has no children', () => {
- const step1 = {
- id: '1',
- name: 'Step 1',
- type: WorkflowActionType.FILTER,
- nextStepIds: ['2'],
- } as WorkflowAction;
- const step2 = createMockAction('2', ['3']);
- const step3 = {
- id: '3',
- name: 'Step 3',
- type: WorkflowActionType.FILTER,
- nextStepIds: ['4'],
- } as WorkflowAction;
- const step4 = createMockAction('4');
-
- const result = removeStep({
- existingTrigger: mockTrigger,
- existingSteps: [step1, step2, step3, step4],
- stepIdToDelete: '4',
- });
-
- expect(result.updatedTrigger).toEqual(mockTrigger);
- expect(result.updatedSteps).toEqual([step1, { ...step2, nextStepIds: [] }]);
- });
-
- it('should remove filter step if it is the last step', () => {
- const step1 = {
- id: '1',
- name: 'Step 1',
- type: WorkflowActionType.FILTER,
- nextStepIds: ['2'],
- } as WorkflowAction;
- const step2 = createMockAction('2');
-
- const result = removeStep({
- existingTrigger: mockTrigger,
- existingSteps: [step1, step2],
- stepIdToDelete: '2',
- });
-
- expect(result.updatedTrigger).toEqual({
- ...mockTrigger,
- nextStepIds: [],
- });
- expect(result.updatedSteps).toEqual([]);
- });
-
it('should remove trigger if steps are null', () => {
const result = removeStep({
existingTrigger: { ...mockTrigger, nextStepIds: [] },
@@ -253,7 +159,7 @@ describe('removeStep', () => {
});
expect(result.updatedTrigger).toEqual(null);
- expect(result.updatedSteps).toEqual([]);
+ expect(result.updatedSteps).toEqual(null);
});
it('should handle removing a step that is part of iteratorLoopStepIds', () => {
diff --git a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts
index c2ad1109b73..4ac65b5c39f 100644
--- a/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts
+++ b/packages/twenty-server/src/modules/workflow/workflow-builder/workflow-version-step/utils/remove-step.ts
@@ -29,7 +29,7 @@ const computeUpdatedNextStepIds = ({
];
};
-const removeOneStep = ({
+export const removeStep = ({
existingTrigger,
existingSteps,
stepIdToDelete,
@@ -39,11 +39,15 @@ const removeOneStep = ({
existingSteps: WorkflowAction[] | null;
stepIdToDelete: string;
stepToDeleteChildrenIds?: string[];
-}): {
- updatedSteps: WorkflowAction[];
- updatedTrigger: WorkflowTrigger | null;
- removedStepIds: string[];
-} => {
+}) => {
+ if (stepIdToDelete === TRIGGER_STEP_ID) {
+ return {
+ updatedSteps: existingSteps,
+ updatedTrigger: null,
+ removedStepIds: [TRIGGER_STEP_ID],
+ };
+ }
+
const updatedSteps =
existingSteps
?.filter((step) => step.id !== stepIdToDelete)
@@ -104,131 +108,3 @@ const removeOneStep = ({
removedStepIds: [stepIdToDelete],
};
};
-
-const removeRegularStep = ({
- existingTrigger,
- existingSteps,
- stepIdToDelete,
- stepToDeleteChildrenIds,
-}: {
- existingTrigger: WorkflowTrigger | null;
- existingSteps: WorkflowAction[] | null;
- stepIdToDelete: string;
- stepToDeleteChildrenIds?: string[];
-}): {
- updatedSteps: WorkflowAction[];
- updatedTrigger: WorkflowTrigger | null;
- removedStepIds: string[];
-} => {
- let { updatedSteps, updatedTrigger, removedStepIds } = removeOneStep({
- existingTrigger,
- existingSteps,
- stepIdToDelete,
- stepToDeleteChildrenIds,
- });
-
- for (const stepId of stepToDeleteChildrenIds ?? []) {
- const step = existingSteps?.find((step) => step.id === stepId);
-
- if (step?.type === WorkflowActionType.FILTER) {
- const {
- updatedSteps: stepsAfterRemovingChildFilter,
- updatedTrigger: triggerAfterRemovingChildFilter,
- removedStepIds: removedStepIdsAfterRemovingChildFilter,
- } = removeOneStep({
- existingTrigger: updatedTrigger,
- existingSteps: updatedSteps,
- stepIdToDelete: stepId,
- stepToDeleteChildrenIds: step.nextStepIds,
- });
-
- updatedSteps = stepsAfterRemovingChildFilter;
- updatedTrigger = triggerAfterRemovingChildFilter;
- removedStepIds = [
- ...removedStepIds,
- ...removedStepIdsAfterRemovingChildFilter,
- ];
- }
- }
-
- for (const step of updatedSteps) {
- if (
- step?.type === WorkflowActionType.FILTER &&
- (!isDefined(step?.nextStepIds) || step.nextStepIds?.length === 0)
- ) {
- const {
- updatedSteps: stepsAfterRemovingFilterWithoutChildren,
- updatedTrigger: triggerAfterRemovingFilterWithoutChildren,
- removedStepIds: removedStepIdsAfterRemovingFilterWithoutChildren,
- } = removeOneStep({
- existingTrigger: updatedTrigger,
- existingSteps: updatedSteps,
- stepIdToDelete: step.id,
- stepToDeleteChildrenIds: step.nextStepIds,
- });
-
- updatedSteps = stepsAfterRemovingFilterWithoutChildren;
- updatedTrigger = triggerAfterRemovingFilterWithoutChildren;
- removedStepIds = [
- ...removedStepIds,
- ...removedStepIdsAfterRemovingFilterWithoutChildren,
- ];
- }
- }
-
- return {
- updatedSteps,
- updatedTrigger,
- removedStepIds,
- };
-};
-
-const removeTrigger = ({
- existingSteps,
- triggerChildrenIds,
-}: {
- existingSteps: WorkflowAction[] | null;
- triggerChildrenIds?: string[];
-}) => {
- const stepIdsToRemove =
- triggerChildrenIds?.filter((id) => {
- const step = existingSteps?.find((step) => step.id === id);
-
- return step?.type === WorkflowActionType.FILTER;
- }) ?? [];
-
- const updatedSteps =
- existingSteps?.filter((step) => !stepIdsToRemove.includes(step.id)) ?? [];
-
- return {
- updatedSteps,
- updatedTrigger: null,
- removedStepIds: [TRIGGER_STEP_ID, ...stepIdsToRemove],
- };
-};
-
-export const removeStep = ({
- existingTrigger,
- existingSteps,
- stepIdToDelete,
- stepToDeleteChildrenIds,
-}: {
- existingTrigger: WorkflowTrigger | null;
- existingSteps: WorkflowAction[] | null;
- stepIdToDelete: string;
- stepToDeleteChildrenIds?: string[];
-}) => {
- if (stepIdToDelete === TRIGGER_STEP_ID) {
- return removeTrigger({
- existingSteps,
- triggerChildrenIds: stepToDeleteChildrenIds,
- });
- } else {
- return removeRegularStep({
- existingTrigger,
- existingSteps,
- stepIdToDelete,
- stepToDeleteChildrenIds,
- });
- }
-};