diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx index 8c3b5d7405f..a1dd481964b 100644 --- a/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx +++ b/client/src/pages/platform/workflow-editor/components/WorkflowNodeDetailsPanel.tsx @@ -1,17 +1,20 @@ import LoadingIcon from '@/components/LoadingIcon'; import {Button} from '@/components/ui/button'; import {Select, SelectContent, SelectItem, SelectTrigger, SelectValue} from '@/components/ui/select'; +import {Skeleton} from '@/components/ui/skeleton'; import {Tooltip, TooltipContent, TooltipTrigger} from '@/components/ui/tooltip'; import Properties from '@/pages/platform/workflow-editor/components/Properties/Properties'; import DataStreamComponentsTab from '@/pages/platform/workflow-editor/components/node-details-tabs/DataStreamComponentsTab'; import { ActionDefinitionApi, + ComponentDefinition, ComponentDefinitionBasic, GetComponentActionDefinitionRequest, GetComponentTriggerDefinitionRequest, TriggerDefinitionApi, WorkflowConnection, WorkflowNodeOutput, + WorkflowTask, } from '@/shared/middleware/platform/configuration'; import {useDeleteWorkflowNodeTestOutputMutation} from '@/shared/mutations/platform/workflowNodeTestOutputs.mutations'; import { @@ -31,6 +34,7 @@ import {useGetWorkflowTestConfigurationConnectionsQuery} from '@/shared/queries/ import { ComponentPropertiesType, DataPillType, + NodeDataType, PropertyAllType, UpdateWorkflowMutationType, WorkflowDefinitionType, @@ -48,6 +52,7 @@ import getAllTaskNames from '../utils/getAllTaskNames'; import getDataPillsFromProperties from '../utils/getDataPillsFromProperties'; import getParametersWithDefaultValues from '../utils/getParametersWithDefaultValues'; import saveWorkflowDefinition from '../utils/saveWorkflowDefinition'; +import updateConditionSubtask from '../utils/updateRootConditionNode'; import CurrentOperationSelect from './CurrentOperationSelect'; import ConnectionTab from './node-details-tabs/ConnectionTab'; import DescriptionTab from './node-details-tabs/DescriptionTab'; @@ -94,7 +99,7 @@ const WorkflowNodeDetailsPanel = ({ const {currentComponent, currentNode, setCurrentComponent, setCurrentNode, workflowNodeDetailsPanelOpen} = useWorkflowNodeDetailsPanelStore(); - const {componentActions, setDataPills, workflow} = useWorkflowDataStore(); + const {componentActions, nodes, setDataPills, workflow} = useWorkflowDataStore(); const {data: currentComponentDefinition} = useGetComponentDefinitionQuery( { @@ -267,19 +272,72 @@ const WorkflowNodeDetailsPanel = ({ const {componentName, description, label, workflowNodeName} = currentComponent; + let nodeData = { + componentName, + description, + label, + name: workflowNodeName || currentNode?.name || '', + operationName: newOperationName, + parameters: getParametersWithDefaultValues({ + properties: operationData.properties as Array, + }), + trigger: currentNode?.trigger, + type: `${componentName}/v${currentComponentDefinition.version}/${newOperationName}`, + }; + + if (currentNode?.conditionData) { + const parentConditionNode = nodes.find( + (node) => node.data.name === currentNode?.conditionData?.conditionId + ); + + if (!parentConditionNode) { + return; + } + + const conditionCase = currentNode.conditionData.conditionCase; + const conditionParameters: Array = parentConditionNode.data.parameters[conditionCase]; + + if (conditionParameters) { + const nodeIndex = conditionParameters.findIndex((subtask) => subtask.name === currentNode.name); + + if (nodeIndex !== -1) { + conditionParameters[nodeIndex] = { + ...conditionParameters[nodeIndex], + type: `${componentName}/v${currentComponentDefinition.version}/${newOperationName}`, + }; + + if (!workflow.definition) { + return; + } + + const tasks = JSON.parse(workflow.definition).tasks; + + const updatedParentConditionTask = workflow.tasks?.find( + (task) => task.name === currentNode.conditionData?.conditionId + ); + + if (!updatedParentConditionTask) { + return; + } + + const updatedRootConditionNode = updateConditionSubtask({ + conditionCase, + conditionId: currentNode.conditionData.conditionId, + nodeIndex, + nodes, + tasks, + updatedParentConditionNodeData: parentConditionNode.data, + updatedParentConditionTask, + workflow, + }); + + nodeData = updatedRootConditionNode.data ?? (updatedRootConditionNode as NodeDataType); + } + } + } + saveWorkflowDefinition({ - nodeData: { - componentName, - description, - label, - name: workflowNodeName || currentNode?.name || '', - operationName: newOperationName, - parameters: getParametersWithDefaultValues({ - properties: operationData.properties as Array, - }), - trigger: currentNode?.trigger, - type: `${componentName}/v${currentComponentDefinition.version}/${newOperationName}`, - }, + nodeData, onSuccess: () => { setCurrentComponent({ ...currentComponent, @@ -293,6 +351,7 @@ const WorkflowNodeDetailsPanel = ({ }); }, queryClient, + subtask: !!currentNode?.conditionData, updateWorkflowMutation, workflow, }); @@ -446,6 +505,10 @@ const WorkflowNodeDetailsPanel = ({ }, [componentActions, currentNode?.name]); const currentTaskData = currentComponentDefinition || currentTaskDispatcherDefinition; + const currentOperationFetched = currentActionFetched || currentTriggerFetched; + + const operationDataMissing = + currentComponent?.operationName && (!matchingOperation?.name || !currentOperationFetched); if (!workflowNodeDetailsPanelOpen || !currentNode?.name || !currentTaskData) { return <>; @@ -496,44 +559,65 @@ const WorkflowNodeDetailsPanel = ({
- + {operationDataMissing && ( +
+ Actions + + +
+ )} + + {(!!(currentTaskData as ComponentDefinition).actions?.length || + !!(currentTaskData as ComponentDefinition).triggers?.length) && + !operationDataMissing && ( + + )} {((!currentNode?.trigger && !currentNode?.taskDispatcher && currentActionFetched) || currentNode?.taskDispatcher || (currentNode?.trigger && currentTriggerFetched)) && - nodeTabs.length > 1 && ( -
- {nodeTabs.map((tab) => ( - - ))} -
- )} + nodeTabs.length > 1 ? ( +
+ {nodeTabs.map((tab) => ( + + ))} +
+ ) : ( +
+ + + + + + + +
+ )}
{currentTaskData && ( @@ -563,6 +647,7 @@ const WorkflowNodeDetailsPanel = ({ {activeTab === 'properties' && currentTaskData && + !operationDataMissing && (currentOperationProperties?.length ? ( { + nodes.forEach((node, index) => { let height = NODE_HEIGHT; if (node.id.includes('placeholder')) { @@ -516,6 +516,16 @@ export default function useLayout({ height = NODE_HEIGHT * 1.2; } + if (index === nodes.length - 1) { + height = 20; + + const penultimateNode = nodes[nodes.length - 2]; + + if (penultimateNode.id.includes('bottom-placeholder')) { + height = 90; + } + } + dagreGraph.setNode(node.id, {height, width: NODE_WIDTH}); }); diff --git a/client/src/pages/platform/workflow-editor/utils/getTasksWithConditionChildNode.ts b/client/src/pages/platform/workflow-editor/utils/insertNewConditionSubtask.ts similarity index 90% rename from client/src/pages/platform/workflow-editor/utils/getTasksWithConditionChildNode.ts rename to client/src/pages/platform/workflow-editor/utils/insertNewConditionSubtask.ts index 6859561b397..82154aa28df 100644 --- a/client/src/pages/platform/workflow-editor/utils/getTasksWithConditionChildNode.ts +++ b/client/src/pages/platform/workflow-editor/utils/insertNewConditionSubtask.ts @@ -3,19 +3,19 @@ import {WorkflowTask} from '@/shared/middleware/automation/configuration'; import getParentConditionTask from './getParentConditionTask'; -interface AddConditionChildNodeProps { +interface GetUpdatedConditionSubtasksProps { conditionId: string; tasks: Array; newTask: WorkflowTask; placeholderId: string; } -export default function getTasksWithConditionChildNode({ +export default function insertNewConditionSubtask({ conditionId, newTask, placeholderId, tasks, -}: AddConditionChildNodeProps): Array { +}: GetUpdatedConditionSubtasksProps): Array { let conditionTask = tasks.find((task) => task.name === conditionId); if (!conditionTask) { diff --git a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts index bd7dc372f80..4adede7fe5e 100644 --- a/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts +++ b/client/src/pages/platform/workflow-editor/utils/saveWorkflowDefinition.ts @@ -10,7 +10,7 @@ import {TaskDispatcherKeys} from '@/shared/queries/platform/taskDispatcherDefini import {NodeDataType, WorkflowDefinitionType} from '@/shared/types'; import {QueryClient, UseMutationResult} from '@tanstack/react-query'; -import getTasksWithConditionChildNode from './getTasksWithConditionChildNode'; +import insertNewConditionSubtask from './insertNewConditionSubtask'; const SPACE = 4; @@ -27,6 +27,7 @@ interface SaveWorkflowDefinitionProps { onSuccess?: () => void; placeholderId?: string; queryClient: QueryClient; + subtask?: boolean; updateWorkflowMutation: UseMutationResult; workflow: Workflow; } @@ -39,6 +40,7 @@ export default async function saveWorkflowDefinition({ onSuccess, placeholderId, queryClient, + subtask, updateWorkflowMutation, workflow, }: SaveWorkflowDefinitionProps) { @@ -130,6 +132,7 @@ export default async function saveWorkflowDefinition({ if ( existingWorkflowTask && !decorative && + !subtask && (!operationName || (existingWorkflowTask.parameters && JSON.stringify(existingWorkflowTask.parameters) === JSON.stringify(newTask.parameters))) && @@ -171,7 +174,7 @@ export default async function saveWorkflowDefinition({ tasks = [...(workflowDefinition.tasks || [])]; if (conditionId && placeholderId) { - tasks = getTasksWithConditionChildNode({conditionId, newTask, placeholderId, tasks}); + tasks = insertNewConditionSubtask({conditionId, newTask, placeholderId, tasks}); } else if (nodeIndex !== undefined && nodeIndex > -1) { const tasksAfterCurrent = tasks.slice(nodeIndex); diff --git a/client/src/pages/platform/workflow-editor/utils/updateRootConditionNode.ts b/client/src/pages/platform/workflow-editor/utils/updateRootConditionNode.ts new file mode 100644 index 00000000000..7b584828f2f --- /dev/null +++ b/client/src/pages/platform/workflow-editor/utils/updateRootConditionNode.ts @@ -0,0 +1,84 @@ +import {Workflow, WorkflowTask} from '@/shared/middleware/automation/configuration'; +import {NodeType} from '@/shared/types'; +import {Node} from 'reactflow'; + +import {WorkflowTaskDataType} from '../stores/useWorkflowDataStore'; +import getParentConditionTask from './getParentConditionTask'; + +interface UpdateRootConditionNodeProps { + conditionCase: string; + conditionId: string; + nodeIndex: number; + tasks: Array; + updatedParentConditionNodeData: NodeType; + updatedParentConditionTask: WorkflowTask; + nodes: Array; + workflow: Workflow & WorkflowTaskDataType; +} + +export default function updateRootConditionNode({ + nodeIndex, + nodes, + tasks, + updatedParentConditionNodeData, + updatedParentConditionTask, + workflow, +}: UpdateRootConditionNodeProps): NodeType { + let currentTaskNode = updatedParentConditionNodeData; + + let currentTaskNodeConditionData = updatedParentConditionNodeData.conditionData; + + while (currentTaskNodeConditionData) { + const parentConditionTask = getParentConditionTask(tasks, currentTaskNodeConditionData.conditionId); + + if (!parentConditionTask) { + break; + } + + const parentConditionTaskNode = nodes.find((node) => node.id === parentConditionTask.name); + + if (!parentConditionTaskNode) { + break; + } + + console.log('parentConditionTaskNode: ', parentConditionTaskNode); + + const currentConditionCase = currentTaskNodeConditionData.conditionCase; + + const parentConditionCaseTasks: Array = + parentConditionTaskNode.data.parameters[currentConditionCase] || []; + + const workflowTasks = workflow.tasks; + + let currentTask = workflowTasks?.find((task) => task.name === currentTaskNode.id); + + if (!currentTask) { + currentTask = updatedParentConditionTask; + } + + const currentTaskIndex = parentConditionCaseTasks.findIndex((task) => task.name === currentTask.name); + + if (currentTaskIndex > -1) { + parentConditionCaseTasks[currentTaskIndex] = currentTask; + } else { + parentConditionCaseTasks[nodeIndex] = currentTask; + } + + parentConditionTaskNode.data.parameters = { + ...parentConditionTaskNode.data.parameters, + [currentConditionCase]: parentConditionCaseTasks, + }; + + currentTaskNode = { + ...parentConditionTaskNode, + name: parentConditionTaskNode.id, + type: parentConditionTaskNode.type || 'workflow', + version: parentConditionTaskNode.data.type.split('/v')[1], + workflowNodeName: parentConditionTaskNode.id, + }; + + currentTaskNodeConditionData = parentConditionTaskNode.data.conditionData; + } + + return currentTaskNode; +} diff --git a/client/src/shared/types.ts b/client/src/shared/types.ts index 736ae945f46..b6b05d6d08a 100644 --- a/client/src/shared/types.ts +++ b/client/src/shared/types.ts @@ -24,6 +24,7 @@ import { } from '@/shared/middleware/platform/configuration'; import {UseMutationResult} from '@tanstack/react-query'; import {ReactNode} from 'react'; +import {Node} from 'reactflow'; export type DataPillType = { componentName?: string; @@ -125,8 +126,14 @@ export type NodeDataType = { export type NodeType = { componentName?: string; + conditionData?: { + conditionCase: string; + conditionId: string; + index: number; + }; connections?: Array; connectionId?: number; + data: NodeDataType; displayConditions?: { // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: boolean; @@ -151,7 +158,7 @@ export type NodeType = { type: string; version: number; workflowNodeName: string; -}; +} & Node; export type SubPropertyType = PropertyAllType & {custom: boolean};