diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx index d2d22cac73..67c754ff9c 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramCanvasBase.tsx @@ -10,6 +10,7 @@ import { useRightDrawerState } from '@/workflow/workflow-diagram/hooks/useRightD import { workflowDiagramComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramComponentState'; import { workflowDiagramPanOnDragComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramPanOnDragComponentState'; import { workflowDiagramWaitingNodesDimensionsComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramWaitingNodesDimensionsComponentState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowDiagram, WorkflowDiagramEdge, @@ -145,6 +146,9 @@ export const WorkflowDiagramCanvasBase = ({ const setWorkflowInsertStepIds = useSetRecoilComponentStateV2( workflowInsertStepIdsComponentState, ); + const setWorkflowSelectedNode = useSetRecoilComponentStateV2( + workflowSelectedNodeComponentState, + ); const workflowDiagramWaitingNodesDimensionsState = useRecoilComponentCallbackStateV2( workflowDiagramWaitingNodesDimensionsComponentState, @@ -192,6 +196,7 @@ export const WorkflowDiagramCanvasBase = ({ parentStepId: undefined, nextStepId: undefined, }); + setWorkflowSelectedNode(undefined); }); const containerRef = useRef(null); diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx index 1b6f74f3c5..5b9a47ec2e 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeEditable.tsx @@ -16,13 +16,15 @@ import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workf import { useOpenWorkflowEditFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu'; import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation'; import { workflowDiagramPanOnDragComponentState } from '@/workflow/workflow-diagram/states/workflowDiagramPanOnDragComponentState'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowDiagramEdge, WorkflowDiagramEdgeData, } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; +import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; import { useDeleteStep } from '@/workflow/workflow-steps/hooks/useDeleteStep'; import { workflowInsertStepIdsComponentState } from '@/workflow/workflow-steps/states/workflowInsertStepIdsComponentState'; -import { useTheme } from '@emotion/react'; +import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; import { isNonEmptyString } from '@sniptt/guards'; import { @@ -54,8 +56,17 @@ const assertFilterEdgeDataOrThrow: ( } }; -const StyledIconButtonGroup = styled(IconButtonGroup)` +const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>` pointer-events: all; + + ${({ selected, theme }) => { + if (!selected) return ''; + const colors = getWorkflowDiagramNodeSelectedColors('default', theme); + return css` + background-color: ${colors.background}; + border: 1px solid ${colors.borderColor}; + `; + }} `; const StyledConfiguredFilterContainer = styled.div` @@ -104,11 +115,18 @@ export const WorkflowDiagramFilterEdgeEditable = ({ workflowInsertStepIdsComponentState, ); - const isSelected = - workflowInsertStepIds.nextStepId === source && - (workflowInsertStepIds.parentStepId === target || - (isNonEmptyString(data.stepId) && - workflowInsertStepIds.parentStepId === data.stepId)); + const isEdgeSelected = + (workflowInsertStepIds.nextStepId === source && + workflowInsertStepIds.parentStepId === target) || + (isNonEmptyString(data.stepId) && + workflowInsertStepIds.parentStepId === data.stepId); + + const workflowSelectedNode = useRecoilComponentValueV2( + workflowSelectedNodeComponentState, + ); + + const isFilterNodeSelected = + isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId; const dropdownId = `${WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID}-${source}-${target}`; @@ -154,7 +172,7 @@ export const WorkflowDiagramFilterEdgeEditable = ({ > - {hovered || isDropdownOpen || isSelected ? ( + {hovered || isDropdownOpen || isEdgeSelected ? ( ) : ( )} diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx index e089acf85b..45547e6489 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeReadonly.tsx @@ -1,14 +1,18 @@ +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; import { CREATE_STEP_NODE_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth'; import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; import { useOpenWorkflowViewFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowViewFilterInCommandMenu'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowDiagramEdge, WorkflowDiagramEdgeData, } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; -import { useTheme } from '@emotion/react'; +import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; +import { css, useTheme } from '@emotion/react'; import styled from '@emotion/styled'; +import { isNonEmptyString } from '@sniptt/guards'; import { BaseEdge, EdgeLabelRenderer, @@ -30,8 +34,17 @@ const assertFilterEdgeDataOrThrow: ( } }; -const StyledIconButtonGroup = styled(IconButtonGroup)` +const StyledIconButtonGroup = styled(IconButtonGroup)<{ selected?: boolean }>` pointer-events: all; + + ${({ selected, theme }) => { + if (!selected) return ''; + const colors = getWorkflowDiagramNodeSelectedColors('default', theme); + return css` + background-color: ${colors.background}; + border: 1px solid ${colors.borderColor}; + `; + }} `; const StyledConfiguredFilterContainer = styled.div` @@ -57,6 +70,13 @@ export const WorkflowDiagramFilterEdgeReadonly = ({ targetY, }); + const workflowSelectedNode = useRecoilComponentValueV2( + workflowSelectedNodeComponentState, + ); + + const isFilterNodeSelected = + isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId; + const { openWorkflowViewFilterInCommandMenu } = useOpenWorkflowViewFilterInCommandMenu(); @@ -92,6 +112,7 @@ export const WorkflowDiagramFilterEdgeReadonly = ({ onClick: handleFilterButtonClick, }, ]} + selected={isFilterNodeSelected} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx index 95db0dbf3d..93116b164f 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramFilterEdgeRun.tsx @@ -1,14 +1,21 @@ +import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2'; import { WorkflowDiagramEdgeV2Container } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2Container'; import { WorkflowDiagramEdgeV2VisibilityContainer } from '@/workflow/workflow-diagram/components/WorkflowDiagramEdgeV2VisibilityContainer'; import { WorkflowRunDiagramBaseEdge } from '@/workflow/workflow-diagram/components/WorkflowRunDiagramBaseEdge'; import { CREATE_STEP_NODE_WIDTH } from '@/workflow/workflow-diagram/constants/CreateStepNodeWidth'; import { WORKFLOW_DIAGRAM_EDGE_OPTIONS_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramEdgeOptionsClickOutsideId'; import { useOpenWorkflowRunFilterInCommandMenu } from '@/workflow/workflow-diagram/hooks/useOpenWorkflowRunFilterInCommandMenu'; +import { workflowSelectedNodeComponentState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeComponentState'; import { WorkflowDiagramEdge, WorkflowDiagramEdgeData, } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; +import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; +import { getNodeVariantFromStepRunStatus } from '@/workflow/workflow-diagram/utils/getNodeVariantFromStepRunStatus'; +import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; +import { css } from '@emotion/react'; import styled from '@emotion/styled'; +import { isNonEmptyString } from '@sniptt/guards'; import { EdgeLabelRenderer, EdgeProps, getStraightPath } from '@xyflow/react'; import { isDefined } from 'twenty-shared/utils'; import { IconFilter } from 'twenty-ui/display'; @@ -26,8 +33,20 @@ const assertFilterEdgeDataOrThrow: ( } }; -const StyledIconButtonGroup = styled(IconButtonGroup)` +const StyledIconButtonGroup = styled(IconButtonGroup)<{ + selected?: boolean; + variant: WorkflowDiagramNodeVariant; +}>` pointer-events: all; + + ${({ selected, variant, theme }) => { + if (!selected) return ''; + const colors = getWorkflowDiagramNodeSelectedColors(variant, theme); + return css` + background-color: ${colors.background}; + border: 1px solid ${colors.borderColor}; + `; + }} `; const StyledConfiguredFilterContainer = styled.div` @@ -49,6 +68,13 @@ export const WorkflowDiagramFilterEdgeRun = ({ targetY, }); + const workflowSelectedNode = useRecoilComponentValueV2( + workflowSelectedNodeComponentState, + ); + + const isFilterNodeSelected = + isNonEmptyString(data.stepId) && workflowSelectedNode === data.stepId; + const { openWorkflowRunFilterInCommandMenu } = useOpenWorkflowRunFilterInCommandMenu(); @@ -87,6 +113,8 @@ export const WorkflowDiagramFilterEdgeRun = ({ onClick: handleFilterButtonClick, }, ]} + selected={isFilterNodeSelected} + variant={getNodeVariantFromStepRunStatus(data.runStatus)} /> diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx index 71fd112619..d7e6722802 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/components/WorkflowDiagramStepNodeBase.tsx @@ -3,6 +3,7 @@ import { NODE_BORDER_WIDTH } from '@/workflow/workflow-diagram/constants/NodeBor import { WORKFLOW_DIAGRAM_STEP_NODE_BASE_CLICK_OUTSIDE_ID } from '@/workflow/workflow-diagram/constants/WorkflowDiagramStepNodeClickOutsideId'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; +import { getWorkflowDiagramNodeSelectedColors } from '@/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors'; import { css } from '@emotion/react'; import styled from '@emotion/styled'; import { Position } from '@xyflow/react'; @@ -106,32 +107,11 @@ const StyledStepNodeInnerContainer = styled.div<{ :is(.workflow-node-container, .workflow-node-container:hover) & { ${({ theme, variant }) => { - switch (variant) { - case 'running': { - return css` - background: ${theme.adaptiveColors.yellow1}; - border-color: ${theme.adaptiveColors.yellow4}; - `; - } - 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 colors = getWorkflowDiagramNodeSelectedColors(variant, theme); + return css` + background: ${colors.background}; + border-color: ${colors.borderColor}; + `; }} } `; diff --git a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts index 1d73bbea88..052ee04334 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/hooks/useOpenWorkflowEditFilterInCommandMenu.ts @@ -47,7 +47,7 @@ export const useOpenWorkflowEditFilterInCommandMenu = () => { ...diagram, nodes: diagram.nodes.map((node) => ({ ...node, - selected: false, + selected: node.id === stepId, })), }; }); 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 new file mode 100644 index 0000000000..d387725ae5 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/workflow-diagram/utils/getWorkflowDiagramNodeSelectedColors.ts @@ -0,0 +1,39 @@ +import { WorkflowDiagramNodeVariant } from '@/workflow/workflow-diagram/types/WorkflowDiagramNodeVariant'; +import { 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, + }; + } + } +};