Fix icon matching + small other fixes on workflows (#9814)

- Record Fields label
- body height fix
- Icons on object picker
- Fix icon matching between nodes and right drawer

<img width="1296" alt="Capture d’écran 2025-01-23 à 18 51 12"
src="https://github.com/user-attachments/assets/ecd5fb00-49cd-416e-96af-9200418294e0"
/>
This commit is contained in:
Thomas Trompette
2025-01-24 15:47:09 +01:00
committed by GitHub
parent 1a42483aa9
commit 29df6e64a0
33 changed files with 247 additions and 226 deletions

View File

@ -12,13 +12,14 @@ import {
WorkflowDiagramNode, WorkflowDiagramNode,
WorkflowDiagramStepNodeData, WorkflowDiagramStepNodeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram'; } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react'; import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { IconBolt, isDefined } from 'twenty-ui'; import { IconBolt, isDefined, useIcons } from 'twenty-ui';
export const WorkflowDiagramCanvasEditableEffect = () => { export const WorkflowDiagramCanvasEditableEffect = () => {
const { getIcon } = useIcons();
const { startNodeCreation } = useStartNodeCreation(); const { startNodeCreation } = useStartNodeCreation();
const { openRightDrawer, closeRightDrawer } = useRightDrawer(); const { openRightDrawer, closeRightDrawer } = useRightDrawer();
@ -66,7 +67,7 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false }); setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
openRightDrawer(RightDrawerPages.WorkflowStepEdit, { openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
title: selectedNodeData.name, title: selectedNodeData.name,
Icon: getWorkflowNodeIcon(selectedNodeData), Icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)),
}); });
}, },
[ [
@ -76,6 +77,7 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
closeRightDrawer, closeRightDrawer,
closeCommandMenu, closeCommandMenu,
startNodeCreation, startNodeCreation,
getIcon,
], ],
); );

View File

@ -9,13 +9,14 @@ import {
WorkflowDiagramNode, WorkflowDiagramNode,
WorkflowDiagramStepNodeData, WorkflowDiagramStepNodeData,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram'; } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react'; import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react';
import { useCallback } from 'react'; import { useCallback } from 'react';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-ui'; import { isDefined, useIcons } from 'twenty-ui';
export const WorkflowDiagramCanvasReadonlyEffect = () => { export const WorkflowDiagramCanvasReadonlyEffect = () => {
const { getIcon } = useIcons();
const { openRightDrawer, closeRightDrawer } = useRightDrawer(); const { openRightDrawer, closeRightDrawer } = useRightDrawer();
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
const setHotkeyScope = useSetHotkeyScope(); const setHotkeyScope = useSetHotkeyScope();
@ -38,7 +39,7 @@ export const WorkflowDiagramCanvasReadonlyEffect = () => {
const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData; const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData;
openRightDrawer(RightDrawerPages.WorkflowStepView, { openRightDrawer(RightDrawerPages.WorkflowStepView, {
title: selectedNodeData.name, title: selectedNodeData.name,
Icon: getWorkflowNodeIcon(selectedNodeData), Icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)),
}); });
}, },
[ [
@ -47,6 +48,7 @@ export const WorkflowDiagramCanvasReadonlyEffect = () => {
openRightDrawer, openRightDrawer,
closeRightDrawer, closeRightDrawer,
closeCommandMenu, closeCommandMenu,
getIcon,
], ],
); );

View File

@ -1,7 +1,5 @@
import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode'; import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode';
import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { IconPlaylistAdd } from 'twenty-ui';
const StyledStepNodeLabelIconContainer = styled.div` const StyledStepNodeLabelIconContainer = styled.div`
align-items: center; align-items: center;
@ -9,25 +7,16 @@ const StyledStepNodeLabelIconContainer = styled.div`
border-radius: ${({ theme }) => theme.spacing(1)}; border-radius: ${({ theme }) => theme.spacing(1)};
display: flex; display: flex;
justify-content: center; justify-content: center;
padding: ${({ theme }) => theme.spacing(1)}; padding: ${({ theme }) => theme.spacing(3)};
`; `;
export const WorkflowDiagramEmptyTrigger = () => { export const WorkflowDiagramEmptyTrigger = () => {
const theme = useTheme();
return ( return (
<WorkflowDiagramBaseStepNode <WorkflowDiagramBaseStepNode
name="Add a Trigger" name="Add a Trigger"
nodeType="trigger" nodeType="trigger"
variant="placeholder" variant="placeholder"
Icon={ Icon={<StyledStepNodeLabelIconContainer />}
<StyledStepNodeLabelIconContainer>
<IconPlaylistAdd
size={theme.icon.size.md}
color={theme.font.color.tertiary}
/>
</StyledStepNodeLabelIconContainer>
}
/> />
); );
}; };

View File

@ -1,9 +1,10 @@
import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode'; import { WorkflowDiagramBaseStepNode } from '@/workflow/workflow-diagram/components/WorkflowDiagramBaseStepNode';
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram'; import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useIcons } from 'twenty-ui';
const StyledStepNodeLabelIconContainer = styled.div` const StyledStepNodeLabelIconContainer = styled.div`
align-items: center; align-items: center;
@ -22,8 +23,8 @@ export const WorkflowDiagramStepNodeBase = ({
RightFloatingElement?: React.ReactNode; RightFloatingElement?: React.ReactNode;
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
const { getIcon } = useIcons();
const Icon = getWorkflowNodeIcon(data); const Icon = getIcon(getWorkflowNodeIconKey(data));
const renderStepIcon = () => { const renderStepIcon = () => {
switch (data.nodeType) { switch (data.nodeType) {
@ -73,9 +74,7 @@ export const WorkflowDiagramStepNodeBase = ({
</StyledStepNodeLabelIconContainer> </StyledStepNodeLabelIconContainer>
); );
} }
case 'CREATE_RECORD': default: {
case 'UPDATE_RECORD':
case 'DELETE_RECORD': {
return ( return (
<StyledStepNodeLabelIconContainer> <StyledStepNodeLabelIconContainer>
<Icon <Icon
@ -89,8 +88,6 @@ export const WorkflowDiagramStepNodeBase = ({
} }
} }
} }
return assertUnreachable(data);
}; };
return ( return (

View File

@ -17,6 +17,7 @@ export type WorkflowDiagramStepNodeData =
nodeType: 'trigger'; nodeType: 'trigger';
triggerType: WorkflowTriggerType; triggerType: WorkflowTriggerType;
name: string; name: string;
icon?: string;
} }
| { | {
nodeType: 'action'; nodeType: 'action';

View File

@ -33,7 +33,7 @@ describe('getWorkflowVersionDiagram', () => {
}); });
}); });
it('returns a diagram with an empty-trigger node if the provided workflow version has no steps', () => { it('returns a diagram with only a trigger node if the provided workflow version has no steps', () => {
const result = getWorkflowVersionDiagram({ const result = getWorkflowVersionDiagram({
__typename: 'WorkflowVersion', __typename: 'WorkflowVersion',
status: 'ACTIVE', status: 'ACTIVE',
@ -42,7 +42,7 @@ describe('getWorkflowVersionDiagram', () => {
name: '', name: '',
steps: null, steps: null,
trigger: { trigger: {
name: 'Company created', name: 'Record is created',
settings: { eventName: 'company.created', outputSchema: {} }, settings: { eventName: 'company.created', outputSchema: {} },
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
}, },
@ -54,9 +54,10 @@ describe('getWorkflowVersionDiagram', () => {
nodes: [ nodes: [
{ {
data: { data: {
name: 'Company created', name: 'Record is created',
nodeType: 'trigger', nodeType: 'trigger',
triggerType: 'DATABASE_EVENT', triggerType: 'DATABASE_EVENT',
icon: 'IconPlus',
}, },
id: 'trigger', id: 'trigger',
position: { x: 0, y: 0 }, position: { x: 0, y: 0 },

View File

@ -7,9 +7,10 @@ import {
WorkflowDiagramEdge, WorkflowDiagramEdge,
WorkflowDiagramNode, WorkflowDiagramNode,
} from '@/workflow/workflow-diagram/types/WorkflowDiagram'; } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { DATABASE_TRIGGER_EVENTS } from '@/workflow/workflow-trigger/constants/DatabaseTriggerEvents'; import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId'; import { TRIGGER_STEP_ID } from '@/workflow/workflow-trigger/constants/TriggerStepId';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
@ -57,11 +58,15 @@ export const generateWorkflowDiagram = ({
const triggerNodeId = TRIGGER_STEP_ID; const triggerNodeId = TRIGGER_STEP_ID;
if (isDefined(trigger)) { if (isDefined(trigger)) {
let triggerLabel: string; let triggerDefaultLabel: string;
let triggerIcon: string | undefined;
switch (trigger.type) { switch (trigger.type) {
case 'MANUAL': { case 'MANUAL': {
triggerLabel = 'Manual Trigger'; triggerDefaultLabel = 'Manual Trigger';
triggerIcon = getTriggerIcon({
type: 'MANUAL',
});
break; break;
} }
@ -70,10 +75,15 @@ export const generateWorkflowDiagram = ({
trigger.settings.eventName, trigger.settings.eventName,
); );
triggerLabel = triggerDefaultLabel =
DATABASE_TRIGGER_EVENTS.find( DATABASE_TRIGGER_TYPES.find(
(event) => event.value === triggerEvent.event, (item) => item.event === triggerEvent.event,
)?.label ?? ''; )?.defaultLabel ?? '';
triggerIcon = getTriggerIcon({
type: 'DATABASE_EVENT',
eventName: triggerEvent.event,
});
break; break;
} }
@ -90,7 +100,8 @@ export const generateWorkflowDiagram = ({
data: { data: {
nodeType: 'trigger', nodeType: 'trigger',
triggerType: trigger.type, triggerType: trigger.type,
name: isDefined(trigger.name) ? trigger.name : triggerLabel, name: isDefined(trigger.name) ? trigger.name : triggerDefaultLabel,
icon: triggerIcon,
}, },
position: { position: {
x: 0, x: 0,

View File

@ -1,56 +0,0 @@
import {
WorkflowActionType,
WorkflowTriggerType,
} from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import {
IconAddressBook,
IconCode,
IconHandMove,
IconMail,
IconPlaylistAdd,
} from 'twenty-ui';
export const getWorkflowNodeIcon = (
data:
| {
nodeType: 'trigger';
triggerType: WorkflowTriggerType;
}
| {
nodeType: 'action';
actionType: WorkflowActionType;
},
) => {
switch (data.nodeType) {
case 'trigger': {
switch (data.triggerType) {
case 'DATABASE_EVENT': {
return IconPlaylistAdd;
}
case 'MANUAL': {
return IconHandMove;
}
}
return assertUnreachable(data.triggerType);
}
case 'action': {
switch (data.actionType) {
case 'CODE': {
return IconCode;
}
case 'SEND_EMAIL': {
return IconMail;
}
case 'CREATE_RECORD':
case 'UPDATE_RECORD':
case 'DELETE_RECORD': {
return IconAddressBook;
}
}
return assertUnreachable(data.actionType);
}
}
};

View File

@ -0,0 +1,13 @@
import { WorkflowDiagramStepNodeData } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
export const getWorkflowNodeIconKey = (data: WorkflowDiagramStepNodeData) => {
switch (data.nodeType) {
case 'trigger': {
return data.icon;
}
case 'action': {
return getActionIcon(data.actionType);
}
}
};

View File

@ -7,6 +7,7 @@ const StyledWorkflowStepBody = styled.div`
padding: ${({ theme }) => theme.spacing(6)}; padding: ${({ theme }) => theme.spacing(6)};
row-gap: ${({ theme }) => theme.spacing(6)}; row-gap: ${({ theme }) => theme.spacing(6)};
flex: 1 1 auto; flex: 1 1 auto;
height: 100%;
`; `;
export { StyledWorkflowStepBody as WorkflowStepBody }; export { StyledWorkflowStepBody as WorkflowStepBody };

View File

@ -7,17 +7,18 @@ import {
WorkflowWithCurrentVersion, WorkflowWithCurrentVersion,
} from '@/workflow/types/Workflow'; } from '@/workflow/types/Workflow';
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState'; import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon'; import { getWorkflowNodeIconKey } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIconKey';
import { useCreateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionStep'; import { useCreateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionStep';
import { workflowCreateStepFromParentStepIdState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState'; import { workflowCreateStepFromParentStepIdState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState';
import { useRecoilValue, useSetRecoilState } from 'recoil'; import { useRecoilValue, useSetRecoilState } from 'recoil';
import { isDefined } from 'twenty-ui'; import { isDefined, useIcons } from 'twenty-ui';
export const useCreateStep = ({ export const useCreateStep = ({
workflow, workflow,
}: { }: {
workflow: WorkflowWithCurrentVersion; workflow: WorkflowWithCurrentVersion;
}) => { }) => {
const { getIcon } = useIcons();
const { createWorkflowVersionStep } = useCreateWorkflowVersionStep(); const { createWorkflowVersionStep } = useCreateWorkflowVersionStep();
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState); const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
const setWorkflowLastCreatedStepId = useSetRecoilState( const setWorkflowLastCreatedStepId = useSetRecoilState(
@ -53,12 +54,15 @@ export const useCreateStep = ({
setWorkflowSelectedNode(createdStep.id); setWorkflowSelectedNode(createdStep.id);
setWorkflowLastCreatedStepId(createdStep.id); setWorkflowLastCreatedStepId(createdStep.id);
const stepIcon = getWorkflowNodeIconKey({
nodeType: 'action',
actionType: createdStep.type as WorkflowStepType,
name: createdStep.name,
});
openRightDrawer(RightDrawerPages.WorkflowStepEdit, { openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
title: createdStep.name, title: createdStep.name,
Icon: getWorkflowNodeIcon({ Icon: getIcon(stepIcon),
nodeType: 'action',
actionType: createdStep.type as WorkflowStepType,
}),
}); });
}; };

View File

@ -3,7 +3,7 @@ import { useCreateStep } from '@/workflow/workflow-steps/hooks/useCreateStep';
import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions'; import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions';
import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions'; import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { MenuItem } from 'twenty-ui'; import { MenuItem, useIcons } from 'twenty-ui';
const StyledActionListContainer = styled.div` const StyledActionListContainer = styled.div`
display: flex; display: flex;
@ -30,6 +30,7 @@ export const RightDrawerWorkflowSelectActionContent = ({
}: { }: {
workflow: WorkflowWithCurrentVersion; workflow: WorkflowWithCurrentVersion;
}) => { }) => {
const { getIcon } = useIcons();
const { createStep } = useCreateStep({ const { createStep } = useCreateStep({
workflow, workflow,
}); });
@ -40,7 +41,7 @@ export const RightDrawerWorkflowSelectActionContent = ({
{RECORD_ACTIONS.map((action) => ( {RECORD_ACTIONS.map((action) => (
<MenuItem <MenuItem
key={action.type} key={action.type}
LeftIcon={action.icon} LeftIcon={getIcon(action.icon)}
text={action.label} text={action.label}
onClick={() => createStep(action.type)} onClick={() => createStep(action.type)}
/> />
@ -49,7 +50,7 @@ export const RightDrawerWorkflowSelectActionContent = ({
{OTHER_ACTIONS.map((action) => ( {OTHER_ACTIONS.map((action) => (
<MenuItem <MenuItem
key={action.type} key={action.type}
LeftIcon={action.icon} LeftIcon={getIcon(action.icon)}
text={action.label} text={action.label}
onClick={() => createStep(action.type)} onClick={() => createStep(action.type)}
/> />

View File

@ -7,15 +7,11 @@ import { useViewOrDefaultViewFromPrefetchedViews } from '@/views/hooks/useViewOr
import { WorkflowCreateRecordAction } from '@/workflow/types/Workflow'; import { WorkflowCreateRecordAction } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { import { HorizontalSeparator, isDefined, useIcons } from 'twenty-ui';
HorizontalSeparator,
IconAddressBook,
isDefined,
useIcons,
} from 'twenty-ui';
import { JsonValue } from 'type-fest'; import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
import { FieldMetadataType } from '~/generated/graphql'; import { FieldMetadataType } from '~/generated/graphql';
@ -162,6 +158,7 @@ export const WorkflowEditActionFormCreateRecord = ({
}, [saveAction]); }, [saveAction]);
const headerTitle = isDefined(action.name) ? action.name : `Create Record`; const headerTitle = isDefined(action.name) ? action.name : `Create Record`;
const headerIcon = getActionIcon(action.type);
return ( return (
<> <>
@ -176,7 +173,7 @@ export const WorkflowEditActionFormCreateRecord = ({
name: newName, name: newName,
}); });
}} }}
Icon={IconAddressBook} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={theme.font.color.tertiary}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType="Action"

View File

@ -5,14 +5,10 @@ import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/Workflo
import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker'; import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { import { HorizontalSeparator, isDefined, useIcons } from 'twenty-ui';
HorizontalSeparator,
IconAddressBook,
isDefined,
useIcons,
} from 'twenty-ui';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { JsonValue } from 'type-fest'; import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
@ -110,6 +106,7 @@ export const WorkflowEditActionFormDeleteRecord = ({
}, [saveAction]); }, [saveAction]);
const headerTitle = isDefined(action.name) ? action.name : `Delete Record`; const headerTitle = isDefined(action.name) ? action.name : `Delete Record`;
const headerIcon = getActionIcon(action.type);
return ( return (
<> <>
@ -124,7 +121,7 @@ export const WorkflowEditActionFormDeleteRecord = ({
name: newName, name: newName,
}); });
}} }}
Icon={IconAddressBook} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={theme.font.color.tertiary}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType="Action"

View File

@ -9,11 +9,12 @@ import { workflowIdState } from '@/workflow/states/workflowIdState';
import { WorkflowSendEmailAction } from '@/workflow/types/Workflow'; import { WorkflowSendEmailAction } from '@/workflow/types/Workflow';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useRecoilValue } from 'recoil'; import { useRecoilValue } from 'recoil';
import { IconMail, IconPlus, isDefined } from 'twenty-ui'; import { IconPlus, isDefined, useIcons } from 'twenty-ui';
import { JsonValue } from 'type-fest'; import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
@ -41,6 +42,7 @@ export const WorkflowEditActionFormSendEmail = ({
actionOptions, actionOptions,
}: WorkflowEditActionFormSendEmailProps) => { }: WorkflowEditActionFormSendEmailProps) => {
const theme = useTheme(); const theme = useTheme();
const { getIcon } = useIcons();
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState); const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
const { triggerApisOAuth } = useTriggerApisOAuth(); const { triggerApisOAuth } = useTriggerApisOAuth();
@ -165,6 +167,7 @@ export const WorkflowEditActionFormSendEmail = ({
}); });
const headerTitle = isDefined(action.name) ? action.name : 'Send Email'; const headerTitle = isDefined(action.name) ? action.name : 'Send Email';
const headerIcon = getActionIcon(action.type);
return ( return (
!loading && ( !loading && (
@ -180,7 +183,7 @@ export const WorkflowEditActionFormSendEmail = ({
name: newName, name: newName,
}); });
}} }}
Icon={IconMail} Icon={getIcon(headerIcon)}
iconColor={theme.color.blue} iconColor={theme.color.blue}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Email" headerType="Email"

View File

@ -23,6 +23,7 @@ import { serverlessFunctionTestDataFamilyState } from '@/workflow/states/serverl
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowEditActionFormServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunctionFields'; import { WorkflowEditActionFormServerlessFunctionFields } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowEditActionFormServerlessFunctionFields';
import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/constants/WorkflowServerlessFunctionTabListComponentId'; import { WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID } from '@/workflow/workflow-steps/workflow-actions/constants/WorkflowServerlessFunctionTabListComponentId';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/utils/getWrongExportedFunctionMarkers'; import { getWrongExportedFunctionMarkers } from '@/workflow/workflow-steps/workflow-actions/utils/getWrongExportedFunctionMarkers';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
@ -32,7 +33,13 @@ import { editor } from 'monaco-editor';
import { AutoTypings } from 'monaco-editor-auto-typings'; import { AutoTypings } from 'monaco-editor-auto-typings';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { CodeEditor, IconCode, IconPlayerPlay, isDefined } from 'twenty-ui'; import {
CodeEditor,
IconCode,
IconPlayerPlay,
isDefined,
useIcons,
} from 'twenty-ui';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -76,10 +83,11 @@ export const WorkflowEditActionFormServerlessFunction = ({
action, action,
actionOptions, actionOptions,
}: WorkflowEditActionFormServerlessFunctionProps) => { }: WorkflowEditActionFormServerlessFunctionProps) => {
const theme = useTheme();
const { getIcon } = useIcons();
const serverlessFunctionId = action.settings.input.serverlessFunctionId; const serverlessFunctionId = action.settings.input.serverlessFunctionId;
const serverlessFunctionVersion = const serverlessFunctionVersion =
action.settings.input.serverlessFunctionVersion; action.settings.input.serverlessFunctionVersion;
const theme = useTheme();
const tabListId = `${WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID}_${serverlessFunctionId}`; const tabListId = `${WORKFLOW_SERVERLESS_FUNCTION_TAB_LIST_COMPONENT_ID}_${serverlessFunctionId}`;
const { activeTabId, setActiveTabId } = useTabList(tabListId); const { activeTabId, setActiveTabId } = useTabList(tabListId);
const { updateOneServerlessFunction, isReady } = const { updateOneServerlessFunction, isReady } =
@ -270,6 +278,11 @@ export const WorkflowEditActionFormServerlessFunction = ({
setFunctionInput(action.settings.input.serverlessFunctionInput); setFunctionInput(action.settings.input.serverlessFunctionInput);
}, [action]); }, [action]);
const headerTitle = isDefined(action.name)
? action.name
: 'Code - Serverless Function';
const headerIcon = getActionIcon(action.type);
return ( return (
!loading && ( !loading && (
<StyledContainer> <StyledContainer>
@ -284,9 +297,9 @@ export const WorkflowEditActionFormServerlessFunction = ({
onTitleChange={(newName: string) => { onTitleChange={(newName: string) => {
updateAction({ name: newName }); updateAction({ name: newName });
}} }}
Icon={IconCode} Icon={getIcon(headerIcon)}
iconColor={theme.color.orange} iconColor={theme.color.orange}
initialTitle={action.name || 'Code - Serverless Function'} initialTitle={headerTitle}
headerType="Code" headerType="Code"
/> />
<WorkflowStepBody> <WorkflowStepBody>

View File

@ -3,12 +3,7 @@ import { Select, SelectOption } from '@/ui/input/components/Select';
import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow'; import { WorkflowUpdateRecordAction } from '@/workflow/types/Workflow';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { import { HorizontalSeparator, isDefined, useIcons } from 'twenty-ui';
HorizontalSeparator,
IconAddressBook,
isDefined,
useIcons,
} from 'twenty-ui';
import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition'; import { formatFieldMetadataItemAsFieldDefinition } from '@/object-metadata/utils/formatFieldMetadataItemAsFieldDefinition';
import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput'; import { FormFieldInput } from '@/object-record/record-field/components/FormFieldInput';
@ -16,6 +11,7 @@ import { FormMultiSelectFieldInput } from '@/object-record/record-field/form-typ
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker'; import { WorkflowSingleRecordPicker } from '@/workflow/workflow-steps/workflow-actions/components/WorkflowSingleRecordPicker';
import { getActionIcon } from '@/workflow/workflow-steps/workflow-actions/utils/getActionIcon';
import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker'; import { WorkflowVariablePicker } from '@/workflow/workflow-variables/components/WorkflowVariablePicker';
import { JsonValue } from 'type-fest'; import { JsonValue } from 'type-fest';
import { useDebouncedCallback } from 'use-debounce'; import { useDebouncedCallback } from 'use-debounce';
@ -157,6 +153,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
}, [saveAction]); }, [saveAction]);
const headerTitle = isDefined(action.name) ? action.name : `Update Record`; const headerTitle = isDefined(action.name) ? action.name : `Update Record`;
const headerIcon = getActionIcon(action.type);
return ( return (
<> <>
@ -171,7 +168,7 @@ export const WorkflowEditActionFormUpdateRecord = ({
name: newName, name: newName,
}); });
}} }}
Icon={IconAddressBook} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={theme.font.color.tertiary}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Action" headerType="Action"

View File

@ -1,19 +1,18 @@
import { WorkflowStepType } from '@/workflow/types/Workflow'; import { WorkflowStepType } from '@/workflow/types/Workflow';
import { IconCode, IconComponent, IconSend } from 'twenty-ui';
export const OTHER_ACTIONS: Array<{ export const OTHER_ACTIONS: Array<{
label: string; label: string;
type: WorkflowStepType; type: WorkflowStepType;
icon: IconComponent; icon: string;
}> = [ }> = [
{ {
label: 'Send Email', label: 'Send Email',
type: 'SEND_EMAIL', type: 'SEND_EMAIL',
icon: IconSend, icon: 'IconSend',
}, },
{ {
label: 'Code', label: 'Code',
type: 'CODE', type: 'CODE',
icon: IconCode, icon: 'IconCode',
}, },
]; ];

View File

@ -1,24 +1,23 @@
import { WorkflowStepType } from '@/workflow/types/Workflow'; import { WorkflowStepType } from '@/workflow/types/Workflow';
import { IconComponent, IconPlus, IconRefreshDot, IconTrash } from 'twenty-ui';
export const RECORD_ACTIONS: Array<{ export const RECORD_ACTIONS: Array<{
label: string; label: string;
type: WorkflowStepType; type: WorkflowStepType;
icon: IconComponent; icon: string;
}> = [ }> = [
{ {
label: 'Create Record', label: 'Create Record',
type: 'CREATE_RECORD', type: 'CREATE_RECORD',
icon: IconPlus, icon: 'IconPlus',
}, },
{ {
label: 'Update Record', label: 'Update Record',
type: 'UPDATE_RECORD', type: 'UPDATE_RECORD',
icon: IconRefreshDot, icon: 'IconRefreshDot',
}, },
{ {
label: 'Delete Record', label: 'Delete Record',
type: 'DELETE_RECORD', type: 'DELETE_RECORD',
icon: IconTrash, icon: 'IconTrash',
}, },
]; ];

View File

@ -0,0 +1,13 @@
import { OTHER_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/OtherActions';
import { RECORD_ACTIONS } from '@/workflow/workflow-steps/workflow-actions/constants/RecordActions';
export const getActionIcon = (actionType: string) => {
switch (actionType) {
case 'CREATE_RECORD':
case 'UPDATE_RECORD':
case 'DELETE_RECORD':
return RECORD_ACTIONS.find((item) => item.type === actionType)?.icon;
default:
return OTHER_ACTIONS.find((item) => item.type === actionType)?.icon;
}
};

View File

@ -10,7 +10,7 @@ import { useUpdateWorkflowVersionTrigger } from '@/workflow/workflow-trigger/hoo
import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition'; import { getTriggerDefaultDefinition } from '@/workflow/workflow-trigger/utils/getTriggerDefaultDefinition';
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { useSetRecoilState } from 'recoil'; import { useSetRecoilState } from 'recoil';
import { MenuItem } from 'twenty-ui'; import { MenuItem, useIcons } from 'twenty-ui';
const StyledActionListContainer = styled.div` const StyledActionListContainer = styled.div`
display: flex; display: flex;
@ -37,6 +37,7 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
}: { }: {
workflow: WorkflowWithCurrentVersion; workflow: WorkflowWithCurrentVersion;
}) => { }) => {
const { getIcon } = useIcons();
const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow }); const { updateTrigger } = useUpdateWorkflowVersionTrigger({ workflow });
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
@ -49,13 +50,13 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
<StyledSectionTitle>Data</StyledSectionTitle> <StyledSectionTitle>Data</StyledSectionTitle>
{DATABASE_TRIGGER_TYPES.map((action) => ( {DATABASE_TRIGGER_TYPES.map((action) => (
<MenuItem <MenuItem
key={action.name} key={action.defaultLabel}
LeftIcon={action.icon} LeftIcon={getIcon(action.icon)}
text={action.name} text={action.defaultLabel}
onClick={async () => { onClick={async () => {
await updateTrigger( await updateTrigger(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: action.name, defaultLabel: action.defaultLabel,
type: action.type, type: action.type,
activeObjectMetadataItems, activeObjectMetadataItems,
}), }),
@ -64,8 +65,8 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
setWorkflowSelectedNode(TRIGGER_STEP_ID); setWorkflowSelectedNode(TRIGGER_STEP_ID);
openRightDrawer(RightDrawerPages.WorkflowStepEdit, { openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
title: action.name, title: action.defaultLabel,
Icon: action.icon, Icon: getIcon(action.icon),
}); });
}} }}
/> />
@ -73,13 +74,13 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
<StyledSectionTitle>Others</StyledSectionTitle> <StyledSectionTitle>Others</StyledSectionTitle>
{OTHER_TRIGGER_TYPES.map((action) => ( {OTHER_TRIGGER_TYPES.map((action) => (
<MenuItem <MenuItem
key={action.name} key={action.defaultLabel}
LeftIcon={action.icon} LeftIcon={getIcon(action.icon)}
text={action.name} text={action.defaultLabel}
onClick={async () => { onClick={async () => {
await updateTrigger( await updateTrigger(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: action.name, defaultLabel: action.defaultLabel,
type: action.type, type: action.type,
activeObjectMetadataItems, activeObjectMetadataItems,
}), }),
@ -88,8 +89,8 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
setWorkflowSelectedNode(TRIGGER_STEP_ID); setWorkflowSelectedNode(TRIGGER_STEP_ID);
openRightDrawer(RightDrawerPages.WorkflowStepEdit, { openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
title: action.name, title: action.defaultLabel,
Icon: action.icon, Icon: getIcon(action.icon),
}); });
}} }}
/> />

View File

@ -4,9 +4,10 @@ import { WorkflowDatabaseEventTrigger } from '@/workflow/types/Workflow';
import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName'; import { splitWorkflowTriggerEventName } from '@/workflow/utils/splitWorkflowTriggerEventName';
import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody'; import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowStepBody';
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { DATABASE_TRIGGER_EVENTS } from '@/workflow/workflow-trigger/constants/DatabaseTriggerEvents'; import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { IconPlaylistAdd, isDefined } from 'twenty-ui'; import { isDefined, useIcons } from 'twenty-ui';
type WorkflowEditTriggerDatabaseEventFormProps = { type WorkflowEditTriggerDatabaseEventFormProps = {
trigger: WorkflowDatabaseEventTrigger; trigger: WorkflowDatabaseEventTrigger;
@ -26,6 +27,7 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
triggerOptions, triggerOptions,
}: WorkflowEditTriggerDatabaseEventFormProps) => { }: WorkflowEditTriggerDatabaseEventFormProps) => {
const theme = useTheme(); const theme = useTheme();
const { getIcon } = useIcons();
const { activeObjectMetadataItems } = useFilteredObjectMetadataItems(); const { activeObjectMetadataItems } = useFilteredObjectMetadataItems();
@ -37,23 +39,23 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
activeObjectMetadataItems.map((item) => ({ activeObjectMetadataItems.map((item) => ({
label: item.labelPlural, label: item.labelPlural,
value: item.nameSingular, value: item.nameSingular,
Icon: getIcon(item.icon),
})); }));
const selectedEvent = isDefined(triggerEvent) const defaultLabel =
? DATABASE_TRIGGER_EVENTS.find( getTriggerDefaultLabel({
(availableEvent) => availableEvent.value === triggerEvent.event, type: 'DATABASE_EVENT',
) eventName: triggerEvent.event,
: undefined; }) ?? '-';
const headerTitle = isDefined(trigger.name) const headerIcon = getTriggerIcon({
? trigger.name type: 'DATABASE_EVENT',
: isDefined(selectedEvent) eventName: triggerEvent.event,
? selectedEvent.label });
: '-';
const headerType = isDefined(selectedEvent) const headerTitle = isDefined(trigger.name) ? trigger.name : defaultLabel;
? `Trigger · ${selectedEvent.label}`
: '-'; const headerType = `Trigger · ${defaultLabel}`;
return ( return (
<> <>
@ -68,7 +70,7 @@ export const WorkflowEditTriggerDatabaseEventForm = ({
name: newName, name: newName,
}); });
}} }}
Icon={IconPlaylistAdd} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={theme.font.color.tertiary}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType={headerType} headerType={headerType}

View File

@ -8,8 +8,9 @@ import { WorkflowStepBody } from '@/workflow/workflow-steps/components/WorkflowS
import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader'; import { WorkflowStepHeader } from '@/workflow/workflow-steps/components/WorkflowStepHeader';
import { MANUAL_TRIGGER_AVAILABILITY_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerAvailabilityOptions'; import { MANUAL_TRIGGER_AVAILABILITY_OPTIONS } from '@/workflow/workflow-trigger/constants/ManualTriggerAvailabilityOptions';
import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings'; import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings';
import { getTriggerIcon } from '@/workflow/workflow-trigger/utils/getTriggerIcon';
import { useTheme } from '@emotion/react'; import { useTheme } from '@emotion/react';
import { IconHandMove, isDefined, useIcons } from 'twenty-ui'; import { isDefined, useIcons } from 'twenty-ui';
type WorkflowEditTriggerManualFormProps = { type WorkflowEditTriggerManualFormProps = {
trigger: WorkflowManualTrigger; trigger: WorkflowManualTrigger;
@ -47,6 +48,10 @@ export const WorkflowEditTriggerManualForm = ({
const headerTitle = isDefined(trigger.name) ? trigger.name : 'Manual Trigger'; const headerTitle = isDefined(trigger.name) ? trigger.name : 'Manual Trigger';
const headerIcon = getTriggerIcon({
type: 'MANUAL',
});
return ( return (
<> <>
<WorkflowStepHeader <WorkflowStepHeader
@ -60,7 +65,7 @@ export const WorkflowEditTriggerManualForm = ({
name: newName, name: newName,
}); });
}} }}
Icon={IconHandMove} Icon={getIcon(headerIcon)}
iconColor={theme.font.color.tertiary} iconColor={theme.font.color.tertiary}
initialTitle={headerTitle} initialTitle={headerTitle}
headerType="Trigger · Manual" headerType="Trigger · Manual"

View File

@ -1,4 +1,4 @@
export enum DatabaseTriggerName { export enum DatabaseTriggerDefaultLabel {
RECORD_IS_CREATED = 'Record is Created', RECORD_IS_CREATED = 'Record is Created',
RECORD_IS_UPDATED = 'Record is Updated', RECORD_IS_UPDATED = 'Record is Updated',
RECORD_IS_DELETED = 'Record is Deleted', RECORD_IS_DELETED = 'Record is Deleted',

View File

@ -1,18 +0,0 @@
import { SelectOption } from '@/ui/input/components/Select';
import { DatabaseTriggerName } from '@/workflow/workflow-trigger/constants/DatabaseTriggerName';
export const DATABASE_TRIGGER_EVENTS: Array<SelectOption<string>> = [
{
label: DatabaseTriggerName.RECORD_IS_CREATED,
value: 'created',
},
{
label: DatabaseTriggerName.RECORD_IS_UPDATED,
value: 'updated',
},
{
label: DatabaseTriggerName.RECORD_IS_DELETED,
value: 'deleted',
},
];

View File

@ -1,25 +1,28 @@
import { WorkflowTriggerType } from '@/workflow/types/Workflow'; import { WorkflowTriggerType } from '@/workflow/types/Workflow';
import { DatabaseTriggerName } from '@/workflow/workflow-trigger/constants/DatabaseTriggerName'; import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel';
import { IconComponent, IconPlus, IconRefreshDot, IconTrash } from 'twenty-ui';
export const DATABASE_TRIGGER_TYPES: Array<{ export const DATABASE_TRIGGER_TYPES: Array<{
name: DatabaseTriggerName; defaultLabel: DatabaseTriggerDefaultLabel;
type: WorkflowTriggerType; type: WorkflowTriggerType;
icon: IconComponent; icon: string;
event: string;
}> = [ }> = [
{ {
name: DatabaseTriggerName.RECORD_IS_CREATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
icon: IconPlus, icon: 'IconPlus',
event: 'created',
}, },
{ {
name: DatabaseTriggerName.RECORD_IS_UPDATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_UPDATED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
icon: IconRefreshDot, icon: 'IconRefreshDot',
event: 'updated',
}, },
{ {
name: DatabaseTriggerName.RECORD_IS_DELETED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_DELETED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
icon: IconTrash, icon: 'IconTrash',
event: 'deleted',
}, },
]; ];

View File

@ -1,14 +1,13 @@
import { WorkflowTriggerType } from '@/workflow/types/Workflow'; import { WorkflowTriggerType } from '@/workflow/types/Workflow';
import { IconClick, IconComponent } from 'twenty-ui';
export const OTHER_TRIGGER_TYPES: Array<{ export const OTHER_TRIGGER_TYPES: Array<{
name: string; defaultLabel: string;
type: WorkflowTriggerType; type: WorkflowTriggerType;
icon: IconComponent; icon: string;
}> = [ }> = [
{ {
name: 'Launch manually', defaultLabel: 'Launch manually',
type: 'MANUAL', type: 'MANUAL',
icon: IconClick, icon: 'IconHandMove',
}, },
]; ];

View File

@ -1,4 +1,4 @@
import { DatabaseTriggerName } from '@/workflow/workflow-trigger/constants/DatabaseTriggerName'; import { DatabaseTriggerDefaultLabel } from '@/workflow/workflow-trigger/constants/DatabaseTriggerDefaultLabel';
import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems'; import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedMockObjectMetadataItems';
import { getTriggerDefaultDefinition } from '../getTriggerDefaultDefinition'; import { getTriggerDefaultDefinition } from '../getTriggerDefaultDefinition';
@ -6,7 +6,7 @@ describe('getTriggerDefaultDefinition', () => {
it('throws if the activeObjectMetadataItems list is empty', () => { it('throws if the activeObjectMetadataItems list is empty', () => {
expect(() => { expect(() => {
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: DatabaseTriggerName.RECORD_IS_CREATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
activeObjectMetadataItems: [], activeObjectMetadataItems: [],
}); });
@ -16,7 +16,7 @@ describe('getTriggerDefaultDefinition', () => {
it('returns a valid configuration for DATABASE_EVENT trigger type creation', () => { it('returns a valid configuration for DATABASE_EVENT trigger type creation', () => {
expect( expect(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: DatabaseTriggerName.RECORD_IS_CREATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
activeObjectMetadataItems: generatedMockObjectMetadataItems, activeObjectMetadataItems: generatedMockObjectMetadataItems,
}), }),
@ -32,7 +32,7 @@ describe('getTriggerDefaultDefinition', () => {
it('returns a valid configuration for DATABASE_EVENT trigger type update', () => { it('returns a valid configuration for DATABASE_EVENT trigger type update', () => {
expect( expect(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: DatabaseTriggerName.RECORD_IS_UPDATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_UPDATED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
activeObjectMetadataItems: generatedMockObjectMetadataItems, activeObjectMetadataItems: generatedMockObjectMetadataItems,
}), }),
@ -48,7 +48,7 @@ describe('getTriggerDefaultDefinition', () => {
it('returns a valid configuration for DATABASE_EVENT trigger type deletion', () => { it('returns a valid configuration for DATABASE_EVENT trigger type deletion', () => {
expect( expect(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: DatabaseTriggerName.RECORD_IS_DELETED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_DELETED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
activeObjectMetadataItems: generatedMockObjectMetadataItems, activeObjectMetadataItems: generatedMockObjectMetadataItems,
}), }),
@ -64,7 +64,7 @@ describe('getTriggerDefaultDefinition', () => {
it('returns a valid configuration for DATABASE_EVENT trigger type creation', () => { it('returns a valid configuration for DATABASE_EVENT trigger type creation', () => {
expect( expect(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: DatabaseTriggerName.RECORD_IS_CREATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'DATABASE_EVENT', type: 'DATABASE_EVENT',
activeObjectMetadataItems: generatedMockObjectMetadataItems, activeObjectMetadataItems: generatedMockObjectMetadataItems,
}), }),
@ -80,7 +80,7 @@ describe('getTriggerDefaultDefinition', () => {
it('returns a valid configuration for MANUAL trigger type', () => { it('returns a valid configuration for MANUAL trigger type', () => {
expect( expect(
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: 'Launch manually', defaultLabel: 'Launch manually',
type: 'MANUAL', type: 'MANUAL',
activeObjectMetadataItems: generatedMockObjectMetadataItems, activeObjectMetadataItems: generatedMockObjectMetadataItems,
}), }),
@ -96,7 +96,7 @@ describe('getTriggerDefaultDefinition', () => {
it('throws when providing an unknown trigger type', () => { it('throws when providing an unknown trigger type', () => {
expect(() => { expect(() => {
getTriggerDefaultDefinition({ getTriggerDefaultDefinition({
name: DatabaseTriggerName.RECORD_IS_CREATED, defaultLabel: DatabaseTriggerDefaultLabel.RECORD_IS_CREATED,
type: 'unknown' as any, type: 'unknown' as any,
activeObjectMetadataItems: generatedMockObjectMetadataItems, activeObjectMetadataItems: generatedMockObjectMetadataItems,
}); });

View File

@ -4,15 +4,15 @@ import {
WorkflowTriggerType, WorkflowTriggerType,
} from '@/workflow/types/Workflow'; } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { DATABASE_TRIGGER_EVENTS } from '@/workflow/workflow-trigger/constants/DatabaseTriggerEvents'; import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings'; import { getManualTriggerDefaultSettings } from '@/workflow/workflow-trigger/utils/getManualTriggerDefaultSettings';
export const getTriggerDefaultDefinition = ({ export const getTriggerDefaultDefinition = ({
name, defaultLabel,
type, type,
activeObjectMetadataItems, activeObjectMetadataItems,
}: { }: {
name: string; defaultLabel: string;
type: WorkflowTriggerType; type: WorkflowTriggerType;
activeObjectMetadataItems: ObjectMetadataItem[]; activeObjectMetadataItems: ObjectMetadataItem[];
}): WorkflowTrigger => { }): WorkflowTrigger => {
@ -28,9 +28,9 @@ export const getTriggerDefaultDefinition = ({
type, type,
settings: { settings: {
eventName: `${activeObjectMetadataItems[0].nameSingular}.${ eventName: `${activeObjectMetadataItems[0].nameSingular}.${
DATABASE_TRIGGER_EVENTS.find( DATABASE_TRIGGER_TYPES.find(
(availableEvent) => availableEvent.label === name, (availableEvent) => availableEvent.defaultLabel === defaultLabel,
)?.value )?.event
}`, }`,
outputSchema: {}, outputSchema: {},
}, },

View File

@ -0,0 +1,21 @@
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/OtherTriggerTypes';
export const getTriggerIcon = (
trigger:
| {
type: 'MANUAL';
}
| {
type: 'DATABASE_EVENT';
eventName: string;
},
): string | undefined => {
if (trigger.type === 'DATABASE_EVENT') {
return DATABASE_TRIGGER_TYPES.find(
(type) => type.event === trigger.eventName,
)?.icon;
}
return OTHER_TRIGGER_TYPES.find((item) => item.type === trigger.type)?.icon;
};

View File

@ -0,0 +1,22 @@
import { DATABASE_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/DatabaseTriggerTypes';
import { OTHER_TRIGGER_TYPES } from '@/workflow/workflow-trigger/constants/OtherTriggerTypes';
export const getTriggerDefaultLabel = (
trigger:
| {
type: 'MANUAL';
}
| {
type: 'DATABASE_EVENT';
eventName: string;
},
): string | undefined => {
if (trigger.type === 'DATABASE_EVENT') {
return DATABASE_TRIGGER_TYPES.find(
(type) => type.event === trigger.eventName,
)?.defaultLabel;
}
return OTHER_TRIGGER_TYPES.find((item) => item.type === trigger.type)
?.defaultLabel;
};

View File

@ -3,7 +3,7 @@ import {
WorkflowTrigger, WorkflowTrigger,
} from '@/workflow/types/Workflow'; } from '@/workflow/types/Workflow';
import { assertUnreachable } from '@/workflow/utils/assertUnreachable'; import { assertUnreachable } from '@/workflow/utils/assertUnreachable';
import { DATABASE_TRIGGER_EVENTS } from '@/workflow/workflow-trigger/constants/DatabaseTriggerEvents'; import { getTriggerDefaultLabel } from '@/workflow/workflow-trigger/utils/getTriggerLabel';
import { capitalize } from 'twenty-shared'; import { capitalize } from 'twenty-shared';
import { isDefined } from 'twenty-ui'; import { isDefined } from 'twenty-ui';
@ -26,8 +26,10 @@ const getDatabaseEventTriggerStepName = (
trigger: WorkflowDatabaseEventTrigger, trigger: WorkflowDatabaseEventTrigger,
): string => { ): string => {
const [, action] = trigger.settings.eventName.split('.'); const [, action] = trigger.settings.eventName.split('.');
const defaultLabel = getTriggerDefaultLabel({
type: 'DATABASE_EVENT',
eventName: action,
});
return ( return defaultLabel ?? '';
DATABASE_TRIGGER_EVENTS.find((event) => event.value === action)?.label ?? ''
);
}; };

View File

@ -49,7 +49,7 @@ export const generateFakeObjectRecordEvent = (
properties: { properties: {
isLeaf: false, isLeaf: false,
value: { after: { isLeaf: false, value: after, label: 'After' } }, value: { after: { isLeaf: false, value: after, label: 'After' } },
label: 'Record fields', label: 'Record Fields',
}, },
}; };
} }
@ -65,7 +65,7 @@ export const generateFakeObjectRecordEvent = (
before: { isLeaf: false, value: before, label: 'Before' }, before: { isLeaf: false, value: before, label: 'Before' },
after: { isLeaf: false, value: after, label: 'After' }, after: { isLeaf: false, value: after, label: 'After' },
}, },
label: 'Record fields', label: 'Record Fields',
}, },
}; };
} }
@ -78,7 +78,7 @@ export const generateFakeObjectRecordEvent = (
value: { value: {
before: { isLeaf: false, value: before, label: 'Before' }, before: { isLeaf: false, value: before, label: 'Before' },
}, },
label: 'Record fields', label: 'Record Fields',
}, },
}; };
} }
@ -91,7 +91,7 @@ export const generateFakeObjectRecordEvent = (
value: { value: {
before: { isLeaf: false, value: before, label: 'Before' }, before: { isLeaf: false, value: before, label: 'Before' },
}, },
label: 'Record fields', label: 'Record Fields',
}, },
}; };
} }