251 create top bar chips inside the command menu (#9809)
Closes #https://github.com/twentyhq/core-team-issues/issues/251 https://github.com/user-attachments/assets/065c97fe-1daf-4b48-9d57-6bbb96d24ede
This commit is contained in:
@ -8,11 +8,15 @@ import { EMPTY_TRIGGER_STEP_ID } from '@/workflow/workflow-diagram/constants/Emp
|
||||
import { useStartNodeCreation } from '@/workflow/workflow-diagram/hooks/useStartNodeCreation';
|
||||
import { useTriggerNodeSelection } from '@/workflow/workflow-diagram/hooks/useTriggerNodeSelection';
|
||||
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||
import { WorkflowDiagramNode } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import {
|
||||
WorkflowDiagramNode,
|
||||
WorkflowDiagramStepNodeData,
|
||||
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon';
|
||||
import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-ui';
|
||||
import { IconBolt, isDefined } from 'twenty-ui';
|
||||
|
||||
export const WorkflowDiagramCanvasEditableEffect = () => {
|
||||
const { startNodeCreation } = useStartNodeCreation();
|
||||
@ -37,7 +41,10 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
|
||||
|
||||
const isEmptyTriggerNode = selectedNode.type === EMPTY_TRIGGER_STEP_ID;
|
||||
if (isEmptyTriggerNode) {
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepSelectTriggerType);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepSelectTriggerType, {
|
||||
title: 'Trigger Type',
|
||||
Icon: IconBolt,
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
@ -53,9 +60,14 @@ export const WorkflowDiagramCanvasEditableEffect = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData;
|
||||
|
||||
setWorkflowSelectedNode(selectedNode.id);
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
|
||||
title: selectedNodeData.name,
|
||||
Icon: getWorkflowNodeIcon(selectedNodeData),
|
||||
});
|
||||
},
|
||||
[
|
||||
setWorkflowSelectedNode,
|
||||
|
||||
@ -5,7 +5,11 @@ import { RightDrawerPages } from '@/ui/layout/right-drawer/types/RightDrawerPage
|
||||
import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope';
|
||||
import { useTriggerNodeSelection } from '@/workflow/workflow-diagram/hooks/useTriggerNodeSelection';
|
||||
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||
import { WorkflowDiagramNode } from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import {
|
||||
WorkflowDiagramNode,
|
||||
WorkflowDiagramStepNodeData,
|
||||
} from '@/workflow/workflow-diagram/types/WorkflowDiagram';
|
||||
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon';
|
||||
import { OnSelectionChangeParams, useOnSelectionChange } from '@xyflow/react';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
@ -30,7 +34,12 @@ export const WorkflowDiagramCanvasReadonlyEffect = () => {
|
||||
|
||||
setWorkflowSelectedNode(selectedNode.id);
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepView);
|
||||
|
||||
const selectedNodeData = selectedNode.data as WorkflowDiagramStepNodeData;
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepView, {
|
||||
title: selectedNodeData.name,
|
||||
Icon: getWorkflowNodeIcon(selectedNodeData),
|
||||
});
|
||||
},
|
||||
[
|
||||
setWorkflowSelectedNode,
|
||||
|
||||
@ -1,15 +1,9 @@
|
||||
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 { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import {
|
||||
IconAddressBook,
|
||||
IconCode,
|
||||
IconHandMove,
|
||||
IconMail,
|
||||
IconPlaylistAdd,
|
||||
} from 'twenty-ui';
|
||||
|
||||
const StyledStepNodeLabelIconContainer = styled.div`
|
||||
align-items: center;
|
||||
@ -29,6 +23,8 @@ export const WorkflowDiagramStepNodeBase = ({
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const Icon = getWorkflowNodeIcon(data);
|
||||
|
||||
const renderStepIcon = () => {
|
||||
switch (data.nodeType) {
|
||||
case 'trigger': {
|
||||
@ -36,7 +32,7 @@ export const WorkflowDiagramStepNodeBase = ({
|
||||
case 'DATABASE_EVENT': {
|
||||
return (
|
||||
<StyledStepNodeLabelIconContainer>
|
||||
<IconPlaylistAdd
|
||||
<Icon
|
||||
size={theme.icon.size.lg}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
@ -46,7 +42,7 @@ export const WorkflowDiagramStepNodeBase = ({
|
||||
case 'MANUAL': {
|
||||
return (
|
||||
<StyledStepNodeLabelIconContainer>
|
||||
<IconHandMove
|
||||
<Icon
|
||||
size={theme.icon.size.lg}
|
||||
color={theme.font.color.tertiary}
|
||||
/>
|
||||
@ -62,17 +58,14 @@ export const WorkflowDiagramStepNodeBase = ({
|
||||
case 'CODE': {
|
||||
return (
|
||||
<StyledStepNodeLabelIconContainer>
|
||||
<IconCode
|
||||
size={theme.icon.size.lg}
|
||||
color={theme.color.orange}
|
||||
/>
|
||||
<Icon size={theme.icon.size.lg} color={theme.color.orange} />
|
||||
</StyledStepNodeLabelIconContainer>
|
||||
);
|
||||
}
|
||||
case 'SEND_EMAIL': {
|
||||
return (
|
||||
<StyledStepNodeLabelIconContainer>
|
||||
<IconMail size={theme.icon.size.lg} color={theme.color.blue} />
|
||||
<Icon size={theme.icon.size.lg} color={theme.color.blue} />
|
||||
</StyledStepNodeLabelIconContainer>
|
||||
);
|
||||
}
|
||||
@ -81,7 +74,7 @@ export const WorkflowDiagramStepNodeBase = ({
|
||||
case 'DELETE_RECORD': {
|
||||
return (
|
||||
<StyledStepNodeLabelIconContainer>
|
||||
<IconAddressBook
|
||||
<Icon
|
||||
size={theme.icon.size.lg}
|
||||
color={theme.font.color.tertiary}
|
||||
stroke={theme.icon.stroke.sm}
|
||||
|
||||
@ -5,6 +5,7 @@ import { useSetHotkeyScope } from '@/ui/utilities/hotkey/hooks/useSetHotkeyScope
|
||||
import { workflowCreateStepFromParentStepIdState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { IconSettingsAutomation } from 'twenty-ui';
|
||||
|
||||
export const useStartNodeCreation = () => {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
@ -22,7 +23,10 @@ export const useStartNodeCreation = () => {
|
||||
setWorkflowCreateStepFromParentStepId(parentNodeId);
|
||||
|
||||
setHotkeyScope(RightDrawerHotkeyScope.RightDrawer, { goto: false });
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepSelectAction);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepSelectAction, {
|
||||
title: 'Select Action',
|
||||
Icon: IconSettingsAutomation,
|
||||
});
|
||||
},
|
||||
[openRightDrawer, setWorkflowCreateStepFromParentStepId, setHotkeyScope],
|
||||
);
|
||||
|
||||
@ -0,0 +1,56 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -5,7 +5,7 @@ import { useCreateStep } from '../useCreateStep';
|
||||
const mockOpenRightDrawer = jest.fn();
|
||||
const mockCreateDraftFromWorkflowVersion = jest.fn().mockResolvedValue('457');
|
||||
const mockCreateWorkflowVersionStep = jest.fn().mockResolvedValue({
|
||||
data: { createWorkflowVersionStep: { id: '1' } },
|
||||
data: { createWorkflowVersionStep: { id: '1', type: 'CODE' } },
|
||||
});
|
||||
|
||||
jest.mock('recoil', () => ({
|
||||
|
||||
@ -7,6 +7,7 @@ import {
|
||||
WorkflowWithCurrentVersion,
|
||||
} from '@/workflow/types/Workflow';
|
||||
import { workflowSelectedNodeState } from '@/workflow/workflow-diagram/states/workflowSelectedNodeState';
|
||||
import { getWorkflowNodeIcon } from '@/workflow/workflow-diagram/utils/getWorkflowNodeIcon';
|
||||
import { useCreateWorkflowVersionStep } from '@/workflow/workflow-steps/hooks/useCreateWorkflowVersionStep';
|
||||
import { workflowCreateStepFromParentStepIdState } from '@/workflow/workflow-steps/states/workflowCreateStepFromParentStepIdState';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
@ -17,7 +18,6 @@ export const useCreateStep = ({
|
||||
}: {
|
||||
workflow: WorkflowWithCurrentVersion;
|
||||
}) => {
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
const { createWorkflowVersionStep } = useCreateWorkflowVersionStep();
|
||||
const setWorkflowSelectedNode = useSetRecoilState(workflowSelectedNodeState);
|
||||
const setWorkflowLastCreatedStepId = useSetRecoilState(
|
||||
@ -30,6 +30,8 @@ export const useCreateStep = ({
|
||||
|
||||
const { getUpdatableWorkflowVersion } = useGetUpdatableWorkflowVersion();
|
||||
|
||||
const { openRightDrawer } = useRightDrawer();
|
||||
|
||||
const createStep = async (newStepType: WorkflowStepType) => {
|
||||
if (!isDefined(workflowCreateStepFromParentStepId)) {
|
||||
throw new Error('Select a step to create a new step from first.');
|
||||
@ -50,7 +52,14 @@ export const useCreateStep = ({
|
||||
|
||||
setWorkflowSelectedNode(createdStep.id);
|
||||
setWorkflowLastCreatedStepId(createdStep.id);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
||||
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
|
||||
title: createdStep.name,
|
||||
Icon: getWorkflowNodeIcon({
|
||||
nodeType: 'action',
|
||||
actionType: createdStep.type as WorkflowStepType,
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@ -63,7 +63,10 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
|
||||
|
||||
setWorkflowSelectedNode(TRIGGER_STEP_ID);
|
||||
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
|
||||
title: action.name,
|
||||
Icon: action.icon,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
@ -84,7 +87,10 @@ export const RightDrawerWorkflowSelectTriggerTypeContent = ({
|
||||
|
||||
setWorkflowSelectedNode(TRIGGER_STEP_ID);
|
||||
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit);
|
||||
openRightDrawer(RightDrawerPages.WorkflowStepEdit, {
|
||||
title: action.name,
|
||||
Icon: action.icon,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user