Implement proper arrows between nodes for workflows and workflow versions (#13909)

> [!IMPORTANT]
> We still have to implement the new arrows for **workflow runs**. I
kept the old arrows because they currently indicate the success state of
an execution. In the new version, arrows will all look the same,
regardless of the execution state of the previous node, so we'll be able
to use the same arrows everywhere.

## Workflows

You can see that arrows start from the right spot and end just above the
target node. **We will soon remove the round top handle, and the arrow
will gently touch the top of the node.**


https://github.com/user-attachments/assets/febd5f49-393f-4d97-addc-bede5cf6944a

## Workflow versions


https://github.com/user-attachments/assets/e519850f-abd9-4d75-8337-c4c44a2dbfed

Closes https://github.com/twentyhq/core-team-issues/issues/1369
This commit is contained in:
Baptiste Devessier
2025-08-14 10:05:42 +02:00
committed by GitHub
parent 88735f6788
commit 36fcb29e86
8 changed files with 50 additions and 52 deletions
@@ -1,10 +1,8 @@
import { EDGE_GRAY_CIRCLE_MARKED_ID } from '@/workflow/workflow-diagram/constants/EdgeGrayCircleMarkedId';
import { EDGE_BRANCH_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeBranchArrowMarkerId';
import { EDGE_GREEN_CIRCLE_MARKED_ID } from '@/workflow/workflow-diagram/constants/EdgeGreenCircleMarkedId';
import { EDGE_GREEN_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeGreenRoundedArrowMarkerId';
import { EDGE_GREEN_ROUNDED_ARROW_MARKER_WIDTH_PX } from '@/workflow/workflow-diagram/constants/EdgeGreenRoundedArrowMarkerWidthPx';
import { EDGE_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeRoundedArrowMarkerId';
import { NODE_HANDLE_HEIGHT_PX } from '@/workflow/workflow-diagram/constants/NodeHandleHeightPx';
import { NODE_HANDLE_WIDTH_PX } from '@/workflow/workflow-diagram/constants/NodeHandleWidthPx';
import { useTheme } from '@emotion/react';
export const WorkflowDiagramCustomMarkers = () => {
@@ -40,17 +38,16 @@ export const WorkflowDiagramCustomMarkers = () => {
</marker>
<marker
markerHeight={NODE_HANDLE_HEIGHT_PX}
markerWidth={NODE_HANDLE_WIDTH_PX}
refX={NODE_HANDLE_WIDTH_PX / 2}
refY={NODE_HANDLE_HEIGHT_PX}
id={EDGE_GRAY_CIRCLE_MARKED_ID}
id={EDGE_BRANCH_ARROW_MARKER_ID}
markerHeight={8}
markerWidth={10}
refX={5}
refY={4}
>
<rect
height={NODE_HANDLE_HEIGHT_PX}
width={NODE_HANDLE_WIDTH_PX}
rx="2"
fill={theme.border.color.strong}
<path
d="M1.7915 1.38672H8.18311C8.57541 1.38705 8.81458 1.81852 8.60693 2.15137L5.41064 7.26465C5.21481 7.57798 4.75882 7.57798 4.56299 7.26465L1.3667 2.15137C1.15906 1.81841 1.39896 1.38672 1.7915 1.38672Z"
stroke={theme.border.color.strong}
fill={theme.background.primary}
/>
</marker>
@@ -51,7 +51,7 @@ export const WorkflowDiagramStepNodeReadonly = ({
variant={getNodeVariantFromStepRunStatus(data.runStatus)}
nodeType={data.nodeType}
Icon={<WorkflowDiagramStepNodeIcon data={data} />}
displayHandle={false}
displayHandle
onClick={() => {
if (
!isDefined(workflowVisualizerWorkflowId) ||
@@ -1,9 +1,9 @@
import { EDGE_GRAY_CIRCLE_MARKED_ID } from '@/workflow/workflow-diagram/constants/EdgeGrayCircleMarkedId';
import { EDGE_GREEN_CIRCLE_MARKED_ID } from '@/workflow/workflow-diagram/constants/EdgeGreenCircleMarkedId';
import { EDGE_GREEN_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeGreenRoundedArrowMarkerId';
import { EDGE_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeRoundedArrowMarkerId';
import { type Theme, useTheme } from '@emotion/react';
import { BaseEdge } from '@xyflow/react';
import { isDefined } from 'twenty-shared/utils';
import { StepStatus } from 'twenty-shared/workflow';
const toMarkerId = (id: string) => `url(#${id})`;
@@ -13,7 +13,7 @@ const getMarkerStart = (edgeExecutionStatus: StepStatus | undefined) => {
return EDGE_GREEN_CIRCLE_MARKED_ID;
}
return EDGE_GRAY_CIRCLE_MARKED_ID;
return undefined;
};
const getMarkerEnd = (edgeExecutionStatus: StepStatus | undefined) => {
@@ -49,9 +49,11 @@ export const WorkflowRunDiagramBaseEdge = ({
}: WorkflowRunDiagramBaseEdgeProps) => {
const theme = useTheme();
const markerStart = getMarkerStart(edgeExecutionStatus);
return (
<BaseEdge
markerStart={toMarkerId(getMarkerStart(edgeExecutionStatus))}
markerStart={isDefined(markerStart) ? toMarkerId(markerStart) : undefined}
markerEnd={toMarkerId(getMarkerEnd(edgeExecutionStatus))}
path={edgePath}
style={{
@@ -0,0 +1 @@
export const EDGE_BRANCH_ARROW_MARKER_ID = 'workflow-edge-branch-arrow';
@@ -1 +0,0 @@
export const EDGE_GRAY_CIRCLE_MARKED_ID = 'workflow-edge-gray-circle';
@@ -1,5 +1,4 @@
import { EDGE_GRAY_CIRCLE_MARKED_ID } from '@/workflow/workflow-diagram/constants/EdgeGrayCircleMarkedId';
import { EDGE_ROUNDED_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeRoundedArrowMarkerId';
import { EDGE_BRANCH_ARROW_MARKER_ID } from '@/workflow/workflow-diagram/constants/EdgeBranchArrowMarkerId';
import {
type WorkflowDiagramEdge,
type WorkflowDiagramEdgeType,
@@ -7,8 +6,8 @@ import {
export const WORKFLOW_VISUALIZER_EDGE_DEFAULT_CONFIGURATION = {
type: 'empty-filter--readonly' satisfies WorkflowDiagramEdgeType,
markerStart: EDGE_GRAY_CIRCLE_MARKED_ID,
markerEnd: EDGE_ROUNDED_ARROW_MARKER_ID,
markerStart: undefined,
markerEnd: EDGE_BRANCH_ARROW_MARKER_ID,
deletable: false,
selectable: false,
data: {
@@ -119,8 +119,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-0",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "trigger",
"target": "step1",
@@ -133,8 +133,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-1",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step1",
"target": "step2",
@@ -147,8 +147,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-2",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step2",
"target": "step3",
@@ -349,8 +349,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-3",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "trigger",
"target": "step1",
@@ -363,8 +363,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-4",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step1",
"target": "step2",
@@ -377,8 +377,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-5",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step2",
"target": "step3",
@@ -579,8 +579,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-6",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "trigger",
"target": "step1",
@@ -593,8 +593,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-7",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step1",
"target": "step2",
@@ -607,8 +607,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-8",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step2",
"target": "step3",
@@ -828,8 +828,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-9",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "trigger",
"target": "step1",
@@ -842,8 +842,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-10",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step1",
"target": "step2",
@@ -856,8 +856,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-11",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step2",
"target": "step3",
@@ -870,8 +870,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-12",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "step3",
"target": "step4",
@@ -1057,8 +1057,8 @@ describe('generateWorkflowRunDiagram', () => {
},
"deletable": false,
"id": "8f3b2121-f194-4ba4-9fbf-13",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": false,
"source": "trigger",
"target": "step1",
@@ -161,8 +161,8 @@ describe('getWorkflowVersionDiagram', () => {
},
"deletable": true,
"id": "8f3b2121-f194-4ba4-9fbf-0",
"markerEnd": "workflow-edge-arrow-rounded",
"markerStart": "workflow-edge-gray-circle",
"markerEnd": "workflow-edge-branch-arrow",
"markerStart": undefined,
"selectable": true,
"source": "trigger",
"target": "step-1",