diff --git a/packages/twenty-e2e-testing/tests/workflow-run.spec.ts b/packages/twenty-e2e-testing/tests/workflow-run.spec.ts index fd7a3a323..ed5b565cd 100644 --- a/packages/twenty-e2e-testing/tests/workflow-run.spec.ts +++ b/packages/twenty-e2e-testing/tests/workflow-run.spec.ts @@ -57,8 +57,7 @@ test('The workflow run visualizer shows the executed draft version without the l ); }); -// FIXME: Documented bug. See https://github.com/twentyhq/core-team-issues/issues/921 -test.fail('Workflow Runs with a pending form step can be opened in the side panel and then in full screen', async ({ +test('Workflow Runs with a pending form step can be opened in the side panel and then in full screen', async ({ workflowVisualizer, page, }) => { diff --git a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx index 4e00fe3aa..9e53b1ed9 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx +++ b/packages/twenty-front/src/modules/command-menu/hooks/__tests__/useWorkflowCommandMenu.test.tsx @@ -4,7 +4,8 @@ import { useRecoilValue } from 'recoil'; import { COMMAND_MENU_COMPONENT_INSTANCE_ID } from '@/command-menu/constants/CommandMenuComponentInstanceId'; import { viewableRecordIdComponentState } from '@/command-menu/pages/record-page/states/viewableRecordIdComponentState'; import { viewableRecordNameSingularComponentState } from '@/command-menu/pages/record-page/states/viewableRecordNameSingularComponentState'; -import { workflowIdComponentState } from '@/command-menu/pages/workflow/states/workflowIdComponentState'; +import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState'; +import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState'; import { commandMenuNavigationMorphItemByPageState } from '@/command-menu/states/commandMenuNavigationMorphItemsState'; import { commandMenuPageState } from '@/command-menu/states/commandMenuPageState'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; @@ -16,10 +17,10 @@ import { ContextStoreViewType } from '@/context-store/types/ContextStoreViewType import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { t } from '@lingui/core/macro'; import { act } from 'react'; +import { IconBolt, IconSettingsAutomation, useIcons } from 'twenty-ui/display'; import { getJestMetadataAndApolloMocksAndActionMenuWrapper } from '~/testing/jest/getJestMetadataAndApolloMocksAndActionMenuWrapper'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { useWorkflowCommandMenu } from '../useWorkflowCommandMenu'; -import { IconBolt, IconSettingsAutomation, useIcons } from 'twenty-ui/display'; jest.mock('uuid', () => ({ v4: jest.fn().mockReturnValue('mocked-uuid'), @@ -95,7 +96,11 @@ const renderHooks = () => { 'mocked-uuid', ); const workflowId = useRecoilComponentValueV2( - workflowIdComponentState, + commandMenuWorkflowIdComponentState, + 'mocked-uuid', + ); + const workflowVersionId = useRecoilComponentValueV2( + commandMenuWorkflowVersionIdComponentState, 'mocked-uuid', ); const { getIcon } = useIcons(); @@ -106,6 +111,7 @@ const renderHooks = () => { openWorkflowEditStepInCommandMenu, openWorkflowViewStepInCommandMenu, workflowId, + workflowVersionId, viewableRecordId, commandMenuPage, commandMenuNavigationMorphItemByPage, @@ -188,14 +194,16 @@ describe('useWorkflowCommandMenu', () => { const { result } = renderHooks(); act(() => { - result.current.openWorkflowViewStepInCommandMenu( - 'test-workflow-id', - 'View Step', - IconSettingsAutomation, - ); + result.current.openWorkflowViewStepInCommandMenu({ + workflowId: 'test-workflow-id', + workflowVersionId: 'test-workflow-version-id', + icon: IconSettingsAutomation, + title: 'View Step', + }); }); expect(result.current.workflowId).toBe('test-workflow-id'); + expect(result.current.workflowVersionId).toBe('test-workflow-version-id'); expect(mockNavigateCommandMenu).toHaveBeenCalledWith({ page: CommandMenuPages.WorkflowStepView, diff --git a/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts b/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts index 0ecedbc0e..4e0997ddf 100644 --- a/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts +++ b/packages/twenty-front/src/modules/command-menu/hooks/useWorkflowCommandMenu.ts @@ -1,5 +1,7 @@ import { useNavigateCommandMenu } from '@/command-menu/hooks/useNavigateCommandMenu'; -import { workflowIdComponentState } from '@/command-menu/pages/workflow/states/workflowIdComponentState'; +import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState'; +import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState'; +import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState'; import { CommandMenuPages } from '@/command-menu/types/CommandMenuPages'; import { useSetInitialWorkflowRunRightDrawerTab } from '@/workflow/workflow-diagram/hooks/useSetInitialWorkflowRunRightDrawerTab'; import { WorkflowDiagramRunStatus } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; @@ -23,7 +25,9 @@ export const useWorkflowCommandMenu = () => { const pageId = v4(); set( - workflowIdComponentState.atomFamily({ instanceId: pageId }), + commandMenuWorkflowIdComponentState.atomFamily({ + instanceId: pageId, + }), workflowId, ); @@ -44,7 +48,9 @@ export const useWorkflowCommandMenu = () => { const pageId = v4(); set( - workflowIdComponentState.atomFamily({ instanceId: pageId }), + commandMenuWorkflowIdComponentState.atomFamily({ + instanceId: pageId, + }), workflowId, ); @@ -65,7 +71,9 @@ export const useWorkflowCommandMenu = () => { const pageId = v4(); set( - workflowIdComponentState.atomFamily({ instanceId: pageId }), + commandMenuWorkflowIdComponentState.atomFamily({ + instanceId: pageId, + }), workflowId, ); @@ -82,13 +90,31 @@ export const useWorkflowCommandMenu = () => { const openWorkflowViewStepInCommandMenu = useRecoilCallback( ({ set }) => { - return (workflowId: string, title: string, icon: IconComponent) => { + return ({ + workflowId, + workflowVersionId, + title, + icon, + }: { + workflowId: string; + workflowVersionId: string; + title: string; + icon: IconComponent; + }) => { const pageId = v4(); set( - workflowIdComponentState.atomFamily({ instanceId: pageId }), + commandMenuWorkflowIdComponentState.atomFamily({ + instanceId: pageId, + }), workflowId, ); + set( + commandMenuWorkflowVersionIdComponentState.atomFamily({ + instanceId: pageId, + }), + workflowVersionId, + ); navigateCommandMenu({ page: CommandMenuPages.WorkflowStepView, @@ -105,12 +131,14 @@ export const useWorkflowCommandMenu = () => { ({ set }) => { return ({ workflowId, + workflowRunId, title, icon, workflowSelectedNode, stepExecutionStatus, }: { workflowId: string; + workflowRunId: string; title: string; icon: IconComponent; workflowSelectedNode: string; @@ -119,9 +147,17 @@ export const useWorkflowCommandMenu = () => { const pageId = v4(); set( - workflowIdComponentState.atomFamily({ instanceId: pageId }), + commandMenuWorkflowIdComponentState.atomFamily({ + instanceId: pageId, + }), workflowId, ); + set( + commandMenuWorkflowRunIdComponentState.atomFamily({ + instanceId: pageId, + }), + workflowRunId, + ); navigateCommandMenu({ page: CommandMenuPages.WorkflowRunStepView, diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx index 45ca049d0..1b9613a3b 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectAction.tsx @@ -1,16 +1,27 @@ import { CommandMenuWorkflowSelectActionContent } from '@/command-menu/pages/workflow/action/components/CommandMenuWorkflowSelectActionContent'; -import { workflowIdComponentState } from '@/command-menu/pages/workflow/states/workflowIdComponentState'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useCommandMenuWorkflowIdOrThrow } from '@/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import { isDefined } from 'twenty-shared/utils'; export const CommandMenuWorkflowSelectAction = () => { - const workflowId = useRecoilComponentValueV2(workflowIdComponentState); + const workflowId = useCommandMenuWorkflowIdOrThrow(); const workflow = useWorkflowWithCurrentVersion(workflowId); if (!isDefined(workflow)) { return null; } - return ; + return ( + + + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow.ts b/packages/twenty-front/src/modules/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow.ts new file mode 100644 index 000000000..047435f05 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow.ts @@ -0,0 +1,16 @@ +import { commandMenuWorkflowIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isDefined } from 'twenty-shared/utils'; + +export const useCommandMenuWorkflowIdOrThrow = () => { + const workflowId = useRecoilComponentValueV2( + commandMenuWorkflowIdComponentState, + ); + if (!isDefined(workflowId)) { + throw new Error( + 'Expected commandMenuWorkflowIdComponentState to be defined', + ); + } + + return workflowId; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/states/workflowIdComponentState.ts b/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState.ts similarity index 84% rename from packages/twenty-front/src/modules/command-menu/pages/workflow/states/workflowIdComponentState.ts rename to packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState.ts index 35bb2d051..19e2b6a6a 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/states/workflowIdComponentState.ts +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowIdComponentState.ts @@ -1,7 +1,7 @@ import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; -export const workflowIdComponentState = createComponentStateV2< +export const commandMenuWorkflowIdComponentState = createComponentStateV2< string | undefined >({ key: 'command-menu/workflow-id', diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState.ts b/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState.ts new file mode 100644 index 000000000..55a39168a --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState.ts @@ -0,0 +1,10 @@ +import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const commandMenuWorkflowRunIdComponentState = createComponentStateV2< + string | undefined +>({ + key: 'command-menu/workflow-run-id', + defaultValue: undefined, + componentInstanceContext: CommandMenuPageComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState.ts b/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState.ts new file mode 100644 index 000000000..cf0f5c15b --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState.ts @@ -0,0 +1,9 @@ +import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; + +export const commandMenuWorkflowVersionIdComponentState = + createComponentStateV2({ + key: 'command-menu/workflow-version-id', + defaultValue: undefined, + componentInstanceContext: CommandMenuPageComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx index 217e50504..27e889273 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStep.tsx @@ -1,12 +1,13 @@ -import { workflowIdComponentState } from '@/command-menu/pages/workflow/states/workflowIdComponentState'; +import { useCommandMenuWorkflowIdOrThrow } from '@/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow'; import { CommandMenuWorkflowEditStepContent } from '@/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import { isDefined } from 'twenty-shared/utils'; export const CommandMenuWorkflowEditStep = () => { - const workflowId = useRecoilComponentValueV2(workflowIdComponentState); + const workflowId = useCommandMenuWorkflowIdOrThrow(); const workflow = useWorkflowWithCurrentVersion(workflowId); if (!isDefined(workflow)) { @@ -14,10 +15,18 @@ export const CommandMenuWorkflowEditStep = () => { } return ( - - - + + + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx index 6f34c1fc1..8bac2a3e3 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx @@ -1,132 +1,20 @@ -import { getIsInputTabDisabled } from '@/command-menu/pages/workflow/step/view-run/utils/getIsInputTabDisabled'; -import { getIsOutputTabDisabled } from '@/command-menu/pages/workflow/step/view-run/utils/getIsOutputTabDisabled'; -import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; -import { SingleTabProps, TabList } from '@/ui/layout/tab/components/TabList'; -import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState'; -import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; -import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; -import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; -import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; -import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; -import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow'; -import { WorkflowRunStepInputDetail } from '@/workflow/workflow-steps/components/WorkflowRunStepInputDetail'; -import { WorkflowRunStepNodeDetail } from '@/workflow/workflow-steps/components/WorkflowRunStepNodeDetail'; -import { WorkflowRunStepOutputDetail } from '@/workflow/workflow-steps/components/WorkflowRunStepOutputDetail'; -import { - WorkflowRunTabId, - WorkflowRunTabIdType, -} from '@/workflow/workflow-steps/types/WorkflowRunTabId'; -import { getWorkflowRunStepExecutionStatus } from '@/workflow/workflow-steps/utils/getWorkflowRunStepExecutionStatus'; -import styled from '@emotion/styled'; -import { isNull } from '@sniptt/guards'; -import { isDefined } from 'twenty-shared/utils'; -import { IconLogin2, IconLogout, IconStepInto } from 'twenty-ui/display'; - -const StyledContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; -`; - -const StyledTabList = styled(TabList)` - background-color: ${({ theme }) => theme.background.secondary}; - padding-left: ${({ theme }) => theme.spacing(2)}; -`; - -type TabId = WorkflowRunTabIdType; +import { CommandMenuWorkflowRunViewStepContent } from '@/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent'; +import { useCommandMenuWorkflowRunIdOrThrow } from '@/command-menu/pages/workflow/step/view-run/hooks/useCommandMenuWorkflowRunIdOrThrow'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; export const CommandMenuWorkflowRunViewStep = () => { - const flow = useFlowOrThrow(); - const workflowSelectedNode = useWorkflowSelectedNodeOrThrow(); - const workflowRunId = useWorkflowRunIdOrThrow(); - - const workflowRun = useWorkflowRun({ workflowRunId }); - - const commandMenuPageComponentInstance = useComponentInstanceStateContext( - CommandMenuPageComponentInstanceContext, - ); - if (isNull(commandMenuPageComponentInstance)) { - throw new Error( - 'CommandMenuPageComponentInstanceContext is not defined. This component should be used within CommandMenuPageComponentInstanceContext.', - ); - } - - const activeTabId = useRecoilComponentValueV2( - activeTabIdComponentState, - commandMenuPageComponentInstance.instanceId, - ); - - if (!isDefined(workflowRun)) { - return null; - } - - const stepExecutionStatus = getWorkflowRunStepExecutionStatus({ - workflowRunOutput: workflowRun.output, - stepId: workflowSelectedNode, - }); - - const isInputTabDisabled = getIsInputTabDisabled({ - stepExecutionStatus, - workflowSelectedNode, - }); - const isOutputTabDisabled = getIsOutputTabDisabled({ - stepExecutionStatus, - }); - - const tabs: SingleTabProps[] = [ - { - id: WorkflowRunTabId.OUTPUT, - title: 'Output', - Icon: IconLogout, - disabled: isOutputTabDisabled, - }, - { id: WorkflowRunTabId.NODE, title: 'Node', Icon: IconStepInto }, - { - id: WorkflowRunTabId.INPUT, - title: 'Input', - Icon: IconLogin2, - disabled: isInputTabDisabled, - }, - ]; + const workflowRunId = useCommandMenuWorkflowRunIdOrThrow(); return ( - - - - - {activeTabId === WorkflowRunTabId.OUTPUT ? ( - - ) : null} - - {activeTabId === WorkflowRunTabId.NODE ? ( - - ) : null} - - {activeTabId === WorkflowRunTabId.INPUT ? ( - - ) : null} - - + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx new file mode 100644 index 000000000..32e278b14 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStepContent.tsx @@ -0,0 +1,132 @@ +import { getIsInputTabDisabled } from '@/command-menu/pages/workflow/step/view-run/utils/getIsInputTabDisabled'; +import { getIsOutputTabDisabled } from '@/command-menu/pages/workflow/step/view-run/utils/getIsOutputTabDisabled'; +import { CommandMenuPageComponentInstanceContext } from '@/command-menu/states/contexts/CommandMenuPageComponentInstanceContext'; +import { SingleTabProps, TabList } from '@/ui/layout/tab/components/TabList'; +import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState'; +import { useComponentInstanceStateContext } from '@/ui/utilities/state/component-state/hooks/useComponentInstanceStateContext'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; +import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; +import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; +import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; +import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow'; +import { WorkflowRunStepInputDetail } from '@/workflow/workflow-steps/components/WorkflowRunStepInputDetail'; +import { WorkflowRunStepNodeDetail } from '@/workflow/workflow-steps/components/WorkflowRunStepNodeDetail'; +import { WorkflowRunStepOutputDetail } from '@/workflow/workflow-steps/components/WorkflowRunStepOutputDetail'; +import { + WorkflowRunTabId, + WorkflowRunTabIdType, +} from '@/workflow/workflow-steps/types/WorkflowRunTabId'; +import { getWorkflowRunStepExecutionStatus } from '@/workflow/workflow-steps/utils/getWorkflowRunStepExecutionStatus'; +import styled from '@emotion/styled'; +import { isNull } from '@sniptt/guards'; +import { isDefined } from 'twenty-shared/utils'; +import { IconLogin2, IconLogout, IconStepInto } from 'twenty-ui/display'; + +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; +`; + +const StyledTabList = styled(TabList)` + background-color: ${({ theme }) => theme.background.secondary}; + padding-left: ${({ theme }) => theme.spacing(2)}; +`; + +type TabId = WorkflowRunTabIdType; + +export const CommandMenuWorkflowRunViewStepContent = () => { + const flow = useFlowOrThrow(); + const workflowSelectedNode = useWorkflowSelectedNodeOrThrow(); + const workflowRunId = useWorkflowRunIdOrThrow(); + + const workflowRun = useWorkflowRun({ workflowRunId }); + + const commandMenuPageComponentInstance = useComponentInstanceStateContext( + CommandMenuPageComponentInstanceContext, + ); + if (isNull(commandMenuPageComponentInstance)) { + throw new Error( + 'CommandMenuPageComponentInstanceContext is not defined. This component should be used within CommandMenuPageComponentInstanceContext.', + ); + } + + const activeTabId = useRecoilComponentValueV2( + activeTabIdComponentState, + commandMenuPageComponentInstance.instanceId, + ); + + if (!isDefined(workflowRun)) { + return null; + } + + const stepExecutionStatus = getWorkflowRunStepExecutionStatus({ + workflowRunOutput: workflowRun.output, + stepId: workflowSelectedNode, + }); + + const isInputTabDisabled = getIsInputTabDisabled({ + stepExecutionStatus, + workflowSelectedNode, + }); + const isOutputTabDisabled = getIsOutputTabDisabled({ + stepExecutionStatus, + }); + + const tabs: SingleTabProps[] = [ + { + id: WorkflowRunTabId.OUTPUT, + title: 'Output', + Icon: IconLogout, + disabled: isOutputTabDisabled, + }, + { id: WorkflowRunTabId.NODE, title: 'Node', Icon: IconStepInto }, + { + id: WorkflowRunTabId.INPUT, + title: 'Input', + Icon: IconLogin2, + disabled: isInputTabDisabled, + }, + ]; + + return ( + + + + + {activeTabId === WorkflowRunTabId.OUTPUT ? ( + + ) : null} + + {activeTabId === WorkflowRunTabId.NODE ? ( + + ) : null} + + {activeTabId === WorkflowRunTabId.INPUT ? ( + + ) : null} + + + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/hooks/useCommandMenuWorkflowRunIdOrThrow.ts b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/hooks/useCommandMenuWorkflowRunIdOrThrow.ts new file mode 100644 index 000000000..6afcf4e71 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/hooks/useCommandMenuWorkflowRunIdOrThrow.ts @@ -0,0 +1,16 @@ +import { commandMenuWorkflowRunIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowRunIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isDefined } from 'twenty-shared/utils'; + +export const useCommandMenuWorkflowRunIdOrThrow = () => { + const workflowRunId = useRecoilComponentValueV2( + commandMenuWorkflowRunIdComponentState, + ); + if (!isDefined(workflowRunId)) { + throw new Error( + 'Expected the commandMenuWorkflowRunIdComponentState to be defined.', + ); + } + + return workflowRunId; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx index 659b17808..4e644f0e6 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx @@ -1,31 +1,20 @@ -import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; -import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; -import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow'; -import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail'; -import styled from '@emotion/styled'; - -const StyledContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; -`; +import { useCommandMenuWorkflowVersionIdOrThrow } from '@/command-menu/pages/workflow/step/view/hooks/useCommandMenuWorkflowVersionIdOrThrow'; +import { CommandMenuWorkflowViewStepContent } from '@/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStepContent'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; export const CommandMenuWorkflowViewStep = () => { - const flow = useFlowOrThrow(); - const workflowSelectedNode = useWorkflowSelectedNodeOrThrow(); + const workflowVersionId = useCommandMenuWorkflowVersionIdOrThrow(); return ( - - - - - + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStepContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStepContent.tsx new file mode 100644 index 000000000..2742cf763 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStepContent.tsx @@ -0,0 +1,31 @@ +import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; +import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; +import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow'; +import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail'; +import styled from '@emotion/styled'; + +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + height: 100%; +`; + +export const CommandMenuWorkflowViewStepContent = () => { + const flow = useFlowOrThrow(); + const workflowSelectedNode = useWorkflowSelectedNodeOrThrow(); + + return ( + + + + + + ); +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/hooks/useCommandMenuWorkflowVersionIdOrThrow.ts b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/hooks/useCommandMenuWorkflowVersionIdOrThrow.ts new file mode 100644 index 000000000..230c61710 --- /dev/null +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/hooks/useCommandMenuWorkflowVersionIdOrThrow.ts @@ -0,0 +1,16 @@ +import { commandMenuWorkflowVersionIdComponentState } from '@/command-menu/pages/workflow/states/commandMenuWorkflowVersionIdComponentState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { isDefined } from 'twenty-shared/utils'; + +export const useCommandMenuWorkflowVersionIdOrThrow = () => { + const workflowVersionId = useRecoilComponentValueV2( + commandMenuWorkflowVersionIdComponentState, + ); + if (!isDefined(workflowVersionId)) { + throw new Error( + 'Expected commandMenuWorkflowVersionIdComponentState to be defined', + ); + } + + return workflowVersionId; +}; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerType.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerType.tsx index 2e4bb6bc1..4877cfeaf 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerType.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerType.tsx @@ -1,16 +1,27 @@ -import { workflowIdComponentState } from '@/command-menu/pages/workflow/states/workflowIdComponentState'; +import { useCommandMenuWorkflowIdOrThrow } from '@/command-menu/pages/workflow/hooks/useCommandMenuWorkflowIdOrThrow'; import { CommandMenuWorkflowSelectTriggerTypeContent } from '@/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent'; -import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import { isDefined } from 'twenty-shared/utils'; export const CommandMenuWorkflowSelectTriggerType = () => { - const workflowId = useRecoilComponentValueV2(workflowIdComponentState); + const workflowId = useCommandMenuWorkflowIdOrThrow(); const workflow = useWorkflowWithCurrentVersion(workflowId); if (!isDefined(workflow)) { return null; } - return ; + return ( + + + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx index 92799918d..3abd377ad 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/trigger-type/components/CommandMenuWorkflowSelectTriggerTypeContent.tsx @@ -1,10 +1,11 @@ import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { WorkflowTriggerType, WorkflowWithCurrentVersion, } from '@/workflow/types/Workflow'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { RightDrawerStepListContainer } from '@/workflow/workflow-steps/components/RightDrawerWorkflowSelectStepContainer'; import { RightDrawerWorkflowSelectStepTitle } from '@/workflow/workflow-steps/components/RightDrawerWorkflowSelectStepTitle'; import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes'; @@ -12,9 +13,8 @@ import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/Other import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition'; -import { useSetRecoilState } from 'recoil'; -import { MenuItemCommand } from 'twenty-ui/navigation'; import { useIcons } from 'twenty-ui/display'; +import { MenuItemCommand } from 'twenty-ui/navigation'; export const CommandMenuWorkflowSelectTriggerTypeContent = ({ workflow, @@ -26,7 +26,9 @@ export const CommandMenuWorkflowSelectTriggerTypeContent = ({ const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu(); const handleTriggerTypeClick = ({ diff --git a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx index 62ddeba26..5fab1d8b4 100644 --- a/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx +++ b/packages/twenty-front/src/modules/object-record/record-show/components/CardComponents.tsx @@ -7,15 +7,19 @@ import { TimelineActivities } from '@/activities/timeline-activities/components/ import { ActivityTargetableObject } from '@/activities/types/ActivityTargetableEntity'; import { FieldsCard } from '@/object-record/record-show/components/FieldsCard'; import { CardType } from '@/object-record/record-show/types/CardType'; +import { ListenRecordUpdatesEffect } from '@/subscription/components/ListenUpdatesEffect'; import { ShowPageActivityContainer } from '@/ui/layout/show-page/components/ShowPageActivityContainer'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; import { WorkflowRunVisualizer } from '@/workflow/workflow-diagram/components/WorkflowRunVisualizer'; import { WorkflowRunVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowRunVisualizerEffect'; import { WorkflowVersionVisualizer } from '@/workflow/workflow-diagram/components/WorkflowVersionVisualizer'; import { WorkflowVersionVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowVersionVisualizerEffect'; import { WorkflowVisualizer } from '@/workflow/workflow-diagram/components/WorkflowVisualizer'; import { WorkflowVisualizerEffect } from '@/workflow/workflow-diagram/components/WorkflowVisualizerEffect'; +import { WorkflowRunVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import styled from '@emotion/styled'; -import { ListenRecordUpdatesEffect } from '@/subscription/components/ListenUpdatesEffect'; +import { useId } from 'react'; const StyledGreyBox = styled.div<{ isInRightDrawer?: boolean }>` background: ${({ theme, isInRightDrawer }) => @@ -81,32 +85,63 @@ export const CardComponents: Record = { ), - [CardType.WorkflowCard]: ({ targetableObject }) => ( - <> - - - - ), + [CardType.WorkflowCard]: ({ targetableObject }) => { + return ( + + + + + ); + }, - [CardType.WorkflowVersionCard]: ({ targetableObject }) => ( - <> - - - - ), + [CardType.WorkflowVersionCard]: ({ targetableObject }) => { + return ( + + + + + ); + }, - [CardType.WorkflowRunCard]: ({ targetableObject }) => ( - <> - - + [CardType.WorkflowRunCard]: ({ targetableObject }) => { + const componentId = useId(); - - - ), + return ( + + + + + + + + ); + }, }; diff --git a/packages/twenty-front/src/modules/workflow/hooks/useFlowOrThrow.ts b/packages/twenty-front/src/modules/workflow/hooks/useFlowOrThrow.ts index f5f99a409..537781083 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useFlowOrThrow.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useFlowOrThrow.ts @@ -1,9 +1,10 @@ -import { flowState } from '@/workflow/states/flowState'; -import { useRecoilValue } from 'recoil'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; import { isDefined } from 'twenty-shared/utils'; export const useFlowOrThrow = () => { - const flow = useRecoilValue(flowState); + const flow = useRecoilComponentValueV2(flowComponentState); + if (!isDefined(flow)) { throw new Error('Expected the flow to be defined'); } diff --git a/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts b/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts index 9a233a042..e34106778 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useRunWorkflowRunOpeningInCommandMenuSideEffects.ts @@ -3,11 +3,12 @@ import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadat import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem'; import { getRecordFromCache } from '@/object-record/cache/utils/getRecordFromCache'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { flowState } from '@/workflow/states/flowState'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { workflowRunIdState } from '@/workflow/states/workflowRunIdState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowVisualizerWorkflowRunIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowRunIdComponentState'; import { WorkflowRun } from '@/workflow/types/Workflow'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { getWorkflowVisualizerComponentInstanceId } from '@/workflow/utils/getWorkflowVisualizerComponentInstanceId'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { generateWorkflowRunDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowRunDiagram'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; import { useApolloClient } from '@apollo/client'; @@ -58,17 +59,46 @@ export const useRunWorkflowRunOpeningInCommandMenuSideEffects = () => { return; } - set(workflowRunIdState, workflowRunRecord.id); - set(workflowIdState, workflowRunRecord.workflowId); - set(flowState, { - workflowVersionId: workflowRunRecord.workflowVersionId, - trigger: workflowRunRecord.output.flow.trigger, - steps: workflowRunRecord.output.flow.steps, - }); - set(workflowSelectedNodeState, stepToOpenByDefault.id); + set( + workflowVisualizerWorkflowRunIdComponentState.atomFamily({ + instanceId: getWorkflowVisualizerComponentInstanceId({ + recordId, + }), + }), + workflowRunRecord.id, + ); + set( + workflowVisualizerWorkflowIdComponentState.atomFamily({ + instanceId: getWorkflowVisualizerComponentInstanceId({ + recordId, + }), + }), + workflowRunRecord.workflowId, + ); + set( + flowComponentState.atomFamily({ + instanceId: getWorkflowVisualizerComponentInstanceId({ + recordId, + }), + }), + { + workflowVersionId: workflowRunRecord.workflowVersionId, + trigger: workflowRunRecord.output.flow.trigger, + steps: workflowRunRecord.output.flow.steps, + }, + ); + set( + workflowSelectedNodeComponentState.atomFamily({ + instanceId: getWorkflowVisualizerComponentInstanceId({ + recordId, + }), + }), + stepToOpenByDefault.id, + ); openWorkflowRunViewStepInCommandMenu({ workflowId: workflowRunRecord.workflowId, + workflowRunId: workflowRunRecord.id, title: stepToOpenByDefault.data.name, icon: getIcon(getWorkflowNodeIconKey(stepToOpenByDefault.data)), workflowSelectedNode: stepToOpenByDefault.id, diff --git a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRunIdOrThrow.ts b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRunIdOrThrow.ts index d76132dcc..98f383790 100644 --- a/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRunIdOrThrow.ts +++ b/packages/twenty-front/src/modules/workflow/hooks/useWorkflowRunIdOrThrow.ts @@ -1,9 +1,12 @@ -import { workflowRunIdState } from '@/workflow/states/workflowRunIdState'; -import { useRecoilValue } from 'recoil'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { workflowVisualizerWorkflowRunIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowRunIdComponentState'; import { isDefined } from 'twenty-shared/utils'; export const useWorkflowRunIdOrThrow = () => { - const workflowRunId = useRecoilValue(workflowRunIdState); + const workflowRunId = useRecoilComponentValueV2( + workflowVisualizerWorkflowRunIdComponentState, + ); + if (!isDefined(workflowRunId)) { throw new Error('Expected the workflow run ID to be defined'); } diff --git a/packages/twenty-front/src/modules/workflow/states/flowComponentState.ts b/packages/twenty-front/src/modules/workflow/states/flowComponentState.ts new file mode 100644 index 000000000..1b5b9ddb6 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/states/flowComponentState.ts @@ -0,0 +1,16 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const flowComponentState = createComponentStateV2< + | { + workflowVersionId: string; + trigger: WorkflowTrigger | null; + steps: WorkflowAction[] | null; + } + | undefined +>({ + key: 'flowComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/workflow/states/flowState.ts b/packages/twenty-front/src/modules/workflow/states/flowState.ts deleted file mode 100644 index 9cdd51b00..000000000 --- a/packages/twenty-front/src/modules/workflow/states/flowState.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow'; -import { createState } from 'twenty-ui/utilities'; - -export const flowState = createState< - | { - workflowVersionId: string; - trigger: WorkflowTrigger | null; - steps: WorkflowAction[] | null; - } - | undefined ->({ - key: 'flowState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts deleted file mode 100644 index 0851b1044..000000000 --- a/packages/twenty-front/src/modules/workflow/states/workflowIdState.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; -export const workflowIdState = createState({ - key: 'workflowIdState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdComponentState.ts b/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdComponentState.ts new file mode 100644 index 000000000..20b09c41e --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdComponentState.ts @@ -0,0 +1,10 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowLastCreatedStepIdComponentState = createComponentStateV2< + string | undefined +>({ + key: 'workflowLastCreatedStepIdComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts deleted file mode 100644 index 14252388f..000000000 --- a/packages/twenty-front/src/modules/workflow/states/workflowLastCreatedStepIdState.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; -export const workflowLastCreatedStepIdState = createState({ - key: 'workflowLastCreatedStepIdState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowRunIdState.ts b/packages/twenty-front/src/modules/workflow/states/workflowRunIdState.ts deleted file mode 100644 index f1460ff00..000000000 --- a/packages/twenty-front/src/modules/workflow/states/workflowRunIdState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; - -export const workflowRunIdState = createState({ - key: 'workflowRunIdState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowIdComponentState.ts b/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowIdComponentState.ts new file mode 100644 index 000000000..cbd75d932 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowIdComponentState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowVisualizerWorkflowIdComponentState = + createComponentStateV2({ + key: 'workflowVisualizerWorkflowIdComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowRunIdComponentState.ts b/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowRunIdComponentState.ts new file mode 100644 index 000000000..905c9fec5 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowRunIdComponentState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowVisualizerWorkflowRunIdComponentState = + createComponentStateV2({ + key: 'workflowVisualizerWorkflowRunIdComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowVersionIdComponentState.ts b/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowVersionIdComponentState.ts new file mode 100644 index 000000000..e60141130 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/states/workflowVisualizerWorkflowVersionIdComponentState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowVisualizerWorkflowVersionIdComponentState = + createComponentStateV2({ + key: 'workflowVisualizerWorkflowVersionIdComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/utils/getWorkflowVisualizerComponentInstanceId.ts b/packages/twenty-front/src/modules/workflow/utils/getWorkflowVisualizerComponentInstanceId.ts new file mode 100644 index 000000000..b8a930e0e --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/getWorkflowVisualizerComponentInstanceId.ts @@ -0,0 +1,7 @@ +export const getWorkflowVisualizerComponentInstanceId = ({ + recordId, +}: { + recordId: string; +}) => { + return recordId; +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx index 5d517e832..ba69cea7c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx @@ -1,8 +1,9 @@ import { useListenRightDrawerClose } from '@/ui/layout/right-drawer/hooks/useListenRightDrawerClose'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { WorkflowDiagramCustomMarkers } from '@/workflow/workflow-diagram/components/WorkflowDiagramCustomMarkers'; import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightDrawerState'; -import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState'; -import { workflowReactFlowRefState } from '@/workflow/workflow-diagram/states/workflowReactFlowRefState'; +import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { WorkflowDiagramEdge, WorkflowDiagramEdgeType, @@ -26,7 +27,6 @@ import { } from '@xyflow/react'; import '@xyflow/react/dist/style.css'; import React, { useEffect, useMemo, useRef } from 'react'; -import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { Tag, TagColor } from 'twenty-ui/components'; import { THEME_COMMON } from 'twenty-ui/theme'; @@ -121,7 +121,9 @@ export const WorkflowDiagramCanvasBase = ({ const reactflow = useReactFlow(); - const workflowDiagram = useRecoilValue(workflowDiagramState); + const workflowDiagram = useRecoilComponentValueV2( + workflowDiagramComponentState, + ); const { nodes, edges } = useMemo( () => @@ -137,14 +139,8 @@ export const WorkflowDiagramCanvasBase = ({ THEME_COMMON.rightDrawerWidth.replace('px', ''), ); - const setWorkflowDiagram = useSetRecoilState(workflowDiagramState); - - const setWorkflowReactFlowRef = useRecoilCallback( - ({ set }) => - (node: HTMLDivElement | null) => { - set(workflowReactFlowRefState, { current: node }); - }, - [], + const setWorkflowDiagram = useSetRecoilComponentStateV2( + workflowDiagramComponentState, ); const handleEdgesChange = ( @@ -199,24 +195,18 @@ export const WorkflowDiagramCanvasBase = ({ ); }, [reactflow, rightDrawerState, rightDrawerWidth]); - const handleNodesChanges = useRecoilCallback( - ({ set }) => - (changes: NodeChange[]) => { - set(workflowDiagramState, (diagram) => { - if (!isDefined(diagram)) { - throw new Error( - 'It must be impossible for the nodes to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.', - ); - } + const handleNodesChanges = (changes: NodeChange[]) => { + setWorkflowDiagram((diagram) => { + if (!isDefined(diagram)) { + return diagram; + } - return { - ...diagram, - nodes: applyNodeChanges(changes, diagram.nodes), - }; - }); - }, - [], - ); + return { + ...diagram, + nodes: applyNodeChanges(changes, diagram.nodes), + }; + }); + }; const handleInit = () => { if (!isDefined(containerRef.current)) { @@ -239,7 +229,6 @@ export const WorkflowDiagramCanvasBase = ({ { openWorkflowEditStepInCommandMenu, } = useWorkflowCommandMenu(); - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); const setCommandMenuNavigationStack = useSetRecoilState( commandMenuNavigationStackState, @@ -37,7 +41,9 @@ export const WorkflowDiagramCanvasEditableEffect = () => { const { isInRightDrawer } = useContext(ActionMenuContext); - const workflowId = useRecoilValue(workflowIdState); + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); const handleSelectionChange = useCallback( ({ nodes }: OnSelectionChangeParams) => { @@ -53,8 +59,8 @@ export const WorkflowDiagramCanvasEditableEffect = () => { const isEmptyTriggerNode = selectedNode.type === EMPTY_TRIGGER_STEP_ID; if (isEmptyTriggerNode) { - if (isDefined(workflowId)) { - openWorkflowTriggerTypeInCommandMenu(workflowId); + if (isDefined(workflowVisualizerWorkflowId)) { + openWorkflowTriggerTypeInCommandMenu(workflowVisualizerWorkflowId); return; } @@ -71,9 +77,9 @@ export const WorkflowDiagramCanvasEditableEffect = () => { setWorkflowSelectedNode(selectedNode.id); - if (isDefined(workflowId)) { + if (isDefined(workflowVisualizerWorkflowId)) { openWorkflowEditStepInCommandMenu( - workflowId, + workflowVisualizerWorkflowId, selectedNodeData.name, getIcon(getWorkflowNodeIconKey(selectedNodeData)), ); @@ -84,7 +90,7 @@ export const WorkflowDiagramCanvasEditableEffect = () => { [ isInRightDrawer, setCommandMenuNavigationStack, - workflowId, + workflowVisualizerWorkflowId, openWorkflowTriggerTypeInCommandMenu, startNodeCreation, openWorkflowEditStepInCommandMenu, 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 index 023f1a78a..9f1252cd3 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect.tsx @@ -1,7 +1,10 @@ import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowVisualizerWorkflowVersionIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowVersionIdComponentState'; import { useTriggerNodeSelection } from '@/workflow/workflow-diagram/hooks/useTriggerNodeSelection'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowDiagramNode, WorkflowDiagramStepNodeData, @@ -9,19 +12,34 @@ import { import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey'; import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react'; import { useCallback } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { useIcons } from 'twenty-ui/display'; export const WorkflowDiagramCanvasReadonlyEffect = () => { const { getIcon } = useIcons(); - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); const { openWorkflowViewStepInCommandMenu } = useWorkflowCommandMenu(); - const workflowId = useRecoilValue(workflowIdState); + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); + const workflowVisualizerWorkflowVersionId = useRecoilComponentValueV2( + workflowVisualizerWorkflowVersionIdComponentState, + ); const handleSelectionChange = useCallback( ({ nodes }: OnSelectionChangeParams) => { + if ( + !( + isDefined(workflowVisualizerWorkflowId) && + isDefined(workflowVisualizerWorkflowVersionId) + ) + ) { + return; + } + const selectedNode = nodes[0] as WorkflowDiagramNode | undefined; if (!isDefined(selectedNode)) { @@ -32,18 +50,18 @@ export const WorkflowDiagramCanvasReadonlyEffect = () => { const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData; - if (isDefined(workflowId)) { - openWorkflowViewStepInCommandMenu( - workflowId, - selectedNodeData.name, - getIcon(getWorkflowNodeIconKey(selectedNodeData)), - ); - } + openWorkflowViewStepInCommandMenu({ + workflowId: workflowVisualizerWorkflowId, + workflowVersionId: workflowVisualizerWorkflowVersionId, + title: selectedNodeData.name, + icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)), + }); }, [ setWorkflowSelectedNode, openWorkflowViewStepInCommandMenu, - workflowId, + workflowVisualizerWorkflowId, + workflowVisualizerWorkflowVersionId, getIcon, ], ); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx index ec652bbad..16c6a2da4 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEffect.tsx @@ -1,18 +1,20 @@ +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; -import { flowState } from '@/workflow/states/flowState'; -import { workflowLastCreatedStepIdState } from '@/workflow/states/workflowLastCreatedStepIdState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; +import { workflowLastCreatedStepIdComponentState } from '@/workflow/states/workflowLastCreatedStepIdComponentState'; import { WorkflowVersion, WorkflowWithCurrentVersion, } from '@/workflow/types/Workflow'; -import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState'; +import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { addCreateStepNodes } from '@/workflow/workflow-diagram/utils/addCreateStepNodes'; import { getWorkflowVersionDiagram } from '@/workflow/workflow-diagram/utils/getWorkflowVersionDiagram'; import { mergeWorkflowDiagrams } from '@/workflow/workflow-diagram/utils/mergeWorkflowDiagrams'; import { useEffect } from 'react'; -import { useRecoilCallback, useSetRecoilState } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const WorkflowDiagramEffect = ({ @@ -20,10 +22,19 @@ export const WorkflowDiagramEffect = ({ }: { workflowWithCurrentVersion: WorkflowWithCurrentVersion | undefined; }) => { - const setWorkflowDiagram = useSetRecoilState(workflowDiagramState); - const setFlow = useSetRecoilState(flowState); + const workflowDiagramState = useRecoilComponentCallbackStateV2( + workflowDiagramComponentState, + ); + const setWorkflowDiagram = useSetRecoilComponentStateV2( + workflowDiagramComponentState, + ); + const setFlow = useSetRecoilComponentStateV2(flowComponentState); const { populateStepsOutputSchema } = useStepsOutputSchema(); + const workflowLastCreatedStepIdState = useRecoilComponentCallbackStateV2( + workflowLastCreatedStepIdComponentState, + ); + const computeAndMergeNewWorkflowDiagram = useRecoilCallback( ({ snapshot, set }) => { return (currentVersion: WorkflowVersion) => { @@ -64,7 +75,7 @@ export const WorkflowDiagramEffect = ({ set(workflowDiagramState, mergedWorkflowDiagram); }; }, - [], + [workflowLastCreatedStepIdState, workflowDiagramState], ); const currentVersion = workflowWithCurrentVersion?.currentVersion; 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 d5f9bd18f..08c188a80 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,10 @@ +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { assertWorkflowWithCurrentVersionIsDefined } from '@/workflow/utils/assertWorkflowWithCurrentVersionIsDefined'; import { WorkflowDiagramStepNodeEditableContent } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep'; -import { useRecoilValue } from 'recoil'; export const WorkflowDiagramStepNodeEditable = ({ id, @@ -15,9 +15,13 @@ export const WorkflowDiagramStepNodeEditable = ({ data: WorkflowDiagramStepNodeData; selected?: boolean; }) => { - const workflowId = useRecoilValue(workflowIdState); + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); - const workflowWithCurrentVersion = useWorkflowWithCurrentVersion(workflowId); + const workflowWithCurrentVersion = useWorkflowWithCurrentVersion( + workflowVisualizerWorkflowId, + ); assertWorkflowWithCurrentVersionIsDefined(workflowWithCurrentVersion); const { deleteStep } = useDeleteStep({ 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 index c524fa985..22042b0e3 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvasEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunDiagramCanvasEffect.tsx @@ -1,8 +1,10 @@ import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { workflowDiagramStatusState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusState'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +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'; @@ -15,10 +17,25 @@ export const WorkflowRunDiagramCanvasEffect = () => { const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu(); + const workflowRunId = useWorkflowRunIdOrThrow(); + + const workflowVisualizerWorkflowIdState = useRecoilComponentCallbackStateV2( + workflowVisualizerWorkflowIdComponentState, + ); + const workflowDiagramStatusState = useRecoilComponentCallbackStateV2( + workflowDiagramStatusComponentState, + ); + const workflowSelectedNodeState = useRecoilComponentCallbackStateV2( + workflowSelectedNodeComponentState, + ); + const handleSelectionChange = useRecoilCallback( ({ snapshot, set }) => ({ nodes }: OnSelectionChangeParams) => { - const workflowId = getSnapshotValue(snapshot, workflowIdState); + const workflowId = getSnapshotValue( + snapshot, + workflowVisualizerWorkflowIdState, + ); if (!isDefined(workflowId)) { throw new Error('Expected the workflowId to be defined.'); @@ -49,13 +66,21 @@ export const WorkflowRunDiagramCanvasEffect = () => { openWorkflowRunViewStepInCommandMenu({ workflowId, + workflowRunId, title: selectedNodeData.name, icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)), workflowSelectedNode: selectedNode.id, stepExecutionStatus: selectedNodeData.runStatus, }); }, - [getIcon, openWorkflowRunViewStepInCommandMenu], + [ + workflowVisualizerWorkflowIdState, + workflowDiagramStatusState, + workflowSelectedNodeState, + openWorkflowRunViewStepInCommandMenu, + workflowRunId, + getIcon, + ], ); useOnSelectionChange({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizer.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizer.tsx index a46d913c7..c5a66d597 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizer.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowRunVisualizer.tsx @@ -1,8 +1,8 @@ +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; import { WorkflowRunDiagramCanvas } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramCanvas'; -import { workflowDiagramStatusState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusState'; +import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState'; import styled from '@emotion/styled'; -import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; const StyledContainer = styled.div` @@ -15,7 +15,9 @@ export const WorkflowRunVisualizer = ({ workflowRunId: string; }) => { const workflowRun = useWorkflowRun({ workflowRunId }); - const workflowDiagramStatus = useRecoilValue(workflowDiagramStatusState); + const workflowDiagramStatus = useRecoilComponentValueV2( + workflowDiagramStatusComponentState, + ); if ( !isDefined(workflowRun) || 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 c49777bc9..a404d8bae 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 @@ -1,18 +1,21 @@ import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; +import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion'; -import { flowState } from '@/workflow/states/flowState'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { workflowRunIdState } from '@/workflow/states/workflowRunIdState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowVisualizerWorkflowRunIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowRunIdComponentState'; import { WorkflowRunOutput } from '@/workflow/types/Workflow'; -import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState'; -import { workflowDiagramStatusState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusState'; -import { workflowRunStepToOpenByDefaultState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultState'; +import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; +import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState'; +import { workflowRunStepToOpenByDefaultComponentState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState'; import { generateWorkflowRunDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowRunDiagram'; import { selectWorkflowDiagramNode } from '@/workflow/workflow-diagram/utils/selectWorkflowDiagramNode'; import { useContext, useEffect } from 'react'; -import { useRecoilCallback, useSetRecoilState } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const WorkflowRunVisualizerEffect = ({ @@ -23,8 +26,24 @@ export const WorkflowRunVisualizerEffect = ({ const workflowRun = useWorkflowRun({ workflowRunId }); const workflowVersion = useWorkflowVersion(workflowRun?.workflowVersionId); - const setWorkflowRunId = useSetRecoilState(workflowRunIdState); - const setWorkflowId = useSetRecoilState(workflowIdState); + const setWorkflowRunId = useSetRecoilComponentStateV2( + workflowVisualizerWorkflowRunIdComponentState, + ); + const setWorkflowVisualizerWorkflowId = useSetRecoilComponentStateV2( + workflowVisualizerWorkflowIdComponentState, + ); + + const flowState = useRecoilComponentCallbackStateV2(flowComponentState); + const workflowDiagramState = useRecoilComponentCallbackStateV2( + workflowDiagramComponentState, + ); + const workflowDiagramStatusState = useRecoilComponentCallbackStateV2( + workflowDiagramStatusComponentState, + ); + const workflowRunStepToOpenByDefaultState = useRecoilComponentCallbackStateV2( + workflowRunStepToOpenByDefaultComponentState, + ); + const { populateStepsOutputSchema } = useStepsOutputSchema(); const { isInRightDrawer } = useContext(ActionMenuContext); @@ -38,11 +57,11 @@ export const WorkflowRunVisualizerEffect = ({ return; } - setWorkflowId(workflowRun.workflowId); - }, [setWorkflowId, workflowRun]); + setWorkflowVisualizerWorkflowId(workflowRun.workflowId); + }, [setWorkflowVisualizerWorkflowId, workflowRun]); const handleWorkflowRunDiagramGeneration = useRecoilCallback( - ({ set }) => + ({ snapshot, set }) => ({ workflowRunOutput, workflowVersionId, @@ -59,7 +78,14 @@ export const WorkflowRunVisualizerEffect = ({ return; } - set(workflowDiagramStatusState, 'computing-diagram'); + const workflowDiagramStatus = getSnapshotValue( + snapshot, + workflowDiagramStatusState, + ); + + if (workflowDiagramStatus !== 'done') { + set(workflowDiagramStatusState, 'computing-diagram'); + } set(flowState, { workflowVersionId, @@ -89,9 +115,16 @@ export const WorkflowRunVisualizerEffect = ({ set(workflowDiagramState, baseWorkflowRunDiagram); } - set(workflowDiagramStatusState, 'computing-dimensions'); + if (workflowDiagramStatus !== 'done') { + set(workflowDiagramStatusState, 'computing-dimensions'); + } }, - [], + [ + flowState, + workflowDiagramState, + workflowDiagramStatusState, + workflowRunStepToOpenByDefaultState, + ], ); useEffect(() => { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVersionVisualizerEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVersionVisualizerEffect.tsx index 7f73f34f1..084fc5037 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVersionVisualizerEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVersionVisualizerEffect.tsx @@ -1,11 +1,12 @@ +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion'; -import { flowState } from '@/workflow/states/flowState'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowVisualizerWorkflowVersionIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowVersionIdComponentState'; +import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { getWorkflowVersionDiagram } from '@/workflow/workflow-diagram/utils/getWorkflowVersionDiagram'; import { useEffect } from 'react'; -import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const WorkflowVersionVisualizerEffect = ({ @@ -15,10 +16,19 @@ export const WorkflowVersionVisualizerEffect = ({ }) => { const workflowVersion = useWorkflowVersion(workflowVersionId); - const setFlow = useSetRecoilState(flowState); - const setWorkflowDiagram = useSetRecoilState(workflowDiagramState); - const setWorkflowId = useSetRecoilState(workflowIdState); + const setFlow = useSetRecoilComponentStateV2(flowComponentState); + const setWorkflowDiagram = useSetRecoilComponentStateV2( + workflowDiagramComponentState, + ); + const setWorkflowVisualizerWorkflowId = useSetRecoilComponentStateV2( + workflowVisualizerWorkflowIdComponentState, + ); + const setWorkflowVisualizerWorkflowVersionId = useSetRecoilComponentStateV2( + workflowVisualizerWorkflowVersionIdComponentState, + ); + const { populateStepsOutputSchema } = useStepsOutputSchema(); + useEffect(() => { if (!isDefined(workflowVersion)) { setFlow(undefined); @@ -32,8 +42,14 @@ export const WorkflowVersionVisualizerEffect = ({ steps: workflowVersion.steps, }); - setWorkflowId(workflowVersion.workflowId); - }, [setFlow, setWorkflowId, workflowVersion]); + setWorkflowVisualizerWorkflowId(workflowVersion.workflowId); + setWorkflowVisualizerWorkflowVersionId(workflowVersion.id); + }, [ + setFlow, + setWorkflowVisualizerWorkflowId, + setWorkflowVisualizerWorkflowVersionId, + workflowVersion, + ]); useEffect(() => { if (!isDefined(workflowVersion)) { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizerEffect.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizerEffect.tsx index f3f7ed097..3d5fa311f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizerEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowVisualizerEffect.tsx @@ -1,17 +1,19 @@ -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { useEffect } from 'react'; -import { useSetRecoilState } from 'recoil'; export const WorkflowVisualizerEffect = ({ workflowId, }: { workflowId: string; }) => { - const setWorkflowId = useSetRecoilState(workflowIdState); + const setWorkflowVisualizerWorkflowId = useSetRecoilComponentStateV2( + workflowVisualizerWorkflowIdComponentState, + ); useEffect(() => { - setWorkflowId(workflowId); - }, [setWorkflowId, workflowId]); + setWorkflowVisualizerWorkflowId(workflowId); + }, [setWorkflowVisualizerWorkflowId, workflowId]); return null; }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramCustomMarkers.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramCustomMarkers.stories.tsx index 8bb3f74b9..8a5eec03c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramCustomMarkers.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramCustomMarkers.stories.tsx @@ -9,11 +9,12 @@ import { WorkflowDiagramStepNodeReadonly } from '@/workflow/workflow-diagram/com import { WorkflowDiagramSuccessEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramSuccessEdge'; import { WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION } from '@/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeDefaultConfiguration'; import { WORKFLOW_VISUALIZER_EDGE_SUCCESS_CONFIGURATION } from '@/workflow/workflow-diagram/constants/WorkflowVisualizerEdgeSuccessConfiguration'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; import { ReactflowDecorator } from '~/testing/decorators/ReactflowDecorator'; import { WorkspaceDecorator } from '~/testing/decorators/WorkspaceDecorator'; import { graphqlMocks } from '~/testing/graphqlMocks'; -import { workflowDiagramState } from '../../states/workflowDiagramState'; +import { workflowDiagramComponentState } from '../../states/workflowDiagramComponentState'; import { WorkflowDiagramCanvasBase } from '../WorkflowDiagramCanvasBase'; const StyledContainer = styled.div` @@ -51,63 +52,79 @@ export const DefaultEdge: Story = { }, }, decorators: [ - (Story) => ( - { - set(workflowDiagramState, { - nodes: [ + (Story) => { + const workflowVisualizerComponentInstanceId = + 'workflow-visualizer-test-id'; + + return ( + { + set( + workflowDiagramComponentState.atomFamily({ + instanceId: workflowVisualizerComponentInstanceId, + }), { - id: 'trigger-1', - type: 'default', - position: { x: 100, y: 100 }, - data: { - nodeType: 'trigger', - triggerType: 'DATABASE_EVENT', - name: 'When record is created', - }, + nodes: [ + { + id: 'trigger-1', + type: 'default', + position: { x: 100, y: 100 }, + data: { + nodeType: 'trigger', + triggerType: 'DATABASE_EVENT', + name: 'When record is created', + }, + }, + { + id: 'action-1', + type: 'default', + position: { x: 300, y: 100 }, + data: { + nodeType: 'action', + actionType: 'CREATE_RECORD', + name: 'Create record', + }, + }, + { + id: 'create-step-1', + type: 'create-step', + position: { x: 500, y: 100 }, + data: { + nodeType: 'create-step', + parentNodeId: 'action-1', + }, + }, + ], + edges: [ + { + ...WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION, + id: 'edge-1', + source: 'trigger-1', + target: 'action-1', + }, + { + ...WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION, + id: 'edge-2', + source: 'action-1', + target: 'create-step-1', + }, + ], }, - { - id: 'action-1', - type: 'default', - position: { x: 300, y: 100 }, - data: { - nodeType: 'action', - actionType: 'CREATE_RECORD', - name: 'Create record', - }, - }, - { - id: 'create-step-1', - type: 'create-step', - position: { x: 500, y: 100 }, - data: { - nodeType: 'create-step', - parentNodeId: 'action-1', - }, - }, - ], - edges: [ - { - ...WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION, - id: 'edge-1', - source: 'trigger-1', - target: 'action-1', - }, - { - ...WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION, - id: 'edge-2', - source: 'action-1', - target: 'create-step-1', - }, - ], - }); - }} - > - - - - - ), + ); + }} + > + + + + + + + ); + }, ], }; @@ -124,49 +141,65 @@ export const SuccessEdge: Story = { }, }, decorators: [ - (Story) => ( - { - set(workflowDiagramState, { - nodes: [ + (Story) => { + const workflowVisualizerComponentInstanceId = + 'workflow-visualizer-test-id'; + + return ( + { + set( + workflowDiagramComponentState.atomFamily({ + instanceId: workflowVisualizerComponentInstanceId, + }), { - id: 'trigger-1', - type: 'default', - position: { x: 100, y: 100 }, - data: { - nodeType: 'trigger', - triggerType: 'DATABASE_EVENT', - name: 'When record is created', - }, + nodes: [ + { + id: 'trigger-1', + type: 'default', + position: { x: 100, y: 100 }, + data: { + nodeType: 'trigger', + triggerType: 'DATABASE_EVENT', + name: 'When record is created', + }, + }, + { + id: 'action-1', + type: 'default', + position: { x: 300, y: 100 }, + data: { + nodeType: 'action', + actionType: 'CREATE_RECORD', + name: 'Create record', + }, + }, + ], + edges: [ + { + ...WORKFLOW_VISUALIZER_EDGE_SUCCESS_CONFIGURATION, + id: 'edge-1', + source: 'trigger-1', + target: 'action-1', + type: 'success', + label: '1 item', + }, + ], }, - { - id: 'action-1', - type: 'default', - position: { x: 300, y: 100 }, - data: { - nodeType: 'action', - actionType: 'CREATE_RECORD', - name: 'Create record', - }, - }, - ], - edges: [ - { - ...WORKFLOW_VISUALIZER_EDGE_SUCCESS_CONFIGURATION, - id: 'edge-1', - source: 'trigger-1', - target: 'action-1', - type: 'success', - label: '1 item', - }, - ], - }); - }} - > - - - - - ), + ); + }} + > + + + + + + + ); + }, ], }; 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 index d28c33ba5..bade0f15d 100644 --- 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 @@ -1,15 +1,25 @@ +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; import { useTriggerNodeSelection } from '@/workflow/workflow-diagram/hooks/useTriggerNodeSelection'; -import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState'; +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, useRecoilState } from 'recoil'; +import { RecoilRoot } from 'recoil'; jest.mock('@xyflow/react', () => ({ useReactFlow: jest.fn(), })); const wrapper = ({ children }: { children: React.ReactNode }) => ( - {children} + + + {children} + + ); describe('useTriggerNodeSelection', () => { @@ -31,7 +41,9 @@ describe('useTriggerNodeSelection', () => { const [ workflowDiagramTriggerNodeSelection, setWorkflowDiagramTriggerNodeSelection, - ] = useRecoilState(workflowDiagramTriggerNodeSelectionState); + ] = useRecoilComponentStateV2( + workflowDiagramTriggerNodeSelectionComponentState, + ); useTriggerNodeSelection(); 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 index ab8849c84..4b52dd43c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useHandleWorkflowRunDiagramCanvasInit.ts @@ -1,10 +1,12 @@ import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext'; import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { workflowDiagramStatusState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusState'; -import { workflowRunStepToOpenByDefaultState } from '@/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultState'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2'; +import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowDiagramStatusComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramStatusComponentState'; +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'; @@ -17,6 +19,21 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => { const { openWorkflowRunViewStepInCommandMenu } = useWorkflowCommandMenu(); const { isInRightDrawer } = useContext(ActionMenuContext); + const workflowRunId = useWorkflowRunIdOrThrow(); + + const workflowVisualizerWorkflowIdState = useRecoilComponentCallbackStateV2( + workflowVisualizerWorkflowIdComponentState, + ); + const workflowDiagramStatusState = useRecoilComponentCallbackStateV2( + workflowDiagramStatusComponentState, + ); + const workflowRunStepToOpenByDefaultState = useRecoilComponentCallbackStateV2( + workflowRunStepToOpenByDefaultComponentState, + ); + const workflowSelectedNodeState = useRecoilComponentCallbackStateV2( + workflowSelectedNodeComponentState, + ); + const handleWorkflowRunDiagramCanvasInit = useRecoilCallback( ({ snapshot, set }) => () => { @@ -43,8 +60,11 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => { ); if (isDefined(workflowStepToOpenByDefault)) { - const workflowId = getSnapshotValue(snapshot, workflowIdState); - if (!isDefined(workflowId)) { + 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.', ); @@ -53,7 +73,8 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => { set(workflowSelectedNodeState, workflowStepToOpenByDefault.id); openWorkflowRunViewStepInCommandMenu({ - workflowId, + workflowId: workflowVisualizerWorkflowId, + workflowRunId, title: workflowStepToOpenByDefault.data.name, icon: getIcon( getWorkflowNodeIconKey(workflowStepToOpenByDefault.data), @@ -65,7 +86,16 @@ export const useHandleWorkflowRunDiagramCanvasInit = () => { set(workflowRunStepToOpenByDefaultState, undefined); } }, - [getIcon, isInRightDrawer, openWorkflowRunViewStepInCommandMenu], + [ + workflowDiagramStatusState, + isInRightDrawer, + workflowRunStepToOpenByDefaultState, + workflowVisualizerWorkflowIdState, + workflowSelectedNodeState, + openWorkflowRunViewStepInCommandMenu, + workflowRunId, + getIcon, + ], ); return { 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 218205f5b..1487c835a 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,18 +1,21 @@ import { useCallback } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { workflowCreateStepFromParentStepIdState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; +import { workflowCreateStepFromParentStepIdComponentState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdComponentState'; import { isDefined } from 'twenty-shared/utils'; export const useStartNodeCreation = () => { - const setWorkflowCreateStepFromParentStepId = useSetRecoilState( - workflowCreateStepFromParentStepIdState, + const setWorkflowCreateStepFromParentStepId = useSetRecoilComponentStateV2( + workflowCreateStepFromParentStepIdComponentState, ); const { openStepSelectInCommandMenu } = useWorkflowCommandMenu(); - const workflowId = useRecoilValue(workflowIdState); + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); /** * This function is used in a context where dependencies shouldn't change much. @@ -22,14 +25,14 @@ export const useStartNodeCreation = () => { (parentNodeId: string) => { setWorkflowCreateStepFromParentStepId(parentNodeId); - if (isDefined(workflowId)) { - openStepSelectInCommandMenu(workflowId); + if (isDefined(workflowVisualizerWorkflowId)) { + openStepSelectInCommandMenu(workflowVisualizerWorkflowId); return; } }, [ setWorkflowCreateStepFromParentStepId, - workflowId, + workflowVisualizerWorkflowId, openStepSelectInCommandMenu, ], ); 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 index fe8a87c3f..bddbaaf12 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useTriggerNodeSelection.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useTriggerNodeSelection.ts @@ -1,11 +1,11 @@ -import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState'; +import { useRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentStateV2'; +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 { useRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; export const useTriggerNodeSelection = () => { @@ -14,7 +14,9 @@ export const useTriggerNodeSelection = () => { const [ workflowDiagramTriggerNodeSelection, setWorkflowDiagramTriggerNodeSelection, - ] = useRecoilState(workflowDiagramTriggerNodeSelectionState); + ] = useRecoilComponentStateV2( + workflowDiagramTriggerNodeSelectionComponentState, + ); useEffect(() => { if (!isDefined(workflowDiagramTriggerNodeSelection)) { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow.ts index 10a62c6c1..b36fb2985 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow.ts @@ -1,9 +1,11 @@ -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; -import { useRecoilValue } from 'recoil'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { isDefined } from 'twenty-shared/utils'; export const useWorkflowSelectedNodeOrThrow = () => { - const workflowSelectedNode = useRecoilValue(workflowSelectedNodeState); + const workflowSelectedNode = useRecoilComponentValueV2( + workflowSelectedNodeComponentState, + ); if (!isDefined(workflowSelectedNode)) { throw new Error( diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext.tsx new file mode 100644 index 000000000..e9787f3ab --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext.tsx @@ -0,0 +1,6 @@ +import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext'; + +export const WorkflowRunVisualizerComponentInstanceContext = + createComponentInstanceContext({ + instanceId: '', + }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext.tsx new file mode 100644 index 000000000..64493b8e2 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext.tsx @@ -0,0 +1,6 @@ +import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext'; + +export const WorkflowVisualizerComponentInstanceContext = + createComponentInstanceContext({ + instanceId: '', + }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramComponentState.ts new file mode 100644 index 000000000..f861397f4 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramComponentState.ts @@ -0,0 +1,11 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; +import { WorkflowDiagram } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; + +export const workflowDiagramComponentState = createComponentStateV2< + WorkflowDiagram | undefined +>({ + key: 'workflowDiagramComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramState.ts deleted file mode 100644 index fc535453b..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { WorkflowDiagram } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { createState } from 'twenty-ui/utilities'; - -export const workflowDiagramState = createState({ - key: 'workflowDiagramState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramStatusComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramStatusComponentState.ts new file mode 100644 index 000000000..f63685f25 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramStatusComponentState.ts @@ -0,0 +1,12 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowRunVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowRunVisualizerComponentInstanceContext'; +import { WorkflowDiagramStatus } from '@/workflow/workflow-diagram/types/WorkflowDiagramStatus'; + +// This state must be fresh every time the Reactflow component is mounted. +// We use another instance context whose instanceId is an id unique to the component hierarchy. +export const workflowDiagramStatusComponentState = + createComponentStateV2({ + key: 'workflowDiagramStatusComponentState', + defaultValue: 'computing-diagram', + componentInstanceContext: WorkflowRunVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramStatusState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramStatusState.ts deleted file mode 100644 index 6a43fac9f..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramStatusState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; - -export const workflowDiagramStatusState = createState< - 'computing-diagram' | 'computing-dimensions' | 'done' ->({ - key: 'workflowDiagramStatusState', - defaultValue: 'computing-diagram', -}); 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 new file mode 100644 index 000000000..341dba6d7 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowDiagramTriggerNodeSelectionComponentState = + createComponentStateV2({ + key: 'workflowDiagramTriggerNodeSelectionComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState.ts deleted file mode 100644 index a1df2bab9..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; -export const workflowDiagramTriggerNodeSelectionState = createState< - string | undefined ->({ - key: 'workflowDiagramTriggerNodeSelectionState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowReactFlowRefState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowReactFlowRefState.ts deleted file mode 100644 index 3f16972b7..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowReactFlowRefState.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { RefObject } from 'react'; -import { createState } from 'twenty-ui/utilities'; - -export const workflowReactFlowRefState = - createState | null>({ - key: 'workflowReactFlowRefState', - defaultValue: null, - }); 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 new file mode 100644 index 000000000..f0830b445 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultComponentState.ts @@ -0,0 +1,16 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; +import { WorkflowRunDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; + +export const workflowRunStepToOpenByDefaultComponentState = + createComponentStateV2< + | { + id: string; + data: WorkflowRunDiagramStepNodeData; + } + | undefined + >({ + key: 'workflowRunStepToOpenByDefaultComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultState.ts deleted file mode 100644 index d17465faf..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowRunStepToOpenByDefaultState.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { WorkflowRunDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { createState } from 'twenty-ui/utilities'; - -export const workflowRunStepToOpenByDefaultState = createState< - | { - id: string; - data: WorkflowRunDiagramStepNodeData; - } - | undefined ->({ - key: 'workflowStepIdToOpenByDefaultState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedNodeComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedNodeComponentState.ts new file mode 100644 index 000000000..0ffad0a3b --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedNodeComponentState.ts @@ -0,0 +1,10 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowSelectedNodeComponentState = createComponentStateV2< + string | undefined +>({ + key: 'workflowSelectedNodeComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, +}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedNodeState.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedNodeState.ts deleted file mode 100644 index 6046be903..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/states/workflowSelectedNodeState.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; - -export const workflowSelectedNodeState = createState({ - key: 'workflowSelectedNodeState', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramStatus.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramStatus.ts new file mode 100644 index 000000000..5b5d7aede --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramStatus.ts @@ -0,0 +1,4 @@ +export type WorkflowDiagramStatus = + | 'computing-diagram' + | 'computing-dimensions' + | 'done'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.ts deleted file mode 100644 index 3d9694c2c..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; -import { renderHook } from '@testing-library/react'; -import { useCreateStep } from '../useCreateStep'; - -const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457'); -const mockCreateWorkflowVersionStep = jest.fn().mockResolvedValue({ - data: { createWorkflowVersionStep: { id: '1', type: 'CODE' } }, -}); - -jest.mock('recoil', () => ({ - useRecoilValue: () => 'parent-step-id', - useSetRecoilState: () => jest.fn(), - atom: (params: any) => params, -})); - -jest.mock( - '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionStep', - () => ({ - useCreateWorkflowVersionStep: () => ({ - createWorkflowVersionStep: mockCreateWorkflowVersionStep, - }), - }), -); - -jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ - useCreateDraftFromWorkflowVersion: () => ({ - createDraftFromWorkflowVersion: mockCreateDraftFromWorkflowVersion, - }), -})); - -describe('useCreateStep', () => { - const mockWorkflow = { - id: '123', - currentVersion: { - id: '456', - status: 'DRAFT', - steps: [], - trigger: { type: 'manual' }, - }, - versions: [], - }; - - it('should create step in draft version', async () => { - const { result } = renderHook(() => - useCreateStep({ - workflow: mockWorkflow as unknown as WorkflowWithCurrentVersion, - }), - ); - await result.current.createStep('CODE'); - - expect(mockCreateWorkflowVersionStep).toHaveBeenCalled(); - }); -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx new file mode 100644 index 000000000..c00e8c934 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/__tests__/useCreateStep.test.tsx @@ -0,0 +1,80 @@ +import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; +import { workflowCreateStepFromParentStepIdComponentState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdComponentState'; +import { renderHook } from '@testing-library/react'; +import { RecoilRoot } from 'recoil'; +import { WorkflowVisualizerComponentInstanceContext } from '../../../workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; +import { useCreateStep } from '../useCreateStep'; + +const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457'); +const mockCreateWorkflowVersionStep = jest.fn().mockResolvedValue({ + data: { createWorkflowVersionStep: { id: '1', type: 'CODE' } }, +}); + +jest.mock( + '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionStep', + () => ({ + useCreateWorkflowVersionStep: () => ({ + createWorkflowVersionStep: mockCreateWorkflowVersionStep, + }), + }), +); + +jest.mock('@/workflow/hooks/useCreateDraftFromWorkflowVersion', () => ({ + useCreateDraftFromWorkflowVersion: () => ({ + createDraftFromWorkflowVersion: mockCreateDraftFromWorkflowVersion, + }), +})); + +const wrapper = ({ children }: { children: React.ReactNode }) => { + const workflowVisualizerComponentInstanceId = + 'workflow-visualizer-instance-id'; + + return ( + { + set( + workflowCreateStepFromParentStepIdComponentState.atomFamily({ + instanceId: workflowVisualizerComponentInstanceId, + }), + 'parent-step-id', + ); + }} + > + + {children} + + + ); +}; + +describe('useCreateStep', () => { + const mockWorkflow = { + id: '123', + currentVersion: { + id: '456', + status: 'DRAFT', + steps: [], + trigger: { type: 'manual' }, + }, + versions: [], + }; + + it('should create step in draft version', async () => { + const { result } = renderHook( + () => + useCreateStep({ + workflow: mockWorkflow as unknown as WorkflowWithCurrentVersion, + }), + { + wrapper, + }, + ); + await result.current.createStep('CODE'); + + expect(mockCreateWorkflowVersionStep).toHaveBeenCalled(); + }); +}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts index 674ca3a0f..d41459a1e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/hooks/useCreateStep.ts @@ -1,13 +1,14 @@ +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; -import { workflowLastCreatedStepIdState } from '@/workflow/states/workflowLastCreatedStepIdState'; +import { workflowLastCreatedStepIdComponentState } from '@/workflow/states/workflowLastCreatedStepIdComponentState'; import { WorkflowStepType, WorkflowWithCurrentVersion, } from '@/workflow/types/Workflow'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { useCreateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionStep'; -import { workflowCreateStepFromParentStepIdState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; +import { workflowCreateStepFromParentStepIdComponentState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdComponentState'; import { isDefined } from 'twenty-shared/utils'; export const useCreateStep = ({ @@ -16,13 +17,15 @@ export const useCreateStep = ({ workflow: WorkflowWithCurrentVersion; }) => { const { createWorkflowVersionStep } = useCreateWorkflowVersionStep(); - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); - const setWorkflowLastCreatedStepId = useSetRecoilState( - workflowLastCreatedStepIdState, + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); + const setWorkflowLastCreatedStepId = useSetRecoilComponentStateV2( + workflowLastCreatedStepIdComponentState, ); - const workflowCreateStepFromParentStepId = useRecoilValue( - workflowCreateStepFromParentStepIdState, + const workflowCreateStepFromParentStepId = useRecoilComponentValueV2( + workflowCreateStepFromParentStepIdComponentState, ); const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdComponentState.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdComponentState.ts new file mode 100644 index 000000000..98153b858 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdComponentState.ts @@ -0,0 +1,9 @@ +import { createComponentStateV2 } from '@/ui/utilities/state/component-state/utils/createComponentStateV2'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; + +export const workflowCreateStepFromParentStepIdComponentState = + createComponentStateV2({ + key: 'workflowCreateStepFromParentStepIdComponentState', + defaultValue: undefined, + componentInstanceContext: WorkflowVisualizerComponentInstanceContext, + }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState.ts deleted file mode 100644 index e8f52d0a6..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createState } from 'twenty-ui/utilities'; -export const workflowCreateStepFromParentStepIdState = createState< - string | undefined ->({ - key: 'workflowCreateStepFromParentStepId', - defaultValue: undefined, -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx index 8de526e9a..2b43b89cd 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx @@ -3,7 +3,7 @@ import { useServerlessFunctionUpdateFormState } from '@/settings/serverless-func import { useUpdateOneServerlessFunction } from '@/settings/serverless-functions/hooks/useUpdateOneServerlessFunction'; import { useGetUpdatableWorkflowVersion } from '@/workflow/hooks/useGetUpdatableWorkflowVersion'; import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowCodeAction } from '@/workflow/types/Workflow'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { setNestedValue } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/setNestedValue'; @@ -36,11 +36,11 @@ import { Monaco } from '@monaco-editor/react'; import { editor } from 'monaco-editor'; import { AutoTypings } from 'monaco-editor-auto-typings'; import { useEffect, useState } from 'react'; -import { useRecoilState, useRecoilValue } from 'recoil'; -import { useDebouncedCallback } from 'use-debounce'; +import { useRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; -import { CodeEditor } from 'twenty-ui/input'; import { IconCode, IconPlayerPlay, useIcons } from 'twenty-ui/display'; +import { CodeEditor } from 'twenty-ui/input'; +import { useDebouncedCallback } from 'use-debounce'; const StyledCodeEditorContainer = styled.div` display: flex; @@ -82,8 +82,10 @@ export const WorkflowEditActionServerlessFunction = ({ useUpdateOneServerlessFunction(serverlessFunctionId); const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion(); - const workflowId = useRecoilValue(workflowIdState); - const workflow = useWorkflowWithCurrentVersion(workflowId); + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); + const workflow = useWorkflowWithCurrentVersion(workflowVisualizerWorkflowId); const { availablePackages } = useGetAvailablePackages({ id: serverlessFunctionId, }); diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx index 0869263f8..bfb6efd0e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx @@ -8,7 +8,8 @@ import { FormTextFieldInput } from '@/object-record/record-field/form-types/comp import { useTriggerApisOAuth } from '@/settings/accounts/hooks/useTriggerApiOAuth'; import { SettingsPath } from '@/types/SettingsPath'; import { Select } from '@/ui/input/components/Select'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowSendEmailAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; @@ -20,11 +21,11 @@ import { useEffect, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { ConnectedAccountProvider } from 'twenty-shared/types'; import { assertUnreachable, isDefined } from 'twenty-shared/utils'; +import { IconPlus, useIcons } from 'twenty-ui/display'; +import { SelectOption } from 'twenty-ui/input'; import { JsonValue } from 'type-fest'; import { useDebouncedCallback } from 'use-debounce'; import { useNavigateSettings } from '~/hooks/useNavigateSettings'; -import { IconPlus, useIcons } from 'twenty-ui/display'; -import { SelectOption } from 'twenty-ui/input'; type WorkflowEditActionSendEmailProps = { action: WorkflowSendEmailAction; @@ -53,8 +54,10 @@ export const WorkflowEditActionSendEmail = ({ const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const { triggerApisOAuth } = useTriggerApisOAuth(); - const workflowId = useRecoilValue(workflowIdState); - const redirectUrl = `/object/workflow/${workflowId}`; + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); + const redirectUrl = `/object/workflow/${workflowVisualizerWorkflowId}`; const [formData, setFormData] = useState({ connectedAccountId: action.settings.input.connectedAccountId, diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerType.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerType.tsx deleted file mode 100644 index c41a8f3fd..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerType.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; -import { RightDrawerWorkflowSelectTriggerTypeContent } from '@/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerTypeContent'; -import { useRecoilValue } from 'recoil'; -import { isDefined } from 'twenty-shared/utils'; - -export const RightDrawerWorkflowSelectTriggerType = () => { - const workflowId = useRecoilValue(workflowIdState); - const workflow = useWorkflowWithCurrentVersion(workflowId); - - if (!isDefined(workflow)) { - return null; - } - - return ; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx deleted file mode 100644 index 20ea25d13..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/RightDrawerWorkflowSelectTriggerTypeContent.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu'; -import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; -import { - WorkflowTriggerType, - WorkflowWithCurrentVersion, -} from '@/workflow/types/Workflow'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; -import { RightDrawerStepListContainer } from '@/workflow/workflow-steps/components/RightDrawerWorkflowSelectStepContainer'; -import { RightDrawerWorkflowSelectStepTitle } from '@/workflow/workflow-steps/components/RightDrawerWorkflowSelectStepTitle'; -import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes'; -import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/OtherTriggerTypes'; -import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; -import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; -import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition'; -import { useSetRecoilState } from 'recoil'; -import { MenuItemCommand } from 'twenty-ui/navigation'; -import { useIcons } from 'twenty-ui/display'; - -export const RightDrawerWorkflowSelectTriggerTypeContent = ({ - workflow, -}: { - workflow: WorkflowWithCurrentVersion; -}) => { - const { getIcon } = useIcons(); - const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow }); - - const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); - - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); - const { openWorkflowEditStepInCommandMenu } = useWorkflowCommandMenu(); - - const handleTriggerTypeClick = ({ - type, - defaultLabel, - icon, - }: { - type: WorkflowTriggerType; - defaultLabel: string; - icon: string; - }) => { - return async () => { - await updateTrigger( - getTriggerDefaultDefinition({ - defaultLabel, - type, - activeObjectMetadataItems, - }), - ); - - setWorkflowSelectedNode(TRIGGER_STEP_ID); - - openWorkflowEditStepInCommandMenu( - workflow.id, - defaultLabel, - getIcon(icon), - ); - }; - }; - - return ( - - - Data - - {DATABASE_TRIGGER_TYPES.map((action) => ( - - ))} - - Others - - {OTHER_TRIGGER_TYPES.map((action) => ( - - ))} - - ); -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx index 0b39d9d53..159be6634 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-trigger/components/WorkflowEditTriggerWebhookForm.tsx @@ -5,7 +5,8 @@ import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/Snac import { useSnackBar } from '@/ui/feedback/snack-bar-manager/hooks/useSnackBar'; import { Select } from '@/ui/input/components/Select'; import { TextInputV2 } from '@/ui/input/components/TextInputV2'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowWebhookTrigger } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; @@ -54,7 +55,9 @@ export const WorkflowEditTriggerWebhookForm = ({ const [errorMessages, setErrorMessages] = useState({}); const [errorMessagesVisible, setErrorMessagesVisible] = useState(false); const { getIcon } = useIcons(); - const workflowId = useRecoilValue(workflowIdState); + const workflowVisualizerWorkflowId = useRecoilComponentValueV2( + workflowVisualizerWorkflowIdComponentState, + ); const currentWorkspace = useRecoilValue(currentWorkspaceState); const onBlur = () => { @@ -66,7 +69,7 @@ export const WorkflowEditTriggerWebhookForm = ({ const headerIcon = getTriggerIcon(trigger); const headerType = getTriggerHeaderType(trigger); - const webhookUrl = `${REACT_APP_SERVER_BASE_URL}/webhooks/workflows/${currentWorkspace?.id}/${workflowId}`; + const webhookUrl = `${REACT_APP_SERVER_BASE_URL}/webhooks/workflows/${currentWorkspace?.id}/${workflowVisualizerWorkflowId}`; const displayWebhookUrl = webhookUrl.replace(/^(https?:\/\/)?(www\.)?/, ''); const copyToClipboard = async () => { diff --git a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx index 5fa9e0a3f..d25d2a057 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-variables/components/WorkflowVariablesDropdownFieldItems.tsx @@ -13,13 +13,12 @@ import { isRecordOutputSchema } from '@/workflow/workflow-variables/utils/isReco import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components/DropdownMenuHeader/internal/DropdownMenuHeaderLeftComponent'; import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState'; import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2'; -import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionState'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { workflowDiagramTriggerNodeSelectionComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramTriggerNodeSelectionComponentState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { getCurrentSubStepFromPath } from '@/workflow/workflow-variables/utils/getCurrentSubStepFromPath'; import { getStepHeaderLabel } from '@/workflow/workflow-variables/utils/getStepHeaderLabel'; import { isLinkOutputSchema } from '@/workflow/workflow-variables/utils/isLinkOutputSchema'; import { useState } from 'react'; -import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; import { IconChevronLeft, @@ -42,13 +41,15 @@ export const WorkflowVariablesDropdownFieldItems = ({ const [currentPath, setCurrentPath] = useState([]); const [searchInputValue, setSearchInputValue] = useState(''); const { getIcon } = useIcons(); - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); const setActiveTabId = useSetRecoilComponentStateV2( activeTabIdComponentState, 'workflow-serverless-function-tab-list-component-id', ); - const setWorkflowDiagramTriggerNodeSelection = useSetRecoilState( - workflowDiagramTriggerNodeSelectionState, + const setWorkflowDiagramTriggerNodeSelection = useSetRecoilComponentStateV2( + workflowDiagramTriggerNodeSelectionComponentState, ); const getDisplayedSubStepFields = () => { diff --git a/packages/twenty-front/src/testing/decorators/WorkflowStepDecorator.tsx b/packages/twenty-front/src/testing/decorators/WorkflowStepDecorator.tsx index 943e8bfd4..71c639bcd 100644 --- a/packages/twenty-front/src/testing/decorators/WorkflowStepDecorator.tsx +++ b/packages/twenty-front/src/testing/decorators/WorkflowStepDecorator.tsx @@ -1,52 +1,75 @@ import { useStepsOutputSchema } from '@/workflow/hooks/useStepsOutputSchema'; import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; -import { flowState } from '@/workflow/states/flowState'; -import { workflowIdState } from '@/workflow/states/workflowIdState'; +import { flowComponentState } from '@/workflow/states/flowComponentState'; +import { workflowVisualizerWorkflowIdComponentState } from '@/workflow/states/workflowVisualizerWorkflowIdComponentState'; import { WorkflowVersion } from '@/workflow/types/Workflow'; -import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; +import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { Decorator } from '@storybook/react'; import { useEffect, useState } from 'react'; -import { useSetRecoilState } from 'recoil'; +import { useRecoilCallback } from 'recoil'; import { getWorkflowMock, getWorkflowNodeIdMock, } from '~/testing/mock-data/workflow'; export const WorkflowStepDecorator: Decorator = (Story) => { - const setWorkflowId = useSetRecoilState(workflowIdState); - const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); - const setFlow = useSetRecoilState(flowState); + const workflowVisualizerComponentInstanceId = 'workflow-visualizer-test-id'; + const workflowVersion = getWorkflowMock().versions.edges[0] .node as WorkflowVersion; const { populateStepsOutputSchema } = useStepsOutputSchema(); const [ready, setReady] = useState(false); + const handleMount = useRecoilCallback( + ({ set }) => + () => { + set( + workflowVisualizerWorkflowIdComponentState.atomFamily({ + instanceId: workflowVisualizerComponentInstanceId, + }), + getWorkflowMock().id, + ); + set( + workflowSelectedNodeComponentState.atomFamily({ + instanceId: workflowVisualizerComponentInstanceId, + }), + getWorkflowNodeIdMock(), + ); + set( + flowComponentState.atomFamily({ + instanceId: workflowVisualizerComponentInstanceId, + }), + { + workflowVersionId: workflowVersion.id, + trigger: workflowVersion.trigger, + steps: workflowVersion.steps, + }, + ); + populateStepsOutputSchema(workflowVersion); + setReady(true); + }, + [populateStepsOutputSchema, workflowVersion], + ); + useEffect(() => { - setWorkflowId(getWorkflowMock().id); - setWorkflowSelectedNode(getWorkflowNodeIdMock()); - setFlow({ - workflowVersionId: workflowVersion.id, - trigger: workflowVersion.trigger, - steps: workflowVersion.steps, - }); - populateStepsOutputSchema(workflowVersion); - setReady(true); - }, [ - setWorkflowId, - setWorkflowSelectedNode, - populateStepsOutputSchema, - workflowVersion, - setFlow, - ]); + handleMount(); + }, [handleMount]); return ( - - {ready && } - + + {ready && } + + ); };