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:
Baptiste Devessier
2025-04-11 14:31:34 +02:00
committed by GitHub
parent c8011da4d7
commit e8488e1da0
25 changed files with 268 additions and 234 deletions
@@ -1,11 +1,15 @@
import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow';
import { WorkflowExecutorOutput } from '@/workflow/types/Workflow';
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
import { WorkflowRunStepJsonContainer } from '@/workflow/workflow-steps/components/WorkflowRunStepJsonContainer';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { getActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow';
import { getTriggerHeaderType } from '@/workflow/workflow-trigger/utils/getTriggerHeaderType';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import { getTriggerIconColor } from '@/workflow/workflow-trigger/utils/getTriggerIconColor';
import { useTheme } from '@emotion/react';
import { useLingui } from '@lingui/react/macro';
import { isDefined } from 'twenty-shared/utils';
@@ -30,24 +34,38 @@ export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
return null;
}
const stepOutput = workflowRun.output.stepsOutput[stepId];
const stepOutput = workflowRun.output.stepsOutput[stepId] as
| WorkflowExecutorOutput
| undefined;
const stepDefinition = getStepDefinitionOrThrow({
stepId,
trigger: workflowRun.output.flow.trigger,
steps: workflowRun.output.flow.steps,
});
if (stepDefinition?.type !== 'action') {
throw new Error('The output tab must be rendered with an action step.');
if (
!isDefined(stepDefinition?.definition) ||
!isDefined(stepDefinition.definition.name)
) {
throw new Error('The step is expected to be properly shaped.');
}
const headerTitle = stepDefinition.definition.name;
const headerIcon = getActionIcon(stepDefinition.definition.type);
const headerIconColor = getActionIconColorOrThrow({
theme,
actionType: stepDefinition.definition.type,
});
const headerType = getActionHeaderTypeOrThrow(stepDefinition.definition.type);
const headerIcon =
stepDefinition.type === 'trigger'
? getTriggerIcon(stepDefinition.definition)
: getActionIcon(stepDefinition.definition.type);
const headerIconColor =
stepDefinition.type === 'trigger'
? getTriggerIconColor({ theme })
: getActionIconColorOrThrow({
theme,
actionType: stepDefinition.definition.type,
});
const headerType =
stepDefinition.type === 'trigger'
? getTriggerHeaderType(stepDefinition.definition)
: i18n._(getActionHeaderTypeOrThrow(stepDefinition.definition.type));
const setRedHighlightingForEveryNode: GetJsonNodeHighlighting = () => 'red';
@@ -58,12 +76,12 @@ export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
Icon={getIcon(headerIcon)}
iconColor={headerIconColor}
initialTitle={headerTitle}
headerType={i18n._(headerType)}
headerType={headerType}
/>
<WorkflowRunStepJsonContainer>
<JsonTree
value={stepOutput}
value={stepOutput ?? t`No output available`}
shouldExpandNodeInitially={isTwoFirstDepths}
emptyArrayLabel={t`Empty Array`}
emptyObjectLabel={t`Empty Object`}
@@ -71,7 +89,7 @@ export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
arrowButtonCollapsedLabel={t`Expand`}
arrowButtonExpandedLabel={t`Collapse`}
getNodeHighlighting={
isDefined(stepOutput.error)
isDefined(stepOutput?.error)
? setRedHighlightingForEveryNode
: undefined
}
@@ -1,5 +1,6 @@
import { WorkflowRunOutput } from '@/workflow/types/Workflow';
import { WorkflowDiagramRunStatus } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
import { isNull } from '@sniptt/guards';
import { isDefined } from 'twenty-shared/utils';
@@ -14,6 +15,10 @@ export const getWorkflowRunStepExecutionStatus = ({
return 'not-executed';
}
if (stepId === TRIGGER_STEP_ID) {
return 'success';
}
const stepOutput = workflowRunOutput.stepsOutput?.[stepId];
if (isDefined(stepOutput?.error)) {