diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagram.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagram.tsx new file mode 100644 index 000000000000..872ab2d2ef0d --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagram.tsx @@ -0,0 +1,86 @@ +import { CreateStepNode } from '@/workflow/components/nodes/CreateStepNode'; +import { StepNode } from '@/workflow/components/nodes/StepNode'; +import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState'; +import { + WorkflowDiagram, + WorkflowDiagramEdge, + WorkflowDiagramNode, +} from '@/workflow/types/WorkflowDiagram'; +import { getOrganizedDiagram } from '@/workflow/utils/getOrganizedDiagram'; +import { + applyEdgeChanges, + applyNodeChanges, + Background, + EdgeChange, + NodeChange, + ReactFlow, +} from '@xyflow/react'; +import '@xyflow/react/dist/style.css'; +import { useMemo } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { GRAY_SCALE, isDefined } from 'twenty-ui'; + +export const WorkflowShowPageDiagram = ({ + diagram, +}: { + diagram: WorkflowDiagram; +}) => { + const { nodes, edges } = useMemo( + () => getOrganizedDiagram(diagram), + [diagram], + ); + + const setShowPageWorkflowDiagram = useSetRecoilState( + showPageWorkflowDiagramState, + ); + + const handleNodesChange = ( + nodeChanges: Array>, + ) => { + setShowPageWorkflowDiagram((diagram) => { + if (isDefined(diagram) === false) { + throw new Error( + 'It must be impossible for the nodes to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.', + ); + } + + return { + ...diagram, + nodes: applyNodeChanges(nodeChanges, diagram.nodes), + }; + }); + }; + + const handleEdgesChange = ( + edgeChanges: Array>, + ) => { + setShowPageWorkflowDiagram((diagram) => { + if (isDefined(diagram) === false) { + throw new Error( + 'It must be impossible for the edges to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.', + ); + } + + return { + ...diagram, + edges: applyEdgeChanges(edgeChanges, diagram.edges), + }; + }); + }; + + return ( + ({ ...node, draggable: false }))} + edges={edges} + onNodesChange={handleNodesChange} + onEdgesChange={handleEdgesChange} + > + + + ); +}; diff --git a/packages/twenty-front/src/pages/workflows/WorkflowShowPageEffect.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageEffect.tsx similarity index 57% rename from packages/twenty-front/src/pages/workflows/WorkflowShowPageEffect.tsx rename to packages/twenty-front/src/modules/workflow/components/WorkflowShowPageEffect.tsx index dce3204b5042..f1667a75220b 100644 --- a/packages/twenty-front/src/pages/workflows/WorkflowShowPageEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageEffect.tsx @@ -3,22 +3,13 @@ import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState'; import { showPageWorkflowErrorState } from '@/workflow/states/showPageWorkflowErrorState'; import { showPageWorkflowLoadingState } from '@/workflow/states/showPageWorkflowLoadingState'; -import { - Workflow, - WorkflowAction, - WorkflowTrigger, -} from '@/workflow/types/Workflow'; -import { - WorkflowDiagram, - WorkflowDiagramEdge, - WorkflowDiagramNode, -} from '@/workflow/types/WorkflowDiagram'; +import { Workflow } from '@/workflow/types/Workflow'; +import { WorkflowDiagram } from '@/workflow/types/WorkflowDiagram'; import { addCreateStepNodes } from '@/workflow/utils/addCreateStepNodes'; -import { MarkerType } from '@xyflow/react'; +import { generateWorklowDiagram } from '@/workflow/utils/generateWorkflowDiagram'; import { useEffect } from 'react'; import { useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-ui'; -import { v4 } from 'uuid'; type WorkflowShowPageEffectProps = { workflowId: string; @@ -29,71 +20,6 @@ const EMPTY_FLOW_DATA: WorkflowDiagram = { edges: [], }; -const generateWorklowDiagram = (trigger: WorkflowTrigger): WorkflowDiagram => { - const nodes: Array = []; - const edges: Array = []; - - // Helper function to generate nodes and edges recursively - const processNode = ( - action: WorkflowAction, - parentNodeId: string, - xPos: number, - yPos: number, - ) => { - const nodeId = v4(); - nodes.push({ - id: nodeId, - data: { - nodeType: 'action', - label: action.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, - }, - }); - - // Recursively generate flow for the next action if it exists - if (isDefined(action.nextAction)) { - processNode(action.nextAction, nodeId, xPos + 150, yPos + 100); - } - }; - - // Start with the trigger node - const triggerNodeId = v4(); - nodes.push({ - id: triggerNodeId, - data: { - nodeType: 'trigger', - label: trigger.settings.triggerName, - }, - position: { - x: 0, - y: 0, - }, - }); - - // If there's a next action, start the recursive generation - if (isDefined(trigger.nextAction)) { - processNode(trigger.nextAction, triggerNodeId, 150, 100); - } - - return { - nodes, - edges, - }; -}; - const getFlowLastVersion = ( workflow: Workflow | undefined, ): WorkflowDiagram => { diff --git a/packages/twenty-front/src/pages/workflows/WorkflowShowPageHeader.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageHeader.tsx similarity index 100% rename from packages/twenty-front/src/pages/workflows/WorkflowShowPageHeader.tsx rename to packages/twenty-front/src/modules/workflow/components/WorkflowShowPageHeader.tsx diff --git a/packages/twenty-front/src/pages/workflows/nodes/CreateStepNode.tsx b/packages/twenty-front/src/modules/workflow/components/nodes/CreateStepNode.tsx similarity index 88% rename from packages/twenty-front/src/pages/workflows/nodes/CreateStepNode.tsx rename to packages/twenty-front/src/modules/workflow/components/nodes/CreateStepNode.tsx index 9759f67baa79..6589f7da4ab4 100644 --- a/packages/twenty-front/src/pages/workflows/nodes/CreateStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/components/nodes/CreateStepNode.tsx @@ -1,9 +1,9 @@ import { IconButton } from '@/ui/input/button/components/IconButton'; import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; +import { StyledTargetHandle } from '@/workflow/components/nodes/common/StyledTargetHandle'; import { Position } from '@xyflow/react'; import { IconPlus } from 'twenty-ui'; -import { StyledTargetHandle } from '~/pages/workflows/nodes/base'; export const CreateStepNode = () => { const { openRightDrawer } = useRightDrawer(); diff --git a/packages/twenty-front/src/pages/workflows/nodes/StepNode.tsx b/packages/twenty-front/src/modules/workflow/components/nodes/StepNode.tsx similarity index 96% rename from packages/twenty-front/src/pages/workflows/nodes/StepNode.tsx rename to packages/twenty-front/src/modules/workflow/components/nodes/StepNode.tsx index 29667f5b374a..5596bba437ea 100644 --- a/packages/twenty-front/src/pages/workflows/nodes/StepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/components/nodes/StepNode.tsx @@ -1,7 +1,7 @@ +import { StyledTargetHandle } from '@/workflow/components/nodes/common/StyledTargetHandle'; import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram'; import styled from '@emotion/styled'; import { Handle, Position } from '@xyflow/react'; -import { StyledTargetHandle } from '~/pages/workflows/nodes/base'; const StyledStepNodeContainer = styled.div` display: flex; diff --git a/packages/twenty-front/src/pages/workflows/nodes/base.tsx b/packages/twenty-front/src/modules/workflow/components/nodes/common/StyledTargetHandle.tsx similarity index 99% rename from packages/twenty-front/src/pages/workflows/nodes/base.tsx rename to packages/twenty-front/src/modules/workflow/components/nodes/common/StyledTargetHandle.tsx index 4df99e6c343d..23f7992b2378 100644 --- a/packages/twenty-front/src/pages/workflows/nodes/base.tsx +++ b/packages/twenty-front/src/modules/workflow/components/nodes/common/StyledTargetHandle.tsx @@ -1,5 +1,4 @@ import styled from '@emotion/styled'; - import { Handle } from '@xyflow/react'; export const StyledTargetHandle = styled(Handle)` diff --git a/packages/twenty-front/src/modules/workflow/utils/generateWorkflowDiagram.ts b/packages/twenty-front/src/modules/workflow/utils/generateWorkflowDiagram.ts new file mode 100644 index 000000000000..9634dc472e9e --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/utils/generateWorkflowDiagram.ts @@ -0,0 +1,76 @@ +import { WorkflowAction, WorkflowTrigger } from '@/workflow/types/Workflow'; +import { + WorkflowDiagram, + WorkflowDiagramEdge, + WorkflowDiagramNode, +} from '@/workflow/types/WorkflowDiagram'; +import { MarkerType } from '@xyflow/react'; +import { isDefined } from 'twenty-ui'; +import { v4 } from 'uuid'; + +export const generateWorklowDiagram = ( + trigger: WorkflowTrigger, +): WorkflowDiagram => { + const nodes: Array = []; + const edges: Array = []; + + // Helper function to generate nodes and edges recursively + const processNode = ( + action: WorkflowAction, + parentNodeId: string, + xPos: number, + yPos: number, + ) => { + const nodeId = v4(); + nodes.push({ + id: nodeId, + data: { + nodeType: 'action', + label: action.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, + }, + }); + + // Recursively generate flow for the next action if it exists + if (isDefined(action.nextAction)) { + processNode(action.nextAction, nodeId, xPos + 150, yPos + 100); + } + }; + + // Start with the trigger node + const triggerNodeId = v4(); + nodes.push({ + id: triggerNodeId, + data: { + nodeType: 'trigger', + label: trigger.settings.triggerName, + }, + position: { + x: 0, + y: 0, + }, + }); + + // If there's a next action, start the recursive generation + if (isDefined(trigger.nextAction)) { + processNode(trigger.nextAction, triggerNodeId, 150, 100); + } + + return { + nodes, + edges, + }; +}; diff --git a/packages/twenty-front/src/pages/workflows/WorkflowShowPage.tsx b/packages/twenty-front/src/pages/workflows/WorkflowShowPage.tsx index 46b7f763e360..0a5125ef9081 100644 --- a/packages/twenty-front/src/pages/workflows/WorkflowShowPage.tsx +++ b/packages/twenty-front/src/pages/workflows/WorkflowShowPage.tsx @@ -1,37 +1,21 @@ -import styled from '@emotion/styled'; -import { useParams } from 'react-router-dom'; - import { PageBody } from '@/ui/layout/page/PageBody'; import { PageContainer } from '@/ui/layout/page/PageContainer'; import { PageTitle } from '@/ui/utilities/page-title/PageTitle'; +import { WorkflowShowPageDiagram } from '@/workflow/components/WorkflowShowPageDiagram'; +import { WorkflowShowPageEffect } from '@/workflow/components/WorkflowShowPageEffect'; +import { WorkflowShowPageHeader } from '@/workflow/components/WorkflowShowPageHeader'; import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState'; -import { - WorkflowDiagram, - WorkflowDiagramEdge, - WorkflowDiagramNode, -} from '@/workflow/types/WorkflowDiagram'; -import { getOrganizedDiagram } from '@/workflow/utils/getOrganizedDiagram'; -import { - applyEdgeChanges, - applyNodeChanges, - Background, - EdgeChange, - NodeChange, - ReactFlow, -} from '@xyflow/react'; +import styled from '@emotion/styled'; import '@xyflow/react/dist/style.css'; -import { useMemo } from 'react'; -import { useRecoilValue, useSetRecoilState } from 'recoil'; -import { GRAY_SCALE, IconSettingsAutomation, isDefined } from 'twenty-ui'; -import { WorkflowShowPageEffect } from '~/pages/workflows/WorkflowShowPageEffect'; -import { WorkflowShowPageHeader } from '~/pages/workflows/WorkflowShowPageHeader'; -import { CreateStepNode } from '~/pages/workflows/nodes/CreateStepNode'; -import { StepNode } from '~/pages/workflows/nodes/StepNode'; +import { useParams } from 'react-router-dom'; +import { useRecoilValue } from 'recoil'; +import { IconSettingsAutomation } from 'twenty-ui'; const StyledFlowContainer = styled.div` height: 100%; width: 100%; + /* Below we reset the default styling of Reactflow */ .react-flow__node-input, .react-flow__node-default, .react-flow__node-output, @@ -46,67 +30,6 @@ const StyledFlowContainer = styled.div` --xy-node-boxshadow-selected: none; `; -const LoadedWorkflow = ({ diagram }: { diagram: WorkflowDiagram }) => { - const { nodes, edges } = useMemo( - () => getOrganizedDiagram(diagram), - [diagram], - ); - - const setShowPageWorkflowDiagram = useSetRecoilState( - showPageWorkflowDiagramState, - ); - - const handleNodesChange = ( - nodeChanges: Array>, - ) => { - setShowPageWorkflowDiagram((diagram) => { - if (isDefined(diagram) === false) { - throw new Error( - 'It must be impossible for the nodes to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.', - ); - } - - return { - ...diagram, - nodes: applyNodeChanges(nodeChanges, diagram.nodes), - }; - }); - }; - - const handleEdgesChange = ( - edgeChanges: Array>, - ) => { - setShowPageWorkflowDiagram((diagram) => { - if (isDefined(diagram) === false) { - throw new Error( - 'It must be impossible for the edges to be updated if the diagram is not defined yet. Be sure the diagram is rendered only when defined.', - ); - } - - return { - ...diagram, - edges: applyEdgeChanges(edgeChanges, diagram.edges), - }; - }); - }; - - return ( - ({ ...node, draggable: false }))} - edges={edges} - onNodesChange={handleNodesChange} - onEdgesChange={handleEdgesChange} - > - - - ); -}; - export const WorkflowShowPage = () => { const parameters = useParams<{ workflowId: string; @@ -128,11 +51,11 @@ export const WorkflowShowPage = () => { + /> {showPageWorkflowDiagram === undefined ? null : ( - + )}