mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 18:12:04 +00:00
feat(editor): Change default node names depending on node operation and resource (#16188)
This commit is contained in:
@@ -9,7 +9,11 @@ import {
|
||||
STICKY_NODE_TYPE,
|
||||
} from '@/constants';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import type { AddedNodesAndConnections, ToggleNodeCreatorOptions } from '@/Interface';
|
||||
import type {
|
||||
AddedNodesAndConnections,
|
||||
NodeTypeSelectedPayload,
|
||||
ToggleNodeCreatorOptions,
|
||||
} from '@/Interface';
|
||||
import { useActions } from './NodeCreator/composables/useActions';
|
||||
import { useThrottleFn } from '@vueuse/core';
|
||||
import KeyboardShortcutTooltip from '@/components/KeyboardShortcutTooltip.vue';
|
||||
@@ -83,8 +87,8 @@ function closeNodeCreator(hasAddedNodes = false) {
|
||||
}
|
||||
}
|
||||
|
||||
function nodeTypeSelected(nodeTypes: string[]) {
|
||||
emit('addNodes', getAddedNodesAndConnections(nodeTypes.map((type) => ({ type }))));
|
||||
function nodeTypeSelected(value: NodeTypeSelectedPayload[]) {
|
||||
emit('addNodes', getAddedNodesAndConnections(value));
|
||||
closeNodeCreator(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import type {
|
||||
IUpdateInformation,
|
||||
ActionCreateElement,
|
||||
NodeCreateElement,
|
||||
NodeTypeSelectedPayload,
|
||||
} from '@/Interface';
|
||||
import {
|
||||
HTTP_REQUEST_NODE_TYPE,
|
||||
@@ -23,7 +24,7 @@ import { useViewStacks } from '../composables/useViewStacks';
|
||||
|
||||
import ItemsRenderer from '../Renderers/ItemsRenderer.vue';
|
||||
import CategorizedItemsRenderer from '../Renderers/CategorizedItemsRenderer.vue';
|
||||
import { type IDataObject } from 'n8n-workflow';
|
||||
import type { IDataObject } from 'n8n-workflow';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useI18n } from '@n8n/i18n';
|
||||
import { useNodeCreatorStore } from '@/stores/nodeCreator.store';
|
||||
@@ -34,7 +35,7 @@ import CommunityNodeInfo from '../Panel/CommunityNodeInfo.vue';
|
||||
import CommunityNodeFooter from '../Panel/CommunityNodeFooter.vue';
|
||||
|
||||
const emit = defineEmits<{
|
||||
nodeTypeSelected: [value: [actionKey: string, nodeName: string] | [nodeName: string]];
|
||||
nodeTypeSelected: [value: NodeTypeSelectedPayload[]];
|
||||
}>();
|
||||
const telemetry = useTelemetry();
|
||||
const i18n = useI18n();
|
||||
@@ -45,6 +46,7 @@ const { registerKeyHook } = useKeyboardNavigation();
|
||||
const {
|
||||
setAddedNodeActionParameters,
|
||||
getActionData,
|
||||
actionDataToNodeTypeSelectedPayload,
|
||||
getPlaceholderTriggerActions,
|
||||
parseCategoryActions,
|
||||
actionsCategoryLocales,
|
||||
@@ -166,17 +168,18 @@ function onSelected(actionCreateElement: INodeCreateElement) {
|
||||
|
||||
if (isPlaceholderTriggerAction && isTriggerRootView.value) {
|
||||
const actionNode = actions.value[0]?.key;
|
||||
if (actionNode) emit('nodeTypeSelected', [actionData.key as string, actionNode]);
|
||||
if (actionNode) emit('nodeTypeSelected', [{ type: actionData.key }, { type: actionNode }]);
|
||||
} else if (
|
||||
actionData?.key === OPEN_AI_NODE_TYPE &&
|
||||
(actionData?.value as IDataObject)?.resource === 'assistant' &&
|
||||
(actionData?.value as IDataObject)?.operation === 'message'
|
||||
) {
|
||||
emit('nodeTypeSelected', [OPEN_AI_NODE_MESSAGE_ASSISTANT_TYPE]);
|
||||
emit('nodeTypeSelected', [{ type: OPEN_AI_NODE_MESSAGE_ASSISTANT_TYPE }]);
|
||||
} else if (isNodePreviewKey(actionData?.key)) {
|
||||
return;
|
||||
} else {
|
||||
emit('nodeTypeSelected', [actionData.key as string]);
|
||||
const payload = actionDataToNodeTypeSelectedPayload(actionData);
|
||||
emit('nodeTypeSelected', [payload]);
|
||||
}
|
||||
|
||||
if (telemetry) setAddedNodeActionParameters(actionData, telemetry, rootView.value);
|
||||
@@ -217,7 +220,7 @@ function addHttpNode() {
|
||||
},
|
||||
} as IUpdateInformation;
|
||||
|
||||
emit('nodeTypeSelected', [HTTP_REQUEST_NODE_TYPE]);
|
||||
emit('nodeTypeSelected', [{ type: HTTP_REQUEST_NODE_TYPE }]);
|
||||
if (telemetry) setAddedNodeActionParameters(updateData);
|
||||
|
||||
const app_identifier = actions.value[0]?.key;
|
||||
|
||||
@@ -6,6 +6,7 @@ import type {
|
||||
INodeCreateElement,
|
||||
NodeCreateElement,
|
||||
NodeFilterType,
|
||||
NodeTypeSelectedPayload,
|
||||
} from '@/Interface';
|
||||
import {
|
||||
TRIGGER_NODE_CREATOR_VIEW,
|
||||
@@ -47,14 +48,14 @@ export interface Props {
|
||||
}
|
||||
|
||||
const emit = defineEmits<{
|
||||
nodeTypeSelected: [nodeTypes: string[]];
|
||||
nodeTypeSelected: [value: NodeTypeSelectedPayload[]];
|
||||
}>();
|
||||
|
||||
const i18n = useI18n();
|
||||
|
||||
const { mergedNodes, actions, onSubcategorySelected } = useNodeCreatorStore();
|
||||
const { pushViewStack, popViewStack, isAiSubcategoryView } = useViewStacks();
|
||||
const { setAddedNodeActionParameters } = useActions();
|
||||
const { setAddedNodeActionParameters, nodeCreateElementToNodeTypeSelectedPayload } = useActions();
|
||||
|
||||
const { registerKeyHook } = useKeyboardNavigation();
|
||||
|
||||
@@ -97,10 +98,6 @@ function getHumanInTheLoopActions(nodeActions: ActionTypeDescription[]) {
|
||||
return nodeActions.filter((action) => action.actionKey === SEND_AND_WAIT_OPERATION);
|
||||
}
|
||||
|
||||
function selectNodeType(nodeTypes: string[]) {
|
||||
emit('nodeTypeSelected', nodeTypes);
|
||||
}
|
||||
|
||||
function onSelected(item: INodeCreateElement) {
|
||||
if (item.type === 'subcategory') {
|
||||
const subcategoryKey = camelCase(item.properties.title);
|
||||
@@ -152,9 +149,11 @@ function onSelected(item: INodeCreateElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = nodeCreateElementToNodeTypeSelectedPayload(item);
|
||||
|
||||
// If there is only one action, use it
|
||||
if (nodeActions.length === 1) {
|
||||
selectNodeType([item.key]);
|
||||
emit('nodeTypeSelected', [payload]);
|
||||
setAddedNodeActionParameters({
|
||||
name: nodeActions[0].defaults.name ?? item.properties.displayName,
|
||||
key: item.key,
|
||||
@@ -165,7 +164,7 @@ function onSelected(item: INodeCreateElement) {
|
||||
|
||||
// Only show actions if there are more than one or if the view is not an AI subcategory
|
||||
if (nodeActions.length === 0 || activeViewStack.value.hideActions) {
|
||||
selectNodeType([item.key]);
|
||||
emit('nodeTypeSelected', [payload]);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -299,8 +298,8 @@ registerKeyHook('MainViewArrowLeft', {
|
||||
:root-view="activeViewStack.rootView"
|
||||
show-icon
|
||||
show-request
|
||||
@add-webhook-node="selectNodeType([WEBHOOK_NODE_TYPE])"
|
||||
@add-http-node="selectNodeType([HTTP_REQUEST_NODE_TYPE])"
|
||||
@add-webhook-node="emit('nodeTypeSelected', [{ type: WEBHOOK_NODE_TYPE }])"
|
||||
@add-http-node="emit('nodeTypeSelected', [{ type: HTTP_REQUEST_NODE_TYPE }])"
|
||||
/>
|
||||
</template>
|
||||
</ItemsRenderer>
|
||||
|
||||
@@ -15,10 +15,11 @@ import { DRAG_EVENT_DATA_KEY } from '@/constants';
|
||||
import { useAssistantStore } from '@/stores/assistant.store';
|
||||
import N8nIconButton from '@n8n/design-system/components/N8nIconButton/IconButton.vue';
|
||||
import { useBuilderStore } from '@/stores/builder.store';
|
||||
import type { NodeTypeSelectedPayload } from '@/Interface';
|
||||
|
||||
export interface Props {
|
||||
active?: boolean;
|
||||
onNodeTypeSelected?: (nodeType: string[]) => void;
|
||||
onNodeTypeSelected?: (value: NodeTypeSelectedPayload[]) => void;
|
||||
}
|
||||
|
||||
const props = defineProps<Props>();
|
||||
@@ -26,7 +27,7 @@ const { resetViewStacks } = useViewStacks();
|
||||
const { registerKeyHook } = useKeyboardNavigation();
|
||||
const emit = defineEmits<{
|
||||
closeNodeCreator: [];
|
||||
nodeTypeSelected: [value: string[]];
|
||||
nodeTypeSelected: [value: NodeTypeSelectedPayload[]];
|
||||
}>();
|
||||
const uiStore = useUIStore();
|
||||
const assistantStore = useAssistantStore();
|
||||
|
||||
@@ -13,6 +13,8 @@ import type {
|
||||
INodeCreateElement,
|
||||
IUpdateInformation,
|
||||
LabelCreateElement,
|
||||
NodeCreateElement,
|
||||
NodeTypeSelectedPayload,
|
||||
} from '@/Interface';
|
||||
import {
|
||||
AGENT_NODE_TYPE,
|
||||
@@ -153,7 +155,13 @@ export const useActions = () => {
|
||||
return filteredActions;
|
||||
}
|
||||
|
||||
function getActionData(actionItem: ActionTypeDescription): IUpdateInformation {
|
||||
type ActionData = {
|
||||
name: string;
|
||||
key: string;
|
||||
value: INodeParameters;
|
||||
};
|
||||
|
||||
function getActionData(actionItem: ActionTypeDescription): ActionData {
|
||||
const displayOptions = actionItem.displayOptions;
|
||||
|
||||
const displayConditions = Object.keys(displayOptions?.show ?? {}).reduce(
|
||||
@@ -171,6 +179,51 @@ export const useActions = () => {
|
||||
};
|
||||
}
|
||||
|
||||
function actionDataToNodeTypeSelectedPayload(actionData: ActionData): NodeTypeSelectedPayload {
|
||||
const result: NodeTypeSelectedPayload = {
|
||||
type: actionData.key,
|
||||
};
|
||||
|
||||
if (
|
||||
typeof actionData.value.resource === 'string' ||
|
||||
typeof actionData.value.operation === 'string'
|
||||
) {
|
||||
result.parameters = {};
|
||||
|
||||
if (typeof actionData.value.resource === 'string') {
|
||||
result.parameters.resource = actionData.value.resource;
|
||||
}
|
||||
|
||||
if (typeof actionData.value.operation === 'string') {
|
||||
result.parameters.operation = actionData.value.operation;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function nodeCreateElementToNodeTypeSelectedPayload(
|
||||
actionData: NodeCreateElement,
|
||||
): NodeTypeSelectedPayload {
|
||||
const result: NodeTypeSelectedPayload = {
|
||||
type: actionData.key,
|
||||
};
|
||||
|
||||
if (typeof actionData.resource === 'string' || typeof actionData.operation === 'string') {
|
||||
result.parameters = {};
|
||||
|
||||
if (typeof actionData.resource === 'string') {
|
||||
result.parameters.resource = actionData.resource;
|
||||
}
|
||||
|
||||
if (typeof actionData.operation === 'string') {
|
||||
result.parameters.operation = actionData.operation;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if added nodes contain trigger followed by another node
|
||||
* In this case, we should connect the trigger with the following node
|
||||
@@ -305,7 +358,7 @@ export const useActions = () => {
|
||||
return { nodes, connections };
|
||||
}
|
||||
|
||||
// Hook into addNode action to set the last node parameters & track the action selected
|
||||
// Hook into addNode action to set the last node parameters, adjust default name and track the action selected
|
||||
function setAddedNodeActionParameters(
|
||||
action: IUpdateInformation,
|
||||
telemetry?: Telemetry,
|
||||
@@ -323,7 +376,6 @@ export const useActions = () => {
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
return storeWatcher;
|
||||
}
|
||||
|
||||
@@ -344,6 +396,8 @@ export const useActions = () => {
|
||||
|
||||
return {
|
||||
actionsCategoryLocales,
|
||||
actionDataToNodeTypeSelectedPayload,
|
||||
nodeCreateElementToNodeTypeSelectedPayload,
|
||||
getPlaceholderTriggerActions,
|
||||
parseCategoryActions,
|
||||
getAddedNodesAndConnections,
|
||||
|
||||
@@ -53,6 +53,7 @@ import { importCurlEventBus, ndvEventBus } from '@/event-bus';
|
||||
import { ProjectTypes } from '@/types/projects.types';
|
||||
import { updateDynamicConnections } from '@/utils/nodeSettingsUtils';
|
||||
import FreeAiCreditsCallout from '@/components/FreeAiCreditsCallout.vue';
|
||||
import { useCanvasOperations } from '@/composables/useCanvasOperations';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -96,6 +97,7 @@ const telemetry = useTelemetry();
|
||||
const nodeHelpers = useNodeHelpers();
|
||||
const externalHooks = useExternalHooks();
|
||||
const i18n = useI18n();
|
||||
const canvasOperations = useCanvasOperations();
|
||||
|
||||
const nodeValid = ref(true);
|
||||
const openPanel = ref<'params' | 'settings'>('params');
|
||||
@@ -579,6 +581,15 @@ const valueChanged = (parameterData: IUpdateInformation) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (NodeHelpers.isDefaultNodeName(_node.name, nodeType, node.value?.parameters ?? {})) {
|
||||
const newName = NodeHelpers.makeNodeName(nodeParameters ?? {}, nodeType);
|
||||
// Account for unique-ified nodes with `<name><digit>`
|
||||
if (!_node.name.startsWith(newName)) {
|
||||
// We need a timeout here to support events reacting to the valueChange based on node names
|
||||
setTimeout(async () => await canvasOperations.renameNode(_node.name, newName));
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(nodeParameters as object)) {
|
||||
if (nodeParameters && nodeParameters[key] !== null && nodeParameters[key] !== undefined) {
|
||||
setValue(`parameters.${key}`, nodeParameters[key] as string);
|
||||
|
||||
Reference in New Issue
Block a user