From 1719ee3608ffc85e6ae6ed0aea54bec1effc0866 Mon Sep 17 00:00:00 2001 From: Lindsey Shelton Date: Wed, 15 Oct 2025 18:43:35 -0500 Subject: [PATCH 1/4] Feat: update agent sessions search Condense common methods Chnagelog: changed EE: true --- .../project/project_agents_platform_index.vue | 21 ++---- .../user/user_agents_platform_index.vue | 21 ++---- .../pages/index/duo_agents_platform_index.vue | 44 +++++++++--- .../index/duo_agents_platform_index_spec.js | 68 +++++++++++++------ 4 files changed, 89 insertions(+), 65 deletions(-) diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/project_agents_platform_index.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/project_agents_platform_index.vue index 502e9af02d4333..741b4d097175a4 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/project_agents_platform_index.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/project/project_agents_platform_index.vue @@ -56,21 +56,10 @@ export default { }, }, methods: { - handleSort(sortBy) { - this.currentSort = sortBy; - - this.resetPagination(); - }, - handleFilters(filters) { + handleVariablesUpdate({ sort, pagination, filters }) { + this.currentSort = sort; + this.paginationVariables = pagination; this.filterVariables = filters; - - this.resetPagination(); - }, - handlePagination(paginationVars) { - this.paginationVariables = paginationVars; - }, - resetPagination() { - this.handlePagination(DEFAULT_AGENT_PLATFORM_PAGINATION_VARIABLES); }, }, }; @@ -82,8 +71,6 @@ export default { :is-loading-workflows="isLoadingWorkflows" :workflows="workflows" :workflows-page-info="workflowsPageInfo" - @update-sort="handleSort" - @update-pagination="handlePagination" - @update-filters="handleFilters" + @variables-updated="handleVariablesUpdate" /> diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/user/user_agents_platform_index.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/user/user_agents_platform_index.vue index 8fbb68834b9ca7..6827814f7d90ab 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/user/user_agents_platform_index.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/namespace/user/user_agents_platform_index.vue @@ -56,21 +56,10 @@ export default { }, }, methods: { - handleSort(sortBy) { - this.currentSort = sortBy; - - this.resetPagination(); - }, - handleFilters(filters) { + handleVariablesUpdate({ sort, pagination, filters }) { + this.currentSort = sort; + this.paginationVariables = pagination; this.filterVariables = filters; - - this.resetPagination(); - }, - handlePagination(paginationVars) { - this.paginationVariables = paginationVars; - }, - resetPagination() { - this.handlePagination(DEFAULT_AGENT_PLATFORM_PAGINATION_VARIABLES); }, }, }; @@ -83,8 +72,6 @@ export default { :workflows="workflows" :workflows-page-info="workflowsPageInfo" class="gl-min-w-full" - @update-sort="handleSort" - @update-pagination="handlePagination" - @update-filters="handleFilters" + @variables-updated="handleVariablesUpdate" /> diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue index 4d3dc570c7aeab..7d84136ca5e83d 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue @@ -9,7 +9,10 @@ import { } from '~/vue_shared/components/filtered_search_bar/constants'; import { __, s__ } from '~/locale'; import AgentFlowList from '../../components/common/agent_flow_list.vue'; -import { AGENT_PLATFORM_INDEX_COMPONENT_NAME } from '../../constants'; +import { + AGENT_PLATFORM_INDEX_COMPONENT_NAME, + DEFAULT_AGENT_PLATFORM_PAGINATION_VARIABLES, +} from '../../constants'; export default { name: AGENT_PLATFORM_INDEX_COMPONENT_NAME, @@ -52,6 +55,9 @@ export default { return { showAlert: false, currentFilters: [], + currentSort: this.initialSort, + paginationVariables: {}, + filterVariables: {}, }; }, computed: { @@ -72,23 +78,34 @@ export default { }, methods: { handleNextPage() { - this.$emit('update-pagination', { + const paginationVars = { before: null, after: this.workflowsPageInfo.endCursor, first: 20, last: null, - }); + }; + this.handlePagination(paginationVars); }, handlePrevPage() { - this.$emit('update-pagination', { + const paginationVars = { after: null, before: this.workflowsPageInfo.startCursor, first: null, last: 20, - }); + }; + this.handlePagination(paginationVars); + }, + handlePagination(paginationVars) { + this.paginationVariables = paginationVars; + this.emitVariables(); + }, + resetPagination() { + this.handlePagination(DEFAULT_AGENT_PLATFORM_PAGINATION_VARIABLES); }, handleSort(sortBy) { - this.$emit('update-sort', sortBy); + this.currentSort = sortBy; + this.resetPagination(); + this.emitVariables(); }, handleFilter(filters) { if (this.hasUnsupportedTextSearch(filters)) { @@ -102,15 +119,24 @@ export default { this.currentFilters = filters; this.refetchWithFilters(this.processedFiltersForGraphQL); }, + refetchWithFilters(filters) { + this.filterVariables = filters; + this.resetPagination(); + this.emitVariables(); + }, hasUnsupportedTextSearch(filters) { return filters.some((filter) => filter.type === FILTERED_SEARCH_TERM && filter.value?.data); }, - refetchWithFilters(processedFilters) { - this.$emit('update-filters', processedFilters); - }, dismissAlert() { this.showAlert = false; }, + emitVariables() { + this.$emit('variables-updated', { + sort: this.currentSort, + pagination: this.paginationVariables, + filters: this.filterVariables, + }); + }, }, emptyStateIllustrationPath, availableSortOptions: [ diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js index 00edd894c8a8f6..c68520ab32dc0f 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js @@ -8,7 +8,6 @@ import AgentsPlatformIndex from 'ee/ai/duo_agents_platform/pages/index/duo_agent import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue'; import waitForPromises from 'helpers/wait_for_promises'; - import { mockAgentFlowsResponse } from '../../../mocks'; jest.mock('~/alert'); @@ -34,7 +33,6 @@ describe('AgentsPlatformIndex', () => { ...provide, }, }); - return waitForPromises(); }; @@ -115,6 +113,12 @@ describe('AgentsPlatformIndex', () => { await createWrapper(); }); + const expectVariablesUpdatedEvent = (expectedPayload) => { + const emittedEvents = wrapper.emitted('variables-updated'); + expect(emittedEvents).toHaveLength(2); + expect(emittedEvents[1]).toEqual([expectedPayload]); + }; + it('renders the filtered search bar with correct props', () => { expect(findFilteredSearchBar().exists()).toBe(true); expect(findFilteredSearchBar().props()).toMatchObject({ @@ -175,21 +179,29 @@ describe('AgentsPlatformIndex', () => { }); describe('when sorting', () => { - it('emits update-sort event when onSort is triggered', () => { + it('emits variables-updated event when onSort is triggered', () => { findFilteredSearchBar().vm.$emit('onSort', 'UPDATED_DESC'); - expect(wrapper.emitted('update-sort')).toEqual([['UPDATED_DESC']]); + expectVariablesUpdatedEvent({ + sort: 'UPDATED_DESC', + pagination: { before: null, after: null, first: 20, last: null }, + filters: {}, + }); }); }); describe('when filtering', () => { describe('with valid flow-name token', () => { - it('emits update-filters event with processed filter parameters', () => { + it('emits variables-updated event with processed filter parameters', () => { const filters = [{ type: 'flow-name', value: { data: 'convert_to_gitlab_ci' } }]; findFilteredSearchBar().vm.$emit('onFilter', filters); - expect(wrapper.emitted('update-filters')).toEqual([[{ type: 'convert_to_gitlab_ci' }]]); + expectVariablesUpdatedEvent({ + sort: 'UPDATED_DESC', + pagination: { before: null, after: null, first: 20, last: null }, + filters: { type: 'convert_to_gitlab_ci' }, + }); }); it('hides alert when valid filters are applied', () => { @@ -202,7 +214,7 @@ describe('AgentsPlatformIndex', () => { }); describe('with unsupported free text search', () => { - it('shows alert and does not emit update-filters', async () => { + it('shows alert and does not emit variables-updated', async () => { const filters = [{ type: 'filtered-search-term', value: { data: 'software dev' } }]; findFilteredSearchBar().vm.$emit('onFilter', filters); @@ -211,15 +223,19 @@ describe('AgentsPlatformIndex', () => { expect(findAlert().exists()).toBe(true); expect(findAlert().props('variant')).toBe('warning'); expect(findAlert().text()).toContain('Raw text search is not currently supported'); - expect(wrapper.emitted('update-filters')).toBeUndefined(); + expect(wrapper.emitted('variables-updated')).toBeUndefined(); }); }); describe('when filters are cleared', () => { - it('emits update-filters event with empty filters', () => { + it('emits variables-updated event with empty filters', () => { findFilteredSearchBar().vm.$emit('onFilter', []); - expect(wrapper.emitted('update-filters')).toEqual([[{}]]); + expectVariablesUpdatedEvent({ + sort: 'UPDATED_DESC', + pagination: { before: null, after: null, first: 20, last: null }, + filters: {}, + }); }); }); }); @@ -248,16 +264,20 @@ describe('AgentsPlatformIndex', () => { }); describe('when next page is requested', () => { - it('emits update-pagination event with correct parameters', () => { + it('emits variables-updated event with correct parameters', () => { findWorkflowsList().vm.$emit('next-page'); - expect(wrapper.emitted('update-pagination')).toEqual([ + expect(wrapper.emitted('variables-updated')).toEqual([ [ { - before: null, - after: 'asdf', - first: 20, - last: null, + sort: 'UPDATED_DESC', + pagination: { + before: null, + after: 'asdf', + first: 20, + last: null, + }, + filters: {}, }, ], ]); @@ -265,16 +285,20 @@ describe('AgentsPlatformIndex', () => { }); describe('when previous page is requested', () => { - it('emits update-pagination event with correct parameters', () => { + it('emits variables-updated event with correct parameters', () => { findWorkflowsList().vm.$emit('prev-page'); - expect(wrapper.emitted('update-pagination')).toEqual([ + expect(wrapper.emitted('variables-updated')).toEqual([ [ { - after: null, - before: 'asdf', - first: null, - last: 20, + sort: 'UPDATED_DESC', + pagination: { + after: null, + before: 'asdf', + first: null, + last: 20, + }, + filters: {}, }, ], ]); -- GitLab From d05048628ba2208befba76644b5d75d1506bb8bf Mon Sep 17 00:00:00 2001 From: Lindsey Shelton Date: Wed, 15 Oct 2025 18:50:51 -0500 Subject: [PATCH 2/4] Add status to agent sessions sorting --- .../pages/index/duo_agents_platform_index.vue | 8 ++++++++ .../pages/index/duo_agents_platform_index_spec.js | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue index 7d84136ca5e83d..2b7e3728035fbe 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue @@ -156,6 +156,14 @@ export default { ascending: 'UPDATED_ASC', }, }, + { + id: 3, + title: __('Status'), + sortDirection: { + descending: 'STATUS_DESC', + ascending: 'STATUS_ASC', + }, + }, ], tokens: [ { diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js index c68520ab32dc0f..21a118459142c0 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js @@ -148,6 +148,14 @@ describe('AgentsPlatformIndex', () => { ascending: 'UPDATED_ASC', }, }, + { + id: 3, + title: 'Status', + sortDirection: { + descending: 'STATUS_DESC', + ascending: 'STATUS_ASC', + }, + }, ]); }); -- GitLab From 96f1642d0dc03de200d9cce448be6f9d31dada6e Mon Sep 17 00:00:00 2001 From: Lindsey Shelton Date: Wed, 22 Oct 2025 11:07:43 -0500 Subject: [PATCH 3/4] Apply status group filtering Update duo_agents_platform_index with status groups --- .../queries/get_agent_flows.query.graphql | 2 + .../queries/get_user_agent_flow.query.graphql | 2 + .../pages/index/duo_agents_platform_index.vue | 18 +++++++++ .../index/duo_agents_platform_index_spec.js | 39 ++++++++++++++++++- locale/gitlab.pot | 9 +++++ 5 files changed, 69 insertions(+), 1 deletion(-) diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql index 7c1e9338c11445..deceb151c39ee3 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql @@ -7,6 +7,7 @@ query getAgentFlows( $first: Int $last: Int $sort: DuoWorkflowsWorkflowSort + $statusGroup: DuoWorkflowStatusGroup $type: String ) { project(fullPath: $projectPath) { @@ -18,6 +19,7 @@ query getAgentFlows( last: $last before: $before sort: $sort + statusGroup: $statusGroup type: $type ) { pageInfo { diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql index 29887605e22699..849f3a4f5bb1d2 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql @@ -7,6 +7,7 @@ query getUserAgentFlows( $last: Int $excludeTypes: [String!] $sort: DuoWorkflowsWorkflowSort + $statusGroup: DuoWorkflowStatusGroup $type: String ) { duoWorkflowWorkflows( @@ -16,6 +17,7 @@ query getUserAgentFlows( before: $before excludeTypes: $excludeTypes sort: $sort + statusGroup: $statusGroup type: $type ) { pageInfo { diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue index 2b7e3728035fbe..f0519b60b8234b 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue @@ -70,6 +70,8 @@ export default { this.currentFilters.forEach((filter) => { if (filter.type === 'flow-name' && filter.value?.data) { processedFilters.type = filter.value.data; + } else if (filter.type === 'flow-status-group' && filter.value?.data) { + processedFilters.statusGroup = filter.value.data; } }); @@ -178,6 +180,22 @@ export default { { value: 'convert_to_gitlab_ci', title: s__('DuoAgentsPlatform|Convert to gitlab ci') }, ], }, + { + type: 'flow-status-group', + title: s__('DuoAgentsPlatform|Status Group'), + icon: 'status-success', + token: GlFilteredSearchToken, + operators: OPERATORS_IS, + unique: true, + options: [ + { value: 'ACTIVE', title: __('Active') }, + { value: 'PAUSED', title: __('Paused') }, + { value: 'AWAITING_INPUT', title: __('Awaiting Input') }, + { value: 'COMPLETED', title: __('Completed') }, + { value: 'FAILED', title: __('Failed') }, + { value: 'CANCELED', title: __('Canceled') }, + ], + }, ], }; diff --git a/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js b/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js index 21a118459142c0..f26a2784e690b2 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/pages/index/duo_agents_platform_index_spec.js @@ -162,7 +162,7 @@ describe('AgentsPlatformIndex', () => { it('renders the filtered search bar with filter tokens', () => { const tokens = findFilteredSearchBar().props('tokens'); - expect(tokens).toHaveLength(1); + expect(tokens).toHaveLength(2); expect(tokens[0]).toMatchObject({ type: 'flow-name', @@ -174,6 +174,21 @@ describe('AgentsPlatformIndex', () => { { value: 'software_development', title: 'Software Development' }, { value: 'convert_to_gitlab_ci', title: 'Convert to gitlab ci' }, ]); + + expect(tokens[1]).toMatchObject({ + type: 'flow-status-group', + title: 'Status Group', + icon: 'status-success', + unique: true, + }); + expect(tokens[1].options).toEqual([ + { value: 'ACTIVE', title: 'Active' }, + { value: 'PAUSED', title: 'Paused' }, + { value: 'AWAITING_INPUT', title: 'Awaiting Input' }, + { value: 'COMPLETED', title: 'Completed' }, + { value: 'FAILED', title: 'Failed' }, + { value: 'CANCELED', title: 'Canceled' }, + ]); }); describe('when hasInitialWorkflows is false', () => { @@ -221,6 +236,28 @@ describe('AgentsPlatformIndex', () => { }); }); + describe('with valid flow-status-group token', () => { + it('emits variables-updated event with processed filter parameters', () => { + const filters = [{ type: 'flow-status-group', value: { data: 'PAUSED' } }]; + + findFilteredSearchBar().vm.$emit('onFilter', filters); + + expectVariablesUpdatedEvent({ + sort: 'UPDATED_DESC', + pagination: { before: null, after: null, first: 20, last: null }, + filters: { statusGroup: 'PAUSED' }, + }); + }); + + it('hides alert when valid filters are applied', () => { + const filters = [{ type: 'flow-status-group', value: { data: 'COMPLETED' } }]; + + findFilteredSearchBar().vm.$emit('onFilter', filters); + + expect(findAlert().exists()).toBe(false); + }); + }); + describe('with unsupported free text search', () => { it('shows alert and does not emit variables-updated', async () => { const filters = [{ type: 'filtered-search-term', value: { data: 'software dev' } }]; diff --git a/locale/gitlab.pot b/locale/gitlab.pot index bda25e7094acee..fa82bf3832e4a7 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -9940,6 +9940,9 @@ msgstr "" msgid "Avg" msgstr "" +msgid "Awaiting Input" +msgstr "" + msgid "Awaiting user signup" msgstr "" @@ -12933,6 +12936,9 @@ msgstr "" msgid "Cancel, keep project" msgstr "" +msgid "Canceled" +msgstr "" + msgid "Canceled deployment to" msgstr "" @@ -25001,6 +25007,9 @@ msgstr "" msgid "DuoAgentsPlatform|Start agent session" msgstr "" +msgid "DuoAgentsPlatform|Status Group" +msgstr "" + msgid "DuoAgentsPlatform|The flow trigger could not be created. Try again." msgstr "" -- GitLab From 795ff955d2f2bc95f8b822ed70e45414f07afb23 Mon Sep 17 00:00:00 2001 From: Lindsey Shelton Date: Wed, 22 Oct 2025 11:24:26 -0500 Subject: [PATCH 4/4] Apply fuzzy search to filtered search Update graphql for search type Update duo_agents_platform_index for fuzzy search --- .../queries/get_agent_flows.query.graphql | 2 + .../queries/get_user_agent_flow.query.graphql | 2 + .../pages/index/duo_agents_platform_index.vue | 49 ++++-------------- .../index/duo_agents_platform_index_spec.js | 51 +++---------------- locale/gitlab.pot | 3 -- 5 files changed, 22 insertions(+), 85 deletions(-) diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql index deceb151c39ee3..f909f3b2731e2f 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_agent_flows.query.graphql @@ -6,6 +6,7 @@ query getAgentFlows( $before: String $first: Int $last: Int + $search: String $sort: DuoWorkflowsWorkflowSort $statusGroup: DuoWorkflowStatusGroup $type: String @@ -18,6 +19,7 @@ query getAgentFlows( after: $after last: $last before: $before + search: $search sort: $sort statusGroup: $statusGroup type: $type diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql index 849f3a4f5bb1d2..7df2cc564d57c9 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/graphql/queries/get_user_agent_flow.query.graphql @@ -6,6 +6,7 @@ query getUserAgentFlows( $first: Int $last: Int $excludeTypes: [String!] + $search: String $sort: DuoWorkflowsWorkflowSort $statusGroup: DuoWorkflowStatusGroup $type: String @@ -16,6 +17,7 @@ query getUserAgentFlows( last: $last before: $before excludeTypes: $excludeTypes + search: $search sort: $sort statusGroup: $statusGroup type: $type diff --git a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue index f0519b60b8234b..5e31a436f83a92 100644 --- a/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue +++ b/ee/app/assets/javascripts/ai/duo_agents_platform/pages/index/duo_agents_platform_index.vue @@ -1,12 +1,9 @@