From a6fb4dd589574f6f40f5b9c8216d8cfc8b0f2843 Mon Sep 17 00:00:00 2001 From: martmull Date: Wed, 27 Aug 2025 10:22:13 +0200 Subject: [PATCH] Fix handle color on workflow run steps (#14091) ## Before image ## After image --- ...DiagramStepNodeEditableContent.stories.tsx | 14 --- .../types/WorkflowDiagramNodeHandles.ts | 4 - .../types/WorkflowDiagramNodeVariant.ts | 7 -- .../getNodeVariantFromStepRunStatus.test.ts | 16 --- .../utils/getNodeVariantFromStepRunStatus.ts | 21 ---- .../utils/getWorkflowDiagramColors.ts | 90 ++++++++++++++ .../getWorkflowDiagramNodeSelectedColors.ts | 39 ------ .../WorkflowDiagramEdgeButtonGroup.tsx | 8 +- .../WorkflowDiagramFilterEdgeEditable.tsx | 8 +- .../WorkflowDiagramFilterEdgeReadonly.tsx | 8 +- .../WorkflowDiagramFilterEdgeRun.tsx | 17 ++- .../WorkflowDiagramEmptyTriggerEditable.tsx | 44 +------ .../WorkflowDiagramEmptyTriggerReadonly.tsx | 44 +------ .../WorkflowDiagramHandleSource.tsx | 43 +++++-- .../WorkflowDiagramStepNodeEditable.tsx | 1 - ...WorkflowDiagramStepNodeEditableContent.tsx | 49 +------- .../WorkflowDiagramStepNodeReadonly.tsx | 40 +------ .../components/WorkflowNodeContainer.tsx | 36 +++++- .../components/WorkflowNodeLabel.tsx | 19 ++- .../components/WorkflowNodeTitle.tsx | 26 +++- .../components/WorkflowRunDiagramStepNode.tsx | 113 ++++-------------- 21 files changed, 258 insertions(+), 389 deletions(-) delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeHandles.ts delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant.ts delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getNodeVariantFromStepRunStatus.test.ts delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus.ts create mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts delete mode 100644 packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors.ts diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramStepNodeEditableContent.stories.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramStepNodeEditableContent.stories.tsx index f52132d506..d4f37905a6 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramStepNodeEditableContent.stories.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/__stories__/WorkflowDiagramStepNodeEditableContent.stories.tsx @@ -2,7 +2,6 @@ import { type Meta, type StoryObj } from '@storybook/react'; import { WorkflowVisualizerComponentInstanceContext } from '@/workflow/workflow-diagram/states/contexts/WorkflowVisualizerComponentInstanceContext'; import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { type WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; import { fn } from '@storybook/test'; import '@xyflow/react/dist/style.css'; import { RecoilRoot } from 'recoil'; @@ -121,7 +120,6 @@ export const Catalog: CatalogStory< args: { id: 'story-node', data: ALL_STEPS[0], - variant: 'default', selected: false, onDelete: fn(), }, @@ -140,18 +138,6 @@ export const Catalog: CatalogStory< values: ALL_STEPS, props: (data: WorkflowDiagramStepNodeData) => ({ data }), }, - { - name: 'variant', - values: [ - 'empty', - 'default', - 'running', - 'success', - 'failure', - 'not-executed', - ] satisfies WorkflowDiagramNodeVariant[], - props: (variant: WorkflowDiagramNodeVariant) => ({ variant }), - }, { name: 'selected', values: [false, true], diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeHandles.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeHandles.ts deleted file mode 100644 index 173727a08e..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeHandles.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type WorkflowDiagramNodeHandles = { - targetHandle: boolean; - sourceHandle: boolean; -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant.ts deleted file mode 100644 index 168bb3f289..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant.ts +++ /dev/null @@ -1,7 +0,0 @@ -export type WorkflowDiagramNodeVariant = - | 'default' - | 'success' - | 'failure' - | 'running' - | 'empty' - | 'not-executed'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getNodeVariantFromStepRunStatus.test.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getNodeVariantFromStepRunStatus.test.ts deleted file mode 100644 index 554ed54e6d..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/__tests__/getNodeVariantFromStepRunStatus.test.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { getNodeVariantFromStepRunStatus } from '@/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus'; -import { StepStatus } from 'twenty-shared/workflow'; - -describe('getNodeVariantFromRunStatus', () => { - it('should return proper variant', () => { - expect(getNodeVariantFromStepRunStatus(StepStatus.SUCCESS)).toBe('success'); - expect(getNodeVariantFromStepRunStatus(StepStatus.STOPPED)).toBe('success'); - expect(getNodeVariantFromStepRunStatus(StepStatus.FAILED)).toBe('failure'); - expect(getNodeVariantFromStepRunStatus(StepStatus.RUNNING)).toBe('running'); - expect(getNodeVariantFromStepRunStatus(StepStatus.PENDING)).toBe('running'); - expect(getNodeVariantFromStepRunStatus(StepStatus.NOT_STARTED)).toBe( - 'not-executed', - ); - expect(getNodeVariantFromStepRunStatus(undefined)).toBe('default'); - }); -}); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus.ts deleted file mode 100644 index 0f91a0487d..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { type WorkflowRunStepStatus } from '@/workflow/types/Workflow'; -import { type WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; - -export const getNodeVariantFromStepRunStatus = ( - runStatus: WorkflowRunStepStatus | undefined, -): WorkflowDiagramNodeVariant => { - switch (runStatus) { - case 'SUCCESS': - case 'STOPPED': - return 'success'; - case 'FAILED': - return 'failure'; - case 'RUNNING': - case 'PENDING': - return 'running'; - case 'NOT_STARTED': - return 'not-executed'; - default: - return 'default'; - } -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts new file mode 100644 index 0000000000..4bcf3ce20a --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramColors.ts @@ -0,0 +1,90 @@ +import { type Theme } from '@emotion/react'; +import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow'; + +type WorkflowDiagramColors = { + background: string; + borderColor: string; + color: string; + titleColor: string; +}; + +export type WorkflowDiagramNodeColors = { + selected: WorkflowDiagramColors; + unselected: WorkflowDiagramColors; +}; + +export const getWorkflowDiagramColors = ({ + theme, + runStatus, +}: { + theme: Theme; + runStatus?: WorkflowRunStepStatus; +}): WorkflowDiagramNodeColors => { + switch (runStatus) { + case 'PENDING': + case 'RUNNING': { + return { + selected: { + background: theme.adaptiveColors.yellow1, + borderColor: theme.color.yellow, + color: theme.tag.text.yellow, + titleColor: theme.font.color.primary, + }, + unselected: { + background: theme.background.secondary, + borderColor: theme.border.color.strong, + color: theme.tag.text.yellow, + titleColor: theme.font.color.primary, + }, + }; + } + case 'FAILED': { + return { + selected: { + background: theme.adaptiveColors.red1, + borderColor: theme.color.red, + color: theme.tag.text.red, + titleColor: theme.font.color.primary, + }, + unselected: { + background: theme.background.secondary, + borderColor: theme.border.color.strong, + color: theme.tag.text.red, + titleColor: theme.font.color.primary, + }, + }; + } + case 'SUCCESS': { + return { + selected: { + background: theme.adaptiveColors.turquoise1, + borderColor: theme.color.turquoise, + color: theme.tag.text.green, + titleColor: theme.font.color.primary, + }, + unselected: { + background: theme.background.secondary, + borderColor: theme.border.color.strong, + color: theme.tag.text.green, + titleColor: theme.font.color.primary, + }, + }; + } + default: { + return { + selected: { + background: theme.adaptiveColors.blue1, + borderColor: theme.color.blue, + color: theme.tag.text.blue, + titleColor: theme.font.color.primary, + }, + unselected: { + background: theme.background.secondary, + borderColor: theme.border.color.strong, + color: theme.font.color.tertiary, + titleColor: theme.font.color.light, + }, + }; + } + } +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors.ts deleted file mode 100644 index 156e7cc48d..0000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { type WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; -import { type Theme } from '@emotion/react'; - -export type WorkflowDiagramNodeSelectedColors = { - background: string; - borderColor: string; -}; - -export const getWorkflowDiagramNodeSelectedColors = ( - variant: WorkflowDiagramNodeVariant, - theme: Theme, -): WorkflowDiagramNodeSelectedColors => { - switch (variant) { - case 'running': { - return { - background: theme.adaptiveColors.yellow1, - borderColor: theme.adaptiveColors.yellow4, - }; - } - case 'success': { - return { - background: theme.adaptiveColors.turquoise1, - borderColor: theme.adaptiveColors.turquoise4, - }; - } - case 'failure': { - return { - background: theme.background.danger, - borderColor: theme.color.red, - }; - } - default: { - return { - background: theme.adaptiveColors.blue1, - borderColor: theme.color.blue, - }; - } - } -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup.tsx index 0980ab9375..19736242d8 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramEdgeButtonGroup.tsx @@ -1,6 +1,6 @@ import styled from '@emotion/styled'; import { IconButtonGroup, type IconButtonGroupProps } from 'twenty-ui/input'; -import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; import { css } from '@emotion/react'; const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>` @@ -8,10 +8,10 @@ const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>` ${({ selected, theme }) => { if (!selected) return ''; - const colors = getWorkflowDiagramNodeSelectedColors('default', theme); + const colors = getWorkflowDiagramColors({ theme }); return css` - background-color: ${colors.background}; - border: 1px solid ${colors.borderColor}; + background-color: ${colors.selected.background}; + border: 1px solid ${colors.selected.borderColor}; `; }} `; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx index cbf66919f5..1351d88592 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeEditable.tsx @@ -23,7 +23,7 @@ import { type WorkflowDiagramEdge, type WorkflowDiagramEdgeData, } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; import { useDeleteEdge } from '@/workflow/workflow-steps/hooks/useDeleteEdge'; import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; @@ -66,10 +66,10 @@ const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>` ${({ selected, theme }) => { if (!selected) return ''; - const colors = getWorkflowDiagramNodeSelectedColors('default', theme); + const colors = getWorkflowDiagramColors({ theme }); return css` - background-color: ${colors.background}; - border: 1px solid ${colors.borderColor}; + background-color: ${colors.selected.background}; + border: 1px solid ${colors.selected.borderColor}; `; }} `; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx index 3d8c8c50c5..5a1b48da0e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeReadonly.tsx @@ -10,7 +10,7 @@ import { type WorkflowDiagramEdge, type WorkflowDiagramEdgeData, } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; @@ -42,10 +42,10 @@ const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>` ${({ selected, theme }) => { if (!selected) return ''; - const colors = getWorkflowDiagramNodeSelectedColors('default', theme); + const colors = getWorkflowDiagramColors({ theme }); return css` - background-color: ${colors.background}; - border: 1px solid ${colors.borderColor}; + background-color: ${colors.selected.background}; + border: 1px solid ${colors.selected.borderColor}; `; }} `; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx index 717d86a3d4..1fb206fa91 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-edges/components/WorkflowDiagramFilterEdgeRun.tsx @@ -9,9 +9,7 @@ import { type WorkflowDiagramEdge, type WorkflowDiagramEdgeData, } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { type WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; -import { getNodeVariantFromStepRunStatus } from '@/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus'; -import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; import { css } from '@emotion/react'; import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; @@ -23,6 +21,7 @@ import { import { isDefined } from 'twenty-shared/utils'; import { IconFilter } from 'twenty-ui/display'; import { IconButtonGroup } from 'twenty-ui/input'; +import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow'; type WorkflowDiagramFilterEdgeRunProps = EdgeProps; @@ -38,16 +37,16 @@ const assertFilterEdgeDataOrThrow: ( const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean; - variant: WorkflowDiagramNodeVariant; + runStatus?: WorkflowRunStepStatus; }>` pointer-events: all; - ${({ selected, variant, theme }) => { + ${({ selected, runStatus, theme }) => { if (!selected) return ''; - const colors = getWorkflowDiagramNodeSelectedColors(variant, theme); + const colors = getWorkflowDiagramColors({ runStatus, theme }); return css` - background-color: ${colors.background}; - border: 1px solid ${colors.borderColor}; + background-color: ${colors.selected.background}; + border: 1px solid ${colors.selected.borderColor}; `; }} `; @@ -122,7 +121,7 @@ export const WorkflowDiagramFilterEdgeRun = ({ }, ]} selected={isFilterNodeSelected} - variant={getNodeVariantFromStepRunStatus(data.runStatus)} + runStatus={data.runStatus} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerEditable.tsx index 014b198adb..9bac70c015 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerEditable.tsx @@ -10,47 +10,11 @@ import { WorkflowNodeLabel } from '@/workflow/workflow-diagram/workflow-nodes/co import { WorkflowNodeLabelWithCounterPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeLabelWithCounterPart'; import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeRightPart'; import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle'; -import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-shared/utils'; -const StyledNodeContainer = styled(WorkflowNodeContainer)` - border-color: ${({ theme }) => theme.border.color.strong}; - background: ${({ theme }) => theme.background.secondary}; - - &:hover { - background: linear-gradient( - 0deg, - ${({ theme }) => theme.background.transparent.lighter} 0%, - ${({ theme }) => theme.background.transparent.lighter} 100% - ), - ${({ theme }) => theme.background.secondary}; - } - - .selected & { - border-color: ${({ theme }) => theme.color.blue}; - background: ${({ theme }) => theme.adaptiveColors.blue1}; - } -`; - -const StyledNodeLabel = styled(WorkflowNodeLabel)` - color: ${({ theme }) => theme.font.color.tertiary}; - - .selected & { - color: ${({ theme }) => theme.tag.text.blue}; - } -`; - -const StyledNodeTitle = styled(WorkflowNodeTitle)` - color: ${({ theme }) => theme.font.color.light}; - - .selected & { - color: ${({ theme }) => theme.font.color.primary}; - } -`; - export const WorkflowDiagramEmptyTriggerEditable = () => { const { t } = useLingui(); @@ -79,7 +43,7 @@ export const WorkflowDiagramEmptyTriggerEditable = () => { }; return ( - @@ -87,11 +51,11 @@ export const WorkflowDiagramEmptyTriggerEditable = () => { - {t`Trigger`} + {t`Trigger`} - {t`Add a Trigger`} + {t`Add a Trigger`} - + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerReadonly.tsx index c067921b86..c2645d3f74 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramEmptyTriggerReadonly.tsx @@ -13,7 +13,6 @@ import { WorkflowNodeLabel } from '@/workflow/workflow-diagram/workflow-nodes/co import { WorkflowNodeLabelWithCounterPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeLabelWithCounterPart'; import { WorkflowNodeRightPart } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeRightPart'; import { WorkflowNodeTitle } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle'; -import styled from '@emotion/styled'; import { useLingui } from '@lingui/react/macro'; import { useContext } from 'react'; import { useSetRecoilState } from 'recoil'; @@ -21,41 +20,6 @@ import { isDefined } from 'twenty-shared/utils'; import { TRIGGER_STEP_ID } from 'twenty-shared/workflow'; import { useIcons } from 'twenty-ui/display'; -const StyledNodeContainer = styled(WorkflowNodeContainer)` - border-color: ${({ theme }) => theme.border.color.strong}; - background: ${({ theme }) => theme.background.secondary}; - - &:hover { - background: linear-gradient( - 0deg, - ${({ theme }) => theme.background.transparent.lighter} 0%, - ${({ theme }) => theme.background.transparent.lighter} 100% - ), - ${({ theme }) => theme.background.secondary}; - } - - .selected & { - border-color: ${({ theme }) => theme.color.blue}; - background: ${({ theme }) => theme.adaptiveColors.blue1}; - } -`; - -const StyledNodeLabel = styled(WorkflowNodeLabel)` - color: ${({ theme }) => theme.font.color.tertiary}; - - .selected & { - color: ${({ theme }) => theme.tag.text.blue}; - } -`; - -const StyledNodeTitle = styled(WorkflowNodeTitle)` - color: ${({ theme }) => theme.font.color.light}; - - .selected & { - color: ${({ theme }) => theme.font.color.primary}; - } -`; - export const WorkflowDiagramEmptyTriggerReadonly = () => { const { getIcon } = useIcons(); const { t } = useLingui(); @@ -104,7 +68,7 @@ export const WorkflowDiagramEmptyTriggerReadonly = () => { }; return ( - @@ -112,11 +76,11 @@ export const WorkflowDiagramEmptyTriggerReadonly = () => { - {t`Trigger`} + {t`Trigger`} - {t`Add a Trigger`} + {t`Add a Trigger`} - + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx index 5222a41c9b..9599445694 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource.tsx @@ -5,36 +5,53 @@ import { css } from '@emotion/react'; import styled from '@emotion/styled'; import { Handle, Position, type HandleProps } from '@xyflow/react'; import { FeatureFlagKey } from '~/generated/graphql'; +import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; type WorkflowDiagramHandleSourceProps = { selected: boolean; hovered?: boolean; readOnly?: boolean; + runStatus?: WorkflowRunStepStatus; }; const HANDLE_SCALE_ON_HOVER = 1.5; const StyledHandle = styled(Handle, { shouldForwardProp: (prop) => - prop !== 'disableHoverEffect' && prop !== 'selected' && prop !== 'hovered', + prop !== 'disableHoverEffect' && + prop !== 'selected' && + prop !== 'hovered' && + prop !== 'runStatus', })<{ type: HandleProps['type']; disableHoverEffect: boolean; selected: boolean; hovered?: boolean; + runStatus?: WorkflowRunStepStatus; }>` &.react-flow__handle { opacity: ${({ type }) => (type === 'target' ? 0 : 1)}; height: ${NODE_HANDLE_HEIGHT_PX}px; width: ${NODE_HANDLE_WIDTH_PX}px; - border-color: ${({ theme, selected, hovered, disableHoverEffect }) => - selected - ? theme.color.blue - : hovered && !disableHoverEffect - ? theme.font.color.light - : theme.border.color.strong}; - background: ${({ theme, selected }) => - selected ? theme.adaptiveColors.blue1 : theme.background.primary}; + + ${({ theme, selected, hovered, disableHoverEffect, runStatus }) => { + if (!selected) { + return css` + background: ${theme.background.primary}; + border-color: ${hovered && !disableHoverEffect + ? theme.font.color.light + : theme.border.color.strong}; + `; + } + + const colors = getWorkflowDiagramColors({ theme, runStatus }); + + return css` + background: ${colors.selected.background}; + border-color: ${colors.selected.borderColor}; + `; + }} transition: transform 0.1s ease-out, background 0.1s, @@ -53,10 +70,12 @@ const StyledHandle = styled(Handle, { return undefined; } + const colors = getWorkflowDiagramColors({ theme }); + return css` &:hover { - background: ${theme.adaptiveColors.blue1} !important; - border-color: ${theme.color.blue} !important; + background: ${colors.selected.background} !important; + border-color: ${colors.selected.borderColor} !important; transform: scale(${HANDLE_SCALE_ON_HOVER}) translate(-50%, 50%); } `; @@ -68,6 +87,7 @@ export const WorkflowDiagramHandleSource = ({ selected, hovered = false, readOnly = false, + runStatus, }: WorkflowDiagramHandleSourceProps) => { const isWorkflowBranchEnabled = useIsFeatureEnabled( FeatureFlagKey.IS_WORKFLOW_BRANCH_ENABLED, @@ -80,6 +100,7 @@ export const WorkflowDiagramHandleSource = ({ disableHoverEffect={!isWorkflowBranchEnabled || readOnly} selected={selected} hovered={hovered} + runStatus={runStatus} /> ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditable.tsx index 264c2de051..27110b1d69 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditable.tsx @@ -47,7 +47,6 @@ export const WorkflowDiagramStepNodeEditable = ({ { if (!isInRightDrawer) { diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx index 58ec629d9e..8e5de68b4b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeEditableContent.tsx @@ -3,7 +3,6 @@ import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/wor import { useEdgeState } from '@/workflow/workflow-diagram/workflow-edges/hooks/useEdgeState'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { type WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { type WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramStepNodeIcon'; import { WorkflowNodeContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer'; import { WorkflowNodeIconContainer } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeIconContainer'; @@ -17,7 +16,6 @@ import { capitalize } from 'twenty-shared/utils'; import { IconTrash } from 'twenty-ui/display'; import { FloatingIconButton } from 'twenty-ui/input'; import { useConnectionState } from '@/workflow/workflow-diagram/workflow-nodes/hooks/useConnectionState'; -import { css } from '@emotion/react'; import { WorkflowDiagramHandleTarget } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget'; import { WorkflowDiagramHandleSource } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource'; @@ -35,44 +33,6 @@ const StyledAddStepButtonContainer = styled.div<{ transform: translateX(-50%) translateY(100%); `; -const StyledNodeContainer = styled(WorkflowNodeContainer)<{ - isConnectable?: boolean; -}>` - border-color: ${({ theme }) => theme.border.color.strong}; - background: ${({ theme }) => theme.background.secondary}; - - &:hover { - background: linear-gradient( - 0deg, - ${({ theme }) => theme.background.transparent.lighter} 0%, - ${({ theme }) => theme.background.transparent.lighter} 100% - ), - ${({ theme }) => theme.background.secondary}; - ${({ theme, isConnectable }) => - isConnectable && - css` - border-color: ${theme.color.blue} !important; - `}; - } - - .selected & { - border-color: ${({ theme }) => theme.color.blue}; - background: ${({ theme }) => theme.adaptiveColors.blue1}; - } -`; - -const StyledNodeLabel = styled(WorkflowNodeLabel)` - color: ${({ theme }) => theme.font.color.tertiary}; - - .selected & { - color: ${({ theme }) => theme.tag.text.blue}; - } -`; - -const StyledNodeTitle = styled(WorkflowNodeTitle)` - color: ${({ theme }) => theme.font.color.primary}; -`; - const StyledDeleteButtonContainer = styled.div` display: flex; align-items: center; @@ -92,7 +52,6 @@ export const WorkflowDiagramStepNodeEditableContent = ({ }: { id: string; data: WorkflowDiagramStepNodeData; - variant: WorkflowDiagramNodeVariant; selected: boolean; onDelete: () => void; onClick?: () => void; @@ -116,7 +75,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({ return ( <> - - {capitalize(data.nodeType)} + {capitalize(data.nodeType)} - {data.name} + {data.name} {selected && ( @@ -145,7 +104,7 @@ export const WorkflowDiagramStepNodeEditableContent = ({ /> )} - + {!data.hasNextStepIds && !isInProgressConnection && ( theme.border.color.strong}; - background: ${({ theme }) => theme.background.secondary}; - - &:hover { - background: linear-gradient( - 0deg, - ${({ theme }) => theme.background.transparent.lighter} 0%, - ${({ theme }) => theme.background.transparent.lighter} 100% - ), - ${({ theme }) => theme.background.secondary}; - } - - .selected & { - border-color: ${({ theme }) => theme.color.blue}; - background: ${({ theme }) => theme.adaptiveColors.blue1}; - } -`; - -const StyledNodeLabel = styled(WorkflowNodeLabel)` - color: ${({ theme }) => theme.font.color.tertiary}; - - .selected & { - color: ${({ theme }) => theme.tag.text.blue}; - } -`; - -const StyledNodeTitle = styled(WorkflowNodeTitle)` - color: ${({ theme }) => theme.font.color.primary}; -`; - export const WorkflowDiagramStepNodeReadonly = ({ id, selected, @@ -109,7 +77,7 @@ export const WorkflowDiagramStepNodeReadonly = ({ return ( <> - @@ -120,12 +88,12 @@ export const WorkflowDiagramStepNodeReadonly = ({ - {capitalize(data.nodeType)} + {capitalize(data.nodeType)} - {data.name} + {data.name} - + diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx index cdde5ab0a0..5f01853a06 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeContainer.tsx @@ -1,6 +1,12 @@ import styled from '@emotion/styled'; +import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow'; +import { css } from '@emotion/react'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; -const StyledNodeContainer = styled.div` +const StyledNodeContainer = styled.div<{ + runStatus?: WorkflowRunStepStatus; + isConnectable?: boolean; +}>` align-items: center; display: flex; gap: ${({ theme }) => theme.spacing(2)}; @@ -14,6 +20,34 @@ const StyledNodeContainer = styled.div` cursor: pointer; position: relative; transition: border-color 0.1s; + + &:hover { + background: linear-gradient( + 0deg, + ${({ theme }) => theme.background.transparent.lighter} 0%, + ${({ theme }) => theme.background.transparent.lighter} 100% + ), + ${({ theme }) => theme.background.secondary}; + ${({ theme, isConnectable }) => + isConnectable && + css` + border-color: ${theme.color.blue} !important; + `}; + } + + ${({ theme, runStatus }) => { + const colors = getWorkflowDiagramColors({ theme, runStatus }); + + return css` + border-color: ${colors.unselected.borderColor}; + background: ${colors.unselected.background}; + + .selected & { + background-color: ${colors.selected.background}; + border: 1px solid ${colors.selected.borderColor}; + } + `; + }} `; export { StyledNodeContainer as WorkflowNodeContainer }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeLabel.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeLabel.tsx index 8c2a482cd7..068c91cf6f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeLabel.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeLabel.tsx @@ -1,9 +1,26 @@ import styled from '@emotion/styled'; import { Label } from 'twenty-ui/display'; +import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow'; +import { css } from '@emotion/react'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; -const StyledNodeLabel = styled(Label)` +const StyledNodeLabel = styled(Label)<{ + runStatus?: WorkflowRunStepStatus; +}>` box-sizing: border-box; flex: 1 0 0; + + ${({ theme, runStatus }) => { + const colors = getWorkflowDiagramColors({ theme, runStatus }); + + return css` + color: ${colors.unselected.color}; + + .selected & { + color: ${colors.selected.color}; + } + `; + }} `; export { StyledNodeLabel as WorkflowNodeLabel }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle.tsx index 58fabb06fb..cd1992d488 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowNodeTitle.tsx @@ -1,6 +1,12 @@ import styled from '@emotion/styled'; +import { getWorkflowDiagramColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramColors'; +import { css } from '@emotion/react'; +import type { WorkflowRunStepStatus } from '@/workflow/types/Workflow'; -const StyledNodeTitle = styled.div` +const StyledNodeTitle = styled.div<{ + highlight?: boolean; + runStatus?: WorkflowRunStepStatus; +}>` box-sizing: border-box; -webkit-box-orient: vertical; -webkit-line-clamp: 1; @@ -10,6 +16,24 @@ const StyledNodeTitle = styled.div` font-weight: ${({ theme }) => theme.font.weight.medium}; overflow: hidden; text-overflow: ellipsis; + + ${({ theme, highlight, runStatus }) => { + const colors = getWorkflowDiagramColors({ theme, runStatus }); + + if (true === highlight) { + return css` + color: ${colors.selected.titleColor}; + `; + } + + return css` + color: ${colors.unselected.titleColor}; + + .selected & { + color: ${colors.selected.titleColor}; + } + `; + }} `; export { StyledNodeTitle as WorkflowNodeTitle }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx index 2f21a4e0c4..6d14430f8b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/workflow-nodes/components/WorkflowRunDiagramStepNode.tsx @@ -27,78 +27,10 @@ import { Loader } from 'twenty-ui/feedback'; import { WorkflowDiagramHandleTarget } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleTarget'; import { WorkflowDiagramHandleSource } from '@/workflow/workflow-diagram/workflow-nodes/components/WorkflowDiagramHandleSource'; -const StyledNodeContainer = styled(WorkflowNodeContainer)` - border-color: ${({ theme }) => theme.border.color.strong}; - background: ${({ theme }) => theme.background.secondary}; - - &:hover { - background: linear-gradient( - 0deg, - ${({ theme }) => theme.background.transparent.lighter} 0%, - ${({ theme }) => theme.background.transparent.lighter} 100% - ), - ${({ theme }) => theme.background.secondary}; - } - - .selected & { - border-color: ${({ theme }) => theme.color.blue}; - background: ${({ theme }) => theme.adaptiveColors.blue1}; - } - - .selected &[data-status='RUNNING'], - .selected &[data-status='PENDING'] { - border-color: ${({ theme }) => theme.color.yellow}; - background: ${({ theme }) => theme.adaptiveColors.yellow1}; - } - - .selected &[data-status='FAILED'] { - border-color: ${({ theme }) => theme.color.red}; - background: ${({ theme }) => theme.adaptiveColors.red1}; - } - - .selected &[data-status='SUCCESS'] { - border-color: ${({ theme }) => theme.color.turquoise}; - background: ${({ theme }) => theme.adaptiveColors.turquoise1}; - } -`; - const StyledNodeLabelWithCounterPart = styled(WorkflowNodeLabelWithCounterPart)` column-gap: ${({ theme }) => theme.spacing(2)}; `; -const StyledNodeLabel = styled(WorkflowNodeLabel)` - color: ${({ theme }) => theme.font.color.tertiary}; - - .selected & { - color: ${({ theme }) => theme.tag.text.blue}; - } - - &[data-status='RUNNING'], - &[data-status='PENDING'] { - color: ${({ theme }) => theme.tag.text.yellow}; - } - - &[data-status='FAILED'] { - color: ${({ theme }) => theme.tag.text.red}; - } - - &[data-status='SUCCESS'] { - color: ${({ theme }) => theme.tag.text.green}; - } -`; - -const StyledNodeTitle = styled(WorkflowNodeTitle)` - color: ${({ theme }) => theme.font.color.light}; - - &[data-status='RUNNING'], - &[data-status='PENDING'], - &[data-status='FAILED'], - &[data-status='SUCCESS'], - .selected & { - color: ${({ theme }) => theme.font.color.primary}; - } -`; - const StyledNodeCounter = styled.div` align-items: center; display: flex; @@ -107,22 +39,17 @@ const StyledNodeCounter = styled.div` box-sizing: border-box; `; -const StyledColorIcon = styled.div` +const StyledColorIcon = styled.div<{ + color: string; +}>` + align-items: center; + border-radius: ${({ theme }) => theme.border.radius.sm}; + box-sizing: border-box; display: flex; - width: 14px; height: 14px; justify-content: center; - align-items: center; - box-sizing: border-box; - border-radius: ${({ theme }) => theme.border.radius.sm}; - - &[data-status='FAILED'] { - background: ${({ theme }) => theme.tag.background.red}; - } - - &[data-status='SUCCESS'] { - background: ${({ theme }) => theme.tag.background.turquoise}; - } + width: 14px; + background: ${({ color }) => color}; `; export const WorkflowRunDiagramStepNode = ({ @@ -177,9 +104,9 @@ export const WorkflowRunDiagramStepNode = ({ return ( <> - @@ -189,13 +116,13 @@ export const WorkflowRunDiagramStepNode = ({ - + {capitalize(data.nodeType)} - + {data.runStatus === StepStatus.SUCCESS && ( - + @@ -203,7 +130,7 @@ export const WorkflowRunDiagramStepNode = ({ {data.runStatus === StepStatus.FAILED && ( - + @@ -217,13 +144,17 @@ export const WorkflowRunDiagramStepNode = ({ )} - + {data.name} - + - + - + ); };