Migrate workflow states to component states (#11773)
- Migrated all workflow Recoil states to component states to isolate each workflow visualizer instance. The use case of having two workflow visualizers displayed at the same time appeared recently and will grow in the near future. - We chose to use the `recordId` as the value for the `instanceId` of the component states. Currently, there are a few cases where two workflows or two workflow runs are rendered at the same time. As a consequence, relying on the `recordId` is enough for the moment. - However, there is one case where it's necessary to have a component state scoped to a workflow visualizer instance: the `workflowVisualizerStatusState`. This component is tightly coupled to the `<Reactflow />` component instance rendered in the workflow visualizer, and it must be set to its default value when the component first renders. I achieved that by using another component instance context whose instanceId is an identifier returned by the `useId()` hook in the Workflow Run Card component.
This commit is contained in:
committed by
GitHub
parent
8b68dce795
commit
1543c900ae
@ -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,
|
||||
}) => {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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 <CommandMenuWorkflowSelectActionContent workflow={workflow} />;
|
||||
return (
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: workflowId,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<CommandMenuWorkflowSelectActionContent workflow={workflow} />
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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;
|
||||
};
|
||||
@ -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',
|
||||
@ -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,
|
||||
});
|
||||
@ -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<string | undefined>({
|
||||
key: 'command-menu/workflow-version-id',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: CommandMenuPageComponentInstanceContext,
|
||||
});
|
||||
@ -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 (
|
||||
<WorkflowStepContextProvider
|
||||
value={{ workflowVersionId: workflow.currentVersion.id }}
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: workflowId,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<CommandMenuWorkflowEditStepContent workflow={workflow} />
|
||||
</WorkflowStepContextProvider>
|
||||
<WorkflowStepContextProvider
|
||||
value={{ workflowVersionId: workflow.currentVersion.id }}
|
||||
>
|
||||
<CommandMenuWorkflowEditStepContent workflow={workflow} />
|
||||
</WorkflowStepContextProvider>
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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<TabId>[] = [
|
||||
{
|
||||
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 (
|
||||
<WorkflowStepContextProvider
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
workflowVersionId: workflowRun.workflowVersionId,
|
||||
workflowRunId: workflowRun.id,
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: workflowRunId,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={commandMenuPageComponentInstance.instanceId}
|
||||
/>
|
||||
|
||||
{activeTabId === WorkflowRunTabId.OUTPUT ? (
|
||||
<WorkflowRunStepOutputDetail
|
||||
key={workflowSelectedNode}
|
||||
stepId={workflowSelectedNode}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{activeTabId === WorkflowRunTabId.NODE ? (
|
||||
<WorkflowRunStepNodeDetail
|
||||
stepId={workflowSelectedNode}
|
||||
trigger={flow.trigger}
|
||||
steps={flow.steps}
|
||||
stepExecutionStatus={stepExecutionStatus}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{activeTabId === WorkflowRunTabId.INPUT ? (
|
||||
<WorkflowRunStepInputDetail
|
||||
key={workflowSelectedNode}
|
||||
stepId={workflowSelectedNode}
|
||||
/>
|
||||
) : null}
|
||||
</StyledContainer>
|
||||
</WorkflowStepContextProvider>
|
||||
<CommandMenuWorkflowRunViewStepContent />
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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<TabId>[] = [
|
||||
{
|
||||
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 (
|
||||
<WorkflowStepContextProvider
|
||||
value={{
|
||||
workflowVersionId: workflowRun.workflowVersionId,
|
||||
workflowRunId: workflowRun.id,
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<StyledTabList
|
||||
tabs={tabs}
|
||||
behaveAsLinks={false}
|
||||
componentInstanceId={commandMenuPageComponentInstance.instanceId}
|
||||
/>
|
||||
|
||||
{activeTabId === WorkflowRunTabId.OUTPUT ? (
|
||||
<WorkflowRunStepOutputDetail
|
||||
key={workflowSelectedNode}
|
||||
stepId={workflowSelectedNode}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{activeTabId === WorkflowRunTabId.NODE ? (
|
||||
<WorkflowRunStepNodeDetail
|
||||
stepId={workflowSelectedNode}
|
||||
trigger={flow.trigger}
|
||||
steps={flow.steps}
|
||||
stepExecutionStatus={stepExecutionStatus}
|
||||
/>
|
||||
) : null}
|
||||
|
||||
{activeTabId === WorkflowRunTabId.INPUT ? (
|
||||
<WorkflowRunStepInputDetail
|
||||
key={workflowSelectedNode}
|
||||
stepId={workflowSelectedNode}
|
||||
/>
|
||||
) : null}
|
||||
</StyledContainer>
|
||||
</WorkflowStepContextProvider>
|
||||
);
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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 (
|
||||
<WorkflowStepContextProvider
|
||||
value={{ workflowVersionId: flow.workflowVersionId }}
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: workflowVersionId,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<WorkflowStepDetail
|
||||
stepId={workflowSelectedNode}
|
||||
trigger={flow.trigger}
|
||||
steps={flow.steps}
|
||||
readonly
|
||||
/>
|
||||
</StyledContainer>
|
||||
</WorkflowStepContextProvider>
|
||||
<CommandMenuWorkflowViewStepContent />
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 (
|
||||
<WorkflowStepContextProvider
|
||||
value={{ workflowVersionId: flow.workflowVersionId }}
|
||||
>
|
||||
<StyledContainer>
|
||||
<WorkflowStepDetail
|
||||
stepId={workflowSelectedNode}
|
||||
trigger={flow.trigger}
|
||||
steps={flow.steps}
|
||||
readonly
|
||||
/>
|
||||
</StyledContainer>
|
||||
</WorkflowStepContextProvider>
|
||||
);
|
||||
};
|
||||
@ -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;
|
||||
};
|
||||
@ -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 <CommandMenuWorkflowSelectTriggerTypeContent workflow={workflow} />;
|
||||
return (
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: workflowId,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<CommandMenuWorkflowSelectTriggerTypeContent workflow={workflow} />
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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 = ({
|
||||
|
||||
@ -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, CardComponentType> = {
|
||||
<Calendar targetableObject={targetableObject} />
|
||||
),
|
||||
|
||||
[CardType.WorkflowCard]: ({ targetableObject }) => (
|
||||
<>
|
||||
<WorkflowVisualizerEffect workflowId={targetableObject.id} />
|
||||
<WorkflowVisualizer workflowId={targetableObject.id} />
|
||||
</>
|
||||
),
|
||||
[CardType.WorkflowCard]: ({ targetableObject }) => {
|
||||
return (
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: targetableObject.id,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<WorkflowVisualizerEffect workflowId={targetableObject.id} />
|
||||
<WorkflowVisualizer workflowId={targetableObject.id} />
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
},
|
||||
|
||||
[CardType.WorkflowVersionCard]: ({ targetableObject }) => (
|
||||
<>
|
||||
<WorkflowVersionVisualizerEffect
|
||||
workflowVersionId={targetableObject.id}
|
||||
/>
|
||||
<WorkflowVersionVisualizer workflowVersionId={targetableObject.id} />
|
||||
</>
|
||||
),
|
||||
[CardType.WorkflowVersionCard]: ({ targetableObject }) => {
|
||||
return (
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: targetableObject.id,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<WorkflowVersionVisualizerEffect
|
||||
workflowVersionId={targetableObject.id}
|
||||
/>
|
||||
<WorkflowVersionVisualizer workflowVersionId={targetableObject.id} />
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
},
|
||||
|
||||
[CardType.WorkflowRunCard]: ({ targetableObject }) => (
|
||||
<>
|
||||
<WorkflowRunVisualizerEffect workflowRunId={targetableObject.id} />
|
||||
<ListenRecordUpdatesEffect
|
||||
objectNameSingular={targetableObject.targetObjectNameSingular}
|
||||
recordId={targetableObject.id}
|
||||
listenedFields={['status', 'output']}
|
||||
/>
|
||||
[CardType.WorkflowRunCard]: ({ targetableObject }) => {
|
||||
const componentId = useId();
|
||||
|
||||
<WorkflowRunVisualizer workflowRunId={targetableObject.id} />
|
||||
</>
|
||||
),
|
||||
return (
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getWorkflowVisualizerComponentInstanceId({
|
||||
recordId: targetableObject.id,
|
||||
}),
|
||||
}}
|
||||
>
|
||||
<WorkflowRunVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: componentId,
|
||||
}}
|
||||
>
|
||||
<WorkflowRunVisualizerEffect workflowRunId={targetableObject.id} />
|
||||
<ListenRecordUpdatesEffect
|
||||
objectNameSingular={targetableObject.targetObjectNameSingular}
|
||||
recordId={targetableObject.id}
|
||||
listenedFields={['status', 'output']}
|
||||
/>
|
||||
<WorkflowRunVisualizer workflowRunId={targetableObject.id} />
|
||||
</WorkflowRunVisualizerComponentInstanceContext.Provider>
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@ -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');
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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');
|
||||
}
|
||||
|
||||
@ -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,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -1,5 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
export const workflowIdState = createState<string | undefined>({
|
||||
key: 'workflowIdState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -1,5 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
export const workflowLastCreatedStepIdState = createState<string | undefined>({
|
||||
key: 'workflowLastCreatedStepIdState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
|
||||
export const workflowRunIdState = createState<string | undefined>({
|
||||
key: 'workflowRunIdState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -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<string | undefined>({
|
||||
key: 'workflowVisualizerWorkflowIdComponentState',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: WorkflowVisualizerComponentInstanceContext,
|
||||
});
|
||||
@ -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<string | undefined>({
|
||||
key: 'workflowVisualizerWorkflowRunIdComponentState',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: WorkflowVisualizerComponentInstanceContext,
|
||||
});
|
||||
@ -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<string | undefined>({
|
||||
key: 'workflowVisualizerWorkflowVersionIdComponentState',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: WorkflowVisualizerComponentInstanceContext,
|
||||
});
|
||||
@ -0,0 +1,7 @@
|
||||
export const getWorkflowVisualizerComponentInstanceId = ({
|
||||
recordId,
|
||||
}: {
|
||||
recordId: string;
|
||||
}) => {
|
||||
return recordId;
|
||||
};
|
||||
@ -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<WorkflowDiagramNode>[]) => {
|
||||
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<WorkflowDiagramNode>[]) => {
|
||||
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 = ({
|
||||
<WorkflowDiagramCustomMarkers />
|
||||
|
||||
<ReactFlow
|
||||
ref={setWorkflowReactFlowRef}
|
||||
onInit={handleInit}
|
||||
minZoom={defaultFitViewOptions.minZoom}
|
||||
maxZoom={defaultFitViewOptions.maxZoom}
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
|
||||
import { ActionMenuContext } from '@/action-menu/contexts/ActionMenuContext';
|
||||
import { commandMenuNavigationStackState } from '@/command-menu/states/commandMenuNavigationStackState';
|
||||
|
||||
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 { EMPTY_TRIGGER_STEP_ID } from '@/workflow/workflow-diagram/constants/EmptyTriggerStepId';
|
||||
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
|
||||
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,
|
||||
@ -29,7 +31,9 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
|
||||
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,
|
||||
|
||||
@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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({
|
||||
|
||||
@ -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) ||
|
||||
|
||||
@ -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(() => {
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
@ -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) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(workflowDiagramState, {
|
||||
nodes: [
|
||||
(Story) => {
|
||||
const workflowVisualizerComponentInstanceId =
|
||||
'workflow-visualizer-test-id';
|
||||
|
||||
return (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
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',
|
||||
},
|
||||
],
|
||||
});
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<Story />
|
||||
</StyledContainer>
|
||||
</RecoilRoot>
|
||||
),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: workflowVisualizerComponentInstanceId,
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<Story />
|
||||
</StyledContainer>
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -124,49 +141,65 @@ export const SuccessEdge: Story = {
|
||||
},
|
||||
},
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(workflowDiagramState, {
|
||||
nodes: [
|
||||
(Story) => {
|
||||
const workflowVisualizerComponentInstanceId =
|
||||
'workflow-visualizer-test-id';
|
||||
|
||||
return (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
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',
|
||||
},
|
||||
],
|
||||
});
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<Story />
|
||||
</StyledContainer>
|
||||
</RecoilRoot>
|
||||
),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: workflowVisualizerComponentInstanceId,
|
||||
}}
|
||||
>
|
||||
<StyledContainer>
|
||||
<Story />
|
||||
</StyledContainer>
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@ -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 }) => (
|
||||
<RecoilRoot>{children}</RecoilRoot>
|
||||
<RecoilRoot>
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: 'test-instance-id',
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
|
||||
describe('useTriggerNodeSelection', () => {
|
||||
@ -31,7 +41,9 @@ describe('useTriggerNodeSelection', () => {
|
||||
const [
|
||||
workflowDiagramTriggerNodeSelection,
|
||||
setWorkflowDiagramTriggerNodeSelection,
|
||||
] = useRecoilState(workflowDiagramTriggerNodeSelectionState);
|
||||
] = useRecoilComponentStateV2(
|
||||
workflowDiagramTriggerNodeSelectionComponentState,
|
||||
);
|
||||
|
||||
useTriggerNodeSelection();
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
],
|
||||
);
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const WorkflowRunVisualizerComponentInstanceContext =
|
||||
createComponentInstanceContext({
|
||||
instanceId: '',
|
||||
});
|
||||
@ -0,0 +1,6 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const WorkflowVisualizerComponentInstanceContext =
|
||||
createComponentInstanceContext({
|
||||
instanceId: '',
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { WorkflowDiagram } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
|
||||
export const workflowDiagramState = createState<WorkflowDiagram | undefined>({
|
||||
key: 'workflowDiagramState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -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<WorkflowDiagramStatus>({
|
||||
key: 'workflowDiagramStatusComponentState',
|
||||
defaultValue: 'computing-diagram',
|
||||
componentInstanceContext: WorkflowRunVisualizerComponentInstanceContext,
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
|
||||
export const workflowDiagramStatusState = createState<
|
||||
'computing-diagram' | 'computing-dimensions' | 'done'
|
||||
>({
|
||||
key: 'workflowDiagramStatusState',
|
||||
defaultValue: 'computing-diagram',
|
||||
});
|
||||
@ -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<string | undefined>({
|
||||
key: 'workflowDiagramTriggerNodeSelectionComponentState',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: WorkflowVisualizerComponentInstanceContext,
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
export const workflowDiagramTriggerNodeSelectionState = createState<
|
||||
string | undefined
|
||||
>({
|
||||
key: 'workflowDiagramTriggerNodeSelectionState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -1,8 +0,0 @@
|
||||
import { RefObject } from 'react';
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
|
||||
export const workflowReactFlowRefState =
|
||||
createState<RefObject<HTMLDivElement> | null>({
|
||||
key: 'workflowReactFlowRefState',
|
||||
defaultValue: null,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
@ -1,6 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
|
||||
export const workflowSelectedNodeState = createState<string | undefined>({
|
||||
key: 'workflowSelectedNodeState',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -0,0 +1,4 @@
|
||||
export type WorkflowDiagramStatus =
|
||||
| 'computing-diagram'
|
||||
| 'computing-dimensions'
|
||||
| 'done';
|
||||
@ -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();
|
||||
});
|
||||
});
|
||||
@ -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 (
|
||||
<RecoilRoot
|
||||
initializeState={({ set }) => {
|
||||
set(
|
||||
workflowCreateStepFromParentStepIdComponentState.atomFamily({
|
||||
instanceId: workflowVisualizerComponentInstanceId,
|
||||
}),
|
||||
'parent-step-id',
|
||||
);
|
||||
}}
|
||||
>
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: workflowVisualizerComponentInstanceId,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
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();
|
||||
});
|
||||
});
|
||||
@ -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();
|
||||
|
||||
@ -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<string | undefined>({
|
||||
key: 'workflowCreateStepFromParentStepIdComponentState',
|
||||
defaultValue: undefined,
|
||||
componentInstanceContext: WorkflowVisualizerComponentInstanceContext,
|
||||
});
|
||||
@ -1,7 +0,0 @@
|
||||
import { createState } from 'twenty-ui/utilities';
|
||||
export const workflowCreateStepFromParentStepIdState = createState<
|
||||
string | undefined
|
||||
>({
|
||||
key: 'workflowCreateStepFromParentStepId',
|
||||
defaultValue: undefined,
|
||||
});
|
||||
@ -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,
|
||||
});
|
||||
|
||||
@ -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<SendEmailFormData>({
|
||||
connectedAccountId: action.settings.input.connectedAccountId,
|
||||
|
||||
@ -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 <RightDrawerWorkflowSelectTriggerTypeContent workflow={workflow} />;
|
||||
};
|
||||
@ -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 (
|
||||
<RightDrawerStepListContainer>
|
||||
<RightDrawerWorkflowSelectStepTitle>
|
||||
Data
|
||||
</RightDrawerWorkflowSelectStepTitle>
|
||||
{DATABASE_TRIGGER_TYPES.map((action) => (
|
||||
<MenuItemCommand
|
||||
key={action.defaultLabel}
|
||||
LeftIcon={getIcon(action.icon)}
|
||||
text={action.defaultLabel}
|
||||
onClick={handleTriggerTypeClick(action)}
|
||||
/>
|
||||
))}
|
||||
<RightDrawerWorkflowSelectStepTitle>
|
||||
Others
|
||||
</RightDrawerWorkflowSelectStepTitle>
|
||||
{OTHER_TRIGGER_TYPES.map((action) => (
|
||||
<MenuItemCommand
|
||||
key={action.defaultLabel}
|
||||
LeftIcon={getIcon(action.icon)}
|
||||
text={action.defaultLabel}
|
||||
onClick={handleTriggerTypeClick(action)}
|
||||
/>
|
||||
))}
|
||||
</RightDrawerStepListContainer>
|
||||
);
|
||||
};
|
||||
@ -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<FormErrorMessages>({});
|
||||
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 () => {
|
||||
|
||||
@ -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<string[]>([]);
|
||||
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 = () => {
|
||||
|
||||
@ -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 (
|
||||
<WorkflowStepContextProvider
|
||||
<WorkflowVisualizerComponentInstanceContext.Provider
|
||||
value={{
|
||||
workflowVersionId: workflowVersion.id,
|
||||
workflowRunId: '123',
|
||||
instanceId: workflowVisualizerComponentInstanceId,
|
||||
}}
|
||||
>
|
||||
{ready && <Story />}
|
||||
</WorkflowStepContextProvider>
|
||||
<WorkflowStepContextProvider
|
||||
value={{
|
||||
workflowVersionId: workflowVersion.id,
|
||||
workflowRunId: '123',
|
||||
}}
|
||||
>
|
||||
{ready && <Story />}
|
||||
</WorkflowStepContextProvider>
|
||||
</WorkflowVisualizerComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user