Only display Flow for Workflow Runs and display Output tab for triggers (#11520)
> [!WARNING] > I refactored a bunch of components into utility functions to make it possible to display the `WorkflowStepHeader` component for **triggers** in the `CommandMenuWorkflowRunViewStep` component. Previously, we were asserting that we were displaying the header in `Output` and `Input` tabs only for **actions**. Handling triggers too required a bunch of changes. We can think of making a bigger refactor of this part. In this PR: - Only display the Flow for Workflow Runs; removed the Code Editor tab - Allows users to see the Output of trigger nodes - Prevent impossible states by manually setting the selected tab when selecting a node ## Demo ### Success, Running and Not Executed steps https://github.com/user-attachments/assets/c6bebd0f-5da2-4ccc-aef2-d9890eafa59a ### Failed step https://github.com/user-attachments/assets/e1f4e13a-2f5e-4792-a089-928e4d6b1ac0 Closes https://github.com/twentyhq/core-team-issues/issues/709
This commit is contained in:
committed by
GitHub
parent
c8011da4d7
commit
e8488e1da0
@ -1,16 +1,19 @@
|
||||
import { useWorkflowCommandMenu } from '@/command-menu/hooks/useWorkflowCommandMenu';
|
||||
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 { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
|
||||
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||
import {
|
||||
WorkflowDiagramNode,
|
||||
WorkflowDiagramStepNodeData,
|
||||
WorkflowDiagramRunStatus,
|
||||
WorkflowRunDiagramStepNodeData,
|
||||
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
|
||||
import { WORKFLOW_RUN_STEP_SIDE_PANEL_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/constants/WorkflowRunStepSidePanelTabListComponentId';
|
||||
import { WorkflowRunTabId } from '@/workflow/workflow-steps/types/WorkflowRunTabId';
|
||||
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
|
||||
import { isNull } from '@sniptt/guards';
|
||||
import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react';
|
||||
import { useCallback } from 'react';
|
||||
import { useRecoilCallback, useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
@ -24,9 +27,15 @@ export const WorkflowRunDiagramCanvasEffect = () => {
|
||||
|
||||
const workflowId = useRecoilValue(workflowIdState);
|
||||
|
||||
const goBackToFirstWorkflowRunRightDrawerTabIfNeeded = useRecoilCallback(
|
||||
const resetWorkflowRunRightDrawerTabIfNeeded = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
() => {
|
||||
({
|
||||
workflowSelectedNode,
|
||||
stepExecutionStatus,
|
||||
}: {
|
||||
workflowSelectedNode: string;
|
||||
stepExecutionStatus: WorkflowDiagramRunStatus;
|
||||
}) => {
|
||||
const activeWorkflowRunRightDrawerTab = getSnapshotValue(
|
||||
snapshot,
|
||||
activeTabIdComponentState.atomFamily({
|
||||
@ -34,15 +43,40 @@ export const WorkflowRunDiagramCanvasEffect = () => {
|
||||
}),
|
||||
) as WorkflowRunTabId | null;
|
||||
|
||||
const isInputTabDisabled = getIsInputTabDisabled({
|
||||
stepExecutionStatus,
|
||||
workflowSelectedNode,
|
||||
});
|
||||
const isOutputTabDisabled = getIsOutputTabDisabled({
|
||||
stepExecutionStatus,
|
||||
});
|
||||
|
||||
if (isNull(activeWorkflowRunRightDrawerTab)) {
|
||||
const defaultTabId = isOutputTabDisabled
|
||||
? WorkflowRunTabId.NODE
|
||||
: WorkflowRunTabId.OUTPUT;
|
||||
|
||||
set(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: WORKFLOW_RUN_STEP_SIDE_PANEL_TAB_LIST_COMPONENT_ID,
|
||||
}),
|
||||
defaultTabId,
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
activeWorkflowRunRightDrawerTab === 'input' ||
|
||||
activeWorkflowRunRightDrawerTab === 'output'
|
||||
(isInputTabDisabled &&
|
||||
activeWorkflowRunRightDrawerTab === WorkflowRunTabId.INPUT) ||
|
||||
(isOutputTabDisabled &&
|
||||
activeWorkflowRunRightDrawerTab === WorkflowRunTabId.OUTPUT)
|
||||
) {
|
||||
set(
|
||||
activeTabIdComponentState.atomFamily({
|
||||
instanceId: WORKFLOW_RUN_STEP_SIDE_PANEL_TAB_LIST_COMPONENT_ID,
|
||||
}),
|
||||
'node',
|
||||
WorkflowRunTabId.NODE,
|
||||
);
|
||||
}
|
||||
},
|
||||
@ -59,15 +93,8 @@ export const WorkflowRunDiagramCanvasEffect = () => {
|
||||
|
||||
setWorkflowSelectedNode(selectedNode.id);
|
||||
|
||||
const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData;
|
||||
|
||||
if (
|
||||
selectedNode.id === TRIGGER_STEP_ID ||
|
||||
selectedNodeData.runStatus === 'not-executed' ||
|
||||
selectedNodeData.runStatus === 'running'
|
||||
) {
|
||||
goBackToFirstWorkflowRunRightDrawerTabIfNeeded();
|
||||
}
|
||||
const selectedNodeData =
|
||||
selectedNode.data as WorkflowRunDiagramStepNodeData;
|
||||
|
||||
if (isDefined(workflowId)) {
|
||||
openWorkflowRunViewStepInCommandMenu(
|
||||
@ -75,14 +102,19 @@ export const WorkflowRunDiagramCanvasEffect = () => {
|
||||
selectedNodeData.name,
|
||||
getIcon(getWorkflowNodeIconKey(selectedNodeData)),
|
||||
);
|
||||
|
||||
resetWorkflowRunRightDrawerTabIfNeeded({
|
||||
workflowSelectedNode: selectedNode.id,
|
||||
stepExecutionStatus: selectedNodeData.runStatus,
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
setWorkflowSelectedNode,
|
||||
resetWorkflowRunRightDrawerTabIfNeeded,
|
||||
workflowId,
|
||||
getIcon,
|
||||
goBackToFirstWorkflowRunRightDrawerTabIfNeeded,
|
||||
openWorkflowRunViewStepInCommandMenu,
|
||||
getIcon,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -1,30 +0,0 @@
|
||||
import { useWorkflowRunUnsafe } from '@/workflow/hooks/useWorkflowRunUnsafe';
|
||||
import styled from '@emotion/styled';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { CodeEditor } from 'twenty-ui/input';
|
||||
|
||||
const StyledSourceCodeContainer = styled.div`
|
||||
margin: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
export const WorkflowRunOutputVisualizer = ({
|
||||
workflowRunId,
|
||||
}: {
|
||||
workflowRunId: string;
|
||||
}) => {
|
||||
const workflowRun = useWorkflowRunUnsafe({ workflowRunId });
|
||||
|
||||
if (!isDefined(workflowRun)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyledSourceCodeContainer>
|
||||
<CodeEditor
|
||||
value={JSON.stringify(workflowRun.output, null, 2)}
|
||||
language="json"
|
||||
options={{ readOnly: true, domReadOnly: true }}
|
||||
/>
|
||||
</StyledSourceCodeContainer>
|
||||
);
|
||||
};
|
||||
@ -41,6 +41,13 @@ export type WorkflowDiagramStepNodeData =
|
||||
runStatus?: WorkflowDiagramRunStatus;
|
||||
};
|
||||
|
||||
export type WorkflowRunDiagramStepNodeData = Exclude<
|
||||
WorkflowDiagramStepNodeData,
|
||||
'runStatus'
|
||||
> & {
|
||||
runStatus: WorkflowDiagramRunStatus;
|
||||
};
|
||||
|
||||
export type WorkflowDiagramCreateStepNodeData = {
|
||||
nodeType: 'create-step';
|
||||
parentNodeId: string;
|
||||
|
||||
@ -19,25 +19,19 @@ export const getWorkflowDiagramTriggerNode = ({
|
||||
switch (trigger.type) {
|
||||
case 'MANUAL': {
|
||||
triggerDefaultLabel = 'Manual Trigger';
|
||||
triggerIcon = getTriggerIcon({
|
||||
type: 'MANUAL',
|
||||
});
|
||||
triggerIcon = getTriggerIcon(trigger);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'CRON': {
|
||||
triggerDefaultLabel = 'On a Schedule';
|
||||
triggerIcon = getTriggerIcon({
|
||||
type: 'CRON',
|
||||
});
|
||||
triggerIcon = getTriggerIcon(trigger);
|
||||
|
||||
break;
|
||||
}
|
||||
case 'WEBHOOK': {
|
||||
triggerDefaultLabel = 'Webhook';
|
||||
triggerIcon = getTriggerIcon({
|
||||
type: 'WEBHOOK',
|
||||
});
|
||||
triggerIcon = getTriggerIcon(trigger);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -50,10 +44,7 @@ export const getWorkflowDiagramTriggerNode = ({
|
||||
DATABASE_TRIGGER_TYPES.find((item) => item.event === triggerEvent.event)
|
||||
?.defaultLabel ?? '';
|
||||
|
||||
triggerIcon = getTriggerIcon({
|
||||
type: 'DATABASE_EVENT',
|
||||
eventName: triggerEvent.event,
|
||||
});
|
||||
triggerIcon = getTriggerIcon(trigger);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user