diff --git a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list_item.vue b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list_item.vue
index ce84b23ea08c524fb77fe0ea5633106038604573..527e38a3dac64c91a2156bee92400293581a7c2b 100644
--- a/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list_item.vue
+++ b/ee/app/assets/javascripts/ai/catalog/components/ai_catalog_list_item.vue
@@ -8,7 +8,7 @@ import {
GlIcon,
GlTooltipDirective,
} from '@gitlab/ui';
-import { __, s__ } from '~/locale';
+import { __, s__, sprintf } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import {
VISIBILITY_TYPE_ICON,
@@ -17,7 +17,13 @@ import {
VISIBILITY_LEVEL_PRIVATE_STRING,
} from '~/visibility_level/constants';
import FoundationalIcon from 'ee/ai/components/foundational_icon.vue';
-import { AI_CATALOG_TYPE_THIRD_PARTY_FLOW } from '../constants';
+import {
+ AI_CATALOG_TYPE_THIRD_PARTY_FLOW,
+ AI_CATALOG_ITEM_LABELS,
+ AI_CATALOG_CONSUMER_TYPE_PROJECT,
+ AI_CATALOG_CONSUMER_TYPE_GROUP,
+ AI_CATALOG_CONSUMER_LABELS,
+} from '../constants';
export default {
name: 'AiCatalogListItem',
@@ -33,6 +39,11 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
+ inject: {
+ projectId: {
+ default: null,
+ },
+ },
props: {
item: {
type: Object,
@@ -44,6 +55,20 @@ export default {
},
},
computed: {
+ isProjectNamespace() {
+ return Boolean(this.projectId);
+ },
+ itemTypeLabel() {
+ return AI_CATALOG_ITEM_LABELS[this.item.itemType];
+ },
+ targetType() {
+ return this.isProjectNamespace
+ ? AI_CATALOG_CONSUMER_TYPE_PROJECT
+ : AI_CATALOG_CONSUMER_TYPE_GROUP;
+ },
+ targetTypeLabel() {
+ return AI_CATALOG_CONSUMER_LABELS[this.targetType];
+ },
actionItems() {
return this.itemTypeConfig.actionItems?.(this.item) || [];
},
@@ -96,6 +121,17 @@ export default {
isThirdPartyFlow() {
return this.item.itemType === AI_CATALOG_TYPE_THIRD_PARTY_FLOW;
},
+ softDeletedTooltipText() {
+ return sprintf(
+ s__(
+ 'AICatalog|This %{itemType} was removed from the AI Catalog. You can still use it in this %{targetType}.',
+ ),
+ {
+ itemType: this.itemTypeLabel,
+ targetType: this.targetTypeLabel,
+ },
+ );
+ },
},
};
@@ -176,6 +212,15 @@ export default {
{{ s__('AICatalog|Update available') }}
+
+ {{ s__('AICatalog|Unlisted') }}
+
{
const findDisclosureDropdownItems = () => wrapper.findAllComponents(GlDisclosureDropdownItem);
const findFoundationalIcon = () => wrapper.findComponent(FoundationalIcon);
const findUpdateAvailableLabel = () => wrapper.findByTestId('ai-catalog-item-update');
+ const findUpdateUnlistedBadge = () => wrapper.findByTestId('ai-catalog-item-unlisted');
beforeEach(() => {
createComponent();
@@ -334,4 +335,34 @@ describe('AiCatalogListItem', () => {
});
});
});
+
+ describe('unlisted badge', () => {
+ describe('when item is soft deleted', () => {
+ beforeEach(() => {
+ createComponent({
+ item: { ...mockItem, softDeleted: true },
+ });
+ });
+ it('renders Unlisted badge when softDeleted is true', () => {
+ expect(findUpdateUnlistedBadge().findComponent(GlBadge).text()).toBe('Unlisted');
+ });
+
+ it('renders tooltip with correct text', () => {
+ expect(findUpdateUnlistedBadge().attributes('title')).toBe(
+ 'This agent was removed from the AI Catalog. You can still use it in this group.',
+ );
+ });
+ });
+
+ describe('when item is not soft deleted', () => {
+ beforeEach(() => {
+ createComponent({
+ item: { ...mockItem, isUpdateAvailable: false },
+ });
+ });
+ it('does not render Unlisted badge when softDeleted is false', () => {
+ expect(findUpdateUnlistedBadge().exists()).toBe(false);
+ });
+ });
+ });
});
diff --git a/ee/spec/frontend/ai/catalog/mock_data.js b/ee/spec/frontend/ai/catalog/mock_data.js
index 1b1c439c4cf04307a0433846f4a0454da27b41d2..0c49947d3e33818b2665884523108b90ba046046 100644
--- a/ee/spec/frontend/ai/catalog/mock_data.js
+++ b/ee/spec/frontend/ai/catalog/mock_data.js
@@ -175,6 +175,7 @@ const mockAgentFactory = (overrides = {}) => ({
itemType: 'AGENT',
description: 'A helpful AI assistant for testing purposes',
createdAt: '2024-01-15T10:30:00Z',
+ softDeleted: false,
public: true,
updatedAt: '2024-08-21T14:30:00Z',
latestVersion: mockBaseVersion,
@@ -468,6 +469,7 @@ const mockFlowFactory = (overrides = {}) => ({
createdAt: '2024-01-15T10:30:00Z',
public: true,
updatedAt: '2024-08-21T14:30:00Z',
+ softDeleted: false,
foundational: false,
latestVersion: mockBaseVersion,
userPermissions: mockUserPermissions,
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index ae50aa33f5dc372fda52b8290ef95526b1d9b1c5..8006b79559e121adac46d0e2202a8f408c6ee724 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -3073,6 +3073,9 @@ msgstr ""
msgid "AICatalog|This %{itemType} requires approval from your parent group owner before it can be used"
msgstr ""
+msgid "AICatalog|This %{itemType} was removed from the AI Catalog. You can still use it in this %{targetType}."
+msgstr ""
+
msgid "AICatalog|This YAML configuration file determines the prompts, tools, and capabilities of your flow. Required properties: injectGatewayToken, image, commands"
msgstr ""
@@ -3115,6 +3118,9 @@ msgstr ""
msgid "AICatalog|Type"
msgstr ""
+msgid "AICatalog|Unlisted"
+msgstr ""
+
msgid "AICatalog|Update available"
msgstr ""