diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx index 12cc5e7b34e..7cafc819fba 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent.tsx @@ -1,4 +1,8 @@ +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowActionType, WorkflowWithCurrentVersion, @@ -10,6 +14,9 @@ import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep'; import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState'; import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; import { useFilteredOtherActions } from '@/workflow/workflow-steps/workflow-actions/hooks/useFilteredOtherActions'; +import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; +import { useSetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; import { MenuItemCommand } from 'twenty-ui/navigation'; @@ -30,22 +37,50 @@ export const CommandMenuWorkflowSelectActionContent = ({ const [workflowInsertStepIds, setWorkflowInsertStepIds] = useRecoilComponentState(workflowInsertStepIdsComponentState); + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu(); + const handleCreateStep = async (actionType: WorkflowActionType) => { + if (!isDefined(workflowVisualizerWorkflowId)) { + throw new Error( + 'Workflow ID must be configured for the edge when creating a step', + ); + } + const { parentStepId, nextStepId, position } = workflowInsertStepIds; - await createStep({ + const createdStep = await createStep({ newStepType: actionType, parentStepId, nextStepId, position, }); + if (!isDefined(createdStep)) { + return; + } + setWorkflowInsertStepIds({ parentStepId: undefined, nextStepId: undefined, position: undefined, }); + closeRightClickMenu(); + + setCommandMenuNavigationStack([]); + + openWorkflowEditStepInCommandMenu( + workflowVisualizerWorkflowId, + createdStep.name, + getIcon(getActionIcon(createdStep.type as WorkflowActionType)), + ); }; return ( diff --git a/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts b/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts index a9f3af0dfb1..3c7fe1d723b 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts @@ -112,7 +112,13 @@ export const useRunWorkflowRunOpeningInCommandMenuSideEffects = () => { recordId, }), }), - (steps) => [...steps, stepToOpenByDefault.id], + (steps) => [ + ...steps, + { + stepId: stepToOpenByDefault.id, + isInRightDrawer: true, + }, + ], ); openWorkflowRunViewStepInCommandMenu({ workflowId: workflowRunRecord.workflowId, 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 1438d9a3415..e3975b03959 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 @@ -4,7 +4,7 @@ import { WorkflowDiagramBlankEdge } from '@/workflow/workflow-diagram/components import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; import { WorkflowDiagramCanvasEditableEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect'; import { WorkflowDiagramDefaultEdgeEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable'; -import { WorkflowDiagramEmptyTrigger } from '@/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger'; +import { WorkflowDiagramEmptyTriggerEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerEditable'; import { WorkflowDiagramFilterEdgeEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable'; import { WorkflowDiagramFilteringDisabledEdgeEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilteringDisabledEdgeEditable'; import { WorkflowDiagramStepNodeEditable } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable'; @@ -19,7 +19,7 @@ import { useCreateEdge } from '@/workflow/workflow-steps/hooks/useCreateEdge'; import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge'; import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; -import { Connection, ReactFlowProvider, addEdge } from '@xyflow/react'; +import { addEdge, Connection, ReactFlowProvider } from '@xyflow/react'; import React from 'react'; import { isDefined } from 'twenty-shared/utils'; @@ -118,7 +118,7 @@ export const WorkflowDiagramCanvasEditable = ({ { - const { getIcon } = useIcons(); - - const { - openWorkflowTriggerTypeInCommandMenu, - openWorkflowEditStepInCommandMenu, - } = useWorkflowCommandMenu(); - const { setEdgeSelected, clearEdgeSelection } = useEdgeSelected(); const isWorkflowBranchEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_WORKFLOW_BRANCH_ENABLED, ); - const setWorkflowSelectedNode = useSetRecoilComponentState( - workflowSelectedNodeComponentState, - ); - - const setCommandMenuNavigationStack = useSetRecoilState( - commandMenuNavigationStackState, - ); - - const { isInRightDrawer } = useContext(ActionMenuContext); - - const workflowVisualizerWorkflowId = useRecoilComponentValue( - workflowVisualizerWorkflowIdComponentState, - ); - - const handleSelectedNodes = useCallback( - ({ nodes }: OnSelectionChangeParams) => { - const selectedNode = nodes[0] as WorkflowDiagramNode | undefined; - - if (!isInRightDrawer) { - setCommandMenuNavigationStack([]); - } - - if (!isDefined(selectedNode)) { - return; - } - - const isEmptyTriggerNode = selectedNode.type === EMPTY_TRIGGER_STEP_ID; - if (isEmptyTriggerNode) { - if (isDefined(workflowVisualizerWorkflowId)) { - openWorkflowTriggerTypeInCommandMenu(workflowVisualizerWorkflowId); - return; - } - - return; - } - - const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData; - - setWorkflowSelectedNode(selectedNode.id); - - if (isDefined(workflowVisualizerWorkflowId)) { - openWorkflowEditStepInCommandMenu( - workflowVisualizerWorkflowId, - selectedNodeData.name, - getIcon(getWorkflowNodeIconKey(selectedNodeData)), - ); - - return; - } - }, - [ - isInRightDrawer, - setCommandMenuNavigationStack, - workflowVisualizerWorkflowId, - openWorkflowTriggerTypeInCommandMenu, - openWorkflowEditStepInCommandMenu, - getIcon, - setWorkflowSelectedNode, - ], - ); - const handleSelectedEdges = useCallback( ({ edges }: OnSelectionChangeParams) => { if (!isWorkflowBranchEnabled) { @@ -119,19 +33,9 @@ export const WorkflowDiagramCanvasEditableEffect = () => { [isWorkflowBranchEnabled, setEdgeSelected, clearEdgeSelection], ); - const handleSelectionChange = useCallback( - (onSelectionChangeParams: OnSelectionChangeParams) => { - handleSelectedNodes(onSelectionChangeParams); - handleSelectedEdges(onSelectionChangeParams); - }, - [handleSelectedNodes, handleSelectedEdges], - ); - useOnSelectionChange({ - onChange: handleSelectionChange, + onChange: handleSelectedEdges, }); - useTriggerNodeSelection(); - return null; }; 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 e103e640b44..1eede1062cb 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 @@ -1,8 +1,7 @@ import { WorkflowVersionStatus } from '@/workflow/types/Workflow'; import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase'; -import { WorkflowDiagramCanvasReadonlyEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect'; import { WorkflowDiagramDefaultEdgeReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeReadonly'; -import { WorkflowDiagramEmptyTrigger } from '@/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger'; +import { WorkflowDiagramEmptyTriggerReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerReadonly'; import { WorkflowDiagramFilterEdgeReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly'; import { WorkflowDiagramFilteringDisabledEdgeReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilteringDisabledEdgeReadonly'; import { WorkflowDiagramStepNodeReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeReadonly'; @@ -23,7 +22,7 @@ export const WorkflowDiagramCanvasReadonly = ({ - - ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect.tsx deleted file mode 100644 index 32a3496a5b9..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect.tsx +++ /dev/null @@ -1,76 +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 { useTriggerNodeSelection } from '@/workflow/workflow-diagram/hooks/useTriggerNodeSelection'; -import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; -import { - WorkflowDiagramNode, - WorkflowDiagramStepNodeData, -} from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; -import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react'; -import { useCallback } from 'react'; -import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; - -export const WorkflowDiagramCanvasReadonlyEffect = () => { - const { getIcon } = useIcons(); - const setWorkflowSelectedNode = useSetRecoilComponentState( - workflowSelectedNodeComponentState, - ); - const { openWorkflowViewStepInCommandMenu } = useWorkflowCommandMenu(); - - const workflowVisualizerWorkflowId = useRecoilComponentValue( - workflowVisualizerWorkflowIdComponentState, - ); - const workflowVisualizerWorkflowVersionId = useRecoilComponentValue( - workflowVisualizerWorkflowVersionIdComponentState, - ); - - const handleSelectionChange = useCallback( - ({ nodes }: OnSelectionChangeParams) => { - if ( - !( - isDefined(workflowVisualizerWorkflowId) && - isDefined(workflowVisualizerWorkflowVersionId) - ) - ) { - return; - } - - const selectedNode = nodes[0] as WorkflowDiagramNode | undefined; - - if (!isDefined(selectedNode)) { - return; - } - - setWorkflowSelectedNode(selectedNode.id); - - const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData; - - openWorkflowViewStepInCommandMenu({ - workflowId: workflowVisualizerWorkflowId, - workflowVersionId: workflowVisualizerWorkflowVersionId, - title: selectedNodeData.name, - icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)), - }); - }, - [ - setWorkflowSelectedNode, - openWorkflowViewStepInCommandMenu, - workflowVisualizerWorkflowId, - workflowVisualizerWorkflowVersionId, - getIcon, - ], - ); - - useOnSelectionChange({ - onChange: handleSelectionChange, - }); - - useTriggerNodeSelection(); - - return null; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx index 1a51318de09..6e49213c09f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeEditable.tsx @@ -1,19 +1,23 @@ +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 { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge'; +import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup'; import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; +import { useEdgeHovered } from '@/workflow/workflow-diagram/hooks/useEdgeHovered'; import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { WorkflowDiagramEdge } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep'; -import { useEdgeHovered } from '@/workflow/workflow-diagram/hooks/useEdgeHovered'; import { EdgeLabelRenderer, EdgeProps, getBezierPath } from '@xyflow/react'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { IconFilter, IconPlus } from 'twenty-ui/display'; -import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge'; -import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup'; type WorkflowDiagramDefaultEdgeEditableProps = EdgeProps; @@ -28,6 +32,8 @@ export const WorkflowDiagramDefaultEdgeEditable = ({ markerStart, markerEnd, }: WorkflowDiagramDefaultEdgeEditableProps) => { + const { isInRightDrawer } = useContext(ActionMenuContext); + const { isEdgeHovered } = useEdgeHovered(); const [edgePath, labelX, labelY] = getBezierPath({ @@ -51,6 +57,10 @@ export const WorkflowDiagramDefaultEdgeEditable = ({ nextStepId: target, }); + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + const { openWorkflowEditFilterInCommandMenu } = useOpenWorkflowEditFilterInCommandMenu(); @@ -65,6 +75,10 @@ export const WorkflowDiagramDefaultEdgeEditable = ({ return; } + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + openWorkflowEditFilterInCommandMenu({ stepId: createdStep.id, stepName: createdStep.name, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx deleted file mode 100644 index 1dd4710f498..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; -import styled from '@emotion/styled'; - -const StyledStepNodeLabelIconContainer = styled.div` - align-items: center; - background: ${({ theme }) => theme.background.transparent.light}; - border-radius: ${({ theme }) => theme.spacing(1)}; - display: flex; - justify-content: center; - padding: ${({ theme }) => theme.spacing(3)}; -`; - -export const WorkflowDiagramEmptyTrigger = () => { - return ( - } - /> - ); -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerEditable.tsx new file mode 100644 index 00000000000..b3f2fd5da7a --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerEditable.tsx @@ -0,0 +1,57 @@ +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; +import styled from '@emotion/styled'; +import { useLingui } from '@lingui/react/macro'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; + +const StyledStepNodeLabelIconContainer = styled.div` + align-items: center; + background: ${({ theme }) => theme.background.transparent.light}; + border-radius: ${({ theme }) => theme.spacing(1)}; + display: flex; + justify-content: center; + padding: ${({ theme }) => theme.spacing(3)}; +`; + +export const WorkflowDiagramEmptyTriggerEditable = () => { + const { t } = useLingui(); + + const { openWorkflowTriggerTypeInCommandMenu } = useWorkflowCommandMenu(); + + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + + const { isInRightDrawer } = useContext(ActionMenuContext); + + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + + return ( + } + onClick={() => { + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + + if (!isDefined(workflowVisualizerWorkflowId)) { + return; + } + + openWorkflowTriggerTypeInCommandMenu(workflowVisualizerWorkflowId); + }} + /> + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerReadonly.tsx new file mode 100644 index 00000000000..1a26f065ccf --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTriggerReadonly.tsx @@ -0,0 +1,82 @@ +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; +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 { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; +import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; +import styled from '@emotion/styled'; +import { useLingui } from '@lingui/react/macro'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; + +const StyledStepNodeLabelIconContainer = styled.div` + align-items: center; + background: ${({ theme }) => theme.background.transparent.light}; + border-radius: ${({ theme }) => theme.spacing(1)}; + display: flex; + justify-content: center; + padding: ${({ theme }) => theme.spacing(3)}; +`; + +export const WorkflowDiagramEmptyTriggerReadonly = () => { + const { getIcon } = useIcons(); + const { t } = useLingui(); + + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + const workflowVisualizerWorkflowVersionId = useRecoilComponentValue( + workflowVisualizerWorkflowVersionIdComponentState, + ); + + const { isInRightDrawer } = useContext(ActionMenuContext); + + const { openWorkflowViewStepInCommandMenu } = useWorkflowCommandMenu(); + + const setWorkflowSelectedNode = useSetRecoilComponentState( + workflowSelectedNodeComponentState, + ); + + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + + return ( + } + onClick={() => { + if ( + !isDefined(workflowVisualizerWorkflowId) || + !isDefined(workflowVisualizerWorkflowVersionId) + ) { + throw new Error( + 'Workflow ID and Version ID must be defined to open the command menu.', + ); + } + + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + + setWorkflowSelectedNode(TRIGGER_STEP_ID); + + openWorkflowViewStepInCommandMenu({ + workflowId: workflowVisualizerWorkflowId, + workflowVersionId: workflowVisualizerWorkflowVersionId, + title: t`Add a Trigger`, + icon: getIcon(null), + }); + }} + /> + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx index 42ded5cd9b8..7f17362082c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx @@ -1,3 +1,5 @@ +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown'; import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent'; import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer'; @@ -9,9 +11,12 @@ import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/ho import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge'; +import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup'; import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; +import { useEdgeHovered } from '@/workflow/workflow-diagram/hooks/useEdgeHovered'; import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { workflowDiagramPanOnDragComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramPanOnDragComponentState'; @@ -26,6 +31,8 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; import { EdgeLabelRenderer, EdgeProps, getBezierPath } from '@xyflow/react'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { IconDotsVertical, @@ -35,9 +42,6 @@ import { } from 'twenty-ui/display'; import { IconButtonGroup } from 'twenty-ui/input'; import { MenuItem } from 'twenty-ui/navigation'; -import { useEdgeHovered } from '@/workflow/workflow-diagram/hooks/useEdgeHovered'; -import { WorkflowDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseEdge'; -import { WorkflowDiagramEdgeButtonGroup } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeButtonGroup'; type WorkflowDiagramFilterEdgeEditableProps = EdgeProps; @@ -94,10 +98,15 @@ export const WorkflowDiagramFilterEdgeEditable = ({ workflowVisualizerWorkflowIdComponentState, ); const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId); + const { isInRightDrawer } = useContext(ActionMenuContext); const { deleteStep } = useDeleteStep({ workflow }); const { startNodeCreation, isNodeCreationStarted } = useStartNodeCreation(); + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + const { openDropdown } = useOpenDropdown(); const { closeDropdown } = useCloseDropdown(); @@ -130,6 +139,10 @@ export const WorkflowDiagramFilterEdgeEditable = ({ useOpenWorkflowEditFilterInCommandMenu(); const handleFilterButtonClick = () => { + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + openWorkflowEditFilterInCommandMenu({ stepId: data.stepId, stepName: data.name, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx index e98a34b9d35..d2d44a1ae53 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx @@ -1,3 +1,5 @@ +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 { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; @@ -18,6 +20,8 @@ import { EdgeProps, getBezierPath, } from '@xyflow/react'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; import { IconFilter } from 'twenty-ui/display'; import { IconButtonGroup } from 'twenty-ui/input'; @@ -62,6 +66,8 @@ export const WorkflowDiagramFilterEdgeReadonly = ({ }: WorkflowDiagramFilterEdgeReadonlyProps) => { assertFilterEdgeDataOrThrow(data); + const { isInRightDrawer } = useContext(ActionMenuContext); + const theme = useTheme(); const [edgePath, labelX, labelY] = getBezierPath({ @@ -81,7 +87,15 @@ export const WorkflowDiagramFilterEdgeReadonly = ({ const { openWorkflowViewFilterInCommandMenu } = useOpenWorkflowViewFilterInCommandMenu(); + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + const handleFilterButtonClick = () => { + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + openWorkflowViewFilterInCommandMenu({ stepId: data.stepId, stepName: data.name, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx index 24878eeb320..2d71aa06e12 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx @@ -150,6 +150,7 @@ export const WorkflowDiagramStepNodeBase = ({ RightFloatingElement, BottomHoverFloatingElement, displayHandle = true, + onClick, onMouseEnter, onMouseLeave, }: { @@ -161,6 +162,7 @@ export const WorkflowDiagramStepNodeBase = ({ RightFloatingElement?: React.ReactNode; BottomHoverFloatingElement?: React.ReactNode; displayHandle?: boolean; + onClick?: () => void; onMouseEnter?: (event: MouseEvent) => void; onMouseLeave?: (event: MouseEvent) => void; }) => { @@ -172,6 +174,7 @@ export const WorkflowDiagramStepNodeBase = ({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx index 323f40e9d01..c8b0a06bc11 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditable.tsx @@ -1,10 +1,20 @@ +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined'; import { WorkflowDiagramStepNodeEditableContent } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; +import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; export const WorkflowDiagramStepNodeEditable = ({ id, @@ -15,6 +25,8 @@ export const WorkflowDiagramStepNodeEditable = ({ data: WorkflowDiagramStepNodeData; selected?: boolean; }) => { + const { getIcon } = useIcons(); + const workflowVisualizerWorkflowId = useRecoilComponentValue( workflowVisualizerWorkflowIdComponentState, ); @@ -28,12 +40,41 @@ export const WorkflowDiagramStepNodeEditable = ({ workflow: workflowWithCurrentVersion, }); + const setWorkflowSelectedNode = useSetRecoilComponentState( + workflowSelectedNodeComponentState, + ); + + const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu(); + + const { isInRightDrawer } = useContext(ActionMenuContext); + + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + return ( { + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + + setWorkflowSelectedNode(id); + + if (isDefined(workflowVisualizerWorkflowId)) { + openWorkflowEditStepInCommandMenu( + workflowVisualizerWorkflowId, + data.name, + getIcon(getWorkflowNodeIconKey(data)), + ); + + return; + } + }} onDelete={() => { deleteStep(id); }} diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx index 862ca7c126b..2c46b78cc98 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx @@ -1,13 +1,13 @@ +import { WorkflowDiagramCreateStepElement } from '@/workflow/workflow-diagram/components/WorkflowDiagramCreateStepElement'; import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeIcon'; +import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; -import { FloatingIconButton } from 'twenty-ui/input'; -import { IconTrash } from 'twenty-ui/display'; -import { WorkflowDiagramCreateStepElement } from '@/workflow/workflow-diagram/components/WorkflowDiagramCreateStepElement'; -import React, { useState } from 'react'; import styled from '@emotion/styled'; -import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; +import { useState } from 'react'; +import { IconTrash } from 'twenty-ui/display'; +import { FloatingIconButton } from 'twenty-ui/input'; const StyledDeleteButtonContainer = styled.div` display: flex; @@ -39,12 +39,14 @@ export const WorkflowDiagramStepNodeEditableContent = ({ selected, variant, onDelete, + onClick, }: { id: string; data: WorkflowDiagramStepNodeData; variant: WorkflowDiagramNodeVariant; selected: boolean; onDelete: () => void; + onClick?: () => void; }) => { const [isHovered, setIsHovered] = useState(false); @@ -60,6 +62,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({ { + const { getIcon } = useIcons(); + + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + const workflowVisualizerWorkflowVersionId = useRecoilComponentValue( + workflowVisualizerWorkflowVersionIdComponentState, + ); + + const setWorkflowSelectedNode = useSetRecoilComponentState( + workflowSelectedNodeComponentState, + ); + + const { openWorkflowViewStepInCommandMenu } = useWorkflowCommandMenu(); + + const { isInRightDrawer } = useContext(ActionMenuContext); + + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + return ( } displayHandle={false} + onClick={() => { + if ( + !isDefined(workflowVisualizerWorkflowId) || + !isDefined(workflowVisualizerWorkflowVersionId) + ) { + throw new Error('Workflow ID and Version ID must be defined'); + } + + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + + setWorkflowSelectedNode(id); + + openWorkflowViewStepInCommandMenu({ + workflowId: workflowVisualizerWorkflowId, + workflowVersionId: workflowVisualizerWorkflowVersionId, + title: data.name, + icon: getIcon(getWorkflowNodeIconKey(data)), + }); + }} /> ); }; 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 ecb37618ba0..dc6a679082a 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 @@ -3,9 +3,7 @@ import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/component import { WorkflowDiagramDefaultEdgeRun } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdgeRun'; import { WorkflowDiagramFilterEdgeRun } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun'; import { WorkflowDiagramFilteringDisabledEdgeRun } from '@/workflow/workflow-diagram/components/WorkflowDiagramFilteringDisabledEdgeRun'; -import { WorkflowDiagramStepNodeReadonly } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeReadonly'; -import { WorkflowRunDiagramCanvasEffect } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramCanvasEffect'; -import { useHandleWorkflowRunDiagramCanvasInit } from '@/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit'; +import { WorkflowRunDiagramStepNode } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramStepNode'; import { getWorkflowRunStatusTagProps } from '@/workflow/workflow-diagram/utils/getWorkflowRunStatusTagProps'; import { ReactFlowProvider } from '@xyflow/react'; @@ -18,14 +16,11 @@ export const WorkflowRunDiagramCanvas = ({ workflowRunStatus, }); - const { handleWorkflowRunDiagramCanvasInit } = - useHandleWorkflowRunDiagramCanvasInit(); - return ( - - ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvasEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvasEffect.tsx deleted file mode 100644 index e2f20479510..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvasEffect.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; -import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; -import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState'; -import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; -import { WorkflowRunDiagramNode } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; -import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react'; -import { useRecoilCallback } from 'recoil'; -import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; - -export const WorkflowRunDiagramCanvasEffect = () => { - const { getIcon } = useIcons(); - - const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu(); - - const workflowRunId = useWorkflowRunIdOrThrow(); - - const workflowVisualizerWorkflowIdState = useRecoilComponentCallbackState( - workflowVisualizerWorkflowIdComponentState, - ); - const workflowDiagramStatusState = useRecoilComponentCallbackState( - workflowDiagramStatusComponentState, - ); - const workflowSelectedNodeState = useRecoilComponentCallbackState( - workflowSelectedNodeComponentState, - ); - - const handleSelectionChange = useRecoilCallback( - ({ snapshot, set }) => - ({ nodes }: OnSelectionChangeParams) => { - const workflowId = getSnapshotValue( - snapshot, - workflowVisualizerWorkflowIdState, - ); - - if (!isDefined(workflowId)) { - throw new Error('Expected the workflowId to be defined.'); - } - - const workflowDiagramStatus = getSnapshotValue( - snapshot, - workflowDiagramStatusState, - ); - - // The `handleSelectionChange` function is called when the diagram initializes and - // a node is selected. In this case, we don't want to execute the rest of this function. - // We open the Side PanelĀ® synchronously after ReactFlow is initialized and a node is selected, - // animations perform better that way. - if (workflowDiagramStatus !== 'done') { - return; - } - - const selectedNode = nodes[0] as WorkflowRunDiagramNode | undefined; - - if (!isDefined(selectedNode)) { - return; - } - - set(workflowSelectedNodeState, selectedNode.id); - - const selectedNodeData = selectedNode.data; - - openWorkflowRunViewStepInCommandMenu({ - workflowId, - workflowRunId, - title: selectedNodeData.name, - icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)), - workflowSelectedNode: selectedNode.id, - stepExecutionStatus: selectedNodeData.runStatus, - }); - }, - [ - workflowVisualizerWorkflowIdState, - workflowDiagramStatusState, - workflowSelectedNodeState, - openWorkflowRunViewStepInCommandMenu, - workflowRunId, - getIcon, - ], - ); - - useOnSelectionChange({ - onChange: handleSelectionChange, - }); - - return null; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramStepNode.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramStepNode.tsx new file mode 100644 index 00000000000..791bba9fefa --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramStepNode.tsx @@ -0,0 +1,75 @@ +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; +import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; +import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; +import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeIcon'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; +import { WorkflowRunDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; +import { getNodeVariantFromStepRunStatus } from '@/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus'; +import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; +import { useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; + +export const WorkflowRunDiagramStepNode = ({ + id, + data, +}: { + id: string; + data: WorkflowRunDiagramStepNodeData; +}) => { + const { getIcon } = useIcons(); + + const workflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + const workflowRunId = useWorkflowRunIdOrThrow(); + + const setWorkflowSelectedNode = useSetRecoilComponentState( + workflowSelectedNodeComponentState, + ); + + const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu(); + + const { isInRightDrawer } = useContext(ActionMenuContext); + + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + + return ( + } + displayHandle={false} + onClick={() => { + if (!isDefined(workflowId)) { + throw new Error('Workflow ID must be defined'); + } + + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + + setWorkflowSelectedNode(id); + + openWorkflowRunViewStepInCommandMenu({ + workflowId, + workflowRunId, + title: data.name, + icon: getIcon(getWorkflowNodeIconKey(data)), + workflowSelectedNode: id, + stepExecutionStatus: data.runStatus, + }); + }} + /> + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx index 446706d8c84..47320367a12 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect.tsx @@ -13,7 +13,6 @@ import { WorkflowRunState } from '@/workflow/types/Workflow'; import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState'; import { workflowRunDiagramAutomaticallyOpenedStepsComponentState } from '@/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState'; -import { workflowRunStepToOpenByDefaultComponentState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { generateWorkflowRunDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowRunDiagram'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; @@ -52,9 +51,6 @@ export const WorkflowRunVisualizerEffect = ({ const workflowDiagramStatusState = useRecoilComponentCallbackState( workflowDiagramStatusComponentState, ); - const workflowRunStepToOpenByDefaultState = useRecoilComponentCallbackState( - workflowRunStepToOpenByDefaultComponentState, - ); const workflowSelectedNodeState = useRecoilComponentCallbackState( workflowSelectedNodeComponentState, ); @@ -126,67 +122,70 @@ export const WorkflowRunVisualizerEffect = ({ isWorkflowFilteringEnabled, }); - if (isDefined(stepToOpenByDefault)) { - if (isInRightDrawer) { - set(workflowDiagramState, baseWorkflowRunDiagram); - - const workflowRunDiagramAutomaticallyOpenedSteps = getSnapshotValue( - snapshot, - workflowRunDiagramAutomaticallyOpenedStepsState, - ); - const hasStepAlreadyBeenOpenedAutomatically = - workflowRunDiagramAutomaticallyOpenedSteps.includes( - stepToOpenByDefault.id, - ); - - if ( - workflowDiagramStatus === 'done' && - !hasStepAlreadyBeenOpenedAutomatically - ) { - set(workflowSelectedNodeState, stepToOpenByDefault.id); - - const workflowVisualizerWorkflowId = getSnapshotValue( - snapshot, - workflowVisualizerWorkflowIdState, - ); - if (!isDefined(workflowVisualizerWorkflowId)) { - throw new Error( - 'The workflow id must be set; ensure the workflow id is always set before rendering the workflow diagram.', - ); - } - - set(workflowRunDiagramAutomaticallyOpenedStepsState, [ - ...workflowRunDiagramAutomaticallyOpenedSteps, - stepToOpenByDefault.id, - ]); - openWorkflowRunViewStepInCommandMenu({ - workflowId: workflowVisualizerWorkflowId, - workflowRunId, - title: stepToOpenByDefault.data.name, - icon: getIcon(getWorkflowNodeIconKey(stepToOpenByDefault.data)), - workflowSelectedNode: stepToOpenByDefault.id, - stepExecutionStatus: stepToOpenByDefault.data.runStatus, - }); - } - } else { - const workflowRunDiagram = selectWorkflowDiagramNode({ - diagram: baseWorkflowRunDiagram, - nodeIdToSelect: stepToOpenByDefault.id, - }); - - set(workflowDiagramState, workflowRunDiagram); - set(workflowRunStepToOpenByDefaultState, { - id: stepToOpenByDefault.id, - data: stepToOpenByDefault.data, - }); - } - } else { - set(workflowDiagramState, baseWorkflowRunDiagram); - } - if (workflowDiagramStatus !== 'done') { set(workflowDiagramStatusState, 'computing-dimensions'); } + + if (!isDefined(stepToOpenByDefault)) { + set(workflowDiagramState, baseWorkflowRunDiagram); + + return; + } + + const workflowRunDiagramAutomaticallyOpenedSteps = getSnapshotValue( + snapshot, + workflowRunDiagramAutomaticallyOpenedStepsState, + ); + const hasStepAlreadyBeenOpenedAutomatically = + workflowRunDiagramAutomaticallyOpenedSteps.some( + (step) => + step.stepId === stepToOpenByDefault.id && + step.isInRightDrawer === isInRightDrawer, + ); + + const workflowVisualizerWorkflowId = getSnapshotValue( + snapshot, + workflowVisualizerWorkflowIdState, + ); + if (!isDefined(workflowVisualizerWorkflowId)) { + throw new Error( + 'The workflow id must be set; ensure the workflow id is always set before rendering the workflow diagram.', + ); + } + + if (isInRightDrawer) { + set(workflowDiagramState, baseWorkflowRunDiagram); + } else { + const workflowRunDiagram = selectWorkflowDiagramNode({ + diagram: baseWorkflowRunDiagram, + nodeIdToSelect: stepToOpenByDefault.id, + }); + + set(workflowDiagramState, workflowRunDiagram); + } + + if (hasStepAlreadyBeenOpenedAutomatically) { + return; + } + + set(workflowSelectedNodeState, stepToOpenByDefault.id); + + set(workflowRunDiagramAutomaticallyOpenedStepsState, [ + ...workflowRunDiagramAutomaticallyOpenedSteps, + { + stepId: stepToOpenByDefault.id, + isInRightDrawer, + }, + ]); + + openWorkflowRunViewStepInCommandMenu({ + workflowId: workflowVisualizerWorkflowId, + workflowRunId, + title: stepToOpenByDefault.data.name, + icon: getIcon(getWorkflowNodeIconKey(stepToOpenByDefault.data)), + workflowSelectedNode: stepToOpenByDefault.id, + stepExecutionStatus: stepToOpenByDefault.data.runStatus, + }); }, [ flowState, @@ -197,7 +196,6 @@ export const WorkflowRunVisualizerEffect = ({ workflowDiagramStatusState, workflowRunDiagramAutomaticallyOpenedStepsState, workflowRunId, - workflowRunStepToOpenByDefaultState, workflowSelectedNodeState, workflowVisualizerWorkflowIdState, ], diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramEmptyTrigger.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramEmptyTriggerEditable.stories.tsx similarity index 76% rename from packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramEmptyTrigger.stories.tsx rename to packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramEmptyTriggerEditable.stories.tsx index 0960cf80542..9b2ada63777 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramEmptyTrigger.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramEmptyTriggerEditable.stories.tsx @@ -1,24 +1,26 @@ import { Meta, StoryObj } from '@storybook/react'; -import '@xyflow/react/dist/style.css'; -import { ReactflowDecorator } from '~/testing/decorators/ReactflowDecorator'; -import { WorkflowDiagramEmptyTrigger } from '../WorkflowDiagramEmptyTrigger'; -import { ComponentDecorator } from 'twenty-ui/testing'; -import { RecoilRoot } from 'recoil'; import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; +import '@xyflow/react/dist/style.css'; +import { RecoilRoot } from 'recoil'; +import { ComponentDecorator } from 'twenty-ui/testing'; +import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; +import { ReactflowDecorator } from '~/testing/decorators/ReactflowDecorator'; +import { WorkflowDiagramEmptyTriggerEditable } from '../WorkflowDiagramEmptyTriggerEditable'; -const meta: Meta = { - title: 'Modules/Workflow/WorkflowDiagramEmptyTrigger', - component: WorkflowDiagramEmptyTrigger, +const meta: Meta = { + title: 'Modules/Workflow/WorkflowDiagramEmptyTriggerEditable', + component: WorkflowDiagramEmptyTriggerEditable, args: { data: { nodeType: 'empty-trigger', }, }, + decorators: [I18nFrontDecorator], }; export default meta; -type Story = StoryObj; +type Story = StoryObj; export const Default: Story = { decorators: [ diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/CreateStepNodeWidth.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/CreateStepNodeWidth.ts deleted file mode 100644 index c03e587e727..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/constants/CreateStepNodeWidth.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBorderWidth'; - -export const STEP_ICON_WIDTH = 24; - -const STEP_PADDING = 8; - -export const CREATE_STEP_NODE_WIDTH = - STEP_ICON_WIDTH + STEP_PADDING + NODE_BORDER_WIDTH; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/__tests__/useTriggerNodeSelection.test.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/__tests__/useTriggerNodeSelection.test.tsx deleted file mode 100644 index 40fb6ca1a35..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/__tests__/useTriggerNodeSelection.test.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; -import { useTriggerNodeSelection } from '@/workflow/workflow-diagram/hooks/useTriggerNodeSelection'; -import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; -import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState'; -import { act, renderHook } from '@testing-library/react'; -import { useReactFlow } from '@xyflow/react'; -import { RecoilRoot } from 'recoil'; - -jest.mock('@xyflow/react', () => ({ - useReactFlow: jest.fn(), -})); - -const wrapper = ({ children }: { children: React.ReactNode }) => ( - - - {children} - - -); - -describe('useTriggerNodeSelection', () => { - const mockSetNodes = jest.fn(); - - beforeEach(() => { - (useReactFlow as jest.Mock).mockReturnValue({ - setNodes: mockSetNodes, - }); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - it('should trigger node selection', () => { - const { result } = renderHook( - () => { - const [ - workflowDiagramTriggerNodeSelection, - setWorkflowDiagramTriggerNodeSelection, - ] = useRecoilComponentState( - workflowDiagramTriggerNodeSelectionComponentState, - ); - - useTriggerNodeSelection(); - - return { - workflowDiagramTriggerNodeSelection, - setWorkflowDiagramTriggerNodeSelection, - }; - }, - { - wrapper, - }, - ); - - const mockNodeId = 'test-node-id'; - - act(() => { - result.current.setWorkflowDiagramTriggerNodeSelection(mockNodeId); - }); - - expect(result.current.workflowDiagramTriggerNodeSelection).toBeUndefined(); - - expect(mockSetNodes).toHaveBeenCalled(); - }); - - it('should not trigger update if state is not defined', () => { - renderHook(() => useTriggerNodeSelection(), { - wrapper, - }); - - // Ensure updateNode is not called when state is undefined - expect(mockSetNodes).not.toHaveBeenCalled(); - }); -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit.ts deleted file mode 100644 index 5167931319a..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; -import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; -import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState'; -import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; -import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; -import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState'; -import { workflowRunDiagramAutomaticallyOpenedStepsComponentState } from '@/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState'; -import { workflowRunStepToOpenByDefaultComponentState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState'; -import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; -import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; -import { useContext } from 'react'; -import { useRecoilCallback } from 'recoil'; -import { isDefined } from 'twenty-shared/utils'; -import { useIcons } from 'twenty-ui/display'; - -export const useHandleWorkflowRunDiagramCanvasInit = () => { - const { getIcon } = useIcons(); - - const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu(); - const { isInRightDrawer } = useContext(ActionMenuContext); - - const workflowRunId = useWorkflowRunIdOrThrow(); - - const workflowVisualizerWorkflowIdState = useRecoilComponentCallbackState( - workflowVisualizerWorkflowIdComponentState, - ); - const workflowDiagramStatusState = useRecoilComponentCallbackState( - workflowDiagramStatusComponentState, - ); - const workflowRunStepToOpenByDefaultState = useRecoilComponentCallbackState( - workflowRunStepToOpenByDefaultComponentState, - ); - const workflowSelectedNodeState = useRecoilComponentCallbackState( - workflowSelectedNodeComponentState, - ); - const workflowRunDiagramAutomaticallyOpenedStepsState = - useRecoilComponentCallbackState( - workflowRunDiagramAutomaticallyOpenedStepsComponentState, - ); - - const handleWorkflowRunDiagramCanvasInit = useRecoilCallback( - ({ snapshot, set }) => - () => { - const workflowDiagramStatus = getSnapshotValue( - snapshot, - workflowDiagramStatusState, - ); - - if (workflowDiagramStatus !== 'computing-dimensions') { - throw new Error( - 'Sequence error: reactflow should be considered initialized only when the workflow diagram status is computing-dimensions.', - ); - } - - set(workflowDiagramStatusState, 'done'); - - if (isInRightDrawer) { - return; - } - - const workflowStepToOpenByDefault = getSnapshotValue( - snapshot, - workflowRunStepToOpenByDefaultState, - ); - - if (isDefined(workflowStepToOpenByDefault)) { - const workflowVisualizerWorkflowId = getSnapshotValue( - snapshot, - workflowVisualizerWorkflowIdState, - ); - if (!isDefined(workflowVisualizerWorkflowId)) { - throw new Error( - 'The workflow id must be set; ensure the workflow id is always set before rendering the workflow diagram.', - ); - } - - set(workflowSelectedNodeState, workflowStepToOpenByDefault.id); - - const workflowRunDiagramAutomaticallyOpenedSteps = getSnapshotValue( - snapshot, - workflowRunDiagramAutomaticallyOpenedStepsState, - ); - const hasStepAlreadyBeenOpenedAutomatically = - workflowRunDiagramAutomaticallyOpenedSteps.includes( - workflowStepToOpenByDefault.id, - ); - - // FIXME: This is a workaround to avoid opening a workflow run step twice when going from the side panel to the fullscreen show page. - // The step is opened in the `handleSelectionChange` function of `WorkflowRunDiagramCanvasEffect`. I think it shouldn't be opened there but - // we should keep opening the step here, in `handleWorkflowRunDiagramCanvasInit`. - if (!hasStepAlreadyBeenOpenedAutomatically) { - openWorkflowRunViewStepInCommandMenu({ - workflowId: workflowVisualizerWorkflowId, - workflowRunId, - title: workflowStepToOpenByDefault.data.name, - icon: getIcon( - getWorkflowNodeIconKey(workflowStepToOpenByDefault.data), - ), - workflowSelectedNode: workflowStepToOpenByDefault.id, - stepExecutionStatus: workflowStepToOpenByDefault.data.runStatus, - }); - } - - set(workflowRunStepToOpenByDefaultState, undefined); - } - }, - [ - workflowDiagramStatusState, - isInRightDrawer, - workflowRunStepToOpenByDefaultState, - workflowVisualizerWorkflowIdState, - workflowSelectedNodeState, - workflowRunDiagramAutomaticallyOpenedStepsState, - openWorkflowRunViewStepInCommandMenu, - workflowRunId, - getIcon, - ], - ); - - return { - handleWorkflowRunDiagramCanvasInit, - }; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useStartNodeCreation.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useStartNodeCreation.ts index ea9c17811f0..9c7bfe443d6 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useStartNodeCreation.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useStartNodeCreation.ts @@ -1,13 +1,17 @@ -import { useCallback } from 'react'; - +import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState'; +import { useCallback, useContext } from 'react'; +import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const useStartNodeCreation = () => { + const { isInRightDrawer } = useContext(ActionMenuContext); + const [workflowInsertStepIds, setWorkflowInsertStepIds] = useRecoilComponentState(workflowInsertStepIdsComponentState); @@ -17,6 +21,10 @@ export const useStartNodeCreation = () => { workflowVisualizerWorkflowIdComponentState, ); + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, + ); + /** * This function is used in a context where dependencies shouldn't change much. * That's why its wrapped in a `useCallback` hook. Removing memoization might break the app unexpectedly. @@ -33,15 +41,22 @@ export const useStartNodeCreation = () => { }) => { setWorkflowInsertStepIds({ parentStepId, nextStepId, position }); - if (isDefined(workflowVisualizerWorkflowId)) { - openStepSelectInCommandMenu(workflowVisualizerWorkflowId); + if (!isDefined(workflowVisualizerWorkflowId)) { return; } + + if (!isInRightDrawer) { + setCommandMenuNavigationStack([]); + } + + openStepSelectInCommandMenu(workflowVisualizerWorkflowId); }, [ setWorkflowInsertStepIds, workflowVisualizerWorkflowId, + isInRightDrawer, openStepSelectInCommandMenu, + setCommandMenuNavigationStack, ], ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useTriggerNodeSelection.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useTriggerNodeSelection.ts deleted file mode 100644 index 2574829d295..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useTriggerNodeSelection.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentState'; -import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState'; -import { - WorkflowDiagramEdge, - WorkflowDiagramNode, -} from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { useReactFlow } from '@xyflow/react'; -import { useEffect } from 'react'; -import { isDefined } from 'twenty-shared/utils'; - -export const useTriggerNodeSelection = () => { - const reactflow = useReactFlow(); - - const [ - workflowDiagramTriggerNodeSelection, - setWorkflowDiagramTriggerNodeSelection, - ] = useRecoilComponentState( - workflowDiagramTriggerNodeSelectionComponentState, - ); - - useEffect(() => { - if (!isDefined(workflowDiagramTriggerNodeSelection)) { - return; - } - - reactflow.setNodes((nodes) => - nodes.map((node) => ({ - ...node, - selected: workflowDiagramTriggerNodeSelection === node.id, - })), - ); - - setWorkflowDiagramTriggerNodeSelection(undefined); - }, [ - reactflow, - setWorkflowDiagramTriggerNodeSelection, - workflowDiagramTriggerNodeSelection, - ]); -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState.ts deleted file mode 100644 index d99710a29a1..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; -import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; - -export const workflowDiagramTriggerNodeSelectionComponentState = - createComponentState({ - key: 'workflowDiagramTriggerNodeSelectionComponentState', - defaultValue: undefined, - componentInstanceContext: WorkflowVisualizerComponentInstanceContext, - }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState.ts index 1851b1f0295..9cf49e83bfa 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunDiagramAutomaticallyOpenedStepsComponentState.ts @@ -2,7 +2,7 @@ import { createComponentState } from '@/ui/utilities/state/component-state/utils import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; export const workflowRunDiagramAutomaticallyOpenedStepsComponentState = - createComponentState({ + createComponentState<{ stepId: string; isInRightDrawer: boolean }[]>({ key: 'workflowRunDiagramAutomaticallyOpenedStepsComponentState', defaultValue: [], componentInstanceContext: WorkflowVisualizerComponentInstanceContext, diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState.ts deleted file mode 100644 index 60e560f9094..00000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState'; -import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; -import { WorkflowRunDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; - -export const workflowRunStepToOpenByDefaultComponentState = - createComponentState< - | { - id: string; - data: WorkflowRunDiagramStepNodeData; - } - | undefined - >({ - key: 'workflowRunStepToOpenByDefaultComponentState', - defaultValue: undefined, - componentInstanceContext: WorkflowVisualizerComponentInstanceContext, - }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts index 6c70c319f17..c1e7e856832 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useDeleteStep.ts @@ -30,8 +30,6 @@ export const useDeleteStep = ({ const deleteStep = async (stepId: string) => { assertWorkflowWithCurrentVersionIsDefined(workflow); - closeCommandMenu(); - const workflowVersionId = await getUpdatableWorkflowVersion(workflow); if (!isDefined(workflowVersionId)) { @@ -51,6 +49,9 @@ export const useDeleteStep = ({ stepId, }); } + + closeCommandMenu(); + deleteStepOutputSchema({ stepId, workflowVersionId, diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts index 57ea4ab78a6..2c8eed8874d 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/hooks/useVariableDropdown.ts @@ -1,10 +1,16 @@ +import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState'; import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState'; +import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue'; import { useSetRecoilComponentState } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentState'; -import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { getVariableTemplateFromPath } from '@/workflow/workflow-variables/utils/getVariableTemplateFromPath'; import { useState } from 'react'; +import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; +import { useIcons } from 'twenty-ui/display'; import { BaseOutputSchema, LinkOutputSchema, @@ -35,9 +41,17 @@ export const useVariableDropdown = ({ onSelect, onBack, }: UseVariableDropdownProps): UseVariableDropdownReturn => { + const { getIcon } = useIcons(); + const [currentPath, setCurrentPath] = useState([]); const [searchInputValue, setSearchInputValue] = useState(''); + const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu(); + + const workflowVisualizerWorkflowId = useRecoilComponentValue( + workflowVisualizerWorkflowIdComponentState, + ); + const setWorkflowSelectedNode = useSetRecoilComponentState( workflowSelectedNodeComponentState, ); @@ -45,8 +59,11 @@ export const useVariableDropdown = ({ activeTabIdComponentState, 'workflow-serverless-function-tab-list-component-id', ); - const setWorkflowDiagramTriggerNodeSelection = useSetRecoilComponentState( - workflowDiagramTriggerNodeSelectionComponentState, + const setWorkflowDiagram = useSetRecoilComponentState( + workflowDiagramComponentState, + ); + const setCommandMenuNavigationStack = useSetRecoilState( + commandMenuNavigationStackState, ); const getDisplayedSubStepFields = () => { @@ -83,8 +100,34 @@ export const useVariableDropdown = ({ const handleSelectLinkOutputSchema = ( linkOutputSchema: LinkOutputSchema, ) => { + if (!isDefined(workflowVisualizerWorkflowId)) { + throw new Error('Workflow ID must be configured'); + } + setWorkflowSelectedNode(step.id); - setWorkflowDiagramTriggerNodeSelection(step.id); + + setWorkflowDiagram((diagram) => { + if (!isDefined(diagram)) { + throw new Error('Workflow diagram must be defined'); + } + + return { + ...diagram, + nodes: diagram.nodes.map((node) => ({ + ...node, + selected: node.id === step.id, + })), + }; + }); + + setCommandMenuNavigationStack([]); + + openWorkflowEditStepInCommandMenu( + workflowVisualizerWorkflowId, + step.name, + getIcon(step.icon), + ); + if (isDefined(linkOutputSchema.link.tab)) { setActiveTabId(linkOutputSchema.link.tab); }