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

![CleanShot 2025-03-21 at 18 30
26@2x](https://github.com/user-attachments/assets/c3fa512b-c371-4d0b-9bf6-a5f84d333dda)

Fixes
https://discord.com/channels/1130383047699738754/1351906809417568376

---------

Co-authored-by: Thomas Trompette <thomas.trompette@sfr.fr>
This commit is contained in:
Baptiste Devessier
2025-03-24 14:06:26 +01:00
committed by GitHub
parent 1c5f3ef5fa
commit e6dec51ca6
23 changed files with 294 additions and 125 deletions

View File

@ -4,6 +4,13 @@ import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hook
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail'; import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail';
import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep';
import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger'; import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hooks/useUpdateWorkflowVersionTrigger';
import styled from '@emotion/styled';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
`;
export const CommandMenuWorkflowEditStepContent = ({ export const CommandMenuWorkflowEditStepContent = ({
workflow, workflow,
@ -19,6 +26,7 @@ export const CommandMenuWorkflowEditStepContent = ({
}); });
return ( return (
<StyledContainer>
<WorkflowStepDetail <WorkflowStepDetail
stepId={workflowSelectedNode} stepId={workflowSelectedNode}
trigger={flow.trigger} trigger={flow.trigger}
@ -26,5 +34,6 @@ export const CommandMenuWorkflowEditStepContent = ({
onActionUpdate={updateStep} onActionUpdate={updateStep}
onTriggerUpdate={updateTrigger} onTriggerUpdate={updateTrigger}
/> />
</StyledContainer>
); );
}; };

View File

@ -20,17 +20,17 @@ import styled from '@emotion/styled';
import { IconLogin2, IconLogout, IconStepInto } from 'twenty-ui'; import { IconLogin2, IconLogout, IconStepInto } from 'twenty-ui';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
const StyledTabList = styled(TabList)`
background-color: ${({ theme }) => theme.background.secondary};
padding-left: ${({ theme }) => theme.spacing(2)};
`;
const StyledContainer = styled.div` const StyledContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
`; `;
const StyledTabList = styled(TabList)`
background-color: ${({ theme }) => theme.background.secondary};
padding-left: ${({ theme }) => theme.spacing(2)};
`;
type TabId = WorkflowRunTabIdType; type TabId = WorkflowRunTabIdType;
export const CommandMenuWorkflowRunViewStep = () => { export const CommandMenuWorkflowRunViewStep = () => {

View File

@ -2,20 +2,30 @@ import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow';
import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext';
import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow'; import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow';
import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail'; 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 CommandMenuWorkflowViewStep = () => { export const CommandMenuWorkflowViewStep = () => {
const flow = useFlowOrThrow(); const flow = useFlowOrThrow();
const workflowSelectedNode = useWorkflowSelectedNodeOrThrow(); const workflowSelectedNode = useWorkflowSelectedNodeOrThrow();
return ( return (
<WorkflowStepContextProvider <WorkflowStepContextProvider
value={{ workflowVersionId: flow.workflowVersionId }} value={{ workflowVersionId: flow.workflowVersionId }}
> >
<StyledContainer>
<WorkflowStepDetail <WorkflowStepDetail
stepId={workflowSelectedNode} stepId={workflowSelectedNode}
trigger={flow.trigger} trigger={flow.trigger}
steps={flow.steps} steps={flow.steps}
readonly readonly
/> />
</StyledContainer>
</WorkflowStepContextProvider> </WorkflowStepContextProvider>
); );
}; };

View File

@ -1,27 +1,29 @@
import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; 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 { getWorkflowPreviousStepId } from '@/workflow/workflow-steps/utils/getWorkflowPreviousStep';
import { getWorkflowRunStepContext } from '@/workflow/workflow-steps/utils/getWorkflowRunStepContext'; import { getWorkflowRunStepContext } from '@/workflow/workflow-steps/utils/getWorkflowRunStepContext';
import { getWorkflowVariablesUsedInStep } from '@/workflow/workflow-steps/utils/getWorkflowVariablesUsedInStep'; 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 { useLingui } from '@lingui/react/macro';
import { import {
IconBrackets, IconBrackets,
JsonNestedNode, JsonNestedNode,
JsonTreeContextProvider, JsonTreeContextProvider,
ShouldExpandNodeInitiallyProps, ShouldExpandNodeInitiallyProps,
useIcons,
} from 'twenty-ui'; } from 'twenty-ui';
import { isDefined } from 'twenty-shared/utils'; 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 }) => { export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
const { t } = useLingui(); const { t, i18n } = useLingui();
const { getIcon } = useIcons();
const theme = useTheme();
const workflowRunId = useWorkflowRunIdOrThrow(); const workflowRunId = useWorkflowRunIdOrThrow();
const workflowRun = useWorkflowRun({ workflowRunId }); const workflowRun = useWorkflowRun({ workflowRunId });
@ -49,6 +51,23 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
return null; 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({ const variablesUsedInStep = getWorkflowVariablesUsedInStep({
step, step,
}); });
@ -69,7 +88,16 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
keyPath.startsWith(previousStepId) && depth < 2; keyPath.startsWith(previousStepId) && depth < 2;
return ( return (
<StyledContainer> <>
<WorkflowStepHeader
disabled
Icon={getIcon(headerIcon)}
iconColor={headerIconColor}
initialTitle={headerTitle}
headerType={i18n._(headerType)}
/>
<WorkflowRunStepJsonContainer>
<JsonTreeContextProvider <JsonTreeContextProvider
value={{ value={{
emptyArrayLabel: t`Empty Array`, emptyArrayLabel: t`Empty Array`,
@ -93,6 +121,7 @@ export const WorkflowRunStepInputDetail = ({ stepId }: { stepId: string }) => {
emptyElementsText="" emptyElementsText=""
/> />
</JsonTreeContextProvider> </JsonTreeContextProvider>
</StyledContainer> </WorkflowRunStepJsonContainer>
</>
); );
}; };

View File

@ -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 };

View File

@ -1,31 +1,58 @@
import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun'; import { useWorkflowRun } from '@/workflow/hooks/useWorkflowRun';
import { useWorkflowRunIdOrThrow } from '@/workflow/hooks/useWorkflowRunIdOrThrow'; 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 { useLingui } from '@lingui/react/macro';
import { isTwoFirstDepths, JsonTree } from 'twenty-ui';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
import { isTwoFirstDepths, JsonTree, useIcons } from 'twenty-ui';
const StyledContainer = styled.div`
display: grid;
overflow-x: auto;
padding-block: ${({ theme }) => theme.spacing(4)};
padding-inline: ${({ theme }) => theme.spacing(3)};
`;
export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => { export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
const { t, i18n } = useLingui();
const theme = useTheme();
const { getIcon } = useIcons();
const workflowRunId = useWorkflowRunIdOrThrow(); const workflowRunId = useWorkflowRunIdOrThrow();
const workflowRun = useWorkflowRun({ workflowRunId }); const workflowRun = useWorkflowRun({ workflowRunId });
const { t } = useLingui();
if (!isDefined(workflowRun?.output?.stepsOutput)) { if (!isDefined(workflowRun?.output?.stepsOutput)) {
return null; return null;
} }
const stepOutput = workflowRun.output.stepsOutput[stepId]; 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 ( return (
<StyledContainer> <>
<WorkflowStepHeader
disabled
Icon={getIcon(headerIcon)}
iconColor={headerIconColor}
initialTitle={headerTitle}
headerType={i18n._(headerType)}
/>
<WorkflowRunStepJsonContainer>
<JsonTree <JsonTree
value={stepOutput} value={stepOutput}
shouldExpandNodeInitially={isTwoFirstDepths} shouldExpandNodeInitially={isTwoFirstDepths}
@ -35,6 +62,7 @@ export const WorkflowRunStepOutputDetail = ({ stepId }: { stepId: string }) => {
arrowButtonCollapsedLabel={t`Expand`} arrowButtonCollapsedLabel={t`Expand`}
arrowButtonExpandedLabel={t`Collapse`} arrowButtonExpandedLabel={t`Collapse`}
/> />
</StyledContainer> </WorkflowRunStepJsonContainer>
</>
); );
}; };

View File

@ -7,7 +7,8 @@ const StyledWorkflowStepBody = styled.div`
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
overflow-y: scroll; 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)}; row-gap: ${({ theme }) => theme.spacing(6)};
`; `;

View File

@ -27,9 +27,10 @@ import { WorkflowEditActionServerlessFunctionFields } from '@/workflow/workflow-
import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowServerlessFunctionTabListComponentId'; import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/code-action/constants/WorkflowServerlessFunctionTabListComponentId';
import { WorkflowServerlessFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowServerlessFunctionTabId'; import { WorkflowServerlessFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowServerlessFunctionTabId';
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Monaco } from '@monaco-editor/react'; import { Monaco } from '@monaco-editor/react';
import { editor } from 'monaco-editor'; import { editor } from 'monaco-editor';
@ -40,12 +41,6 @@ import { CodeEditor, IconCode, IconPlayerPlay, useIcons } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
`;
const StyledCodeEditorContainer = styled.div` const StyledCodeEditorContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -76,7 +71,6 @@ export const WorkflowEditActionServerlessFunction = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionServerlessFunctionProps) => { }: WorkflowEditActionServerlessFunctionProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const serverlessFunctionId = action.settings.input.serverlessFunctionId; const serverlessFunctionId = action.settings.input.serverlessFunctionId;
const activeTabId = useRecoilComponentValueV2( const activeTabId = useRecoilComponentValueV2(
@ -287,10 +281,12 @@ export const WorkflowEditActionServerlessFunction = ({
? action.name ? action.name
: 'Code - Serverless Function'; : 'Code - Serverless Function';
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
return ( return (
!loading && ( !loading && (
<StyledContainer> <>
<StyledTabList <StyledTabList
tabs={tabs} tabs={tabs}
behaveAsLinks={false} behaveAsLinks={false}
@ -303,9 +299,9 @@ export const WorkflowEditActionServerlessFunction = ({
updateAction({ name: newName }); updateAction({ name: newName });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.color.orange} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Code" headerType={headerType}
disabled={actionOptions.readonly} disabled={actionOptions.readonly}
/> />
<WorkflowStepBody> <WorkflowStepBody>
@ -373,7 +369,7 @@ export const WorkflowEditActionServerlessFunction = ({
]} ]}
/> />
)} )}
</StyledContainer> </>
) )
); );
}; };

View File

@ -7,8 +7,9 @@ import { INDEX_FILE_PATH } from '@/serverless-functions/constants/IndexFilePath'
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowEditActionServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunctionFields'; import { WorkflowEditActionServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunctionFields';
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/code-action/utils/getWrongExportedFunctionMarkers';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { Monaco } from '@monaco-editor/react'; import { Monaco } from '@monaco-editor/react';
import { editor } from 'monaco-editor'; import { editor } from 'monaco-editor';
@ -16,12 +17,6 @@ import { AutoTypings } from 'monaco-editor-auto-typings';
import { CodeEditor, useIcons } from 'twenty-ui'; import { CodeEditor, useIcons } from 'twenty-ui';
import { isDefined } from 'twenty-shared/utils'; import { isDefined } from 'twenty-shared/utils';
const StyledContainer = styled.div`
display: flex;
flex-direction: column;
height: 100%;
`;
const StyledCodeEditorContainer = styled.div` const StyledCodeEditorContainer = styled.div`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -34,7 +29,6 @@ type WorkflowReadonlyActionServerlessFunctionProps = {
export const WorkflowReadonlyActionServerlessFunction = ({ export const WorkflowReadonlyActionServerlessFunction = ({
action, action,
}: WorkflowReadonlyActionServerlessFunctionProps) => { }: WorkflowReadonlyActionServerlessFunctionProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const serverlessFunctionId = action.settings.input.serverlessFunctionId; const serverlessFunctionId = action.settings.input.serverlessFunctionId;
const serverlessFunctionVersion = const serverlessFunctionVersion =
@ -66,18 +60,20 @@ export const WorkflowReadonlyActionServerlessFunction = ({
? action.name ? action.name
: 'Code - Serverless Function'; : 'Code - Serverless Function';
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
if (loading) { if (loading) {
return null; return null;
} }
return ( return (
<StyledContainer> <>
<WorkflowStepHeader <WorkflowStepHeader
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.color.orange} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Code" headerType={headerType}
disabled disabled
/> />
<WorkflowStepBody> <WorkflowStepBody>
@ -99,6 +95,6 @@ export const WorkflowReadonlyActionServerlessFunction = ({
/> />
</StyledCodeEditorContainer> </StyledCodeEditorContainer>
</WorkflowStepBody> </WorkflowStepBody>
</StyledContainer> </>
); );
}; };

View File

@ -7,9 +7,10 @@ import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOr
import { WorkflowCreateRecordAction } from '@/workflow/types/Workflow'; import { WorkflowCreateRecordAction } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { HorizontalSeparator, useIcons } from 'twenty-ui';
import { JsonValue } from 'type-fest'; import { JsonValue } from 'type-fest';
@ -57,7 +58,6 @@ export const WorkflowEditActionCreateRecord = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionCreateRecordProps) => { }: WorkflowEditActionCreateRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
@ -159,6 +159,8 @@ export const WorkflowEditActionCreateRecord = ({
const headerTitle = isDefined(action.name) ? action.name : `Create Record`; const headerTitle = isDefined(action.name) ? action.name : `Create Record`;
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
return ( return (
<> <>
@ -174,9 +176,9 @@ export const WorkflowEditActionCreateRecord = ({
}); });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType={headerType}
disabled={isFormDisabled} disabled={isFormDisabled}
/> />
<WorkflowStepBody> <WorkflowStepBody>

View File

@ -3,10 +3,11 @@ import { Select, SelectOption } from '@/ui/input/components/Select';
import { WorkflowDeleteRecordAction } from '@/workflow/types/Workflow'; import { WorkflowDeleteRecordAction } from '@/workflow/types/Workflow';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker'; import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { HorizontalSeparator, useIcons } from 'twenty-ui';
import { JsonValue } from 'type-fest'; import { JsonValue } from 'type-fest';
@ -34,7 +35,6 @@ export const WorkflowEditActionDeleteRecord = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionDeleteRecordProps) => { }: WorkflowEditActionDeleteRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
@ -108,6 +108,8 @@ export const WorkflowEditActionDeleteRecord = ({
const headerTitle = isDefined(action.name) ? action.name : `Delete Record`; const headerTitle = isDefined(action.name) ? action.name : `Delete Record`;
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
return ( return (
<> <>
@ -123,9 +125,9 @@ export const WorkflowEditActionDeleteRecord = ({
}); });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType={headerType}
disabled={isFormDisabled} disabled={isFormDisabled}
/> />
<WorkflowStepBody> <WorkflowStepBody>

View File

@ -2,11 +2,12 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilte
import { Select, SelectOption } from '@/ui/input/components/Select'; import { Select, SelectOption } from '@/ui/input/components/Select';
import { WorkflowFindRecordsAction } from '@/workflow/types/Workflow'; import { WorkflowFindRecordsAction } from '@/workflow/types/Workflow';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput'; import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { HorizontalSeparator, useIcons } from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
@ -33,7 +34,6 @@ export const WorkflowEditActionFindRecords = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionFindRecordsProps) => { }: WorkflowEditActionFindRecordsProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
@ -90,6 +90,8 @@ export const WorkflowEditActionFindRecords = ({
const headerTitle = isDefined(action.name) ? action.name : `Search Records`; const headerTitle = isDefined(action.name) ? action.name : `Search Records`;
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
return ( return (
<> <>
@ -105,9 +107,9 @@ export const WorkflowEditActionFindRecords = ({
}); });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType={headerType}
disabled={isFormDisabled} disabled={isFormDisabled}
/> />
<WorkflowStepBody> <WorkflowStepBody>

View File

@ -12,9 +12,10 @@ import { workflowIdState } from '@/workflow/states/workflowIdState';
import { WorkflowSendEmailAction } from '@/workflow/types/Workflow'; import { WorkflowSendEmailAction } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { IconPlus, useIcons } from 'twenty-ui'; import { IconPlus, useIcons } from 'twenty-ui';
@ -47,7 +48,6 @@ export const WorkflowEditActionSendEmail = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionSendEmailProps) => { }: WorkflowEditActionSendEmailProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { triggerApisOAuth } = useTriggerApisOAuth(); const { triggerApisOAuth } = useTriggerApisOAuth();
@ -188,6 +188,9 @@ export const WorkflowEditActionSendEmail = ({
const headerTitle = isDefined(action.name) ? action.name : 'Send Email'; const headerTitle = isDefined(action.name) ? action.name : 'Send Email';
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
const navigate = useNavigateSettings(); const navigate = useNavigateSettings();
const { closeCommandMenu } = useCommandMenu(); const { closeCommandMenu } = useCommandMenu();
@ -206,9 +209,9 @@ export const WorkflowEditActionSendEmail = ({
}); });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.color.blue} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Email" headerType={headerType}
disabled={actionOptions.readonly} disabled={actionOptions.readonly}
/> />
<WorkflowStepBody> <WorkflowStepBody>

View File

@ -1,7 +1,6 @@
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
import { Select, SelectOption } from '@/ui/input/components/Select'; import { Select, SelectOption } from '@/ui/input/components/Select';
import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow'; import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow';
import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition'; import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
@ -10,6 +9,8 @@ import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-typ
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker'; import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { HorizontalSeparator, useIcons } from 'twenty-ui';
@ -59,7 +60,6 @@ export const WorkflowEditActionUpdateRecord = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionUpdateRecordProps) => { }: WorkflowEditActionUpdateRecordProps) => {
const theme = useTheme();
const { getIcon } = useIcons(); const { getIcon } = useIcons();
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
@ -160,6 +160,8 @@ export const WorkflowEditActionUpdateRecord = ({
const headerTitle = isDefined(action.name) ? action.name : `Update Record`; const headerTitle = isDefined(action.name) ? action.name : `Update Record`;
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
return ( return (
<> <>
@ -175,9 +177,9 @@ export const WorkflowEditActionUpdateRecord = ({
}); });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType={headerType}
disabled={isFormDisabled} disabled={isFormDisabled}
/> />

View File

@ -3,6 +3,7 @@ import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflo
import { Meta, StoryObj } from '@storybook/react'; import { Meta, StoryObj } from '@storybook/react';
import { expect, fn, userEvent, within } from '@storybook/test'; import { expect, fn, userEvent, within } from '@storybook/test';
import { ComponentDecorator, RouterDecorator } from 'twenty-ui'; import { ComponentDecorator, RouterDecorator } from 'twenty-ui';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator';
import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator';
import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator'; import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator';
@ -50,6 +51,7 @@ const meta: Meta<typeof WorkflowEditActionFindRecords> = {
SnackBarDecorator, SnackBarDecorator,
RouterDecorator, RouterDecorator,
WorkspaceDecorator, WorkspaceDecorator,
I18nFrontDecorator,
], ],
}; };

View File

@ -1,8 +1,11 @@
import { WorkflowStepType } from '@/workflow/types/Workflow'; import { WorkflowActionType } from '@/workflow/types/Workflow';
export const OTHER_ACTIONS: Array<{ export const OTHER_ACTIONS: Array<{
label: string; label: string;
type: WorkflowStepType; type: Exclude<
WorkflowActionType,
'CREATE_RECORD' | 'UPDATE_RECORD' | 'DELETE_RECORD' | 'FIND_RECORDS'
>;
icon: string; icon: string;
}> = [ }> = [
{ {

View File

@ -1,8 +1,11 @@
import { WorkflowStepType } from '@/workflow/types/Workflow'; import { WorkflowActionType } from '@/workflow/types/Workflow';
export const RECORD_ACTIONS: Array<{ export const RECORD_ACTIONS: Array<{
label: string; label: string;
type: WorkflowStepType; type: Extract<
WorkflowActionType,
'CREATE_RECORD' | 'UPDATE_RECORD' | 'DELETE_RECORD' | 'FIND_RECORDS'
>;
icon: string; icon: string;
}> = [ }> = [
{ {

View File

@ -8,6 +8,8 @@ import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/Workflo
import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings'; import { WorkflowEditActionFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormFieldSettings';
import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField'; import { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField';
import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings'; import { getDefaultFormFieldSettings } from '@/workflow/workflow-steps/workflow-actions/form-action/utils/getDefaultFormFieldSettings';
import { useActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow';
import { useActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon'; import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
@ -99,6 +101,9 @@ export const WorkflowEditActionFormBuilder = ({
const headerTitle = isDefined(action.name) ? action.name : `Form`; const headerTitle = isDefined(action.name) ? action.name : `Form`;
const headerIcon = getActionIcon(action.type); const headerIcon = getActionIcon(action.type);
const headerIconColor = useActionIconColorOrThrow(action.type);
const headerType = useActionHeaderTypeOrThrow(action.type);
const [selectedField, setSelectedField] = useState<string | null>(null); const [selectedField, setSelectedField] = useState<string | null>(null);
const isFieldSelected = (fieldName: string) => selectedField === fieldName; const isFieldSelected = (fieldName: string) => selectedField === fieldName;
const handleFieldClick = (fieldName: string) => { const handleFieldClick = (fieldName: string) => {
@ -161,9 +166,9 @@ export const WorkflowEditActionFormBuilder = ({
}); });
}} }}
Icon={getIcon(headerIcon)} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={headerIconColor}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType={headerType}
disabled={actionOptions.readonly} disabled={actionOptions.readonly}
/> />
<WorkflowStepBody> <WorkflowStepBody>

View File

@ -0,0 +1,9 @@
import { WorkflowActionType } from '@/workflow/types/Workflow';
import { getActionHeaderTypeOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow';
import { useLingui } from '@lingui/react';
export const useActionHeaderTypeOrThrow = (actionType: WorkflowActionType) => {
const { _ } = useLingui();
return _(getActionHeaderTypeOrThrow(actionType));
};

View File

@ -0,0 +1,9 @@
import { WorkflowActionType } from '@/workflow/types/Workflow';
import { getActionIconColorOrThrow } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow';
import { useTheme } from '@emotion/react';
export const useActionIconColorOrThrow = (actionType: WorkflowActionType) => {
const theme = useTheme();
return getActionIconColorOrThrow({ theme, actionType });
};

View File

@ -0,0 +1,20 @@
import { WorkflowActionType } from '@/workflow/types/Workflow';
import { msg } from '@lingui/core/macro';
import { assertUnreachable } from 'twenty-shared/utils';
export const getActionHeaderTypeOrThrow = (actionType: WorkflowActionType) => {
switch (actionType) {
case 'CODE':
return msg`Code`;
case 'CREATE_RECORD':
case 'UPDATE_RECORD':
case 'DELETE_RECORD':
case 'FIND_RECORDS':
case 'FORM':
return msg`Action`;
case 'SEND_EMAIL':
return msg`Email`;
default:
assertUnreachable(actionType, `Unsupported action type: ${actionType}`);
}
};

View File

@ -1,7 +1,8 @@
import { WorkflowActionType } from '@/workflow/types/Workflow';
import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions'; import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions';
import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions';
export const getActionIcon = (actionType: string) => { export const getActionIcon = (actionType: WorkflowActionType) => {
switch (actionType) { switch (actionType) {
case 'CREATE_RECORD': case 'CREATE_RECORD':
case 'UPDATE_RECORD': case 'UPDATE_RECORD':

View File

@ -0,0 +1,26 @@
import { WorkflowActionType } from '@/workflow/types/Workflow';
import { Theme } from '@emotion/react';
import { assertUnreachable } from 'twenty-shared/utils';
export const getActionIconColorOrThrow = ({
theme,
actionType,
}: {
theme: Theme;
actionType: WorkflowActionType;
}) => {
switch (actionType) {
case 'CODE':
return theme.color.orange;
case 'CREATE_RECORD':
case 'UPDATE_RECORD':
case 'DELETE_RECORD':
case 'FIND_RECORDS':
case 'FORM':
return theme.font.color.tertiary;
case 'SEND_EMAIL':
return theme.color.blue;
default:
assertUnreachable(actionType, `Unsupported action type: ${actionType}`);
}
};