diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx index b7620d641..31f36eb58 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/edit/components/CommandMenuWorkflowEditStepContent.tsx @@ -4,6 +4,13 @@ import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hook import { WorkflowStepDetail } from '@/workflow/workflow-steps/components/WorkflowStepDetail'; import { useUpdateStep } from '@/workflow/workflow-steps/hooks/useUpdateStep'; 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 = ({ workflow, @@ -19,12 +26,14 @@ export const CommandMenuWorkflowEditStepContent = ({ }); return ( - + + + ); }; diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx index 337cd439d..b288a5750 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view-run/components/CommandMenuWorkflowRunViewStep.tsx @@ -20,17 +20,17 @@ import styled from '@emotion/styled'; import { IconLogin2, IconLogout, IconStepInto } from 'twenty-ui'; 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` display: flex; flex-direction: column; height: 100%; `; +const StyledTabList = styled(TabList)` + background-color: ${({ theme }) => theme.background.secondary}; + padding-left: ${({ theme }) => theme.spacing(2)}; +`; + type TabId = WorkflowRunTabIdType; export const CommandMenuWorkflowRunViewStep = () => { diff --git a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx index 97825c9c1..659b17808 100644 --- a/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx +++ b/packages/twenty-front/src/modules/command-menu/pages/workflow/step/view/components/CommandMenuWorkflowViewStep.tsx @@ -2,20 +2,30 @@ import { useFlowOrThrow } from '@/workflow/hooks/useFlowOrThrow'; import { WorkflowStepContextProvider } from '@/workflow/states/context/WorkflowStepContext'; import { useWorkflowSelectedNodeOrThrow } from '@/workflow/workflow-diagram/hooks/useWorkflowSelectedNodeOrThrow'; 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 = () => { const flow = useFlowOrThrow(); const workflowSelectedNode = useWorkflowSelectedNodeOrThrow(); + return ( - + + + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx index d56c6eeed..f86e36d55 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepInputDetail.tsx @@ -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 ( - - variablesUsedInStep.has(keyPath), - shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep, - }} - > - ({ - id, - label: name, - value: context, - }))} - Icon={IconBrackets} - depth={0} - keyPath="" - emptyElementsText="" - /> - - + <> + + + + variablesUsedInStep.has(keyPath), + shouldExpandNodeInitially: isFirstNodeDepthOfPreviousStep, + }} + > + ({ + id, + label: name, + value: context, + }))} + Icon={IconBrackets} + depth={0} + keyPath="" + emptyElementsText="" + /> + + + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepJsonContainer.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepJsonContainer.tsx new file mode 100644 index 000000000..bd61776e4 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepJsonContainer.tsx @@ -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 }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx index c535bd24a..8009bae8e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowRunStepOutputDetail.tsx @@ -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 ( - - + - + + + + + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepBody.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepBody.tsx index 636f77d4e..59068a397 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepBody.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/components/WorkflowStepBody.tsx @@ -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)}; `; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx index 4b790f4dd..71c2dbd40 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowEditActionServerlessFunction.tsx @@ -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 { WorkflowServerlessFunctionTabId } from '@/workflow/workflow-steps/workflow-actions/code-action/types/WorkflowServerlessFunctionTabId'; 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 { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; -import { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Monaco } from '@monaco-editor/react'; import { editor } from 'monaco-editor'; @@ -40,12 +41,6 @@ import { CodeEditor, IconCode, IconPlayerPlay, useIcons } from 'twenty-ui'; import { useDebouncedCallback } from 'use-debounce'; import { isDefined } from 'twenty-shared/utils'; -const StyledContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; -`; - const StyledCodeEditorContainer = styled.div` display: flex; flex-direction: column; @@ -76,7 +71,6 @@ export const WorkflowEditActionServerlessFunction = ({ action, actionOptions, }: WorkflowEditActionServerlessFunctionProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const serverlessFunctionId = action.settings.input.serverlessFunctionId; const activeTabId = useRecoilComponentValueV2( @@ -287,10 +281,12 @@ export const WorkflowEditActionServerlessFunction = ({ ? action.name : 'Code - Serverless Function'; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); return ( !loading && ( - + <> @@ -373,7 +369,7 @@ export const WorkflowEditActionServerlessFunction = ({ ]} /> )} - + ) ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionServerlessFunction.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionServerlessFunction.tsx index f6855c3df..aeb241520 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionServerlessFunction.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/code-action/components/WorkflowReadonlyActionServerlessFunction.tsx @@ -7,8 +7,9 @@ import { INDEX_FILE_PATH } from '@/serverless-functions/constants/IndexFilePath' import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; 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 { 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 { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { Monaco } from '@monaco-editor/react'; import { editor } from 'monaco-editor'; @@ -16,12 +17,6 @@ import { AutoTypings } from 'monaco-editor-auto-typings'; import { CodeEditor, useIcons } from 'twenty-ui'; import { isDefined } from 'twenty-shared/utils'; -const StyledContainer = styled.div` - display: flex; - flex-direction: column; - height: 100%; -`; - const StyledCodeEditorContainer = styled.div` display: flex; flex-direction: column; @@ -34,7 +29,6 @@ type WorkflowReadonlyActionServerlessFunctionProps = { export const WorkflowReadonlyActionServerlessFunction = ({ action, }: WorkflowReadonlyActionServerlessFunctionProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const serverlessFunctionId = action.settings.input.serverlessFunctionId; const serverlessFunctionVersion = @@ -66,18 +60,20 @@ export const WorkflowReadonlyActionServerlessFunction = ({ ? action.name : 'Code - Serverless Function'; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); if (loading) { return null; } return ( - + <> @@ -99,6 +95,6 @@ export const WorkflowReadonlyActionServerlessFunction = ({ /> - + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx index 8213a530a..4c2fc1f46 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionCreateRecord.tsx @@ -7,9 +7,10 @@ import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOr import { WorkflowCreateRecordAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; 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 { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; -import { useTheme } from '@emotion/react'; import { useEffect, useState } from 'react'; import { HorizontalSeparator, useIcons } from 'twenty-ui'; import { JsonValue } from 'type-fest'; @@ -57,7 +58,6 @@ export const WorkflowEditActionCreateRecord = ({ action, actionOptions, }: WorkflowEditActionCreateRecordProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -159,6 +159,8 @@ export const WorkflowEditActionCreateRecord = ({ const headerTitle = isDefined(action.name) ? action.name : `Create Record`; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); return ( <> @@ -174,9 +176,9 @@ export const WorkflowEditActionCreateRecord = ({ }); }} Icon={getIcon(headerIcon)} - iconColor={theme.font.color.tertiary} + iconColor={headerIconColor} initialTitle={headerTitle} - headerType="Action" + headerType={headerType} disabled={isFormDisabled} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx index 38d24f452..c24d42756 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionDeleteRecord.tsx @@ -3,10 +3,11 @@ import { Select, SelectOption } from '@/ui/input/components/Select'; import { WorkflowDeleteRecordAction } from '@/workflow/types/Workflow'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker'; -import { useTheme } from '@emotion/react'; import { useEffect, useState } from 'react'; 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 { HorizontalSeparator, useIcons } from 'twenty-ui'; import { JsonValue } from 'type-fest'; @@ -34,7 +35,6 @@ export const WorkflowEditActionDeleteRecord = ({ action, actionOptions, }: WorkflowEditActionDeleteRecordProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -108,6 +108,8 @@ export const WorkflowEditActionDeleteRecord = ({ const headerTitle = isDefined(action.name) ? action.name : `Delete Record`; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); return ( <> @@ -123,9 +125,9 @@ export const WorkflowEditActionDeleteRecord = ({ }); }} Icon={getIcon(headerIcon)} - iconColor={theme.font.color.tertiary} + iconColor={headerIconColor} initialTitle={headerTitle} - headerType="Action" + headerType={headerType} disabled={isFormDisabled} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx index 96d970f0a..60bbe3069 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFindRecords.tsx @@ -2,11 +2,12 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilte import { Select, SelectOption } from '@/ui/input/components/Select'; import { WorkflowFindRecordsAction } from '@/workflow/types/Workflow'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; -import { useTheme } from '@emotion/react'; import { useEffect, useState } from 'react'; import { FormNumberFieldInput } from '@/object-record/record-field/form-types/components/FormNumberFieldInput'; 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 { HorizontalSeparator, useIcons } from 'twenty-ui'; import { useDebouncedCallback } from 'use-debounce'; @@ -33,7 +34,6 @@ export const WorkflowEditActionFindRecords = ({ action, actionOptions, }: WorkflowEditActionFindRecordsProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -90,6 +90,8 @@ export const WorkflowEditActionFindRecords = ({ const headerTitle = isDefined(action.name) ? action.name : `Search Records`; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); return ( <> @@ -105,9 +107,9 @@ export const WorkflowEditActionFindRecords = ({ }); }} Icon={getIcon(headerIcon)} - iconColor={theme.font.color.tertiary} + iconColor={headerIconColor} initialTitle={headerTitle} - headerType="Action" + headerType={headerType} disabled={isFormDisabled} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx index ddd875dd2..bbbb1ba20 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionSendEmail.tsx @@ -12,9 +12,10 @@ import { workflowIdState } from '@/workflow/states/workflowIdState'; import { WorkflowSendEmailAction } from '@/workflow/types/Workflow'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; 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 { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; -import { useTheme } from '@emotion/react'; import { useEffect, useState } from 'react'; import { useRecoilValue } from 'recoil'; import { IconPlus, useIcons } from 'twenty-ui'; @@ -47,7 +48,6 @@ export const WorkflowEditActionSendEmail = ({ action, actionOptions, }: WorkflowEditActionSendEmailProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const { triggerApisOAuth } = useTriggerApisOAuth(); @@ -188,6 +188,9 @@ export const WorkflowEditActionSendEmail = ({ const headerTitle = isDefined(action.name) ? action.name : 'Send Email'; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); + const navigate = useNavigateSettings(); const { closeCommandMenu } = useCommandMenu(); @@ -206,9 +209,9 @@ export const WorkflowEditActionSendEmail = ({ }); }} Icon={getIcon(headerIcon)} - iconColor={theme.color.blue} + iconColor={headerIconColor} initialTitle={headerTitle} - headerType="Email" + headerType={headerType} disabled={actionOptions.readonly} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx index f1592e8d8..707a9b710 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionUpdateRecord.tsx @@ -1,7 +1,6 @@ import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems'; import { Select, SelectOption } from '@/ui/input/components/Select'; import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow'; -import { useTheme } from '@emotion/react'; import { useEffect, useState } from 'react'; 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 { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; 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 { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { HorizontalSeparator, useIcons } from 'twenty-ui'; @@ -59,7 +60,6 @@ export const WorkflowEditActionUpdateRecord = ({ action, actionOptions, }: WorkflowEditActionUpdateRecordProps) => { - const theme = useTheme(); const { getIcon } = useIcons(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); @@ -160,6 +160,8 @@ export const WorkflowEditActionUpdateRecord = ({ const headerTitle = isDefined(action.name) ? action.name : `Update Record`; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); return ( <> @@ -175,9 +177,9 @@ export const WorkflowEditActionUpdateRecord = ({ }); }} Icon={getIcon(headerIcon)} - iconColor={theme.font.color.tertiary} + iconColor={headerIconColor} initialTitle={headerTitle} - headerType="Action" + headerType={headerType} disabled={isFormDisabled} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx index ad054856e..d3e3f3bca 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/components/__stories__/WorkflowEditActionFindRecords.stories.tsx @@ -3,6 +3,7 @@ import { WorkflowEditActionFindRecords } from '@/workflow/workflow-steps/workflo import { Meta, StoryObj } from '@storybook/react'; import { expect, fn, userEvent, within } from '@storybook/test'; import { ComponentDecorator, RouterDecorator } from 'twenty-ui'; +import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator'; import { ObjectMetadataItemsDecorator } from '~/testing/decorators/ObjectMetadataItemsDecorator'; import { SnackBarDecorator } from '~/testing/decorators/SnackBarDecorator'; import { WorkflowStepActionDrawerDecorator } from '~/testing/decorators/WorkflowStepActionDrawerDecorator'; @@ -50,6 +51,7 @@ const meta: Meta = { SnackBarDecorator, RouterDecorator, WorkspaceDecorator, + I18nFrontDecorator, ], }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts index 45c6d6606..909c7b8fc 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/OtherActions.ts @@ -1,8 +1,11 @@ -import { WorkflowStepType } from '@/workflow/types/Workflow'; +import { WorkflowActionType } from '@/workflow/types/Workflow'; export const OTHER_ACTIONS: Array<{ label: string; - type: WorkflowStepType; + type: Exclude< + WorkflowActionType, + 'CREATE_RECORD' | 'UPDATE_RECORD' | 'DELETE_RECORD' | 'FIND_RECORDS' + >; icon: string; }> = [ { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts index f97eca306..2d27e9761 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/constants/RecordActions.ts @@ -1,8 +1,11 @@ -import { WorkflowStepType } from '@/workflow/types/Workflow'; +import { WorkflowActionType } from '@/workflow/types/Workflow'; export const RECORD_ACTIONS: Array<{ label: string; - type: WorkflowStepType; + type: Extract< + WorkflowActionType, + 'CREATE_RECORD' | 'UPDATE_RECORD' | 'DELETE_RECORD' | 'FIND_RECORDS' + >; icon: string; }> = [ { diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx index 88b61ef4f..87200657e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/form-action/components/WorkflowEditActionFormBuilder.tsx @@ -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 { WorkflowFormActionField } from '@/workflow/workflow-steps/workflow-actions/form-action/types/WorkflowFormActionField'; 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 { useTheme } from '@emotion/react'; import styled from '@emotion/styled'; @@ -99,6 +101,9 @@ export const WorkflowEditActionFormBuilder = ({ const headerTitle = isDefined(action.name) ? action.name : `Form`; const headerIcon = getActionIcon(action.type); + const headerIconColor = useActionIconColorOrThrow(action.type); + const headerType = useActionHeaderTypeOrThrow(action.type); + const [selectedField, setSelectedField] = useState(null); const isFieldSelected = (fieldName: string) => selectedField === fieldName; const handleFieldClick = (fieldName: string) => { @@ -161,9 +166,9 @@ export const WorkflowEditActionFormBuilder = ({ }); }} Icon={getIcon(headerIcon)} - iconColor={theme.font.color.tertiary} + iconColor={headerIconColor} initialTitle={headerTitle} - headerType="Action" + headerType={headerType} disabled={actionOptions.readonly} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow.ts new file mode 100644 index 000000000..0c336898c --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useActionHeaderTypeOrThrow.ts @@ -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)); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow.ts new file mode 100644 index 000000000..c1246ce8c --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/hooks/useActionIconColorOrThrow.ts @@ -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 }); +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts new file mode 100644 index 000000000..692c6b1ad --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionHeaderTypeOrThrow.ts @@ -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}`); + } +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIcon.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIcon.ts index a571fcbe7..7feb3fefe 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIcon.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIcon.ts @@ -1,7 +1,8 @@ +import { WorkflowActionType } from '@/workflow/types/Workflow'; import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions'; import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; -export const getActionIcon = (actionType: string) => { +export const getActionIcon = (actionType: WorkflowActionType) => { switch (actionType) { case 'CREATE_RECORD': case 'UPDATE_RECORD': diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow.ts b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow.ts new file mode 100644 index 000000000..348f49df8 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/utils/getActionIconColorOrThrow.ts @@ -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}`); + } +};