feat(editor): Show changes to publish status on agent cards

Show a warning status dot and the "Changes to publish" label on a
published agent card when the draft version differs from the
published version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
aaguirre-rdit 2026-10-02 12:09:58 +01:00
parent 4f64f8c4a6
commit 1d86fa89c3
No known key found for this signature in database
3 changed files with 64 additions and 1 deletions

View File

@ -8038,6 +8038,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

@ -102,6 +102,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:
@ -328,4 +332,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

@ -55,6 +55,11 @@ 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 isMcpEnabled = computed(
() => settingsStore.isModuleActive('mcp') && !!settingsStore.moduleSettings.mcp?.mcpAccessEnabled,
);
@ -216,7 +221,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')">