diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStep.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStep.tsx index 96b82e2f003c..bc0b4b54d5d6 100644 --- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStep.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStep.tsx @@ -1,10 +1,16 @@ -import { showPageWorkflowSelectedNodeState } from '@/workflow/states/showPageWorkflowSelectedNodeState'; +import { RightDrawerWorkflowEditStepContent } from '@/workflow/components/RightDrawerWorkflowEditStepContent'; +import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { workflowIdState } from '@/workflow/states/workflowIdState'; import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-ui'; export const RightDrawerWorkflowEditStep = () => { - const showPageWorkflowSelectedNode = useRecoilValue( - showPageWorkflowSelectedNodeState, - ); + const workflowId = useRecoilValue(workflowIdState); + const workflow = useWorkflowWithCurrentVersion(workflowId); - return

{showPageWorkflowSelectedNode}

; + if (!isDefined(workflow)) { + return null; + } + + return ; }; diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx new file mode 100644 index 000000000000..a5669f989878 --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowEditStepContent.tsx @@ -0,0 +1,88 @@ +import { WorkflowEditActionForm } from '@/workflow/components/WorkflowEditActionForm'; +import { WorkflowEditTriggerForm } from '@/workflow/components/WorkflowEditTriggerForm'; +import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId'; +import { useUpdateWorkflowVersionStep } from '@/workflow/hooks/useUpdateWorkflowVersionStep'; +import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger'; +import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState'; +import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; +import { findStepPositionOrThrow } from '@/workflow/utils/findStepPositionOrThrow'; +import { useRecoilValue } from 'recoil'; +import { isDefined } from 'twenty-ui'; + +const getStepDefinitionOrThrow = ({ + stepId, + workflow, +}: { + stepId: string; + workflow: WorkflowWithCurrentVersion; +}) => { + const currentVersion = workflow.currentVersion; + if (!isDefined(currentVersion)) { + throw new Error('Expected to find a current version'); + } + + if (stepId === TRIGGER_STEP_ID) { + if (!isDefined(currentVersion.trigger)) { + throw new Error('Expected to find the definition of the trigger'); + } + + return { + type: 'trigger', + definition: currentVersion.trigger, + } as const; + } + + if (!isDefined(currentVersion.steps)) { + throw new Error('Expected to find an array of steps'); + } + + const selectedNodePosition = findStepPositionOrThrow({ + steps: currentVersion.steps, + stepId: stepId, + }); + + return { + type: 'action', + definition: selectedNodePosition.steps[selectedNodePosition.index], + } as const; +}; + +export const RightDrawerWorkflowEditStepContent = ({ + workflow, +}: { + workflow: WorkflowWithCurrentVersion; +}) => { + const workflowSelectedNode = useRecoilValue(workflowSelectedNodeState); + if (!isDefined(workflowSelectedNode)) { + throw new Error( + 'Expected a node to be selected. Selecting a node is mandatory to edit it.', + ); + } + + const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow }); + const { updateStep } = useUpdateWorkflowVersionStep({ + workflow, + stepId: workflowSelectedNode, + }); + + const stepDefinition = getStepDefinitionOrThrow({ + stepId: workflowSelectedNode, + workflow, + }); + + if (stepDefinition.type === 'trigger') { + return ( + + ); + } + + return ( + + ); +}; diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectAction.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectAction.tsx index d99189f66fcf..dadc160f4a8a 100644 --- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectAction.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectAction.tsx @@ -1,24 +1,12 @@ -import { CoreObjectNameSingular } from '@/object-metadata/types/CoreObjectNameSingular'; -import { useFindOneRecord } from '@/object-record/hooks/useFindOneRecord'; import { RightDrawerWorkflowSelectActionContent } from '@/workflow/components/RightDrawerWorkflowSelectActionContent'; -import { showPageWorkflowIdState } from '@/workflow/states/showPageWorkflowIdState'; -import { Workflow } from '@/workflow/types/Workflow'; +import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion'; +import { workflowIdState } from '@/workflow/states/workflowIdState'; import { useRecoilValue } from 'recoil'; import { isDefined } from 'twenty-ui'; export const RightDrawerWorkflowSelectAction = () => { - const showPageWorkflowId = useRecoilValue(showPageWorkflowIdState); - - const { record: workflow } = useFindOneRecord({ - objectNameSingular: CoreObjectNameSingular.Workflow, - objectRecordId: showPageWorkflowId, - recordGqlFields: { - id: true, - name: true, - versions: true, - publishedVersionId: true, - }, - }); + const workflowId = useRecoilValue(workflowIdState); + const workflow = useWorkflowWithCurrentVersion(workflowId); if (!isDefined(workflow)) { return null; diff --git a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx index bc888238b2ef..094cc99e0996 100644 --- a/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx +++ b/packages/twenty-front/src/modules/workflow/components/RightDrawerWorkflowSelectActionContent.tsx @@ -1,19 +1,9 @@ -import { TabList } from '@/ui/layout/tab/components/TabList'; import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem'; -import { useRightDrawerWorkflowSelectAction } from '@/workflow/hooks/useRightDrawerWorkflowSelectAction'; -import { Workflow } from '@/workflow/types/Workflow'; +import { ACTIONS } from '@/workflow/constants/Actions'; +import { useCreateStep } from '@/workflow/hooks/useCreateStep'; +import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow'; import styled from '@emotion/styled'; -// FIXME: copy-pasted -const StyledTabListContainer = styled.div` - align-items: center; - border-bottom: ${({ theme }) => `1px solid ${theme.border.color.light}`}; - box-sizing: border-box; - display: flex; - gap: ${({ theme }) => theme.spacing(2)}; - height: 40px; -`; - const StyledActionListContainer = styled.div` display: flex; flex-direction: column; @@ -24,33 +14,24 @@ const StyledActionListContainer = styled.div` padding-inline: ${({ theme }) => theme.spacing(2)}; `; -export const TAB_LIST_COMPONENT_ID = - 'workflow-select-action-page-right-tab-list'; - export const RightDrawerWorkflowSelectActionContent = ({ workflow, }: { - workflow: Workflow; + workflow: WorkflowWithCurrentVersion; }) => { - const tabListId = `${TAB_LIST_COMPONENT_ID}`; - - const { tabs, options, handleActionClick } = - useRightDrawerWorkflowSelectAction({ tabListId, workflow }); + const { createStep } = useCreateStep({ + workflow, + }); return ( <> - - - - - {options.map((option) => ( + {ACTIONS.map((action) => ( { - handleActionClick(option.id); + return createStep(action.type); }} /> ))} diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagram.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvas.tsx similarity index 69% rename from packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagram.tsx rename to packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvas.tsx index 9643aaa7a045..6d65014a8995 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagram.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvas.tsx @@ -1,7 +1,7 @@ -import { WorkflowShowPageDiagramCreateStepNode } from '@/workflow/components/WorkflowShowPageDiagramCreateStepNode'; -import { WorkflowShowPageDiagramEffect } from '@/workflow/components/WorkflowShowPageDiagramEffect'; -import { WorkflowShowPageDiagramStepNode } from '@/workflow/components/WorkflowShowPageDiagramStepNode'; -import { showPageWorkflowDiagramState } from '@/workflow/states/showPageWorkflowDiagramState'; +import { WorkflowDiagramCanvasEffect } from '@/workflow/components/WorkflowDiagramCanvasEffect'; +import { WorkflowDiagramCreateStepNode } from '@/workflow/components/WorkflowDiagramCreateStepNode'; +import { WorkflowDiagramStepNode } from '@/workflow/components/WorkflowDiagramStepNode'; +import { workflowDiagramState } from '@/workflow/states/workflowDiagramState'; import { WorkflowDiagram, WorkflowDiagramEdge, @@ -21,7 +21,7 @@ import { useMemo } from 'react'; import { useSetRecoilState } from 'recoil'; import { GRAY_SCALE, isDefined } from 'twenty-ui'; -export const WorkflowShowPageDiagram = ({ +export const WorkflowDiagramCanvas = ({ diagram, }: { diagram: WorkflowDiagram; @@ -31,14 +31,12 @@ export const WorkflowShowPageDiagram = ({ [diagram], ); - const setShowPageWorkflowDiagram = useSetRecoilState( - showPageWorkflowDiagramState, - ); + const setWorkflowDiagram = useSetRecoilState(workflowDiagramState); const handleNodesChange = ( nodeChanges: Array>, ) => { - setShowPageWorkflowDiagram((diagram) => { + setWorkflowDiagram((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.', @@ -55,7 +53,7 @@ export const WorkflowShowPageDiagram = ({ const handleEdgesChange = ( edgeChanges: Array>, ) => { - setShowPageWorkflowDiagram((diagram) => { + setWorkflowDiagram((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.', @@ -72,8 +70,8 @@ export const WorkflowShowPageDiagram = ({ return ( ({ ...node, draggable: false }))} @@ -81,7 +79,7 @@ export const WorkflowShowPageDiagram = ({ onNodesChange={handleNodesChange} onEdgesChange={handleEdgesChange} > - + diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramEffect.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvasEffect.tsx similarity index 67% rename from packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramEffect.tsx rename to packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvasEffect.tsx index 227d66a8d599..4f6bfde09488 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramEffect.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCanvasEffect.tsx @@ -1,8 +1,8 @@ import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer'; import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages'; import { useStartNodeCreation } from '@/workflow/hooks/useStartNodeCreation'; -import { showPageWorkflowDiagramTriggerNodeSelectionState } from '@/workflow/states/showPageWorkflowDiagramTriggerNodeSelectionState'; -import { showPageWorkflowSelectedNodeState } from '@/workflow/states/showPageWorkflowSelectedNodeState'; +import { workflowDiagramTriggerNodeSelectionState } from '@/workflow/states/workflowDiagramTriggerNodeSelectionState'; +import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState'; import { WorkflowDiagramEdge, WorkflowDiagramNode, @@ -16,18 +16,16 @@ import { useCallback, useEffect } from 'react'; import { useRecoilValue, useSetRecoilState } from 'recoil'; import { isDefined } from 'twenty-ui'; -export const WorkflowShowPageDiagramEffect = () => { +export const WorkflowDiagramCanvasEffect = () => { const reactflow = useReactFlow(); const { startNodeCreation } = useStartNodeCreation(); const { openRightDrawer, closeRightDrawer } = useRightDrawer(); - const setShowPageWorkflowSelectedNode = useSetRecoilState( - showPageWorkflowSelectedNodeState, - ); + const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); - const showPageWorkflowDiagramTriggerNodeSelection = useRecoilValue( - showPageWorkflowDiagramTriggerNodeSelectionState, + const workflowDiagramTriggerNodeSelection = useRecoilValue( + workflowDiagramTriggerNodeSelectionState, ); const handleSelectionChange = useCallback( @@ -52,13 +50,13 @@ export const WorkflowShowPageDiagramEffect = () => { return; } - setShowPageWorkflowSelectedNode(selectedNode.id); + setWorkflowSelectedNode(selectedNode.id); openRightDrawer(RightDrawerPages.WorkflowStepEdit); }, [ closeRightDrawer, openRightDrawer, - setShowPageWorkflowSelectedNode, + setWorkflowSelectedNode, startNodeCreation, ], ); @@ -68,14 +66,14 @@ export const WorkflowShowPageDiagramEffect = () => { }); useEffect(() => { - if (!isDefined(showPageWorkflowDiagramTriggerNodeSelection)) { + if (!isDefined(workflowDiagramTriggerNodeSelection)) { return; } - reactflow.updateNode(showPageWorkflowDiagramTriggerNodeSelection, { + reactflow.updateNode(workflowDiagramTriggerNodeSelection, { selected: true, }); - }, [reactflow, showPageWorkflowDiagramTriggerNodeSelection]); + }, [reactflow, workflowDiagramTriggerNodeSelection]); return null; }; diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramCreateStepNode.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCreateStepNode.tsx similarity index 87% rename from packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramCreateStepNode.tsx rename to packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCreateStepNode.tsx index 95fb6e836682..27706668b4b1 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramCreateStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramCreateStepNode.tsx @@ -7,7 +7,7 @@ export const StyledTargetHandle = styled(Handle)` visibility: hidden; `; -export const WorkflowShowPageDiagramCreateStepNode = () => { +export const WorkflowDiagramCreateStepNode = () => { return ( <> diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramStepNode.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx similarity index 98% rename from packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramStepNode.tsx rename to packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx index f8f9a3719f12..fe005cc595b4 100644 --- a/packages/twenty-front/src/modules/workflow/components/WorkflowShowPageDiagramStepNode.tsx +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowDiagramStepNode.tsx @@ -64,7 +64,7 @@ export const StyledTargetHandle = styled(Handle)` visibility: hidden; `; -export const WorkflowShowPageDiagramStepNode = ({ +export const WorkflowDiagramStepNode = ({ data, }: { data: WorkflowDiagramStepNodeData; diff --git a/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionForm.tsx b/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionForm.tsx new file mode 100644 index 000000000000..3cddde9f02db --- /dev/null +++ b/packages/twenty-front/src/modules/workflow/components/WorkflowEditActionForm.tsx @@ -0,0 +1,103 @@ +import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions'; +import { Select, SelectOption } from '@/ui/input/components/Select'; +import { WorkflowAction } from '@/workflow/types/Workflow'; +import { useTheme } from '@emotion/react'; +import styled from '@emotion/styled'; +import { IconCode, isDefined } from 'twenty-ui'; + +const StyledTriggerHeader = styled.div` + background-color: ${({ theme }) => theme.background.secondary}; + border-bottom: 1px solid ${({ theme }) => theme.border.color.medium}; + display: flex; + flex-direction: column; + padding: ${({ theme }) => theme.spacing(6)}; +`; + +const StyledTriggerHeaderTitle = styled.p` + color: ${({ theme }) => theme.font.color.primary}; + font-weight: ${({ theme }) => theme.font.weight.semiBold}; + font-size: ${({ theme }) => theme.font.size.xl}; + + margin: ${({ theme }) => theme.spacing(3)} 0; +`; + +const StyledTriggerHeaderType = styled.p` + color: ${({ theme }) => theme.font.color.tertiary}; + margin: 0; +`; + +const StyledTriggerHeaderIconContainer = styled.div` + align-self: flex-start; + display: flex; + justify-content: center; + align-items: center; + background-color: ${({ theme }) => theme.background.transparent.light}; + border-radius: ${({ theme }) => theme.border.radius.xs}; + padding: ${({ theme }) => theme.spacing(1)}; +`; + +const StyledTriggerSettings = styled.div` + padding: ${({ theme }) => theme.spacing(6)}; + display: flex; + flex-direction: column; + row-gap: ${({ theme }) => theme.spacing(4)}; +`; + +export const WorkflowEditActionForm = ({ + action, + onUpdateAction, +}: { + action: WorkflowAction; + onUpdateAction: (trigger: WorkflowAction) => void; +}) => { + const theme = useTheme(); + + const { serverlessFunctions } = useGetManyServerlessFunctions(); + + const availableFunctions: Array> = [ + { label: 'None', value: '' }, + ...serverlessFunctions + .filter((serverlessFunction) => + isDefined(serverlessFunction.latestVersion), + ) + .map((serverlessFunction) => ({ + label: serverlessFunction.name, + value: serverlessFunction.id, + })), + ]; + + return ( + <> + + + + + + + Code - Serverless Function + + + Code + + + + { + onUpdateTrigger({ + ...trigger, + settings: { + ...trigger.settings, + eventName: `${updatedRecordType}.${triggerEvent.event}`, + }, + }); + }} + /> +