diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode.tsx deleted file mode 100644 index a1cb0f14d..000000000 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode.tsx +++ /dev/null @@ -1,219 +0,0 @@ -import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBorderWidth'; -import { NODE_HANDLE_HEIGHT_PX } from '@/workflow/workflow-diagram/constants/NodeHandleHeightPx'; -import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/NodeHandleWidthPx'; -import { NODE_ICON_LEFT_MARGIN } from '@/workflow/workflow-diagram/constants/NodeIconLeftMargin'; -import { NODE_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/NodeIconWidth'; -import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; -import { css } from '@emotion/react'; -import styled from '@emotion/styled'; -import { Handle, Position } from '@xyflow/react'; -import React from 'react'; -import { capitalize, isDefined } from 'twenty-shared'; -import { Label, OverflowingTextWithTooltip } from 'twenty-ui'; - -const StyledStepNodeContainer = styled.div` - display: flex; - flex-direction: column; - - padding-block: ${({ theme }) => theme.spacing(3)}; -`; - -const StyledStepNodeType = styled.div<{ - nodeVariant: WorkflowDiagramNodeVariant; -}>` - ${({ nodeVariant, theme }) => { - switch (nodeVariant) { - case 'success': { - return css` - background-color: ${theme.tag.background.turquoise}; - color: ${theme.tag.text.turquoise}; - `; - } - case 'failure': { - return css` - background-color: ${theme.tag.background.red}; - color: ${theme.color.red}; - `; - } - default: { - return css` - background-color: ${theme.background.tertiary}; - `; - } - } - }} - - align-self: flex-start; - border-radius: ${({ theme }) => - `${theme.border.radius.sm} ${theme.border.radius.sm} 0 0`}; - margin-left: ${({ theme }) => theme.spacing(2)}; - padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)}; - - .selectable:is(.selected, :focus, :focus-visible) & { - ${({ nodeVariant, theme }) => { - switch (nodeVariant) { - case 'empty': - case 'default': - case 'not-executed': - return css` - background-color: ${theme.color.blue}; - color: ${theme.font.color.inverted}; - `; - } - }} - } -`.withComponent(Label); - -const StyledStepNodeInnerContainer = styled.div<{ - variant: WorkflowDiagramNodeVariant; -}>` - background: ${({ theme }) => theme.background.secondary}; - border-color: ${({ theme }) => theme.border.color.medium}; - - border-radius: ${({ theme }) => theme.border.radius.md}; - border-style: solid; - border-width: ${NODE_BORDER_WIDTH}px; - box-shadow: ${({ variant, theme }) => - variant === 'empty' ? 'none' : theme.boxShadow.strong}; - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; - padding: ${({ theme }) => theme.spacing(2)}; - - position: relative; - - transition: background ${({ theme }) => theme.animation.duration.fast} ease; - - .workflow-node-container:hover & { - ${({ theme }) => { - return css` - background: linear-gradient( - 0deg, - ${theme.background.transparent.lighter} 0%, - ${theme.background.transparent.lighter} 100% - ), - ${theme.background.secondary}; - `; - }} - } - - .selectable:is(.selected, :focus, :focus-visible) - :is(.workflow-node-container, .workflow-node-container:hover) - & { - ${({ theme, variant }) => { - switch (variant) { - case 'success': { - return css` - background: ${theme.adaptiveColors.turquoise1}; - border-color: ${theme.adaptiveColors.turquoise4}; - `; - } - case 'failure': { - return css` - background: ${theme.background.danger}; - border-color: ${theme.color.red}; - `; - } - default: { - return css` - background: ${theme.adaptiveColors.blue1}; - border-color: ${theme.color.blue}; - `; - } - } - }} - } -`; - -const StyledStepNodeLabel = styled.div<{ - variant: WorkflowDiagramNodeVariant; -}>` - align-items: center; - display: flex; - font-size: 13px; - font-weight: ${({ theme }) => theme.font.weight.medium}; - column-gap: ${({ theme }) => theme.spacing(2)}; - color: ${({ variant, theme }) => { - switch (variant) { - case 'empty': - case 'not-executed': - return theme.font.color.light; - default: - return theme.font.color.primary; - } - }}; - max-width: 200px; - - .selectable:is(.selected, :focus, :focus-visible) & { - color: ${({ theme }) => theme.font.color.primary}; - } -`; - -export const StyledHandle = styled(Handle)` - background-color: ${({ theme }) => theme.grayScale.gray25}; - border: none; - width: ${NODE_HANDLE_WIDTH_PX}px; - height: ${NODE_HANDLE_HEIGHT_PX}px; -`; - -const StyledSourceHandle = styled(StyledHandle)` - background-color: ${({ theme }) => theme.border.color.strong}; - left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px; -`; - -const StyledTargetHandle = styled(StyledSourceHandle)` - left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px; - visibility: hidden; -`; - -const StyledRightFloatingElementContainer = styled.div` - display: flex; - align-items: center; - position: absolute; - right: ${({ theme }) => theme.spacing(-4)}; - bottom: 0; - top: 0; - transform: translateX(100%); -`; - -export const WorkflowDiagramBaseStepNode = ({ - nodeType, - name, - variant, - Icon, - RightFloatingElement, -}: { - nodeType: WorkflowDiagramStepNodeData['nodeType']; - name: string; - variant: WorkflowDiagramNodeVariant; - Icon?: React.ReactNode; - RightFloatingElement?: React.ReactNode; -}) => { - return ( - - {nodeType !== 'trigger' ? ( - - ) : null} - - - {capitalize(nodeType)} - - - - - {Icon} - - - - - {isDefined(RightFloatingElement) ? ( - - {RightFloatingElement} - - ) : null} - - - - - ); -}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCreateStepNode.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCreateStepNode.tsx index 170d1dfd4..c060ff842 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCreateStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCreateStepNode.tsx @@ -1,4 +1,4 @@ -import { StyledHandle } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode'; +import { StyledHandle } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBorderWidth'; import { NODE_ICON_LEFT_MARGIN } from '@/workflow/workflow-diagram/constants/NodeIconLeftMargin'; import { NODE_ICON_WIDTH } from '@/workflow/workflow-diagram/constants/NodeIconWidth'; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx index 60a6bb96c..dd1528b8f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramEmptyTrigger.tsx @@ -1,4 +1,4 @@ -import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode'; +import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; import styled from '@emotion/styled'; const StyledStepNodeLabelIconContainer = styled.div` @@ -12,7 +12,7 @@ const StyledStepNodeLabelIconContainer = styled.div` export const WorkflowDiagramEmptyTrigger = () => { return ( - theme.background.transparent.light}; - border-radius: ${({ theme }) => theme.spacing(1)}; +const StyledStepNodeContainer = styled.div` display: flex; - justify-content: center; - padding: ${({ theme }) => theme.spacing(1)}; + flex-direction: column; + + padding-block: ${({ theme }) => theme.spacing(3)}; +`; + +const StyledStepNodeType = styled.div<{ + nodeVariant: WorkflowDiagramNodeVariant; +}>` + ${({ nodeVariant, theme }) => { + switch (nodeVariant) { + case 'success': { + return css` + background-color: ${theme.tag.background.turquoise}; + color: ${theme.tag.text.turquoise}; + `; + } + case 'failure': { + return css` + background-color: ${theme.tag.background.red}; + color: ${theme.color.red}; + `; + } + default: { + return css` + background-color: ${theme.background.tertiary}; + `; + } + } + }} + + align-self: flex-start; + border-radius: ${({ theme }) => + `${theme.border.radius.sm} ${theme.border.radius.sm} 0 0`}; + margin-left: ${({ theme }) => theme.spacing(2)}; + padding: ${({ theme }) => theme.spacing(1)} ${({ theme }) => theme.spacing(2)}; + + .selectable:is(.selected, :focus, :focus-visible) & { + ${({ nodeVariant, theme }) => { + switch (nodeVariant) { + case 'empty': + case 'default': + case 'not-executed': + return css` + background-color: ${theme.color.blue}; + color: ${theme.font.color.inverted}; + `; + } + }} + } +`.withComponent(Label); + +const StyledStepNodeInnerContainer = styled.div<{ + variant: WorkflowDiagramNodeVariant; +}>` + background: ${({ theme }) => theme.background.secondary}; + border-color: ${({ theme }) => theme.border.color.medium}; + + border-radius: ${({ theme }) => theme.border.radius.md}; + border-style: solid; + border-width: ${NODE_BORDER_WIDTH}px; + box-shadow: ${({ variant, theme }) => + variant === 'empty' ? 'none' : theme.boxShadow.strong}; + display: flex; + gap: ${({ theme }) => theme.spacing(2)}; + padding: ${({ theme }) => theme.spacing(2)}; + + position: relative; + + transition: background ${({ theme }) => theme.animation.duration.fast} ease; + + .workflow-node-container:hover & { + ${({ theme }) => { + return css` + background: linear-gradient( + 0deg, + ${theme.background.transparent.lighter} 0%, + ${theme.background.transparent.lighter} 100% + ), + ${theme.background.secondary}; + `; + }} + } + + .selectable:is(.selected, :focus, :focus-visible) + :is(.workflow-node-container, .workflow-node-container:hover) + & { + ${({ theme, variant }) => { + switch (variant) { + case 'success': { + return css` + background: ${theme.adaptiveColors.turquoise1}; + border-color: ${theme.adaptiveColors.turquoise4}; + `; + } + case 'failure': { + return css` + background: ${theme.background.danger}; + border-color: ${theme.color.red}; + `; + } + default: { + return css` + background: ${theme.adaptiveColors.blue1}; + border-color: ${theme.color.blue}; + `; + } + } + }} + } +`; + +const StyledStepNodeLabel = styled.div<{ + variant: WorkflowDiagramNodeVariant; +}>` + align-items: center; + display: flex; + font-size: 13px; + font-weight: ${({ theme }) => theme.font.weight.medium}; + column-gap: ${({ theme }) => theme.spacing(2)}; + color: ${({ variant, theme }) => { + switch (variant) { + case 'empty': + case 'not-executed': + return theme.font.color.light; + default: + return theme.font.color.primary; + } + }}; + max-width: 200px; + + .selectable:is(.selected, :focus, :focus-visible) & { + color: ${({ theme }) => theme.font.color.primary}; + } +`; + +export const StyledHandle = styled(Handle)` + background-color: ${({ theme }) => theme.grayScale.gray25}; + border: none; + width: ${NODE_HANDLE_WIDTH_PX}px; + height: ${NODE_HANDLE_HEIGHT_PX}px; +`; + +const StyledSourceHandle = styled(StyledHandle)` + background-color: ${({ theme }) => theme.border.color.strong}; + left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px; +`; + +const StyledTargetHandle = styled(StyledSourceHandle)` + left: ${NODE_ICON_WIDTH + NODE_ICON_LEFT_MARGIN + NODE_BORDER_WIDTH}px; + visibility: hidden; +`; + +const StyledRightFloatingElementContainer = styled.div` + display: flex; + align-items: center; + position: absolute; + right: ${({ theme }) => theme.spacing(-4)}; + bottom: 0; + top: 0; + transform: translateX(100%); `; export const WorkflowDiagramStepNodeBase = ({ - data, + nodeType, + name, variant, + Icon, RightFloatingElement, }: { - data: WorkflowDiagramStepNodeData; + nodeType: WorkflowDiagramStepNodeData['nodeType']; + name: string; variant: WorkflowDiagramNodeVariant; + Icon?: React.ReactNode; RightFloatingElement?: React.ReactNode; }) => { - const theme = useTheme(); - const { getIcon } = useIcons(); - const Icon = getIcon(getWorkflowNodeIconKey(data)); - - const renderStepIcon = () => { - switch (data.nodeType) { - case 'trigger': { - switch (data.triggerType) { - case 'DATABASE_EVENT': { - return ( - - - - ); - } - case 'MANUAL': { - return ( - - - - ); - } - } - - return assertUnreachable(data.triggerType); - } - case 'action': { - switch (data.actionType) { - case 'CODE': { - return ( - - - - ); - } - case 'SEND_EMAIL': { - return ( - - - - ); - } - default: { - return ( - - - - ); - } - } - } - } - }; - return ( - + + {nodeType !== 'trigger' ? ( + + ) : null} + + + {capitalize(nodeType)} + + + + + {Icon} + + + + + {isDefined(RightFloatingElement) ? ( + + {RightFloatingElement} + + ) : null} + + + + ); }; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx index 3c98a60a1..ff03ef61d 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeEditableContent.tsx @@ -1,4 +1,5 @@ import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; +import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeIcon'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; import { FloatingIconButton, IconTrash } from 'twenty-ui'; @@ -16,8 +17,10 @@ export const WorkflowDiagramStepNodeEditableContent = ({ }) => { return ( } RightFloatingElement={ selected ? ( theme.background.transparent.light}; + border-radius: ${({ theme }) => theme.spacing(1)}; + display: flex; + justify-content: center; + padding: ${({ theme }) => theme.spacing(1)}; +`; + +export const WorkflowDiagramStepNodeIcon = ({ + data, +}: { + data: WorkflowDiagramStepNodeData; +}) => { + const theme = useTheme(); + const { getIcon } = useIcons(); + const Icon = getIcon(getWorkflowNodeIconKey(data)); + + switch (data.nodeType) { + case 'trigger': { + switch (data.triggerType) { + case 'DATABASE_EVENT': { + return ( + + + + ); + } + case 'MANUAL': { + return ( + + + + ); + } + } + + return assertUnreachable(data.triggerType); + } + case 'action': { + switch (data.actionType) { + case 'CODE': { + return ( + + + + ); + } + case 'SEND_EMAIL': { + return ( + + + + ); + } + default: { + return ( + + + + ); + } + } + } + } +}; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeReadonly.tsx index 0b9b493bc..48bd8a9c1 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeReadonly.tsx @@ -1,4 +1,5 @@ import { WorkflowDiagramStepNodeBase } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase'; +import { WorkflowDiagramStepNodeIcon } from '@/workflow/workflow-diagram/components/WorkflowDiagramStepNodeIcon'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; export const WorkflowDiagramStepNodeReadonly = ({ @@ -6,5 +7,12 @@ export const WorkflowDiagramStepNodeReadonly = ({ }: { data: WorkflowDiagramStepNodeData; }) => { - return ; + return ( + } + /> + ); };