Display workflow step header in workflow run input and output tabs (#11102)
- Wrap the content of Workflow View, Workflow Edit, and Workflow Run side panels with a container making them take all the available height - Remove the `StyledContainer` of code steps as it's redundant with the global container - Add the WorkflowStepHeader to the input and output tabs - Make the JSON visualizer take all the available height in input and output tabs - Reuse the WorkflowStepBody component in the input and output tabs as it applies proper background color ## Demo  Fixes https://discord.com/channels/1130383047699738754/1351906809417568376 --------- Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
This commit is contained in:
committed by
GitHub
parent
1c5f3ef5fa
commit
e6dec51ca6
+63
-34
@@ -1,27 +1,29 @@
|
||||
import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
|
||||
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow';
|
||||
import { getStepDefinitionOrThrow } from '@/workflow/utils/getStepDefinitionOrThrow';
|
||||
import { WorkflowRunStepJsonContainer } from '@/workflow/workflow-steps/components/WorkflowRunStepJsonContainer';
|
||||
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
|
||||
import { getWorkflowPreviousStepId } from '@/workflow/workflow-steps/utils/getWorkflowPreviousStep';
|
||||
import { getWorkflowRunStepContext } from '@/workflow/workflow-steps/utils/getWorkflowRunStepContext';
|
||||
import { getWorkflowVariablesUsedInStep } from '@/workflow/workflow-steps/utils/getWorkflowVariablesUsedInStep';
|
||||
import styled from '@emotion/styled';
|
||||
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 { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import {
|
||||
IconBrackets,
|
||||
JsonNestedNode,
|
||||
JsonTreeContextProvider,
|
||||
ShouldExpandNodeInitiallyProps,
|
||||
useIcons,
|
||||
} from 'twenty-ui';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: grid;
|
||||
overflow-x: auto;
|
||||
padding-block: ${({ theme }) => theme.spacing(4)};
|
||||
padding-inline: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
|
||||
export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
|
||||
const { t } = useLingui();
|
||||
const { t, i18n } = useLingui();
|
||||
const { getIcon } = useIcons();
|
||||
const theme = useTheme();
|
||||
|
||||
const workflowRunId = useWorkflowRunIdOrThrow();
|
||||
const workflowRun = useWorkflowRun({ workflowRunId });
|
||||
@@ -49,6 +51,23 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stepDefinition = getStepDefinitionOrThrow({
|
||||
stepId,
|
||||
trigger: workflowRun.output.flow.trigger,
|
||||
steps: workflowRun.output.flow.steps,
|
||||
});
|
||||
if (stepDefinition?.type !== 'action') {
|
||||
throw new Error('The input tab must be rendered with an action step.');
|
||||
}
|
||||
|
||||
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 variablesUsedInStep = getWorkflowVariablesUsedInStep({
|
||||
step,
|
||||
});
|
||||
@@ -69,30 +88,40 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
|
||||
keyPath.startsWith(previousStepId) && depth < 2;
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<JsonTreeContextProvider
|
||||
value={{
|
||||
emptyArrayLabel: t`Empty Array`,
|
||||
emptyObjectLabel: t`Empty Object`,
|
||||
emptyStringLabel: t`[empty string]`,
|
||||
arrowButtonCollapsedLabel: t`Expand`,
|
||||
arrowButtonExpandedLabel: t`Collapse`,
|
||||
shouldHighlightNode: (keyPath) => variablesUsedInStep.has(keyPath),
|
||||
shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep,
|
||||
}}
|
||||
>
|
||||
<JsonNestedNode
|
||||
elements={stepContext.map(({ id, name, context }) => ({
|
||||
id,
|
||||
label: name,
|
||||
value: context,
|
||||
}))}
|
||||
Icon={IconBrackets}
|
||||
depth={0}
|
||||
keyPath=""
|
||||
emptyElementsText=""
|
||||
/>
|
||||
</JsonTreeContextProvider>
|
||||
</StyledContainer>
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
disabled
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={headerIconColor}
|
||||
initialTitle={headerTitle}
|
||||
headerType={i18n._(headerType)}
|
||||
/>
|
||||
|
||||
<WorkflowRunStepJsonContainer>
|
||||
<JsonTreeContextProvider
|
||||
value={{
|
||||
emptyArrayLabel: t`Empty Array`,
|
||||
emptyObjectLabel: t`Empty Object`,
|
||||
emptyStringLabel: t`[empty string]`,
|
||||
arrowButtonCollapsedLabel: t`Expand`,
|
||||
arrowButtonExpandedLabel: t`Collapse`,
|
||||
shouldHighlightNode: (keyPath) => variablesUsedInStep.has(keyPath),
|
||||
shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep,
|
||||
}}
|
||||
>
|
||||
<JsonNestedNode
|
||||
elements={stepContext.map(({ id, name, context }) => ({
|
||||
id,
|
||||
label: name,
|
||||
value: context,
|
||||
}))}
|
||||
Icon={IconBrackets}
|
||||
depth={0}
|
||||
keyPath=""
|
||||
emptyElementsText=""
|
||||
/>
|
||||
</JsonTreeContextProvider>
|
||||
</WorkflowRunStepJsonContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledWorkflowRunStepJsonContainer = styled(WorkflowStepBody)`
|
||||
grid-template-rows: max-content;
|
||||
gap: 0;
|
||||
display: grid;
|
||||
overflow: auto;
|
||||
`;
|
||||
|
||||
export { StyledWorkflowRunStepJsonContainer as WorkflowRunStepJsonContainer };
|
||||
+49
-21
@@ -1,40 +1,68 @@
|
||||
import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
|
||||
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow';
|
||||
import styled from '@emotion/styled';
|
||||
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 { useTheme } from '@emotion/react';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { isTwoFirstDepths, JsonTree } from 'twenty-ui';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
display: grid;
|
||||
overflow-x: auto;
|
||||
padding-block: ${({ theme }) => theme.spacing(4)};
|
||||
padding-inline: ${({ theme }) => theme.spacing(3)};
|
||||
`;
|
||||
import { isTwoFirstDepths, JsonTree, useIcons } from 'twenty-ui';
|
||||
|
||||
export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
|
||||
const { t, i18n } = useLingui();
|
||||
const theme = useTheme();
|
||||
const { getIcon } = useIcons();
|
||||
|
||||
const workflowRunId = useWorkflowRunIdOrThrow();
|
||||
const workflowRun = useWorkflowRun({ workflowRunId });
|
||||
|
||||
const { t } = useLingui();
|
||||
|
||||
if (!isDefined(workflowRun?.output?.stepsOutput)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const stepOutput = workflowRun.output.stepsOutput[stepId];
|
||||
|
||||
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.');
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<JsonTree
|
||||
value={stepOutput}
|
||||
shouldExpandNodeInitially={isTwoFirstDepths}
|
||||
emptyArrayLabel={t`Empty Array`}
|
||||
emptyObjectLabel={t`Empty Object`}
|
||||
emptyStringLabel={t`[empty string]`}
|
||||
arrowButtonCollapsedLabel={t`Expand`}
|
||||
arrowButtonExpandedLabel={t`Collapse`}
|
||||
<>
|
||||
<WorkflowStepHeader
|
||||
disabled
|
||||
Icon={getIcon(headerIcon)}
|
||||
iconColor={headerIconColor}
|
||||
initialTitle={headerTitle}
|
||||
headerType={i18n._(headerType)}
|
||||
/>
|
||||
</StyledContainer>
|
||||
|
||||
<WorkflowRunStepJsonContainer>
|
||||
<JsonTree
|
||||
value={stepOutput}
|
||||
shouldExpandNodeInitially={isTwoFirstDepths}
|
||||
emptyArrayLabel={t`Empty Array`}
|
||||
emptyObjectLabel={t`Empty Object`}
|
||||
emptyStringLabel={t`[empty string]`}
|
||||
arrowButtonCollapsedLabel={t`Expand`}
|
||||
arrowButtonExpandedLabel={t`Collapse`}
|
||||
/>
|
||||
</WorkflowRunStepJsonContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
+2
-1
@@ -7,7 +7,8 @@ const StyledWorkflowStepBody = styled.div`
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow-y: scroll;
|
||||
padding: ${({ theme }) => theme.spacing(4)};
|
||||
padding-block: ${({ theme }) => theme.spacing(4)};
|
||||
padding-inline: ${({ theme }) => theme.spacing(3)};
|
||||
row-gap: ${({ theme }) => theme.spacing(6)};
|
||||
`;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user