refactor(editor): Add create new agent from dropdown (#30225)

Co-authored-by: Robin Braumann <robin.braumann@n8n.io>
This commit is contained in:
Rob Hough 2026-05-19 07:50:54 +01:00 committed by GitHub
parent 5476637ede
commit ddda043221
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 43 additions and 2 deletions

View File

@ -6125,6 +6125,7 @@
"agents.builder.header.workspaceCrumb": "Workspace",
"agents.builder.header.projectFallback": "Project",
"agents.builder.header.switcher.empty": "No other agents",
"agents.builder.header.switcher.newAgent": "New agent",
"agents.builder.header.switcher.ariaLabel": "Switch agent",
"agents.builder.header.saving": "Saving…",
"agents.builder.header.saved": "Saved",

View File

@ -38,6 +38,12 @@ vi.mock('@n8n/design-system', () => ({
props: ['variant', 'size', 'icon', 'iconOnly', 'disabled'],
emits: ['click'],
},
N8nDropdownMenuItem: {
name: 'N8nDropdownMenuItem',
template: '<button :data-testid="testId" @click="$emit(\'select\', id)">{{ label }}</button>',
props: ['id', 'label', 'icon', 'testId'],
emits: ['select'],
},
N8nTooltip: {
name: 'N8nTooltip',
template:
@ -52,13 +58,13 @@ vi.mock('@n8n/design-system', () => ({
},
N8nDropdownMenu: {
name: 'N8nDropdownMenu',
template: '<div v-bind="$attrs"><slot name="trigger" /></div>',
template: '<div v-bind="$attrs"><slot name="trigger" /><slot name="footer" /></div>',
props: ['items'],
emits: ['select'],
},
'n8n-dropdown-menu': {
name: 'N8nDropdownMenu',
template: '<div v-bind="$attrs"><slot name="trigger" /></div>',
template: '<div v-bind="$attrs"><slot name="trigger" /><slot name="footer" /></div>',
props: ['items'],
emits: ['select'],
},
@ -305,4 +311,15 @@ describe('AgentBuilderHeader', () => {
nav.vm.$emit('select', 'a2');
expect(wrapper.emitted('switch-agent')).toEqual([['a2']]);
});
it('navigates to the new agent page from the switcher footer', async () => {
const wrapper = mountHeader();
await wrapper.find('[data-testid="agent-header-new-agent"]').trigger('click');
expect(routerPush).toHaveBeenCalledWith({
name: 'NewAgentView',
query: { projectId: 'p1' },
});
});
});

View File

@ -13,6 +13,7 @@ import {
N8nBreadcrumbs,
N8nButton,
N8nDropdownMenu,
N8nDropdownMenuItem,
N8nIcon,
N8nTooltip,
} from '@n8n/design-system';
@ -21,6 +22,7 @@ import type { DropdownMenuItemProps } from '@n8n/design-system';
import type { ActionDropdownItem } from '@n8n/design-system/types/action-dropdown';
import { useI18n, type BaseTextKey } from '@n8n/i18n';
import { VIEWS } from '@/app/constants';
import { NEW_AGENT_VIEW } from '@/features/agents/constants';
import AgentPublishButton from './AgentPublishButton.vue';
import { useProjectAgentsList } from '../composables/useProjectAgentsList';
@ -118,6 +120,10 @@ function onSwitcherSelect(id: string) {
emit('switch-agent', id);
}
function onCreateAgent() {
void router.push({ name: NEW_AGENT_VIEW, query: { projectId: props.projectId } });
}
function onBreadcrumbSelect(item: PathItem) {
if (item.id !== props.projectId) return;
void router.push(projectRoute.value);
@ -137,6 +143,7 @@ function onOpenPreview() {
<span :class="$style.crumbSeparator" aria-hidden="true">/</span>
<N8nDropdownMenu
:items="switcherOptions"
placement="bottom-start"
data-testid="agent-header-switcher"
@select="onSwitcherSelect"
>
@ -153,6 +160,17 @@ function onOpenPreview() {
<N8nIcon icon="chevron-down" :size="12" />
</N8nButton>
</template>
<template #footer>
<div :class="$style.switcherFooter">
<N8nDropdownMenuItem
id="__new_agent__"
:label="i18n.baseText('agents.builder.header.switcher.newAgent')"
:icon="{ type: 'icon', value: 'plus' }"
test-id="agent-header-new-agent"
@select="onCreateAgent"
/>
</div>
</template>
</N8nDropdownMenu>
<template v-if="isPreview">
<span :class="$style.crumbSeparator" aria-hidden="true">/</span>
@ -306,6 +324,11 @@ function onOpenPreview() {
max-width: clamp(320px, 42vw, 640px);
}
.switcherFooter {
border-top: var(--border);
padding: var(--spacing--3xs);
}
.right {
margin-left: auto;
display: flex;