From 772857dbad916d6f03110e069b8f25e5383e6793 Mon Sep 17 00:00:00 2001 From: Lisbet Alvarez <107949262+lisbet-alvarez@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:38:54 -0800 Subject: [PATCH] Icu 18518 UI add sorting support to cache daemon handler (#3168) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add sorting support & sort feature on targets name column * test: 💍 cleanup and add tests * refactor: 💡 remove log * fix: 🐛 dont add undefined/null values from sort to query ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-18518 --- .../addon/handlers/cache-daemon-handler.js | 8 + .../scopes/scope/projects/targets/index.js | 16 ++ .../scopes/scope/projects/targets/index.js | 14 +- .../scopes/scope/projects/targets/index.hbs | 191 +++++++++--------- .../src/services/cache-daemon-manager.js | 4 + .../acceptance/projects/targets/index-test.js | 39 ++++ .../scope/projects/targets/index-test.js | 12 ++ 7 files changed, 192 insertions(+), 92 deletions(-) diff --git a/addons/api/addon/handlers/cache-daemon-handler.js b/addons/api/addon/handlers/cache-daemon-handler.js index daedc0203..851a827e8 100644 --- a/addons/api/addon/handlers/cache-daemon-handler.js +++ b/addons/api/addon/handlers/cache-daemon-handler.js @@ -101,6 +101,14 @@ export default class CacheDaemonHandler { const token = sessionData?.token; const resourceName = resourceNames[type]; + if (remainingQuery.query?.sort) { + const { attribute, direction } = remainingQuery.query.sort; + if (attribute && direction) { + remainingQuery.sort_by = attribute; + remainingQuery.sort_direction = direction; + } + } + remainingQuery = { ...remainingQuery, query: searchQuery, diff --git a/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js b/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js index eb6f03996..2f98bf9f4 100644 --- a/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js +++ b/ui/desktop/app/controllers/scopes/scope/projects/targets/index.js @@ -37,6 +37,8 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle { types: { type: 'array' } }, 'page', 'pageSize', + 'sortAttribute', + 'sortDirection', ]; @tracked search; @@ -45,6 +47,8 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle @tracked types = []; @tracked page = 1; @tracked pageSize = 10; + @tracked sortAttribute; + @tracked sortDirection; @tracked selectedTarget; // =methods @@ -333,4 +337,16 @@ export default class ScopesScopeProjectsTargetsIndexController extends Controlle .then(() => this.quickConnectAndLaunchRdp(target)); } } + + /** + * Sets sort values and sets page to 1 + * @param {string} sortBy + * @param {string} sortOrder + */ + @action + onSort(sortBy, sortOrder) { + this.sortAttribute = sortBy; + this.sortDirection = sortOrder; + this.page = 1; + } } diff --git a/ui/desktop/app/routes/scopes/scope/projects/targets/index.js b/ui/desktop/app/routes/scopes/scope/projects/targets/index.js index be5200012..2b99abd7d 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/targets/index.js +++ b/ui/desktop/app/routes/scopes/scope/projects/targets/index.js @@ -50,6 +50,14 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route { pageSize: { refreshModel: true, }, + sortAttribute: { + refreshModel: true, + replace: true, + }, + sortDirection: { + refreshModel: true, + replace: true, + }, }; // =methods @@ -97,6 +105,8 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route { types, page, pageSize, + sortAttribute, + sortDirection, useDebounce, }) => { if (useDebounce) { @@ -122,11 +132,11 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route { const sessions = await this.getSessions(orgScope, scopes, orgFilter); this.addActiveSessionFilters(filters, availableSessions, sessions); - + const sort = { attribute: sortAttribute, direction: sortDirection }; const query = { recursive: true, scope_id: orgScope.id, - query: { search, filters }, + query: { search, filters, sort }, page, pageSize, force_refresh: true, diff --git a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs index d3c45bd2d..641684346 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs @@ -81,108 +81,119 @@ {{#if @model.targets}} + <:head as |H|> + + {{t 'form.name.label'}} + {{t 'resources.alias.title_plural'}} + {{t 'resources.target.titles.active-sessions'}} + {{t 'form.type.label'}} + {{t 'resources.project.title'}} + {{t + 'titles.actions' + }} + + <:body as |B|> - - - - {{#if (can 'read model' B.data)}} - - {{B.data.displayName}} - - {{else}} - {{B.data.displayName}} + {{#each @model.targets as |target|}} + + + + {{#if (can 'read model' target)}} + + {{target.displayName}} + + {{else}} + {{target.displayName}} + {{/if}} + {{#if target.description}} + + {{target.description}} + + {{/if}} + + + + {{truncate-list 'actions.more' target.associatedAliases}} + + + {{#if target.isActive}} + {{/if}} - {{#if B.data.description}} - - {{B.data.description}} - + + + {{#if target.type}} + {{/if}} - - - - {{truncate-list 'actions.more' B.data.associatedAliases}} - - - {{#if B.data.isActive}} - - {{/if}} - - - {{#if B.data.type}} - - {{/if}} - - -
- -
- {{#if B.data.project.name}} - - {{/if}} -
- -
- {{#if (can 'connect target' B.data)}} - {{#if (can 'read target' B.data)}} - {{#if (and B.data.isRDP this.rdp.isPreferredRdpClientSet)}} - + + +
+ +
+ {{#if target.project.name}} + + {{/if}} +
+ +
+ {{#if (can 'connect target' target)}} + {{#if (can 'read target' target)}} + {{#if (and target.isRDP this.rdp.isPreferredRdpClientSet)}} + + {{else}} + + {{/if}} {{else}} {{/if}} - {{else}} - {{/if}} - {{/if}} -
-
- +
+
+
+ {{/each}}
{{#if @model.isLoadIncomplete}} diff --git a/ui/desktop/electron-app/src/services/cache-daemon-manager.js b/ui/desktop/electron-app/src/services/cache-daemon-manager.js index 54d0962c9..30c65efe9 100644 --- a/ui/desktop/electron-app/src/services/cache-daemon-manager.js +++ b/ui/desktop/electron-app/src/services/cache-daemon-manager.js @@ -161,6 +161,10 @@ const searchCliCommand = (requestData) => { if (requestData.filter) { searchCommand.push(`-filter=${requestData.filter}`); } + if (requestData.sort_by && requestData.sort_direction) { + searchCommand.push(`-sort-by=${requestData.sort_by}`); + searchCommand.push(`-sort-direction=${requestData.sort_direction}`); + } if (requestData.force_refresh) { searchCommand.push(`-force-refresh=true`); } diff --git a/ui/desktop/tests/acceptance/projects/targets/index-test.js b/ui/desktop/tests/acceptance/projects/targets/index-test.js index b70b12f85..fad86505d 100644 --- a/ui/desktop/tests/acceptance/projects/targets/index-test.js +++ b/ui/desktop/tests/acceptance/projects/targets/index-test.js @@ -49,6 +49,12 @@ module('Acceptance | projects | targets | index', function (hooks) { const TARGET_OPEN_BUTTON = (id) => `[data-test-targets-open-button="${id}"]`; const TARGET_CONNECT_BUTTON = (id) => `[data-test-targets-connect-button="${id}"]`; + const TABLE_SORT_BTN = 'thead tr th:nth-child(1) button'; + const TABLE_SORT_BTN_ARROW_UP = + 'thead tr th:nth-child(1) button .hds-icon-arrow-up'; + const TABLE_SORT_BTN_ARROW_DOWN = + 'thead tr th:nth-child(1) button .hds-icon-arrow-down'; + const instances = { scopes: { global: null, @@ -874,4 +880,37 @@ module('Acceptance | projects | targets | index', function (hooks) { // Assert that the confirm modal appears assert.dom(HDS_DIALOG_MODAL).isVisible(); }); + + test('sorting by target name updates url', async function (assert) { + this.stubCacheDaemonSearch( + 'sessions', + 'targets', + 'aliases', + 'sessions', + + 'sessions', + 'targets', + 'aliases', + 'sessions', + + 'sessions', + 'targets', + 'aliases', + 'sessions', + ); + await visit(urls.projects); + + await click(`[href="${urls.targets}"]`); + await click(TABLE_SORT_BTN); + + assert.true(currentURL().includes('sortAttribute=name')); + assert.true(currentURL().includes('sortDirection=asc')); + assert.dom(TABLE_SORT_BTN_ARROW_UP).isVisible(); + + await click(TABLE_SORT_BTN); + + assert.true(currentURL().includes('sortAttribute=name')); + assert.true(currentURL().includes('sortDirection=desc')); + assert.dom(TABLE_SORT_BTN_ARROW_DOWN).isVisible(); + }); }); diff --git a/ui/desktop/tests/unit/controllers/scopes/scope/projects/targets/index-test.js b/ui/desktop/tests/unit/controllers/scopes/scope/projects/targets/index-test.js index 6ffddc056..e5348d017 100644 --- a/ui/desktop/tests/unit/controllers/scopes/scope/projects/targets/index-test.js +++ b/ui/desktop/tests/unit/controllers/scopes/scope/projects/targets/index-test.js @@ -256,5 +256,17 @@ module( assert.strictEqual(session.status, STATUS_SESSION_CANCELING); }); + + test('onSort action sets expected values correctly', async function (assert) { + this.stubCacheDaemonSearch(); + assert.notOk(controller.sortAttribute); + assert.notOk(controller.sortDirection); + + controller.onSort('name', 'desc'); + + assert.strictEqual(controller.page, 1); + assert.strictEqual(controller.sortAttribute, 'name'); + assert.strictEqual(controller.sortDirection, 'desc'); + }); }, );