Add workflow run visualizer (#10146)

- Remove the tabs from the workflowRun show page; now, we only show the
visualizer with the nodes highlighted based on the run's output
- Create the `generateWorkflowRunDiagram` function to go other each step
and assign a `runStatus` to it based on the workflow run's output

Remaining to do:

- Show the output of each step in the right drawer when selecting one
- The labels (e.g. "1 item") are not set on the edges; we might
implement that later


https://github.com/user-attachments/assets/bcf22f4c-db8c-4b02-9a1a-62d688b4c28e

Closes https://github.com/twentyhq/core-team-issues/issues/338
Closes https://github.com/twentyhq/core-team-issues/issues/336
This commit is contained in:
Baptiste Devessier
2025-02-13 18:57:54 +01:00
committed by GitHub
parent 1863ef7d10
commit 81b2d5bc89
24 changed files with 1374 additions and 170 deletions
@@ -1,4 +1,4 @@
import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
import { WorkflowVersionStatus } from '@/workflow/types/Workflow';
import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase';
import { WorkflowDiagramCanvasEditableEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasEditableEffect';
import { WorkflowDiagramCreateStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramCreateStepNode';
@@ -8,14 +8,14 @@ import { WorkflowDiagramStepNodeEditable } from '@/workflow/workflow-diagram/com
import { ReactFlowProvider } from '@xyflow/react';
export const WorkflowDiagramCanvasEditable = ({
workflowWithCurrentVersion,
versionStatus,
}: {
workflowWithCurrentVersion: WorkflowWithCurrentVersion;
versionStatus: WorkflowVersionStatus;
}) => {
return (
<ReactFlowProvider>
<WorkflowDiagramCanvasBase
status={workflowWithCurrentVersion.currentVersion.status}
status={versionStatus}
nodeTypes={{
default: WorkflowDiagramStepNodeEditable,
'create-step': WorkflowDiagramCreateStepNode,
@@ -1,4 +1,4 @@
import { WorkflowVersion } from '@/workflow/types/Workflow';
import { WorkflowVersionStatus } from '@/workflow/types/Workflow';
import { WorkflowDiagramCanvasBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase';
import { WorkflowDiagramCanvasReadonlyEffect } from '@/workflow/workflow-diagram/components/WorkflowDiagramCanvasReadonlyEffect';
import { WorkflowDiagramDefaultEdge } from '@/workflow/workflow-diagram/components/WorkflowDiagramDefaultEdge';
@@ -8,14 +8,14 @@ import { WorkflowDiagramSuccessEdge } from '@/workflow/workflow-diagram/componen
import { ReactFlowProvider } from '@xyflow/react';
export const WorkflowDiagramCanvasReadonly = ({
workflowVersion,
versionStatus,
}: {
workflowVersion: WorkflowVersion;
versionStatus: WorkflowVersionStatus;
}) => {
return (
<ReactFlowProvider>
<WorkflowDiagramCanvasBase
status={workflowVersion.status}
status={versionStatus}
nodeTypes={{
default: WorkflowDiagramStepNodeReadonly,
'empty-trigger': WorkflowDiagramEmptyTrigger,
@@ -1,6 +1,26 @@
import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase';
import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeIcon';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import {
WorkflowDiagramRunStatus,
WorkflowDiagramStepNodeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
const getNodeVariantFromRunStatus = (
runStatus: WorkflowDiagramRunStatus | undefined,
) => {
switch (runStatus) {
case 'success':
return 'success';
case 'failure':
return 'failure';
case 'running':
return 'default';
case 'not-executed':
return 'not-executed';
default:
return 'default';
}
};
export const WorkflowDiagramStepNodeReadonly = ({
data,
@@ -10,7 +30,7 @@ export const WorkflowDiagramStepNodeReadonly = ({
return (
<WorkflowDiagramStepNodeBase
name={data.name}
variant="default"
variant={getNodeVariantFromRunStatus(data.runStatus)}
nodeType={data.nodeType}
Icon={<WorkflowDiagramStepNodeIcon data={data} />}
isLeafNode={data.isLeafNode}
@@ -0,0 +1,49 @@
import { useWorkflowVersion } from '@/workflow/hooks/useWorkflowVersion';
import { workflowVersionIdState } from '@/workflow/states/workflowVersionIdState';
import { WorkflowRun } from '@/workflow/types/Workflow';
import { workflowDiagramState } from '@/workflow/workflow-diagram/states/workflowDiagramState';
import { generateWorkflowRunDiagram } from '@/workflow/workflow-diagram/utils/generateWorkflowRunDiagram';
import { useEffect } from 'react';
import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-shared';
export const WorkflowRunVisualizerEffect = ({
workflowVersionId,
workflowRun,
}: {
workflowVersionId: string;
workflowRun: WorkflowRun;
}) => {
const workflowVersion = useWorkflowVersion(workflowVersionId);
const setWorkflowVersionId = useSetRecoilState(workflowVersionIdState);
const setWorkflowDiagram = useSetRecoilState(workflowDiagramState);
useEffect(() => {
setWorkflowVersionId(workflowVersionId);
}, [setWorkflowVersionId, workflowVersionId]);
useEffect(() => {
if (
!(
isDefined(workflowVersion) &&
isDefined(workflowVersion.trigger) &&
isDefined(workflowVersion.steps)
)
) {
setWorkflowDiagram(undefined);
return;
}
const nextWorkflowDiagram = generateWorkflowRunDiagram({
trigger: workflowVersion.trigger,
steps: workflowVersion.steps,
output: workflowRun.output,
});
setWorkflowDiagram(nextWorkflowDiagram);
}, [setWorkflowDiagram, workflowRun.output, workflowVersion]);
return null;
};
@@ -11,6 +11,6 @@ export const WorkflowVersionVisualizer = ({
const workflowVersion = useWorkflowVersion(workflowVersionId);
return isDefined(workflowVersion) ? (
<WorkflowDiagramCanvasReadonly workflowVersion={workflowVersion} />
<WorkflowDiagramCanvasReadonly versionStatus={workflowVersion.status} />
) : null;
};
@@ -15,7 +15,7 @@ export const WorkflowVisualizer = ({ workflowId }: { workflowId: string }) => {
{isDefined(workflowWithCurrentVersion) ? (
<WorkflowDiagramCanvasEditable
workflowWithCurrentVersion={workflowWithCurrentVersion}
versionStatus={workflowWithCurrentVersion.currentVersion.status}
/>
) : null}
</>