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,
WorkflowDiagramStepNodeData,
} 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 { useCallback } from 'react';
import { useSetRecoilState } from 'recoil';
import { IconBolt, isDefined } from 'twenty-ui';
import { IconBolt, isDefined, useIcons } from 'twenty-ui';
export const WorkflowDiagramCanvasEditableEffect = () => {
const { getIcon } = useIcons();
const { startNodeCreation } = useStartNodeCreation();
const { openRightDrawer, closeRightDrawer } = useRightDrawer();
@ -66,7 +67,7 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
title: selectedNodeData.name,
Icon: getWorkflowNodeIcon(selectedNodeData),
Icon: getIcon(getWorkflowNodeIconKey(selectedNodeData)),
});
},
[
@ -76,6 +77,7 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
closeRightDrawer,
closeCommandMenu,
startNodeCreation,
getIcon,
],
);

View File

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

View File

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

View File

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

View File

@ -17,6 +17,7 @@ export type WorkflowDiagramStepNodeData =
nodeType: 'trigger';
triggerType: WorkflowTriggerType;
name: string;
icon?: string;
}
| {
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({
__typename: 'WorkflowVersion',
status: 'ACTIVE',
@ -42,7 +42,7 @@ describe('getWorkflowVersionDiagram', () => {
name: '',
steps: null,
trigger: {
name: 'Company created',
name: 'Record is created',
settings: { eventName: 'company.created', outputSchema: {} },
type: 'DATABASE_EVENT',
},
@ -54,9 +54,10 @@ describe('getWorkflowVersionDiagram', () => {
nodes: [
{
data: {
name: 'Company created',
name: 'Record is created',
nodeType: 'trigger',
triggerType: 'DATABASE_EVENT',
icon: 'IconPlus',
},
id: 'trigger',
position: { x: 0, y: 0 },

View File

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