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
@ -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 = ({
|
||||
|
||||
Reference in New Issue
Block a user