mirror of
				https://github.com/lingble/twenty.git
				synced 2025-11-03 22:27:57 +00:00 
			
		
		
		
	Add Manual Triggers (#8024)
In this PR: - Add support for manual triggers in the backend - Add a right drawer to let users select the type of trigger they want - Create a specific right drawer for database event triggers - Create a right drawer for manual triggers; let the user select where the manual trigger should be made available - Create a default trigger as soon as the user selects the type of trigger they want. It prevents the user to see empty selects for record type and event type. By default, the database event trigger will be set to "company.created". It should be visible enough for users to understand what happens and choose another record type or event type. https://github.com/user-attachments/assets/29a21985-1823-4890-9eb3-e4f876459c7a
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							bf2ba25a6e
						
					
				
				
					commit
					0144553667
				
			@@ -15,6 +15,7 @@ import { RightDrawerWorkflowViewStep } from '@/workflow/components/RightDrawerWo
 | 
				
			|||||||
import { isDefined } from 'twenty-ui';
 | 
					import { isDefined } from 'twenty-ui';
 | 
				
			||||||
import { rightDrawerPageState } from '../states/rightDrawerPageState';
 | 
					import { rightDrawerPageState } from '../states/rightDrawerPageState';
 | 
				
			||||||
import { RightDrawerPages } from '../types/RightDrawerPages';
 | 
					import { RightDrawerPages } from '../types/RightDrawerPages';
 | 
				
			||||||
 | 
					import { RightDrawerWorkflowSelectTriggerType } from '@/workflow/components/RightDrawerWorkflowSelectTriggerType';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledRightDrawerPage = styled.div`
 | 
					const StyledRightDrawerPage = styled.div`
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
@@ -38,6 +39,9 @@ const RIGHT_DRAWER_PAGES_CONFIG: ComponentByRightDrawerPage = {
 | 
				
			|||||||
  [RightDrawerPages.ViewCalendarEvent]: <RightDrawerCalendarEvent />,
 | 
					  [RightDrawerPages.ViewCalendarEvent]: <RightDrawerCalendarEvent />,
 | 
				
			||||||
  [RightDrawerPages.ViewRecord]: <RightDrawerRecord />,
 | 
					  [RightDrawerPages.ViewRecord]: <RightDrawerRecord />,
 | 
				
			||||||
  [RightDrawerPages.Copilot]: <RightDrawerAIChat />,
 | 
					  [RightDrawerPages.Copilot]: <RightDrawerAIChat />,
 | 
				
			||||||
 | 
					  [RightDrawerPages.WorkflowStepSelectTriggerType]: (
 | 
				
			||||||
 | 
					    <RightDrawerWorkflowSelectTriggerType />
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepSelectAction]: (
 | 
					  [RightDrawerPages.WorkflowStepSelectAction]: (
 | 
				
			||||||
    <RightDrawerWorkflowSelectAction />
 | 
					    <RightDrawerWorkflowSelectAction />
 | 
				
			||||||
  ),
 | 
					  ),
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,8 @@ export const RIGHT_DRAWER_PAGE_ICONS = {
 | 
				
			|||||||
  [RightDrawerPages.ViewCalendarEvent]: 'IconCalendarEvent',
 | 
					  [RightDrawerPages.ViewCalendarEvent]: 'IconCalendarEvent',
 | 
				
			||||||
  [RightDrawerPages.ViewRecord]: 'Icon123',
 | 
					  [RightDrawerPages.ViewRecord]: 'Icon123',
 | 
				
			||||||
  [RightDrawerPages.Copilot]: 'IconSparkles',
 | 
					  [RightDrawerPages.Copilot]: 'IconSparkles',
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepEdit]: 'IconSparkles',
 | 
					  [RightDrawerPages.WorkflowStepSelectTriggerType]: 'IconSparkles',
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepSelectAction]: 'IconSparkles',
 | 
					  [RightDrawerPages.WorkflowStepSelectAction]: 'IconSparkles',
 | 
				
			||||||
 | 
					  [RightDrawerPages.WorkflowStepEdit]: 'IconSparkles',
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepView]: 'IconSparkles',
 | 
					  [RightDrawerPages.WorkflowStepView]: 'IconSparkles',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,8 @@ export const RIGHT_DRAWER_PAGE_TITLES = {
 | 
				
			|||||||
  [RightDrawerPages.ViewCalendarEvent]: 'Calendar Event',
 | 
					  [RightDrawerPages.ViewCalendarEvent]: 'Calendar Event',
 | 
				
			||||||
  [RightDrawerPages.ViewRecord]: 'Record Editor',
 | 
					  [RightDrawerPages.ViewRecord]: 'Record Editor',
 | 
				
			||||||
  [RightDrawerPages.Copilot]: 'Copilot',
 | 
					  [RightDrawerPages.Copilot]: 'Copilot',
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepEdit]: 'Workflow',
 | 
					  [RightDrawerPages.WorkflowStepSelectTriggerType]: 'Workflow',
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepSelectAction]: 'Workflow',
 | 
					  [RightDrawerPages.WorkflowStepSelectAction]: 'Workflow',
 | 
				
			||||||
 | 
					  [RightDrawerPages.WorkflowStepEdit]: 'Workflow',
 | 
				
			||||||
  [RightDrawerPages.WorkflowStepView]: 'Workflow',
 | 
					  [RightDrawerPages.WorkflowStepView]: 'Workflow',
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@ export enum RightDrawerPages {
 | 
				
			|||||||
  ViewCalendarEvent = 'view-calendar-event',
 | 
					  ViewCalendarEvent = 'view-calendar-event',
 | 
				
			||||||
  ViewRecord = 'view-record',
 | 
					  ViewRecord = 'view-record',
 | 
				
			||||||
  Copilot = 'copilot',
 | 
					  Copilot = 'copilot',
 | 
				
			||||||
 | 
					  WorkflowStepSelectTriggerType = 'workflow-step-select-trigger-type',
 | 
				
			||||||
  WorkflowStepSelectAction = 'workflow-step-select-action',
 | 
					  WorkflowStepSelectAction = 'workflow-step-select-action',
 | 
				
			||||||
  WorkflowStepView = 'workflow-step-view',
 | 
					  WorkflowStepView = 'workflow-step-view',
 | 
				
			||||||
  WorkflowStepEdit = 'workflow-step-edit',
 | 
					  WorkflowStepEdit = 'workflow-step-edit',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,6 @@ export const RightDrawerWorkflowSelectActionContent = ({
 | 
				
			|||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					 | 
				
			||||||
    <StyledActionListContainer>
 | 
					    <StyledActionListContainer>
 | 
				
			||||||
      {ACTIONS.map((action) => (
 | 
					      {ACTIONS.map((action) => (
 | 
				
			||||||
        <MenuItem
 | 
					        <MenuItem
 | 
				
			||||||
@@ -36,6 +35,5 @@ export const RightDrawerWorkflowSelectActionContent = ({
 | 
				
			|||||||
        />
 | 
					        />
 | 
				
			||||||
      ))}
 | 
					      ))}
 | 
				
			||||||
    </StyledActionListContainer>
 | 
					    </StyledActionListContainer>
 | 
				
			||||||
    </>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,16 @@
 | 
				
			|||||||
 | 
					import { RightDrawerWorkflowSelectTriggerTypeContent } from '@/workflow/components/RightDrawerWorkflowSelectTriggerTypeContent';
 | 
				
			||||||
 | 
					import { useWorkflowWithCurrentVersion } from '@/workflow/hooks/useWorkflowWithCurrentVersion';
 | 
				
			||||||
 | 
					import { workflowIdState } from '@/workflow/states/workflowIdState';
 | 
				
			||||||
 | 
					import { useRecoilValue } from 'recoil';
 | 
				
			||||||
 | 
					import { isDefined } from 'twenty-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const RightDrawerWorkflowSelectTriggerType = () => {
 | 
				
			||||||
 | 
					  const workflowId = useRecoilValue(workflowIdState);
 | 
				
			||||||
 | 
					  const workflow = useWorkflowWithCurrentVersion(workflowId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!isDefined(workflow)) {
 | 
				
			||||||
 | 
					    return null;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return <RightDrawerWorkflowSelectTriggerTypeContent workflow={workflow} />;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -0,0 +1,58 @@
 | 
				
			|||||||
 | 
					import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
 | 
				
			||||||
 | 
					import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
 | 
				
			||||||
 | 
					import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
 | 
				
			||||||
 | 
					import { MenuItem } from '@/ui/navigation/menu-item/components/MenuItem';
 | 
				
			||||||
 | 
					import { TRIGGER_STEP_ID } from '@/workflow/constants/TriggerStepId';
 | 
				
			||||||
 | 
					import { TRIGGER_TYPES } from '@/workflow/constants/TriggerTypes';
 | 
				
			||||||
 | 
					import { useUpdateWorkflowVersionTrigger } from '@/workflow/hooks/useUpdateWorkflowVersionTrigger';
 | 
				
			||||||
 | 
					import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
 | 
				
			||||||
 | 
					import { WorkflowWithCurrentVersion } from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { getTriggerDefaultDefinition } from '@/workflow/utils/getTriggerDefaultDefinition';
 | 
				
			||||||
 | 
					import styled from '@emotion/styled';
 | 
				
			||||||
 | 
					import { useSetRecoilState } from 'recoil';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const StyledActionListContainer = styled.div`
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  overflow-y: auto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  padding-block: ${({ theme }) => theme.spacing(1)};
 | 
				
			||||||
 | 
					  padding-inline: ${({ theme }) => theme.spacing(2)};
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const RightDrawerWorkflowSelectTriggerTypeContent = ({
 | 
				
			||||||
 | 
					  workflow,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  workflow: WorkflowWithCurrentVersion;
 | 
				
			||||||
 | 
					}) => {
 | 
				
			||||||
 | 
					  const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { openRightDrawer } = useRightDrawer();
 | 
				
			||||||
 | 
					  const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <StyledActionListContainer>
 | 
				
			||||||
 | 
					      {TRIGGER_TYPES.map((action) => (
 | 
				
			||||||
 | 
					        <MenuItem
 | 
				
			||||||
 | 
					          LeftIcon={action.icon}
 | 
				
			||||||
 | 
					          text={action.label}
 | 
				
			||||||
 | 
					          onClick={async () => {
 | 
				
			||||||
 | 
					            await updateTrigger(
 | 
				
			||||||
 | 
					              getTriggerDefaultDefinition({
 | 
				
			||||||
 | 
					                type: action.type,
 | 
				
			||||||
 | 
					                activeObjectMetadataItems,
 | 
				
			||||||
 | 
					              }),
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            setWorkflowSelectedNode(TRIGGER_STEP_ID);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            openRightDrawer(RightDrawerPages.WorkflowStepEdit);
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      ))}
 | 
				
			||||||
 | 
					    </StyledActionListContainer>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,5 +1,7 @@
 | 
				
			|||||||
import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
 | 
					import { useRightDrawer } from '@/ui/layout/right-drawer/hooks/useRightDrawer';
 | 
				
			||||||
import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
 | 
					import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPages';
 | 
				
			||||||
 | 
					import { CREATE_STEP_STEP_ID } from '@/workflow/constants/CreateStepStepId';
 | 
				
			||||||
 | 
					import { EMPTY_TRIGGER_STEP_ID } from '@/workflow/constants/EmptyTriggerStepId';
 | 
				
			||||||
import { useStartNodeCreation } from '@/workflow/hooks/useStartNodeCreation';
 | 
					import { useStartNodeCreation } from '@/workflow/hooks/useStartNodeCreation';
 | 
				
			||||||
import { useTriggerNodeSelection } from '@/workflow/hooks/useTriggerNodeSelection';
 | 
					import { useTriggerNodeSelection } from '@/workflow/hooks/useTriggerNodeSelection';
 | 
				
			||||||
import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
 | 
					import { workflowSelectedNodeState } from '@/workflow/states/workflowSelectedNodeState';
 | 
				
			||||||
@@ -26,7 +28,14 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
 | 
				
			|||||||
        return;
 | 
					        return;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      const isCreateStepNode = selectedNode.type === 'create-step';
 | 
					      const isEmptyTriggerNode = selectedNode.type === EMPTY_TRIGGER_STEP_ID;
 | 
				
			||||||
 | 
					      if (isEmptyTriggerNode) {
 | 
				
			||||||
 | 
					        openRightDrawer(RightDrawerPages.WorkflowStepSelectTriggerType);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      const isCreateStepNode = selectedNode.type === CREATE_STEP_STEP_ID;
 | 
				
			||||||
      if (isCreateStepNode) {
 | 
					      if (isCreateStepNode) {
 | 
				
			||||||
        if (selectedNode.data.nodeType !== 'create-step') {
 | 
					        if (selectedNode.data.nodeType !== 'create-step') {
 | 
				
			||||||
          throw new Error('Expected selected node to be a create step node.');
 | 
					          throw new Error('Expected selected node to be a create step node.');
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,7 @@ import { WorkflowDiagramStepNodeData } from '@/workflow/types/WorkflowDiagram';
 | 
				
			|||||||
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
 | 
					import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
 | 
				
			||||||
import { useTheme } from '@emotion/react';
 | 
					import { useTheme } from '@emotion/react';
 | 
				
			||||||
import styled from '@emotion/styled';
 | 
					import styled from '@emotion/styled';
 | 
				
			||||||
import { IconCode, IconMail, IconPlaylistAdd } from 'twenty-ui';
 | 
					import { IconCode, IconHandMove, IconMail, IconPlaylistAdd } from 'twenty-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledStepNodeLabelIconContainer = styled.div`
 | 
					const StyledStepNodeLabelIconContainer = styled.div`
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
@@ -26,6 +26,8 @@ export const WorkflowDiagramStepNodeBase = ({
 | 
				
			|||||||
  const renderStepIcon = () => {
 | 
					  const renderStepIcon = () => {
 | 
				
			||||||
    switch (data.nodeType) {
 | 
					    switch (data.nodeType) {
 | 
				
			||||||
      case 'trigger': {
 | 
					      case 'trigger': {
 | 
				
			||||||
 | 
					        switch (data.triggerType) {
 | 
				
			||||||
 | 
					          case 'DATABASE_EVENT': {
 | 
				
			||||||
            return (
 | 
					            return (
 | 
				
			||||||
              <StyledStepNodeLabelIconContainer>
 | 
					              <StyledStepNodeLabelIconContainer>
 | 
				
			||||||
                <IconPlaylistAdd
 | 
					                <IconPlaylistAdd
 | 
				
			||||||
@@ -35,8 +37,19 @@ export const WorkflowDiagramStepNodeBase = ({
 | 
				
			|||||||
              </StyledStepNodeLabelIconContainer>
 | 
					              </StyledStepNodeLabelIconContainer>
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
      case 'condition': {
 | 
					          case 'MANUAL': {
 | 
				
			||||||
        return null;
 | 
					            return (
 | 
				
			||||||
 | 
					              <StyledStepNodeLabelIconContainer>
 | 
				
			||||||
 | 
					                <IconHandMove
 | 
				
			||||||
 | 
					                  size={theme.icon.size.sm}
 | 
				
			||||||
 | 
					                  color={theme.font.color.tertiary}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					              </StyledStepNodeLabelIconContainer>
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return assertUnreachable(data.triggerType);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      case 'action': {
 | 
					      case 'action': {
 | 
				
			||||||
        switch (data.actionType) {
 | 
					        switch (data.actionType) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,25 +5,17 @@ import { useFindManyRecords } from '@/object-record/hooks/useFindManyRecords';
 | 
				
			|||||||
import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth';
 | 
					import { useTriggerGoogleApisOAuth } from '@/settings/accounts/hooks/useTriggerGoogleApisOAuth';
 | 
				
			||||||
import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
					import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
				
			||||||
import { TextArea } from '@/ui/input/components/TextArea';
 | 
					import { TextArea } from '@/ui/input/components/TextArea';
 | 
				
			||||||
import { WorkflowEditActionFormBase } from '@/workflow/components/WorkflowEditActionFormBase';
 | 
					import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
 | 
				
			||||||
import { VariableTagInput } from '@/workflow/search-variables/components/VariableTagInput';
 | 
					import VariableTagInput from '@/workflow/search-variables/components/VariableTagInput';
 | 
				
			||||||
import { workflowIdState } from '@/workflow/states/workflowIdState';
 | 
					import { workflowIdState } from '@/workflow/states/workflowIdState';
 | 
				
			||||||
import { WorkflowSendEmailStep } from '@/workflow/types/Workflow';
 | 
					import { WorkflowSendEmailStep } from '@/workflow/types/Workflow';
 | 
				
			||||||
import { useTheme } from '@emotion/react';
 | 
					import { useTheme } from '@emotion/react';
 | 
				
			||||||
import styled from '@emotion/styled';
 | 
					 | 
				
			||||||
import { useEffect } from 'react';
 | 
					import { useEffect } from 'react';
 | 
				
			||||||
import { Controller, useForm } from 'react-hook-form';
 | 
					import { Controller, useForm } from 'react-hook-form';
 | 
				
			||||||
import { useRecoilValue } from 'recoil';
 | 
					import { useRecoilValue } from 'recoil';
 | 
				
			||||||
import { IconMail, IconPlus, isDefined } from 'twenty-ui';
 | 
					import { IconMail, IconPlus, isDefined } from 'twenty-ui';
 | 
				
			||||||
import { useDebouncedCallback } from 'use-debounce';
 | 
					import { useDebouncedCallback } from 'use-debounce';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledTriggerSettings = styled.div`
 | 
					 | 
				
			||||||
  padding: ${({ theme }) => theme.spacing(6)};
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  row-gap: ${({ theme }) => theme.spacing(4)};
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type WorkflowEditActionFormSendEmailProps =
 | 
					type WorkflowEditActionFormSendEmailProps =
 | 
				
			||||||
  | {
 | 
					  | {
 | 
				
			||||||
      action: WorkflowSendEmailStep;
 | 
					      action: WorkflowSendEmailStep;
 | 
				
			||||||
@@ -174,12 +166,11 @@ export const WorkflowEditActionFormSendEmail = (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    !loading && (
 | 
					    !loading && (
 | 
				
			||||||
      <WorkflowEditActionFormBase
 | 
					      <WorkflowEditGenericFormBase
 | 
				
			||||||
        ActionIcon={<IconMail color={theme.color.blue} />}
 | 
					        HeaderIcon={<IconMail color={theme.color.blue} />}
 | 
				
			||||||
        actionTitle="Send Email"
 | 
					        headerTitle="Send Email"
 | 
				
			||||||
        actionType="Email"
 | 
					        headerType="Email"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <StyledTriggerSettings>
 | 
					 | 
				
			||||||
        <Controller
 | 
					        <Controller
 | 
				
			||||||
          name="connectedAccountId"
 | 
					          name="connectedAccountId"
 | 
				
			||||||
          control={form.control}
 | 
					          control={form.control}
 | 
				
			||||||
@@ -253,8 +244,7 @@ export const WorkflowEditActionFormSendEmail = (
 | 
				
			|||||||
            />
 | 
					            />
 | 
				
			||||||
          )}
 | 
					          )}
 | 
				
			||||||
        />
 | 
					        />
 | 
				
			||||||
        </StyledTriggerSettings>
 | 
					      </WorkflowEditGenericFormBase>
 | 
				
			||||||
      </WorkflowEditActionFormBase>
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,18 +1,10 @@
 | 
				
			|||||||
import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions';
 | 
					import { useGetManyServerlessFunctions } from '@/settings/serverless-functions/hooks/useGetManyServerlessFunctions';
 | 
				
			||||||
import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
					import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
				
			||||||
import { WorkflowEditActionFormBase } from '@/workflow/components/WorkflowEditActionFormBase';
 | 
					import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
 | 
				
			||||||
import { WorkflowCodeStep } from '@/workflow/types/Workflow';
 | 
					import { WorkflowCodeStep } from '@/workflow/types/Workflow';
 | 
				
			||||||
import { useTheme } from '@emotion/react';
 | 
					import { useTheme } from '@emotion/react';
 | 
				
			||||||
import styled from '@emotion/styled';
 | 
					 | 
				
			||||||
import { IconCode, isDefined } from 'twenty-ui';
 | 
					import { IconCode, isDefined } from 'twenty-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledTriggerSettings = styled.div`
 | 
					 | 
				
			||||||
  padding: ${({ theme }) => theme.spacing(6)};
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  row-gap: ${({ theme }) => theme.spacing(4)};
 | 
					 | 
				
			||||||
`;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type WorkflowEditActionFormServerlessFunctionProps =
 | 
					type WorkflowEditActionFormServerlessFunctionProps =
 | 
				
			||||||
  | {
 | 
					  | {
 | 
				
			||||||
      action: WorkflowCodeStep;
 | 
					      action: WorkflowCodeStep;
 | 
				
			||||||
@@ -44,12 +36,11 @@ export const WorkflowEditActionFormServerlessFunction = (
 | 
				
			|||||||
  ];
 | 
					  ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <WorkflowEditActionFormBase
 | 
					    <WorkflowEditGenericFormBase
 | 
				
			||||||
      ActionIcon={<IconCode color={theme.color.orange} />}
 | 
					      HeaderIcon={<IconCode color={theme.color.orange} />}
 | 
				
			||||||
      actionTitle="Code - Serverless Function"
 | 
					      headerTitle="Code - Serverless Function"
 | 
				
			||||||
      actionType="Code"
 | 
					      headerType="Code"
 | 
				
			||||||
    >
 | 
					    >
 | 
				
			||||||
      <StyledTriggerSettings>
 | 
					 | 
				
			||||||
      <Select
 | 
					      <Select
 | 
				
			||||||
        dropdownId="workflow-edit-action-function"
 | 
					        dropdownId="workflow-edit-action-function"
 | 
				
			||||||
        label="Function"
 | 
					        label="Function"
 | 
				
			||||||
@@ -73,7 +64,6 @@ export const WorkflowEditActionFormServerlessFunction = (
 | 
				
			|||||||
          });
 | 
					          });
 | 
				
			||||||
        }}
 | 
					        }}
 | 
				
			||||||
      />
 | 
					      />
 | 
				
			||||||
      </StyledTriggerSettings>
 | 
					    </WorkflowEditGenericFormBase>
 | 
				
			||||||
    </WorkflowEditActionFormBase>
 | 
					 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import styled from '@emotion/styled';
 | 
					import styled from '@emotion/styled';
 | 
				
			||||||
import React from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledTriggerHeader = styled.div`
 | 
					const StyledHeader = styled.div`
 | 
				
			||||||
  background-color: ${({ theme }) => theme.background.secondary};
 | 
					  background-color: ${({ theme }) => theme.background.secondary};
 | 
				
			||||||
  border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
 | 
					  border-bottom: 1px solid ${({ theme }) => theme.border.color.medium};
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
@@ -9,7 +9,7 @@ const StyledTriggerHeader = styled.div`
 | 
				
			|||||||
  padding: ${({ theme }) => theme.spacing(6)};
 | 
					  padding: ${({ theme }) => theme.spacing(6)};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledTriggerHeaderTitle = styled.p`
 | 
					const StyledHeaderTitle = styled.p`
 | 
				
			||||||
  color: ${({ theme }) => theme.font.color.primary};
 | 
					  color: ${({ theme }) => theme.font.color.primary};
 | 
				
			||||||
  font-weight: ${({ theme }) => theme.font.weight.semiBold};
 | 
					  font-weight: ${({ theme }) => theme.font.weight.semiBold};
 | 
				
			||||||
  font-size: ${({ theme }) => theme.font.size.xl};
 | 
					  font-size: ${({ theme }) => theme.font.size.xl};
 | 
				
			||||||
@@ -17,12 +17,12 @@ const StyledTriggerHeaderTitle = styled.p`
 | 
				
			|||||||
  margin: ${({ theme }) => theme.spacing(3)} 0;
 | 
					  margin: ${({ theme }) => theme.spacing(3)} 0;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledTriggerHeaderType = styled.p`
 | 
					const StyledHeaderType = styled.p`
 | 
				
			||||||
  color: ${({ theme }) => theme.font.color.tertiary};
 | 
					  color: ${({ theme }) => theme.font.color.tertiary};
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const StyledTriggerHeaderIconContainer = styled.div`
 | 
					const StyledHeaderIconContainer = styled.div`
 | 
				
			||||||
  align-self: flex-start;
 | 
					  align-self: flex-start;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  justify-content: center;
 | 
					  justify-content: center;
 | 
				
			||||||
@@ -32,30 +32,35 @@ const StyledTriggerHeaderIconContainer = styled.div`
 | 
				
			|||||||
  padding: ${({ theme }) => theme.spacing(1)};
 | 
					  padding: ${({ theme }) => theme.spacing(1)};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const WorkflowEditActionFormBase = ({
 | 
					const StyledContentContainer = styled.div`
 | 
				
			||||||
  ActionIcon,
 | 
					  padding: ${({ theme }) => theme.spacing(6)};
 | 
				
			||||||
  actionTitle,
 | 
					  display: flex;
 | 
				
			||||||
  actionType,
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  row-gap: ${({ theme }) => theme.spacing(4)};
 | 
				
			||||||
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const WorkflowEditGenericFormBase = ({
 | 
				
			||||||
 | 
					  HeaderIcon,
 | 
				
			||||||
 | 
					  headerTitle,
 | 
				
			||||||
 | 
					  headerType,
 | 
				
			||||||
  children,
 | 
					  children,
 | 
				
			||||||
}: {
 | 
					}: {
 | 
				
			||||||
  ActionIcon: React.ReactNode;
 | 
					  HeaderIcon: React.ReactNode;
 | 
				
			||||||
  actionTitle: string;
 | 
					  headerTitle: string;
 | 
				
			||||||
  actionType: string;
 | 
					  headerType: string;
 | 
				
			||||||
  children: React.ReactNode;
 | 
					  children: React.ReactNode;
 | 
				
			||||||
}) => {
 | 
					}) => {
 | 
				
			||||||
  return (
 | 
					  return (
 | 
				
			||||||
    <>
 | 
					    <>
 | 
				
			||||||
      <StyledTriggerHeader>
 | 
					      <StyledHeader>
 | 
				
			||||||
        <StyledTriggerHeaderIconContainer>
 | 
					        <StyledHeaderIconContainer>{HeaderIcon}</StyledHeaderIconContainer>
 | 
				
			||||||
          {ActionIcon}
 | 
					 | 
				
			||||||
        </StyledTriggerHeaderIconContainer>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <StyledTriggerHeaderTitle>{actionTitle}</StyledTriggerHeaderTitle>
 | 
					        <StyledHeaderTitle>{headerTitle}</StyledHeaderTitle>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <StyledTriggerHeaderType>{actionType}</StyledTriggerHeaderType>
 | 
					        <StyledHeaderType>{headerType}</StyledHeaderType>
 | 
				
			||||||
      </StyledTriggerHeader>
 | 
					      </StyledHeader>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      {children}
 | 
					      <StyledContentContainer>{children}</StyledContentContainer>
 | 
				
			||||||
    </>
 | 
					    </>
 | 
				
			||||||
  );
 | 
					  );
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -1,7 +1,7 @@
 | 
				
			|||||||
import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
 | 
					import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
 | 
				
			||||||
import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
					import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
				
			||||||
import { OBJECT_EVENT_TRIGGERS } from '@/workflow/constants/ObjectEventTriggers';
 | 
					import { OBJECT_EVENT_TRIGGERS } from '@/workflow/constants/ObjectEventTriggers';
 | 
				
			||||||
import { WorkflowTrigger } from '@/workflow/types/Workflow';
 | 
					import { WorkflowDatabaseEventTrigger } from '@/workflow/types/Workflow';
 | 
				
			||||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
 | 
					import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
 | 
				
			||||||
import { useTheme } from '@emotion/react';
 | 
					import { useTheme } from '@emotion/react';
 | 
				
			||||||
import styled from '@emotion/styled';
 | 
					import styled from '@emotion/styled';
 | 
				
			||||||
@@ -45,23 +45,23 @@ const StyledTriggerSettings = styled.div`
 | 
				
			|||||||
  row-gap: ${({ theme }) => theme.spacing(4)};
 | 
					  row-gap: ${({ theme }) => theme.spacing(4)};
 | 
				
			||||||
`;
 | 
					`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type WorkflowEditTriggerFormProps =
 | 
					type WorkflowEditTriggerDatabaseEventFormProps =
 | 
				
			||||||
  | {
 | 
					  | {
 | 
				
			||||||
      trigger: WorkflowTrigger | undefined;
 | 
					      trigger: WorkflowDatabaseEventTrigger;
 | 
				
			||||||
      readonly: true;
 | 
					      readonly: true;
 | 
				
			||||||
      onTriggerUpdate?: undefined;
 | 
					      onTriggerUpdate?: undefined;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  | {
 | 
					  | {
 | 
				
			||||||
      trigger: WorkflowTrigger | undefined;
 | 
					      trigger: WorkflowDatabaseEventTrigger;
 | 
				
			||||||
      readonly?: false;
 | 
					      readonly?: false;
 | 
				
			||||||
      onTriggerUpdate: (trigger: WorkflowTrigger) => void;
 | 
					      onTriggerUpdate: (trigger: WorkflowDatabaseEventTrigger) => void;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const WorkflowEditTriggerForm = ({
 | 
					export const WorkflowEditTriggerDatabaseEventForm = ({
 | 
				
			||||||
  trigger,
 | 
					  trigger,
 | 
				
			||||||
  readonly,
 | 
					  readonly,
 | 
				
			||||||
  onTriggerUpdate,
 | 
					  onTriggerUpdate,
 | 
				
			||||||
}: WorkflowEditTriggerFormProps) => {
 | 
					}: WorkflowEditTriggerDatabaseEventFormProps) => {
 | 
				
			||||||
  const theme = useTheme();
 | 
					  const theme = useTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
 | 
					  const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
 | 
				
			||||||
@@ -102,7 +102,7 @@ export const WorkflowEditTriggerForm = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <StyledTriggerHeaderType>
 | 
					        <StyledTriggerHeaderType>
 | 
				
			||||||
          {isDefined(selectedEvent)
 | 
					          {isDefined(selectedEvent)
 | 
				
			||||||
            ? `Trigger . Record is ${selectedEvent.label}`
 | 
					            ? `Trigger · Record is ${selectedEvent.label}`
 | 
				
			||||||
            : '-'}
 | 
					            : '-'}
 | 
				
			||||||
        </StyledTriggerHeaderType>
 | 
					        </StyledTriggerHeaderType>
 | 
				
			||||||
      </StyledTriggerHeader>
 | 
					      </StyledTriggerHeader>
 | 
				
			||||||
@@ -0,0 +1,97 @@
 | 
				
			|||||||
 | 
					import { useFilteredObjectMetadataItems } from '@/object-metadata/hooks/useFilteredObjectMetadataItems';
 | 
				
			||||||
 | 
					import { Select, SelectOption } from '@/ui/input/components/Select';
 | 
				
			||||||
 | 
					import { WorkflowEditGenericFormBase } from '@/workflow/components/WorkflowEditGenericFormBase';
 | 
				
			||||||
 | 
					import { MANUAL_TRIGGER_AVAILABILITY_OPTIONS } from '@/workflow/constants/ManualTriggerAvailabilityOptions';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  WorkflowManualTrigger,
 | 
				
			||||||
 | 
					  WorkflowManualTriggerAvailability,
 | 
				
			||||||
 | 
					} from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { getManualTriggerDefaultSettings } from '@/workflow/utils/getManualTriggerDefaultSettings';
 | 
				
			||||||
 | 
					import { useTheme } from '@emotion/react';
 | 
				
			||||||
 | 
					import { IconHandMove, isDefined } from 'twenty-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WorkflowEditTriggerManualFormProps =
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					      trigger: WorkflowManualTrigger;
 | 
				
			||||||
 | 
					      readonly: true;
 | 
				
			||||||
 | 
					      onTriggerUpdate?: undefined;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  | {
 | 
				
			||||||
 | 
					      trigger: WorkflowManualTrigger;
 | 
				
			||||||
 | 
					      readonly?: false;
 | 
				
			||||||
 | 
					      onTriggerUpdate: (trigger: WorkflowManualTrigger) => void;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const WorkflowEditTriggerManualForm = ({
 | 
				
			||||||
 | 
					  trigger,
 | 
				
			||||||
 | 
					  readonly,
 | 
				
			||||||
 | 
					  onTriggerUpdate,
 | 
				
			||||||
 | 
					}: WorkflowEditTriggerManualFormProps) => {
 | 
				
			||||||
 | 
					  const theme = useTheme();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const availableMetadata: Array<SelectOption<string>> =
 | 
				
			||||||
 | 
					    activeObjectMetadataItems.map((item) => ({
 | 
				
			||||||
 | 
					      label: item.labelPlural,
 | 
				
			||||||
 | 
					      value: item.nameSingular,
 | 
				
			||||||
 | 
					    }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const manualTriggerAvailability: WorkflowManualTriggerAvailability =
 | 
				
			||||||
 | 
					    isDefined(trigger.settings.objectType)
 | 
				
			||||||
 | 
					      ? 'WHEN_RECORD_SELECTED'
 | 
				
			||||||
 | 
					      : 'EVERYWHERE';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    <WorkflowEditGenericFormBase
 | 
				
			||||||
 | 
					      HeaderIcon={<IconHandMove color={theme.font.color.tertiary} />}
 | 
				
			||||||
 | 
					      headerTitle="Manual Trigger"
 | 
				
			||||||
 | 
					      headerType="Trigger · Manual"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <Select
 | 
				
			||||||
 | 
					        dropdownId="workflow-edit-manual-trigger-availability"
 | 
				
			||||||
 | 
					        label="Available"
 | 
				
			||||||
 | 
					        fullWidth
 | 
				
			||||||
 | 
					        disabled={readonly}
 | 
				
			||||||
 | 
					        value={manualTriggerAvailability}
 | 
				
			||||||
 | 
					        options={MANUAL_TRIGGER_AVAILABILITY_OPTIONS}
 | 
				
			||||||
 | 
					        onChange={(updatedTriggerType) => {
 | 
				
			||||||
 | 
					          if (readonly === true) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          onTriggerUpdate({
 | 
				
			||||||
 | 
					            ...trigger,
 | 
				
			||||||
 | 
					            settings: getManualTriggerDefaultSettings({
 | 
				
			||||||
 | 
					              availability: updatedTriggerType,
 | 
				
			||||||
 | 
					              activeObjectMetadataItems,
 | 
				
			||||||
 | 
					            }),
 | 
				
			||||||
 | 
					          });
 | 
				
			||||||
 | 
					        }}
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      {manualTriggerAvailability === 'WHEN_RECORD_SELECTED' ? (
 | 
				
			||||||
 | 
					        <Select
 | 
				
			||||||
 | 
					          dropdownId="workflow-edit-manual-trigger-object"
 | 
				
			||||||
 | 
					          label="Object"
 | 
				
			||||||
 | 
					          fullWidth
 | 
				
			||||||
 | 
					          value={trigger.settings.objectType}
 | 
				
			||||||
 | 
					          options={availableMetadata}
 | 
				
			||||||
 | 
					          disabled={readonly}
 | 
				
			||||||
 | 
					          onChange={(updatedObject) => {
 | 
				
			||||||
 | 
					            if (readonly === true) {
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            onTriggerUpdate({
 | 
				
			||||||
 | 
					              ...trigger,
 | 
				
			||||||
 | 
					              settings: {
 | 
				
			||||||
 | 
					                objectType: updatedObject,
 | 
				
			||||||
 | 
					              },
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					          }}
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      ) : null}
 | 
				
			||||||
 | 
					    </WorkflowEditGenericFormBase>
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,6 +1,7 @@
 | 
				
			|||||||
import { WorkflowEditActionFormSendEmail } from '@/workflow/components/WorkflowEditActionFormSendEmail';
 | 
					import { WorkflowEditActionFormSendEmail } from '@/workflow/components/WorkflowEditActionFormSendEmail';
 | 
				
			||||||
import { WorkflowEditActionFormServerlessFunction } from '@/workflow/components/WorkflowEditActionFormServerlessFunction';
 | 
					import { WorkflowEditActionFormServerlessFunction } from '@/workflow/components/WorkflowEditActionFormServerlessFunction';
 | 
				
			||||||
import { WorkflowEditTriggerForm } from '@/workflow/components/WorkflowEditTriggerForm';
 | 
					import { WorkflowEditTriggerDatabaseEventForm } from '@/workflow/components/WorkflowEditTriggerDatabaseEventForm';
 | 
				
			||||||
 | 
					import { WorkflowEditTriggerManualForm } from '@/workflow/components/WorkflowEditTriggerManualForm';
 | 
				
			||||||
import {
 | 
					import {
 | 
				
			||||||
  WorkflowAction,
 | 
					  WorkflowAction,
 | 
				
			||||||
  WorkflowTrigger,
 | 
					  WorkflowTrigger,
 | 
				
			||||||
@@ -41,14 +42,38 @@ export const WorkflowStepDetail = ({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  switch (stepDefinition.type) {
 | 
					  switch (stepDefinition.type) {
 | 
				
			||||||
    case 'trigger': {
 | 
					    case 'trigger': {
 | 
				
			||||||
 | 
					      if (!isDefined(stepDefinition.definition)) {
 | 
				
			||||||
 | 
					        throw new Error(
 | 
				
			||||||
 | 
					          'Expected the trigger to be defined at this point. Ensure the trigger has been set with a default value before trying to edit it.',
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      switch (stepDefinition.definition.type) {
 | 
				
			||||||
 | 
					        case 'DATABASE_EVENT': {
 | 
				
			||||||
          return (
 | 
					          return (
 | 
				
			||||||
        <WorkflowEditTriggerForm
 | 
					            <WorkflowEditTriggerDatabaseEventForm
 | 
				
			||||||
              trigger={stepDefinition.definition}
 | 
					              trigger={stepDefinition.definition}
 | 
				
			||||||
              // eslint-disable-next-line react/jsx-props-no-spreading
 | 
					              // eslint-disable-next-line react/jsx-props-no-spreading
 | 
				
			||||||
              {...props}
 | 
					              {...props}
 | 
				
			||||||
            />
 | 
					            />
 | 
				
			||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        case 'MANUAL': {
 | 
				
			||||||
 | 
					          return (
 | 
				
			||||||
 | 
					            <WorkflowEditTriggerManualForm
 | 
				
			||||||
 | 
					              trigger={stepDefinition.definition}
 | 
				
			||||||
 | 
					              // eslint-disable-next-line react/jsx-props-no-spreading
 | 
				
			||||||
 | 
					              {...props}
 | 
				
			||||||
 | 
					            />
 | 
				
			||||||
 | 
					          );
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return assertUnreachable(
 | 
				
			||||||
 | 
					        stepDefinition.definition,
 | 
				
			||||||
 | 
					        `Expected the step to have an handler; ${JSON.stringify(stepDefinition)}`,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    case 'action': {
 | 
					    case 'action': {
 | 
				
			||||||
      switch (stepDefinition.definition.type) {
 | 
					      switch (stepDefinition.definition.type) {
 | 
				
			||||||
        case 'CODE': {
 | 
					        case 'CODE': {
 | 
				
			||||||
@@ -70,6 +95,11 @@ export const WorkflowStepDetail = ({
 | 
				
			|||||||
          );
 | 
					          );
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return assertUnreachable(
 | 
				
			||||||
 | 
					        stepDefinition.definition,
 | 
				
			||||||
 | 
					        `Expected the step to have an handler; ${JSON.stringify(stepDefinition)}`,
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export const CREATE_STEP_STEP_ID = 'create-step';
 | 
				
			||||||
@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					export const EMPTY_TRIGGER_STEP_ID = 'empty-trigger';
 | 
				
			||||||
@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { WorkflowManualTriggerAvailability } from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { IconCheckbox, IconComponent, IconSquare } from 'twenty-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const MANUAL_TRIGGER_AVAILABILITY_OPTIONS: Array<{
 | 
				
			||||||
 | 
					  label: string;
 | 
				
			||||||
 | 
					  value: WorkflowManualTriggerAvailability;
 | 
				
			||||||
 | 
					  Icon: IconComponent;
 | 
				
			||||||
 | 
					}> = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    label: 'When record(s) are selected',
 | 
				
			||||||
 | 
					    value: 'WHEN_RECORD_SELECTED',
 | 
				
			||||||
 | 
					    Icon: IconCheckbox,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    label: 'When no record(s) are selected',
 | 
				
			||||||
 | 
					    value: 'EVERYWHERE',
 | 
				
			||||||
 | 
					    Icon: IconSquare,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
@@ -0,0 +1,19 @@
 | 
				
			|||||||
 | 
					import { WorkflowTriggerType } from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { IconComponent, IconSettingsAutomation } from 'twenty-ui';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const TRIGGER_TYPES: Array<{
 | 
				
			||||||
 | 
					  label: string;
 | 
				
			||||||
 | 
					  type: WorkflowTriggerType;
 | 
				
			||||||
 | 
					  icon: IconComponent;
 | 
				
			||||||
 | 
					}> = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    label: 'Database Event',
 | 
				
			||||||
 | 
					    type: 'DATABASE_EVENT',
 | 
				
			||||||
 | 
					    icon: IconSettingsAutomation,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    label: 'Manual',
 | 
				
			||||||
 | 
					    type: 'MANUAL',
 | 
				
			||||||
 | 
					    icon: IconSettingsAutomation,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
@@ -48,10 +48,8 @@ export type WorkflowActionType = WorkflowAction['type'];
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type WorkflowStepType = WorkflowStep['type'];
 | 
					export type WorkflowStepType = WorkflowStep['type'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type WorkflowTriggerType = 'DATABASE_EVENT';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type BaseTrigger = {
 | 
					type BaseTrigger = {
 | 
				
			||||||
  type: WorkflowTriggerType;
 | 
					  type: string;
 | 
				
			||||||
  input?: object;
 | 
					  input?: object;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -62,7 +60,24 @@ export type WorkflowDatabaseEventTrigger = BaseTrigger & {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type WorkflowTrigger = WorkflowDatabaseEventTrigger;
 | 
					export type WorkflowManualTrigger = BaseTrigger & {
 | 
				
			||||||
 | 
					  type: 'MANUAL';
 | 
				
			||||||
 | 
					  settings: {
 | 
				
			||||||
 | 
					    objectType?: string;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowManualTriggerSettings = WorkflowManualTrigger['settings'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowManualTriggerAvailability =
 | 
				
			||||||
 | 
					  | 'EVERYWHERE'
 | 
				
			||||||
 | 
					  | 'WHEN_RECORD_SELECTED';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowTrigger =
 | 
				
			||||||
 | 
					  | WorkflowDatabaseEventTrigger
 | 
				
			||||||
 | 
					  | WorkflowManualTrigger;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowTriggerType = WorkflowTrigger['type'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type WorkflowStatus = 'DRAFT' | 'ACTIVE' | 'DEACTIVATED';
 | 
					export type WorkflowStatus = 'DRAFT' | 'ACTIVE' | 'DEACTIVATED';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,7 @@
 | 
				
			|||||||
import { WorkflowActionType } from '@/workflow/types/Workflow';
 | 
					import {
 | 
				
			||||||
 | 
					  WorkflowActionType,
 | 
				
			||||||
 | 
					  WorkflowTriggerType,
 | 
				
			||||||
 | 
					} from '@/workflow/types/Workflow';
 | 
				
			||||||
import { Edge, Node } from '@xyflow/react';
 | 
					import { Edge, Node } from '@xyflow/react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type WorkflowDiagramNode = Node<WorkflowDiagramNodeData>;
 | 
					export type WorkflowDiagramNode = Node<WorkflowDiagramNodeData>;
 | 
				
			||||||
@@ -11,7 +14,8 @@ export type WorkflowDiagram = {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export type WorkflowDiagramStepNodeData =
 | 
					export type WorkflowDiagramStepNodeData =
 | 
				
			||||||
  | {
 | 
					  | {
 | 
				
			||||||
      nodeType: 'trigger' | 'condition';
 | 
					      nodeType: 'trigger';
 | 
				
			||||||
 | 
					      triggerType: WorkflowTriggerType;
 | 
				
			||||||
      label: string;
 | 
					      label: string;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  | {
 | 
					  | {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -55,6 +55,7 @@ describe('getWorkflowVersionDiagram', () => {
 | 
				
			|||||||
          data: {
 | 
					          data: {
 | 
				
			||||||
            label: 'Company is Created',
 | 
					            label: 'Company is Created',
 | 
				
			||||||
            nodeType: 'trigger',
 | 
					            nodeType: 'trigger',
 | 
				
			||||||
 | 
					            triggerType: 'DATABASE_EVENT',
 | 
				
			||||||
          },
 | 
					          },
 | 
				
			||||||
          id: 'trigger',
 | 
					          id: 'trigger',
 | 
				
			||||||
          position: { x: 0, y: 0 },
 | 
					          position: { x: 0, y: 0 },
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,6 +5,7 @@ import {
 | 
				
			|||||||
  WorkflowDiagramEdge,
 | 
					  WorkflowDiagramEdge,
 | 
				
			||||||
  WorkflowDiagramNode,
 | 
					  WorkflowDiagramNode,
 | 
				
			||||||
} from '@/workflow/types/WorkflowDiagram';
 | 
					} from '@/workflow/types/WorkflowDiagram';
 | 
				
			||||||
 | 
					import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
 | 
				
			||||||
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
 | 
					import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
 | 
				
			||||||
import { MarkerType } from '@xyflow/react';
 | 
					import { MarkerType } from '@xyflow/react';
 | 
				
			||||||
import { isDefined } from 'twenty-ui';
 | 
					import { isDefined } from 'twenty-ui';
 | 
				
			||||||
@@ -59,15 +60,37 @@ export const generateWorkflowDiagram = ({
 | 
				
			|||||||
  const triggerNodeId = TRIGGER_STEP_ID;
 | 
					  const triggerNodeId = TRIGGER_STEP_ID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (isDefined(trigger)) {
 | 
					  if (isDefined(trigger)) {
 | 
				
			||||||
 | 
					    let triggerLabel: string;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    switch (trigger.type) {
 | 
				
			||||||
 | 
					      case 'MANUAL': {
 | 
				
			||||||
 | 
					        triggerLabel = 'Manual Trigger';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        break;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      case 'DATABASE_EVENT': {
 | 
				
			||||||
        const triggerEvent = splitWorkflowTriggerEventName(
 | 
					        const triggerEvent = splitWorkflowTriggerEventName(
 | 
				
			||||||
          trigger.settings.eventName,
 | 
					          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({
 | 
					    nodes.push({
 | 
				
			||||||
      id: triggerNodeId,
 | 
					      id: triggerNodeId,
 | 
				
			||||||
      data: {
 | 
					      data: {
 | 
				
			||||||
        nodeType: 'trigger',
 | 
					        nodeType: 'trigger',
 | 
				
			||||||
        label: `${capitalize(triggerEvent.objectType)} is ${capitalize(triggerEvent.event)}`,
 | 
					        triggerType: trigger.type,
 | 
				
			||||||
 | 
					        label: triggerLabel,
 | 
				
			||||||
      },
 | 
					      },
 | 
				
			||||||
      position: {
 | 
					      position: {
 | 
				
			||||||
        x: 0,
 | 
					        x: 0,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  WorkflowManualTriggerAvailability,
 | 
				
			||||||
 | 
					  WorkflowManualTriggerSettings,
 | 
				
			||||||
 | 
					} from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getManualTriggerDefaultSettings = ({
 | 
				
			||||||
 | 
					  availability,
 | 
				
			||||||
 | 
					  activeObjectMetadataItems,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  availability: WorkflowManualTriggerAvailability;
 | 
				
			||||||
 | 
					  activeObjectMetadataItems: ObjectMetadataItem[];
 | 
				
			||||||
 | 
					}): WorkflowManualTriggerSettings => {
 | 
				
			||||||
 | 
					  switch (availability) {
 | 
				
			||||||
 | 
					    case 'EVERYWHERE': {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        objectType: undefined,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 'WHEN_RECORD_SELECTED': {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        objectType: activeObjectMetadataItems[0].nameSingular,
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return assertUnreachable(availability);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import { WorkflowStep, WorkflowStepType } from '@/workflow/types/Workflow';
 | 
					import { WorkflowStep, WorkflowStepType } from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
 | 
				
			||||||
import { v4 } from 'uuid';
 | 
					import { v4 } from 'uuid';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const getStepDefaultDefinition = (
 | 
					export const getStepDefaultDefinition = (
 | 
				
			||||||
@@ -53,7 +54,7 @@ export const getStepDefaultDefinition = (
 | 
				
			|||||||
      };
 | 
					      };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    default: {
 | 
					    default: {
 | 
				
			||||||
      throw new Error(`Unknown type: ${type}`);
 | 
					      return assertUnreachable(type, `Unknown type: ${type}`);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,45 @@
 | 
				
			|||||||
 | 
					import { ObjectMetadataItem } from '@/object-metadata/types/ObjectMetadataItem';
 | 
				
			||||||
 | 
					import { OBJECT_EVENT_TRIGGERS } from '@/workflow/constants/ObjectEventTriggers';
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  WorkflowTrigger,
 | 
				
			||||||
 | 
					  WorkflowTriggerType,
 | 
				
			||||||
 | 
					} from '@/workflow/types/Workflow';
 | 
				
			||||||
 | 
					import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
 | 
				
			||||||
 | 
					import { getManualTriggerDefaultSettings } from '@/workflow/utils/getManualTriggerDefaultSettings';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const getTriggerDefaultDefinition = ({
 | 
				
			||||||
 | 
					  type,
 | 
				
			||||||
 | 
					  activeObjectMetadataItems,
 | 
				
			||||||
 | 
					}: {
 | 
				
			||||||
 | 
					  type: WorkflowTriggerType;
 | 
				
			||||||
 | 
					  activeObjectMetadataItems: ObjectMetadataItem[];
 | 
				
			||||||
 | 
					}): WorkflowTrigger => {
 | 
				
			||||||
 | 
					  if (activeObjectMetadataItems.length === 0) {
 | 
				
			||||||
 | 
					    throw new Error(
 | 
				
			||||||
 | 
					      'This function need to receive at least one object metadata item to run.',
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (type) {
 | 
				
			||||||
 | 
					    case 'DATABASE_EVENT': {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        type,
 | 
				
			||||||
 | 
					        settings: {
 | 
				
			||||||
 | 
					          eventName: `${activeObjectMetadataItems[0].nameSingular}.${OBJECT_EVENT_TRIGGERS[0].value}`,
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    case 'MANUAL': {
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        type,
 | 
				
			||||||
 | 
					        settings: getManualTriggerDefaultSettings({
 | 
				
			||||||
 | 
					          availability: 'WHEN_RECORD_SELECTED',
 | 
				
			||||||
 | 
					          activeObjectMetadataItems,
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    default: {
 | 
				
			||||||
 | 
					      return assertUnreachable(type, `Unknown type: ${type}`);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
export enum WorkflowTriggerType {
 | 
					export enum WorkflowTriggerType {
 | 
				
			||||||
  DATABASE_EVENT = 'DATABASE_EVENT',
 | 
					  DATABASE_EVENT = 'DATABASE_EVENT',
 | 
				
			||||||
 | 
					  MANUAL = 'MANUAL',
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BaseTrigger = {
 | 
					type BaseTrigger = {
 | 
				
			||||||
@@ -15,4 +16,20 @@ export type WorkflowDatabaseEventTrigger = BaseTrigger & {
 | 
				
			|||||||
  };
 | 
					  };
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export type WorkflowTrigger = WorkflowDatabaseEventTrigger;
 | 
					export enum WorkflowManualTriggerAvailability {
 | 
				
			||||||
 | 
					  EVERYWHERE = 'EVERYWHERE',
 | 
				
			||||||
 | 
					  WHEN_RECORD_SELECTED = 'WHEN_RECORD_SELECTED',
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowManualTrigger = BaseTrigger & {
 | 
				
			||||||
 | 
					  type: WorkflowTriggerType.MANUAL;
 | 
				
			||||||
 | 
					  settings: {
 | 
				
			||||||
 | 
					    objectType?: string;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowManualTriggerSettings = WorkflowManualTrigger['settings'];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export type WorkflowTrigger =
 | 
				
			||||||
 | 
					  | WorkflowDatabaseEventTrigger
 | 
				
			||||||
 | 
					  | WorkflowManualTrigger;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,6 +68,8 @@ function assertTriggerSettingsAreValid(
 | 
				
			|||||||
    case WorkflowTriggerType.DATABASE_EVENT:
 | 
					    case WorkflowTriggerType.DATABASE_EVENT:
 | 
				
			||||||
      assertDatabaseEventTriggerSettingsAreValid(settings);
 | 
					      assertDatabaseEventTriggerSettingsAreValid(settings);
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case WorkflowTriggerType.MANUAL:
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
    default:
 | 
					    default:
 | 
				
			||||||
      throw new WorkflowTriggerException(
 | 
					      throw new WorkflowTriggerException(
 | 
				
			||||||
        'Invalid trigger type for enabling workflow trigger',
 | 
					        'Invalid trigger type for enabling workflow trigger',
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,7 @@ import {
 | 
				
			|||||||
} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
 | 
					} from 'src/modules/workflow/workflow-trigger/exceptions/workflow-trigger.exception';
 | 
				
			||||||
import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
 | 
					import { WorkflowTriggerType } from 'src/modules/workflow/workflow-trigger/types/workflow-trigger.type';
 | 
				
			||||||
import { assertVersionCanBeActivated } from 'src/modules/workflow/workflow-trigger/utils/assert-version-can-be-activated.util';
 | 
					import { assertVersionCanBeActivated } from 'src/modules/workflow/workflow-trigger/utils/assert-version-can-be-activated.util';
 | 
				
			||||||
 | 
					import { assertNever } from 'src/utils/assert';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@Injectable()
 | 
					@Injectable()
 | 
				
			||||||
export class WorkflowTriggerWorkspaceService {
 | 
					export class WorkflowTriggerWorkspaceService {
 | 
				
			||||||
@@ -315,9 +316,13 @@ export class WorkflowTriggerWorkspaceService {
 | 
				
			|||||||
          workflowVersion.trigger,
 | 
					          workflowVersion.trigger,
 | 
				
			||||||
          manager,
 | 
					          manager,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        break;
 | 
					
 | 
				
			||||||
      default:
 | 
					        return;
 | 
				
			||||||
        break;
 | 
					      case WorkflowTriggerType.MANUAL:
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      default: {
 | 
				
			||||||
 | 
					        assertNever(workflowVersion.trigger);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -333,9 +338,12 @@ export class WorkflowTriggerWorkspaceService {
 | 
				
			|||||||
          workflowVersion.workflowId,
 | 
					          workflowVersion.workflowId,
 | 
				
			||||||
          manager,
 | 
					          manager,
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
        break;
 | 
					
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					      case WorkflowTriggerType.MANUAL:
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
      default:
 | 
					      default:
 | 
				
			||||||
        break;
 | 
					        assertNever(workflowVersion.trigger);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -25,3 +25,7 @@ export const assert: Assert = (condition, message, ErrorType) => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export const assertNotNull = <T>(item: T): item is NonNullable<T> =>
 | 
					export const assertNotNull = <T>(item: T): item is NonNullable<T> =>
 | 
				
			||||||
  item !== null && item !== undefined;
 | 
					  item !== null && item !== undefined;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const assertNever = (_value: never, message?: string): never => {
 | 
				
			||||||
 | 
					  throw new Error(message ?? "Didn't expect to get here.");
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -181,6 +181,8 @@ export {
 | 
				
			|||||||
  IconPlayerPlay,
 | 
					  IconPlayerPlay,
 | 
				
			||||||
  IconPlayerStop,
 | 
					  IconPlayerStop,
 | 
				
			||||||
  IconPlaylistAdd,
 | 
					  IconPlaylistAdd,
 | 
				
			||||||
 | 
					  IconHandMove,
 | 
				
			||||||
 | 
					  IconSquare,
 | 
				
			||||||
  IconPlaystationSquare,
 | 
					  IconPlaystationSquare,
 | 
				
			||||||
  IconPlug,
 | 
					  IconPlug,
 | 
				
			||||||
  IconPlus,
 | 
					  IconPlus,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user