From d423d66293ceed8761acb7e612f04909a2c5304a Mon Sep 17 00:00:00 2001 From: katarina-cala Date: Tue, 19 Aug 2025 13:16:08 +0200 Subject: [PATCH 1/3] 1652-replace cluster root check through variable with new clusterRoot boolean from component definition --- .../workflow-editor/WorkflowEditorLayout.tsx | 11 +++++----- .../node-details-tabs/DescriptionTab.tsx | 4 ++-- .../workflow-editor/hooks/useNodeClick.ts | 2 +- .../utils/handleComponentAddedSuccess.ts | 7 ++----- .../workflow-editor/utils/handleDeleteTask.ts | 2 +- .../workflow-editor/utils/layoutUtils.tsx | 20 +++++++------------ .../utils/saveClusterElementFieldChange.ts | 7 ++++--- .../utils/saveClusterElementNodesPosition.ts | 2 +- .../utils/saveWorkflowDefinition.ts | 3 +-- client/src/shared/constants.tsx | 2 -- client/src/shared/types.ts | 2 +- 11 files changed, 25 insertions(+), 37 deletions(-) diff --git a/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx b/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx index 35ca03d2cd..36b8d1481a 100644 --- a/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx +++ b/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx @@ -13,7 +13,6 @@ import {useWorkflowEditor} from '@/pages/platform/workflow-editor/providers/work import useRightSidebarStore from '@/pages/platform/workflow-editor/stores/useRightSidebarStore'; import useWorkflowEditorStore from '@/pages/platform/workflow-editor/stores/useWorkflowEditorStore'; import {useCopilotStore} from '@/shared/components/copilot/stores/useCopilotStore'; -import {ROOT_CLUSTER_ELEMENT_NAMES} from '@/shared/constants'; import {XIcon} from 'lucide-react'; import {Suspense, lazy, useEffect} from 'react'; import {twMerge} from 'tailwind-merge'; @@ -87,13 +86,13 @@ const WorkflowEditorLayout = ({includeComponents, runDisabled, showWorkflowInput const {invalidateWorkflowQueries, updateWorkflowMutation} = useWorkflowEditor(); - const isRootClusterElement = ROOT_CLUSTER_ELEMENT_NAMES.includes(currentComponent?.componentName as string); + const isMainRootClusterElement = currentNode?.clusterRoot && !currentNode.isNestedClusterRoot; useEffect(() => { - if (currentNode?.rootClusterElement) { + if (isMainRootClusterElement) { setRootClusterElementNodeData(currentNode); } - }, [currentNode, setRootClusterElementNodeData]); + }, [isMainRootClusterElement, setRootClusterElementNodeData, currentNode]); return ( @@ -136,7 +135,7 @@ const WorkflowEditorLayout = ({includeComponents, runDisabled, showWorkflowInput )} - {currentComponent && !isRootClusterElement && ( + {currentComponent && !isMainRootClusterElement && ( }> } - {currentComponent && !isRootClusterElement && dataPillPanelOpen && ( + {currentComponent && !isMainRootClusterElement && dataPillPanelOpen && ( }> ), operationName: task.type.split('/')[2], - rootClusterElement: isRootClusterElement, taskDispatcher: isTaskDispatcher, taskDispatcherId: isTaskDispatcher ? task.name : undefined, trigger: index === 0, @@ -100,7 +96,7 @@ export const convertTaskToNode = ( }, id: task.name, position: {x: 0, y: 0}, - type: componentName === 'aiAgent' ? 'aiAgentNode' : 'workflow', + type: task.clusterRoot ? 'aiAgentNode' : 'workflow', }; }; @@ -134,7 +130,7 @@ export const getLayoutedElements = async ({ width = 15; } - if (node.data.rootClusterElement) { + if (node.data.clusterRoot) { width = ROOT_CLUSTER_WIDTH; } } else { @@ -147,7 +143,7 @@ export const getLayoutedElements = async ({ edges.forEach((edge) => { if (edge.target.includes('bottom-ghost')) { dagreGraph.setEdge(edge.source, edge.target, {minlen: 2}); - } else if (ROOT_CLUSTER_ELEMENT_NAMES.includes(edge.source.split('_')[0])) { + } else { const sourceNode = nodes.find((node) => node.id === edge.source); const hasValidClusterElements = @@ -159,10 +155,8 @@ export const getLayoutedElements = async ({ if (hasValidClusterElements) { dagreGraph.setEdge(edge.source, edge.target, {minlen: 2}); - } else { - dagreGraph.setEdge(edge.source, edge.target); } - } else { + dagreGraph.setEdge(edge.source, edge.target); } }); @@ -214,7 +208,7 @@ export const getLayoutedElements = async ({ ]; } - positionedNodes = [...positionedNodes, ...placeholderNodes, ...centeredMainRootNode]; + positionedNodes = [...positionedNodes, ...centeredMainRootNode, ...placeholderNodes]; return { edges, @@ -231,7 +225,7 @@ export const getLayoutedElements = async ({ ([, value]) => value !== null && value !== undefined && !(Array.isArray(value) && value.length === 0) ); - if (hasValidClusterElements && ROOT_CLUSTER_ELEMENT_NAMES.includes(node.data.componentName as string)) { + if (hasValidClusterElements && node.data.clusterRoot) { positionX -= 85; } @@ -289,7 +283,7 @@ export const getLayoutedElements = async ({ condition: sourceNode.type === 'taskDispatcherTopGhostNode', }, { - condition: sourceNode.data.rootClusterElement, + condition: sourceNode.data.clusterRoot, }, { condition: sourceNode.data.componentName === 'branch', diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts index 9143eb068c..4c4bd45c78 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementFieldChange.ts @@ -51,8 +51,9 @@ export default function saveClusterElementFieldChange({ let updatedClusterElements: ClusterElementsType; if ( - currentNode.rootClusterElement && - currentNode.workflowNodeName === rootClusterElementNodeData?.workflowNodeName + currentNode.clusterRoot && + currentNode.workflowNodeName === rootClusterElementNodeData?.workflowNodeName && + !currentNode.isNestedClusterRoot ) { updatedMainRootData = updateClusterRootElementField({ currentComponentDefinition, @@ -115,7 +116,7 @@ export default function saveClusterElementFieldChange({ }); if (rootClusterElementNodeData) { - if (currentNode.rootClusterElement) { + if (currentNode.clusterRoot && !currentNode.isNestedClusterRoot) { setRootClusterElementNodeData({ ...rootClusterElementNodeData, ...updatedStateData, diff --git a/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts b/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts index b65f4593a0..190145154f 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveClusterElementNodesPosition.ts @@ -77,7 +77,7 @@ export default function saveClusterElementNodesPosition({ metadata, } as typeof rootClusterElementNodeData); - if (currentNode?.rootClusterElement) { + if (currentNode?.clusterRoot && !currentNode.isNestedClusterRoot) { setCurrentNode({ ...currentNode, clusterElements: updatedClusterElements, diff --git a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts index a07dd12ffc..75829dc2fc 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts @@ -1,4 +1,3 @@ -import {ROOT_CLUSTER_ELEMENT_NAMES} from '@/shared/constants'; import {Workflow, WorkflowTask, WorkflowTrigger} from '@/shared/middleware/platform/configuration'; import {BranchCaseType, NodeDataType, TaskDispatcherContextType, WorkflowDefinitionType} from '@/shared/types'; import {UseMutationResult} from '@tanstack/react-query'; @@ -168,7 +167,7 @@ export default async function saveWorkflowDefinition({ combinedParameters = newTask.parameters ?? {}; } - if (ROOT_CLUSTER_ELEMENT_NAMES.includes(existingWorkflowTask.type.split('/')[0])) { + if (existingWorkflowTask.clusterRoot) { const rootClusterElementTask: WorkflowTask = { ...newTask, clusterElements: { diff --git a/client/src/shared/constants.tsx b/client/src/shared/constants.tsx index 4a23854288..c34312f316 100644 --- a/client/src/shared/constants.tsx +++ b/client/src/shared/constants.tsx @@ -101,8 +101,6 @@ export const TASK_DISPATCHER_NAMES = [ 'subflow', ]; -export const ROOT_CLUSTER_ELEMENT_NAMES = ['aiAgent']; - export const SORT_OPTIONS = [ { label: 'Last edited', diff --git a/client/src/shared/types.ts b/client/src/shared/types.ts index fc3b8ef918..abdbaa10a2 100644 --- a/client/src/shared/types.ts +++ b/client/src/shared/types.ts @@ -164,6 +164,7 @@ export type NodeDataType = { clusterElements?: ClusterElementsType | Array; clusterElementName?: string; clusterElementType?: string; + clusterRoot?: boolean; componentName: string; conditionCase?: 'caseTrue' | 'caseFalse'; conditionData?: ConditionDataType; @@ -201,7 +202,6 @@ export type NodeDataType = { index: number; }; parentClusterRootId?: string; - rootClusterElement?: boolean; taskDispatcher?: boolean; taskDispatcherId?: string; title?: string; From 3d1171bfa8be6b7c27552aa2f3a1fc8ba33e37a4 Mon Sep 17 00:00:00 2001 From: katarina-cala Date: Tue, 19 Aug 2025 13:36:14 +0200 Subject: [PATCH 2/3] 1652-filter clusterElementTypes through mainRoot's type and actionClusterElementTypes object where it is needed --- .../hooks/useClusterElementsLayout.ts | 1 + .../utils/clusterElementsUtils.ts | 76 +++++++++++++++++-- .../utils/createClusterElementsNodes.ts | 36 ++++++++- .../components/WorkflowNodeDetailsPanel.tsx | 27 ++++++- .../WorkflowNodesPopoverMenuOperationList.tsx | 21 +++-- .../workflow-editor/nodes/WorkflowNode.tsx | 18 +++-- 6 files changed, 149 insertions(+), 30 deletions(-) diff --git a/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts b/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts index 36db0c5c39..5d20bc82a1 100644 --- a/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts +++ b/client/src/pages/platform/cluster-element-editor/hooks/useClusterElementsLayout.ts @@ -110,6 +110,7 @@ const useClusterElementsLayout = () => { clusterRootId: rootClusterElementNodeData.workflowNodeName, currentNodePositions: nodePositions, nestedClusterRootsDefinitions: nestedClusterRootsDefinitions || {}, + operationName: rootClusterElementNodeData.operationName, }); nodes.push(...clusterElementNodes); diff --git a/client/src/pages/platform/cluster-element-editor/utils/clusterElementsUtils.ts b/client/src/pages/platform/cluster-element-editor/utils/clusterElementsUtils.ts index d1f67957f8..54bdce6464 100644 --- a/client/src/pages/platform/cluster-element-editor/utils/clusterElementsUtils.ts +++ b/client/src/pages/platform/cluster-element-editor/utils/clusterElementsUtils.ts @@ -1,19 +1,49 @@ import {ROOT_CLUSTER_HANDLE_STEP, ROOT_CLUSTER_WIDTH} from '@/shared/constants'; -import {ComponentDefinition} from '@/shared/middleware/platform/configuration'; +import {ComponentDefinition, WorkflowTask} from '@/shared/middleware/platform/configuration'; import {ClusterElementItemType, ClusterElementsType, WorkflowNodeType} from '@/shared/types'; -export function initializeClusterElementsObject( - clusterElementsData: ClusterElementsType, - rootClusterElementDefinition: ComponentDefinition -) { +interface InitializeClusterElementsObjectProps { + clusterElementsData: ClusterElementsType; + mainClusterRootTask: WorkflowTask; + rootClusterElementDefinition: ComponentDefinition; +} + +export function initializeClusterElementsObject({ + clusterElementsData, + mainClusterRootTask, + rootClusterElementDefinition, +}: InitializeClusterElementsObjectProps) { const clusterElements: ClusterElementsType = {}; if (!rootClusterElementDefinition.clusterElementTypes) { return clusterElements; } + const mainClusterRootType = mainClusterRootTask.type?.split('/')[2]; + + const filteredClusterElementTypes = (() => { + if ( + rootClusterElementDefinition.actionClusterElementTypes && + Object.keys(rootClusterElementDefinition.actionClusterElementTypes).length > 0 && + mainClusterRootType + ) { + return rootClusterElementDefinition.actionClusterElementTypes[mainClusterRootType] || []; + } else { + return rootClusterElementDefinition.clusterElementTypes.map((type) => type.name); + } + })(); + rootClusterElementDefinition.clusterElementTypes.forEach((elementType) => { + const matchingType = filteredClusterElementTypes.some((type) => { + return type === elementType.name; + }); + + if (!matchingType) { + return; + } + const clusterElementType = convertNameToCamelCase(elementType.name || ''); + const elementData = clusterElementsData?.[clusterElementType]; if (elementType.multipleElements) { @@ -135,6 +165,42 @@ export function extractClusterElementComponentOperations( }, existingClusterElementsOperations); } +interface GetClusterElementTypesCountProps { + clusterRootComponentDefinition: ComponentDefinition; + operationName?: string; +} + +export function getClusterElementTypesCount({ + clusterRootComponentDefinition, + operationName, +}: GetClusterElementTypesCountProps): number { + if (!clusterRootComponentDefinition.clusterElementTypes) { + return 0; + } + + if (!operationName) { + return clusterRootComponentDefinition.clusterElementTypes.length; + } + + const actionTypes = clusterRootComponentDefinition.actionClusterElementTypes; + + if (!actionTypes || Object.keys(actionTypes).length === 0) { + return clusterRootComponentDefinition.clusterElementTypes.length; + } + + const operationElementTypes = actionTypes[operationName]; + + if (!operationElementTypes || operationElementTypes.length === 0) { + return clusterRootComponentDefinition.clusterElementTypes.length; + } + + const filteredElementTypes = clusterRootComponentDefinition.clusterElementTypes.filter((elementType) => + operationElementTypes.includes(elementType.name || '') + ); + + return filteredElementTypes.length; +} + export function calculateNodeWidth(handleCount: number): number { const baseWidth = ROOT_CLUSTER_WIDTH; const handleStep = ROOT_CLUSTER_HANDLE_STEP; diff --git a/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts b/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts index fac7be60ba..781ef1af4f 100644 --- a/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts +++ b/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts @@ -3,7 +3,7 @@ import {ClusterElementsType} from '@/shared/types'; import {Node} from '@xyflow/react'; import {createMultipleElementsNode, createPlaceholderNode, createSingleElementsNode} from './clusterElementsNodesUtils'; -import {convertNameToCamelCase, isPlainObject} from './clusterElementsUtils'; +import {convertNameToCamelCase, getClusterElementTypesCount, isPlainObject} from './clusterElementsUtils'; interface CreateClusterElementNodesProps { clusterElements: ClusterElementsType; @@ -11,6 +11,7 @@ interface CreateClusterElementNodesProps { clusterRootId: string; currentNodePositions: Record; nestedClusterRootsDefinitions: Record; + operationName?: string; } export default function createClusterElementNodes({ @@ -19,15 +20,42 @@ export default function createClusterElementNodes({ clusterRootId, currentNodePositions = {}, nestedClusterRootsDefinitions, + operationName = '', }: CreateClusterElementNodesProps) { - if (!clusterRootComponentDefinition?.clusterElementTypes || !clusterElements) { + if (!clusterRootComponentDefinition || !clusterRootComponentDefinition.clusterElementTypes || !clusterElements) { return []; } const createdNodes: Node[] = []; - const totalClusterElementTypeCount = clusterRootComponentDefinition.clusterElementTypes.length; - clusterRootComponentDefinition.clusterElementTypes.forEach((clusterElementType, clusterElementTypeIndex) => { + const totalClusterElementTypeCount = getClusterElementTypesCount({ + clusterRootComponentDefinition, + operationName, + }); + + if (totalClusterElementTypeCount === 0) { + return []; + } + + const elementTypesToUse = clusterRootComponentDefinition.clusterElementTypes!.filter((elementType) => { + if (!operationName) { + return true; + } + + const actionTypes = clusterRootComponentDefinition.actionClusterElementTypes; + if (!actionTypes || Object.keys(actionTypes).length === 0) { + return true; + } + + const operationElementTypes = actionTypes[operationName]; + if (!operationElementTypes || operationElementTypes.length === 0) { + return true; + } + + return operationElementTypes.includes(elementType.name || ''); + }); + + elementTypesToUse.forEach((clusterElementType, clusterElementTypeIndex) => { const clusterElementTypeName = convertNameToCamelCase(clusterElementType.name || ''); const clusterElementTypeLabel = clusterElementType.label || ''; const isMultipleClusterElementsNode = clusterElementType.multipleElements; diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx index ce2c207617..df69f8c15f 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx @@ -58,6 +58,7 @@ import {twMerge} from 'tailwind-merge'; import {useShallow} from 'zustand/shallow'; import { + convertNameToSnakeCase, extractClusterElementComponentOperations, getClusterElementsLabel, } from '../../cluster-element-editor/utils/clusterElementsUtils'; @@ -494,6 +495,20 @@ const WorkflowNodeDetailsPanel = ({ ); }, [clusterElementsCanvasOpen, currentWorkflowNode, isClusterElement]); + const filteredClusterElementOperations = useMemo(() => { + if (currentComponentDefinition?.clusterElement && currentNode?.clusterElementType) { + return currentComponentDefinition?.clusterElements?.filter((clusterElement) => { + return clusterElement.type === convertNameToSnakeCase(currentNode.clusterElementType as string); + }); + } + + return currentComponentDefinition?.clusterElements; + }, [ + currentComponentDefinition?.clusterElement, + currentComponentDefinition?.clusterElements, + currentNode?.clusterElementType, + ]); + const handleOperationSelectChange = useCallback( async (newOperationName: string) => { if (currentOperationName === newOperationName) { @@ -900,7 +915,7 @@ const WorkflowNodeDetailsPanel = ({ (workflowNode) => workflowNode.workflowNodeName === currentNode?.workflowNodeName ); } else if (clusterElementsCanvasOpen) { - if (currentNode?.rootClusterElement) { + if (currentNode?.clusterRoot && !currentNode.isNestedClusterRoot) { currentWorkflowNode = workflowNodes.find( (workflowNodeType) => workflowNodeType.workflowNodeName === currentNode?.workflowNodeName ); @@ -950,8 +965,12 @@ const WorkflowNodeDetailsPanel = ({ return; } - if (clusterElementsCanvasOpen && currentComponentDefinition?.clusterElement) { - if (!!currentComponentDefinition.clusterElements && !!matchingOperation) { + if ( + clusterElementsCanvasOpen && + currentComponentDefinition?.clusterElement && + currentNode?.parentClusterRootId + ) { + if (matchingOperation) { fetchClusterElementDefinition(); } else { setCurrentActionDefinition(undefined); @@ -1049,7 +1068,7 @@ const WorkflowNodeDetailsPanel = ({ (currentNode?.trigger ? currentComponentDefinition?.triggers : clusterElementsCanvasOpen && currentComponentDefinition?.clusterElement - ? currentComponentDefinition?.clusterElements + ? filteredClusterElementOperations : currentComponentDefinition?.actions)! } triggerSelect={currentNode?.trigger} diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx index 9448f755a3..06cd117735 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodesPopoverMenuOperationList.tsx @@ -1,4 +1,3 @@ -import {ROOT_CLUSTER_ELEMENT_NAMES} from '@/shared/constants'; import {useAnalytics} from '@/shared/hooks/useAnalytics'; import { ActionDefinition, @@ -87,7 +86,8 @@ const WorkflowNodesPopoverMenuOperationList = ({ const queryClient = useQueryClient(); - const {actions, clusterElement, clusterElements, icon, name, title, triggers, version} = componentDefinition; + const {actions, clusterElement, clusterElements, clusterRoot, icon, name, title, triggers, version} = + componentDefinition; const clusterElementOperations = useMemo(() => { if (!clusterElementType) { @@ -110,10 +110,8 @@ const WorkflowNodesPopoverMenuOperationList = ({ (operation: ClickedOperationType, definition: ActionDefinition | TriggerDefinition) => { const {componentLabel, componentName, icon, operationName, version} = operation; - const isRootClusterElement = ROOT_CLUSTER_ELEMENT_NAMES.includes(componentName); - return { - ...(isRootClusterElement + ...(clusterRoot ? { clusterElements: {}, } @@ -138,7 +136,7 @@ const WorkflowNodesPopoverMenuOperationList = ({ workflowNodeName: trigger ? 'trigger_1' : getFormattedName(componentName), }; }, - [trigger] + [clusterRoot, trigger] ); const saveNodeToWorkflow = useCallback( @@ -197,10 +195,11 @@ const WorkflowNodesPopoverMenuOperationList = ({ {} ); - const clusterElements = initializeClusterElementsObject( - mainClusterRootTask?.clusterElements || {}, - rootClusterElementDefinition - ); + const clusterElements = initializeClusterElementsObject({ + clusterElementsData: mainClusterRootTask?.clusterElements || {}, + mainClusterRootTask, + rootClusterElementDefinition, + }); const updatedClusterElements = processClusterElementsHierarchy({ clusterElementData, @@ -236,7 +235,7 @@ const WorkflowNodesPopoverMenuOperationList = ({ metadata, } as typeof rootClusterElementNodeData); - if (currentNode?.rootClusterElement) { + if (currentNode?.clusterRoot && !currentNode.isNestedClusterRoot) { setCurrentNode({ ...currentNode, clusterElements: updatedClusterElements.nestedClusterElements, diff --git a/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx b/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx index 7bc6460efd..583bb54aed 100644 --- a/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx +++ b/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx @@ -19,6 +19,7 @@ import {useShallow} from 'zustand/react/shallow'; import { calculateNodeWidth, convertNameToCamelCase, + getClusterElementTypesCount, getHandlePosition, } from '../../cluster-element-editor/utils/clusterElementsUtils'; import useNodeClickHandler from '../hooks/useNodeClick'; @@ -73,7 +74,7 @@ const WorkflowNode = ({data, id}: {data: NodeDataType; id: string}) => { const isSelected = currentNode?.name === data.name; - const isMainRootClusterElement = data.rootClusterElement; + const isMainRootClusterElement = !!data.clusterRoot && !data.isNestedClusterRoot; const isClusterElement = !!data.clusterElementType; const isNestedClusterRoot = !!data.isNestedClusterRoot; const parentClusterRootId = data.parentClusterRootId || id.split('-')[0]; @@ -112,15 +113,20 @@ const WorkflowNode = ({data, id}: {data: NodeDataType; id: string}) => { const clusterElementTypesCount = useMemo( () => - (clusterElementsCanvasOpen && - (isMainRootClusterElement || isNestedClusterRoot) && - rootClusterElementDefinition?.clusterElementTypes?.length) || - 0, + clusterElementsCanvasOpen && + (isMainRootClusterElement || isNestedClusterRoot) && + rootClusterElementDefinition + ? getClusterElementTypesCount({ + clusterRootComponentDefinition: rootClusterElementDefinition, + operationName: data.operationName, + }) + : 0, [ clusterElementsCanvasOpen, isNestedClusterRoot, isMainRootClusterElement, - rootClusterElementDefinition?.clusterElementTypes?.length, + rootClusterElementDefinition, + data.operationName, ] ); From 69f357d3d7c211ab7fa4317ba859f6bd868b5b7d Mon Sep 17 00:00:00 2001 From: katarina-cala Date: Wed, 20 Aug 2025 12:05:13 +0200 Subject: [PATCH 3/3] 1652-PR fixes --- .../utils/createClusterElementsNodes.ts | 2 ++ .../workflow-editor/WorkflowEditorLayout.tsx | 7 ++-- .../components/WorkflowNodeDetailsPanel.tsx | 7 ++-- .../workflow-editor/nodes/WorkflowNode.tsx | 35 ++++++++++--------- 4 files changed, 30 insertions(+), 21 deletions(-) diff --git a/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts b/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts index 781ef1af4f..09231022a4 100644 --- a/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts +++ b/client/src/pages/platform/cluster-element-editor/utils/createClusterElementsNodes.ts @@ -43,11 +43,13 @@ export default function createClusterElementNodes({ } const actionTypes = clusterRootComponentDefinition.actionClusterElementTypes; + if (!actionTypes || Object.keys(actionTypes).length === 0) { return true; } const operationElementTypes = actionTypes[operationName]; + if (!operationElementTypes || operationElementTypes.length === 0) { return true; } diff --git a/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx b/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx index 36b8d1481a..616d265098 100644 --- a/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx +++ b/client/src/pages/platform/workflow-editor/WorkflowEditorLayout.tsx @@ -14,7 +14,7 @@ import useRightSidebarStore from '@/pages/platform/workflow-editor/stores/useRig import useWorkflowEditorStore from '@/pages/platform/workflow-editor/stores/useWorkflowEditorStore'; import {useCopilotStore} from '@/shared/components/copilot/stores/useCopilotStore'; import {XIcon} from 'lucide-react'; -import {Suspense, lazy, useEffect} from 'react'; +import {Suspense, lazy, useEffect, useMemo} from 'react'; import {twMerge} from 'tailwind-merge'; import {useShallow} from 'zustand/shallow'; @@ -86,7 +86,10 @@ const WorkflowEditorLayout = ({includeComponents, runDisabled, showWorkflowInput const {invalidateWorkflowQueries, updateWorkflowMutation} = useWorkflowEditor(); - const isMainRootClusterElement = currentNode?.clusterRoot && !currentNode.isNestedClusterRoot; + const isMainRootClusterElement = useMemo( + () => currentNode?.clusterRoot && !currentNode?.isNestedClusterRoot, + [currentNode?.clusterRoot, currentNode?.isNestedClusterRoot] + ); useEffect(() => { if (isMainRootClusterElement) { diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx index df69f8c15f..72b8bb4bee 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx @@ -497,9 +497,10 @@ const WorkflowNodeDetailsPanel = ({ const filteredClusterElementOperations = useMemo(() => { if (currentComponentDefinition?.clusterElement && currentNode?.clusterElementType) { - return currentComponentDefinition?.clusterElements?.filter((clusterElement) => { - return clusterElement.type === convertNameToSnakeCase(currentNode.clusterElementType as string); - }); + return currentComponentDefinition?.clusterElements?.filter( + (clusterElement) => + clusterElement.type === convertNameToSnakeCase(currentNode.clusterElementType as string) + ); } return currentComponentDefinition?.clusterElements; diff --git a/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx b/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx index 583bb54aed..b3b6e58a94 100644 --- a/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx +++ b/client/src/pages/platform/workflow-editor/nodes/WorkflowNode.tsx @@ -111,24 +111,27 @@ const WorkflowNode = ({data, id}: {data: NodeDataType; id: string}) => { clusterElementsCanvasOpen && (!!isMainRootClusterElement || isNestedClusterRoot) ); - const clusterElementTypesCount = useMemo( - () => + const clusterElementTypesCount = useMemo(() => { + const clusterRootRequirementMet = clusterElementsCanvasOpen && (isMainRootClusterElement || isNestedClusterRoot) && - rootClusterElementDefinition - ? getClusterElementTypesCount({ - clusterRootComponentDefinition: rootClusterElementDefinition, - operationName: data.operationName, - }) - : 0, - [ - clusterElementsCanvasOpen, - isNestedClusterRoot, - isMainRootClusterElement, - rootClusterElementDefinition, - data.operationName, - ] - ); + rootClusterElementDefinition; + + if (!clusterRootRequirementMet) { + return 0; + } + + return getClusterElementTypesCount({ + clusterRootComponentDefinition: rootClusterElementDefinition, + operationName: data.operationName, + }); + }, [ + clusterElementsCanvasOpen, + isNestedClusterRoot, + isMainRootClusterElement, + rootClusterElementDefinition, + data.operationName, + ]); const nodeWidth = useMemo( () =>