- Increase the dimensions of the ReactFlow nodes. This allows to ditch scaling which made it hard to get the width of the nodes as they were visually scaled by 1.3. - Center the flow when the flow mounts and when the state of the right drawer opens. - Put the node type inside of the node so it doesn't overlap with the arrow - Make the edges non deletable We'll have to make a refactor so the viewport can be animated properly: https://github.com/twentyhq/twenty/issues/8387. https://github.com/user-attachments/assets/69494a32-5403-4898-be75-7fc38058e263 --------- Co-authored-by: Félix Malfait <felix@twenty.com>
124 lines
2.7 KiB
TypeScript
124 lines
2.7 KiB
TypeScript
import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
|
|
import { WorkflowStep, WorkflowTrigger } from '@/workflow/types/Workflow';
|
|
import {
|
|
WorkflowDiagram,
|
|
WorkflowDiagramEdge,
|
|
WorkflowDiagramNode,
|
|
} from '@/workflow/types/WorkflowDiagram';
|
|
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
|
|
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
|
|
import { MarkerType } from '@xyflow/react';
|
|
import { isDefined } from 'twenty-ui';
|
|
import { v4 } from 'uuid';
|
|
import { capitalize } from '~/utils/string/capitalize';
|
|
|
|
export const generateWorkflowDiagram = ({
|
|
trigger,
|
|
steps,
|
|
}: {
|
|
trigger: WorkflowTrigger | undefined;
|
|
steps: Array<WorkflowStep>;
|
|
}): WorkflowDiagram => {
|
|
const nodes: Array<WorkflowDiagramNode> = [];
|
|
const edges: Array<WorkflowDiagramEdge> = [];
|
|
|
|
// Helper function to generate nodes and edges recursively
|
|
const processNode = (
|
|
step: WorkflowStep,
|
|
parentNodeId: string,
|
|
xPos: number,
|
|
yPos: number,
|
|
) => {
|
|
const nodeId = step.id;
|
|
nodes.push({
|
|
id: nodeId,
|
|
data: {
|
|
nodeType: 'action',
|
|
actionType: step.type,
|
|
label: step.name,
|
|
},
|
|
position: {
|
|
x: xPos,
|
|
y: yPos,
|
|
},
|
|
});
|
|
|
|
// Create an edge from the parent node to this node
|
|
edges.push({
|
|
id: v4(),
|
|
source: parentNodeId,
|
|
target: nodeId,
|
|
markerEnd: {
|
|
type: MarkerType.ArrowClosed,
|
|
},
|
|
deletable: false,
|
|
});
|
|
|
|
return nodeId;
|
|
};
|
|
|
|
// Start with the trigger node
|
|
const triggerNodeId = TRIGGER_STEP_ID;
|
|
|
|
if (isDefined(trigger)) {
|
|
let triggerLabel: string;
|
|
|
|
switch (trigger.type) {
|
|
case 'MANUAL': {
|
|
triggerLabel = 'Manual Trigger';
|
|
|
|
break;
|
|
}
|
|
case 'DATABASE_EVENT': {
|
|
const triggerEvent = splitWorkflowTriggerEventName(
|
|
trigger.settings.eventName,
|
|
);
|
|
|
|
triggerLabel = `${capitalize(triggerEvent.objectType)} is ${capitalize(triggerEvent.event)}`;
|
|
|
|
break;
|
|
}
|
|
default: {
|
|
return assertUnreachable(
|
|
trigger,
|
|
`Expected the trigger "${JSON.stringify(trigger)}" to be supported.`,
|
|
);
|
|
}
|
|
}
|
|
|
|
nodes.push({
|
|
id: triggerNodeId,
|
|
data: {
|
|
nodeType: 'trigger',
|
|
triggerType: trigger.type,
|
|
label: triggerLabel,
|
|
},
|
|
position: {
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
});
|
|
} else {
|
|
nodes.push({
|
|
id: triggerNodeId,
|
|
type: 'empty-trigger',
|
|
data: {} as any,
|
|
position: {
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
});
|
|
}
|
|
|
|
let lastStepId = triggerNodeId;
|
|
|
|
for (const step of steps) {
|
|
lastStepId = processNode(step, lastStepId, 150, 100);
|
|
}
|
|
|
|
return {
|
|
nodes,
|
|
edges,
|
|
};
|
|
};
|