feat(core): Indicate unpublished changes in agent resource list view (#40177)

This commit is contained in:
Anne Aguirre 2026-10-05 10:06:20 +00:00 committed by GitHub
parent f47a2fe235
commit 6a5aa7862e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 63 additions and 1 deletions

View File

@ -8147,6 +8147,7 @@
"agents.backToWorkflow": "Back to workflow",
"agents.heading": "Agents",
"agents.list.published": "Published",
"agents.list.changesToPublish": "Changes to publish",
"agents.list.noDescription": "No description",
"agents.list.updatedAt": "Updated {date}",
"agents.list.updated": "Last updated",

View File

@ -104,6 +104,10 @@ const STUBS = {
template: '<span :data-test-id="$attrs[\'data-test-id\']"><slot /></span>',
props: ['theme', 'bold'],
},
PublicationIndicator: {
template: '<span :data-variant="variant">{{ label }}</span>',
props: ['label', 'variant'],
},
N8nActionToggle: {
name: 'N8nActionToggle',
template:
@ -345,4 +349,51 @@ describe('AgentCard', () => {
'agents.list.actions.disableMCPAccess',
);
});
describe('publication indicator', () => {
const findIndicator = (wrapper: Awaited<ReturnType<typeof renderComponent>>) =>
wrapper.find('[data-test-id="agent-card-publish-indicator"]');
it('is hidden on an unpublished agent', async () => {
const wrapper = await renderComponent(createAgent({ activeVersionId: null }));
expect(findIndicator(wrapper).exists()).toBe(false);
});
it('shows Published with a success dot when the draft matches the published version', async () => {
const wrapper = await renderComponent(
createAgent({ versionId: 'v1', activeVersionId: 'v1', activeVersion }),
);
const indicator = findIndicator(wrapper);
expect(indicator.text()).toBe('agents.list.published');
expect(indicator.attributes('data-state')).toBe('published');
expect(indicator.attributes('data-variant')).toBe('success');
});
it('shows Changes to publish with a warning dot when the draft differs from the published version', async () => {
const wrapper = await renderComponent(
createAgent({ versionId: 'v2', activeVersionId: 'v1', activeVersion }),
);
const indicator = findIndicator(wrapper);
expect(indicator.text()).toBe('agents.list.changesToPublish');
expect(indicator.attributes('data-state')).toBe('changes-to-publish');
expect(indicator.attributes('data-variant')).toBe('warning');
});
it('returns to Published after the agent is republished', async () => {
const wrapper = await renderComponent(
createAgent({ versionId: 'v2', activeVersionId: 'v1', activeVersion }),
);
await wrapper.setProps({
agent: createAgent({ versionId: 'v2', activeVersionId: 'v2', activeVersion }),
});
const indicator = findIndicator(wrapper);
expect(indicator.text()).toBe('agents.list.published');
expect(indicator.attributes('data-variant')).toBe('success');
});
});
});

View File

@ -56,6 +56,10 @@ const { canCreate, canUpdate, canDelete, canPublish, canUnpublish } = useAgentPe
const isPublished = computed(() => props.agent.activeVersionId !== null);
// Saving a draft bumps `versionId`, so a mismatch means unpublished edits.
const hasUnpublishedChanges = computed(
() => isPublished.value && props.agent.versionId !== props.agent.activeVersionId,
);
const isUntitled = computed(() => props.agent.name === locale.baseText('agents.new.defaultName'));
const isMcpEnabled = computed(
@ -225,7 +229,13 @@ async function toggleMCPAccess(enabled: boolean) {
<div :class="$style.cardActions" @click.stop>
<PublicationIndicator
v-if="isPublished"
:label="locale.baseText('agents.list.published')"
:label="
locale.baseText(
hasUnpublishedChanges ? 'agents.list.changesToPublish' : 'agents.list.published',
)
"
:variant="hasUnpublishedChanges ? 'warning' : 'success'"
:data-state="hasUnpublishedChanges ? 'changes-to-publish' : 'published'"
data-test-id="agent-card-publish-indicator"
/>
<N8nTooltip :content="locale.baseText('agents.list.actions.newChat')">