diff --git a/ui/admin/app/controllers/scopes/scope/aliases/index.js b/ui/admin/app/controllers/scopes/scope/aliases/index.js index c9c3785d2..bbf06a3cf 100644 --- a/ui/admin/app/controllers/scopes/scope/aliases/index.js +++ b/ui/admin/app/controllers/scopes/scope/aliases/index.js @@ -20,11 +20,19 @@ export default class ScopesScopeAliasesIndexController extends Controller { // =attributes - queryParams = ['search', 'page', 'pageSize']; + queryParams = [ + 'search', + 'page', + 'pageSize', + 'sortAttribute', + 'sortDirection', + ]; @tracked search; @tracked page = 1; @tracked pageSize = 10; + @tracked sortAttribute; + @tracked sortDirection; /** * If can list (at least): return default welcome message. @@ -124,4 +132,11 @@ export default class ScopesScopeAliasesIndexController extends Controller { this.search = value; this.page = 1; } + + @action + sortBy(attribute, direction) { + this.sortAttribute = attribute; + this.sortDirection = direction; + this.page = 1; + } } diff --git a/ui/admin/app/routes/scopes/scope/aliases/index.js b/ui/admin/app/routes/scopes/scope/aliases/index.js index b78171f54..7b83b6747 100644 --- a/ui/admin/app/routes/scopes/scope/aliases/index.js +++ b/ui/admin/app/routes/scopes/scope/aliases/index.js @@ -27,6 +27,14 @@ export default class ScopesScopeAliasesIndexRoute extends Route { pageSize: { refreshModel: true, }, + sortAttribute: { + refreshModel: true, + replace: true, + }, + sortDirection: { + refreshModel: true, + replace: true, + }, }; // =methods @@ -42,7 +50,14 @@ export default class ScopesScopeAliasesIndexRoute extends Route { } retrieveData = restartableTask( - async ({ search, page, pageSize, useDebounce }) => { + async ({ + search, + page, + pageSize, + useDebounce, + sortAttribute, + sortDirection, + }) => { if (useDebounce) { await timeout(250); } @@ -59,12 +74,18 @@ export default class ScopesScopeAliasesIndexRoute extends Route { collection: 'aliases', }) ) { + const sort = { + attribute: sortAttribute, + direction: sortDirection, + }; + aliases = await this.store.query('alias', { scope_id, - query: { search }, + query: { search, sort }, page, pageSize, }); + totalItems = aliases.meta?.totalItems; // since we don't receive target info from aliases list API, // we query the store to fetch target information based on the destination id diff --git a/ui/admin/app/templates/scopes/scope/aliases/index.hbs b/ui/admin/app/templates/scopes/scope/aliases/index.hbs index 6fff969b7..78c7c62f8 100644 --- a/ui/admin/app/templates/scopes/scope/aliases/index.hbs +++ b/ui/admin/app/templates/scopes/scope/aliases/index.hbs @@ -44,113 +44,136 @@ {{#if @model.aliases}} - <:body as |B|> - - - {{#if (can 'read model' B.data.alias)}} - - {{B.data.alias.value}} - - {{else}} - - {{B.data.alias.value}} - - {{/if}} - {{#if B.data.alias.description}} - - {{B.data.alias.description}} - - {{/if}} - - - {{#if B.data.target.displayName}} -
- - {{B.data.target.displayName}} -
- {{else}} - - {{t 'titles.not-set'}} - - {{/if}} -
- - - - - {{#if B.data.alias.destination_id}} - - {{/if}} - - - {{#let - (hash - canUpdate=(can 'update model' B.data.alias) - canDelete=(can 'delete model' B.data.alias) - ) - as |perms| - }} - {{#if (or perms.canUpdate perms.canDelete)}} - + <:head as |H|> + + + {{t 'resources.alias.title'}} + + + {{t 'resources.alias.titles.points_to'}} + + + {{t 'resources.scope.title'}} + + + {{t 'resources.alias.titles.resource_id'}} + + + {{t 'titles.actions'}} + + + - - {{#if perms.canUpdate}} - - {{t 'actions.edit'}} - - {{#if B.data.alias.destination_id}} + <:body as |B|> + {{#each @model.aliases as |data|}} + + + {{#if (can 'read model' data.alias)}} + + {{data.alias.value}} + + {{else}} + + {{data.alias.value}} + + {{/if}} + {{#if data.alias.description}} + + {{data.alias.description}} + + {{/if}} + + + {{#if data.target.displayName}} +
+ + {{data.target.displayName}} +
+ {{else}} + + {{t 'titles.not-set'}} + + {{/if}} +
+ + + + + {{#if data.alias.destination_id}} + + {{/if}} + + + {{#let + (hash + canUpdate=(can 'update model' data.alias) + canDelete=(can 'delete model' data.alias) + ) + as |perms| + }} + {{#if (or perms.canUpdate perms.canDelete)}} + + + + {{#if perms.canUpdate}} - {{t 'actions.clear'}} + {{t 'actions.edit'}} + + {{#if data.alias.destination_id}} + + {{t 'actions.clear'}} + + {{/if}} + {{/if}} + + {{#if perms.canDelete}} + + {{t 'actions.delete'}} {{/if}} - {{/if}} + - {{#if perms.canDelete}} - - {{t 'actions.delete'}} - - {{/if}} -
- - {{/if}} - {{/let}} -
-
+ {{/if}} + {{/let}} + + + {{/each}}
{ + return this.server.create('alias', { + id: `alt_${year}`, + value: `Alias ${year}`, + created_time: `${year}-01-01T00:00:00Z`, + scope: instances.scopes.global, + destination_id: instances.target.id, + }); + }); + + await visit(urls.aliases); + + assert.dom(commonSelectors.TABLE_ROWS).exists({ count: years.length }); + assert.dom(commonSelectors.TABLE_ROW(1)).containsText('Alias 2026'); + assert.dom(commonSelectors.TABLE_ROW(2)).containsText('Alias 2025'); + assert.dom(commonSelectors.TABLE_ROW(3)).containsText('Alias 2024'); + assert.dom(commonSelectors.TABLE_ROW(4)).containsText('Alias 2023'); + }); + + test.each( + 'sorting', + { + 'on value': { + attribute: { + key: 'value', + values: ['alpha', 'beta', 'delta', 'gamma', 'epsilon'], + }, + expectedAscendingSort: ['alpha', 'beta', 'delta', 'epsilon', 'gamma'], + column: 1, + }, + }, + async function (assert, input) { + this.server.schema.aliases.all().destroy(); + faker.helpers.shuffle(input.attribute.values).forEach((value) => { + this.server.create('alias', { + [input.attribute.key]: value, + scope: instances.scopes.global, + destination_id: instances.target.id, + }); + }); + + await visit(urls.aliases); + + // click the sort button again to sort in ascending order + await click(commonSelectors.TABLE_SORT_BTN(input.column)); + assert.true(currentURL().includes('sortDirection=asc')); + assert.true( + currentURL().includes(`sortAttribute=${input.attribute.key}`), + ); + assert + .dom(commonSelectors.TABLE_SORT_BTN_ARROW_UP(input.column)) + .isVisible(); + assert + .dom(commonSelectors.TABLE_ROWS) + .isVisible({ count: input.attribute.values.length }); + + input.expectedAscendingSort.forEach((expected, index) => { + // nth-child index starts at 1 + assert.dom(commonSelectors.TABLE_ROW(index + 1)).containsText(expected); + }); + + // click the sort button again to sort in descending order + await click(commonSelectors.TABLE_SORT_BTN(input.column)); + assert.true(currentURL().includes('sortDirection=desc')); + assert.true( + currentURL().includes(`sortAttribute=${input.attribute.key}`), + ); + assert + .dom(commonSelectors.TABLE_SORT_BTN_ARROW_DOWN(input.column)) + .isVisible(); + input.expectedAscendingSort.toReversed().forEach((expected, index) => { + // nth-child index starts at 1 + assert.dom(commonSelectors.TABLE_ROW(index + 1)).containsText(expected); + }); + }, + ); });