diff --git a/addons/core/addon/styles/addon.scss b/addons/core/addon/styles/addon.scss index 1d0a09eb3..4fc768b81 100644 --- a/addons/core/addon/styles/addon.scss +++ b/addons/core/addon/styles/addon.scss @@ -38,6 +38,26 @@ .hds-table { margin-bottom: 1rem; } + + .search-filtering-toolbar { + display: flex; + + // TODO: Remove these copied styles by migrating to HDS for the toolbar refresher component. + // The original styles assumed we were using the old toolbar component under a "rose-toolbar" class. + .rose-button-inline-link-action { + font-size: 0.8125rem; + line-height: 1.8461538462; + font-weight: 600; + color: var(--stark); + display: inline-block; + vertical-align: middle; + padding: 0.375rem 1rem; + } + + > :last-child { + margin-left: auto; + } + } } // Flyout with table diff --git a/addons/rose/app/styles/rose/components/toolbar/_info.scss b/addons/rose/app/styles/rose/components/toolbar/_info.scss index a4d07460f..1c1a6330b 100644 --- a/addons/rose/app/styles/rose/components/toolbar/_info.scss +++ b/addons/rose/app/styles/rose/components/toolbar/_info.scss @@ -7,19 +7,17 @@ @use '../../variables/media'; @use '../../utilities/type'; -.rose-toolbar { - .rose-toolbar-info { - @include type.type(button); +.rose-toolbar-info { + @include type.type(button); - color: var(--ui-gray); - position: relative; - display: inline-block; - vertical-align: middle; - white-space: nowrap; - overflow: hidden; + color: var(--ui-gray); + position: relative; + display: inline-block; + vertical-align: middle; + white-space: nowrap; + overflow: hidden; - $y-padding: sizing.rems(xxs) + sizing.rems(xxxs); + $y-padding: sizing.rems(xxs) + sizing.rems(xxxs); - padding: $y-padding sizing.rems(m) $y-padding sizing.rems(l); - } + padding: $y-padding sizing.rems(m) $y-padding sizing.rems(l); } diff --git a/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js b/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js index 7576afe89..5b7847b40 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js +++ b/ui/desktop/app/routes/scopes/scope/projects/sessions/index.js @@ -18,6 +18,7 @@ export default class ScopesScopeProjectsSessionsIndexRoute extends Route { @service session; @service store; @service ipc; + @service router; // =attributes @@ -66,8 +67,7 @@ export default class ScopesScopeProjectsSessionsIndexRoute extends Route { * all targets and all projects for filtering options for the current user. * @return {Promise<{sessions: [SessionModel], projects: [ScopeModel], allSessions: [SessionModel], allTargets: [TargetModel], totalItems: number}>} */ - async model({ targets, status, scopes, page, pageSize }, transition) { - const from = transition.from?.name; + async model({ targets, status, scopes, page, pageSize }) { const projects = this.modelFor('scopes.scope.projects'); const orgScope = this.modelFor('scopes.scope'); // orgFilter used to narrow down resources to only those under @@ -105,34 +105,11 @@ export default class ScopesScopeProjectsSessionsIndexRoute extends Route { const totalItems = sessions.meta?.totalItems; // Query all sessions and all targets for defining filtering values if entering route for the first time - if (from !== 'scopes.scope.projects.sessions.index') { - const options = { pushToStore: false }; - const allSessionsQuery = { - scope_id: orgScope.id, - recursive: true, - query: { - filters: { - user_id: [{ equals: this.session.data.authenticated.user_id }], - }, - }, - }; - if (orgScope.isOrg && scopes.length === 0) { - allSessionsQuery.filter = orgFilter; - } - this.allSessions = await this.store.query( - 'session', - allSessionsQuery, - options, - ); - const allTargetsQuery = { scope_id: orgScope.id, recursive: true }; - if (orgScope.isOrg) { - allTargetsQuery.filter = orgFilter; - } - this.allTargets = await this.store.query( - 'target', - allTargetsQuery, - options, - ); + if (!this.allSessions) { + await this.getAllSessions(orgScope, scopes, orgFilter); + } + if (!this.allTargets) { + await this.getAllTargets(orgScope, orgFilter); } return { @@ -145,6 +122,39 @@ export default class ScopesScopeProjectsSessionsIndexRoute extends Route { }; } + async getAllTargets(orgScope, orgFilter) { + const allTargetsQuery = { + scope_id: orgScope.id, + recursive: true, + force_refresh: true, + }; + if (orgScope.isOrg) { + allTargetsQuery.filter = orgFilter; + } + this.allTargets = await this.store.query('target', allTargetsQuery, { + pushToStore: false, + }); + } + + async getAllSessions(orgScope, scopes, orgFilter) { + const allSessionsQuery = { + scope_id: orgScope.id, + recursive: true, + query: { + filters: { + user_id: [{ equals: this.session.data.authenticated.user_id }], + }, + }, + force_refresh: true, + }; + if (orgScope.isOrg && scopes.length === 0) { + allSessionsQuery.filter = orgFilter; + } + this.allSessions = await this.store.query('session', allSessionsQuery, { + pushToStore: false, + }); + } + resetController(controller, isExiting, transition) { const fromScope = transition.from.find( (routeInfo) => routeInfo.name === 'scopes.scope', @@ -166,4 +176,20 @@ export default class ScopesScopeProjectsSessionsIndexRoute extends Route { }); } } + + @action + async refreshAll() { + const orgScope = this.modelFor('scopes.scope'); + const orgFilter = `"/item/scope/parent_scope_id" == "${orgScope.id}"`; + + await this.getAllTargets(orgScope, orgFilter); + await this.getAllSessions(orgScope, [], orgFilter); + + // Prime the store by searching for orgs only in case there are new org scopes; + // otherwise we won't be able to correctly peek the org scopes for their display name in the UI. + await this.store.query('scope', {}); + + // Refresh the proj scopes so our `modelFor` returns accurate data + this.router.refresh('scopes.scope.projects'); + } } 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 f2786ab20..4bed8bdcf 100644 --- a/ui/desktop/app/routes/scopes/scope/projects/targets/index.js +++ b/ui/desktop/app/routes/scopes/scope/projects/targets/index.js @@ -80,15 +80,16 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route { * * @returns {Promise<{totalItems: number, targets: [TargetModel], projects: [ScopeModel], allTargets: [TargetModel] }> } */ - async model( - { search, scopes, availableSessions, types, page, pageSize }, - transition, - ) { + async model({ search, scopes, availableSessions, types, page, pageSize }) { const orgScope = this.modelFor('scopes.scope'); // orgFilter used to narrow down resources to only those under // the current org scope if org is not global const orgFilter = `"/item/scope/parent_scope_id" == "${orgScope.id}"`; - await this.getAllTargets(transition, orgScope, orgFilter); + + if (!this.allTargets) { + await this.getAllTargets(orgScope, orgFilter); + } + const projects = this.modelFor('scopes.scope.projects'); const filters = { scope_id: [], id: { values: [] }, type: [] }; @@ -172,26 +173,27 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route { /** * Get all the targets but only load them once when entering the route. Ideally we would lazy load it when needed * but this can be revisited in the future. - * @param transition + * @param orgScope + * @param orgFilter * @returns {Promise} */ - async getAllTargets(transition, orgScope, orgFilter) { - const from = transition.from?.name; - + async getAllTargets(orgScope, orgFilter) { // Query all targets for defining filtering values if entering route for first time - if (from !== 'scopes.scope.projects.targets.index') { - const query = { scope_id: orgScope.id, recursive: true }; - if (orgScope.isOrg) { - query.filter = orgFilter; - } - const options = { pushToStore: false }; - const allTargets = await this.store.query('target', query, options); - - // Filter out targets to which users do not have the connect ability - this.allTargets = allTargets.filter((target) => - target.authorized_actions.includes('authorize-session'), - ); + const query = { + scope_id: orgScope.id, + recursive: true, + force_refresh: true, + }; + if (orgScope.isOrg) { + query.filter = orgFilter; } + const options = { pushToStore: false }; + const allTargets = await this.store.query('target', query, options); + + // Filter out targets to which users do not have the connect ability + this.allTargets = allTargets.filter((target) => + target.authorized_actions.includes('authorize-session'), + ); } /** @@ -233,4 +235,18 @@ export default class ScopesScopeProjectsTargetsIndexRoute extends Route { } }); }; + + @action + async refreshAll() { + const orgScope = this.modelFor('scopes.scope'); + const orgFilter = `"/item/scope/parent_scope_id" == "${orgScope.id}"`; + await this.getAllTargets(orgScope, orgFilter); + + // Prime the store by searching for only orgs in case there are new org scopes; + // otherwise we won't be able to correctly peek the org scopes for their display name in the UI. + await this.store.query('scope', {}); + + // Refresh the proj scopes so our `modelFor` returns accurate data + this.router.refresh('scopes.scope.projects'); + } } diff --git a/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs b/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs index d52d47551..f164e2418 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/sessions/index.hbs @@ -5,54 +5,60 @@ {{#if (or @model.sessions @model.allSessions)}} {{#if @model.isClientDaemonRunning}} - - - - - - - - - - {{#let (group-by itemOptions 'scopeModel') as |orgs|}} - {{#each orgs as |org|}} - - - {{org.key.displayName}} - - {{#each org.items as |project|}} - - {{project.displayName}} - +
+ + + + + + + + + + {{#let (group-by itemOptions 'scopeModel') as |orgs|}} + {{#each orgs as |org|}} + + + {{org.key.displayName}} + + {{#each org.items as |project|}} + + {{project.displayName}} + + {{/each}} {{/each}} - {{/each}} - {{/let}} - - - + {{/let}} + + + + + + +
+ {{/if}} {{#if @model.sessions}} 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 cdec110c4..55b61e48a 100644 --- a/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs +++ b/ui/desktop/app/templates/scopes/scope/projects/targets/index.hbs @@ -5,60 +5,65 @@ {{#if this.showFilters}} {{#if @model.isClientDaemonRunning}} - - - - + + - - - - - - - {{#let (group-by itemOptions 'scopeModel') as |orgs|}} - {{#each orgs as |org|}} - - - {{org.key.displayName}} - - {{#each org.items as |project|}} - - {{project.displayName}} - + + + + + + + + + {{#let (group-by itemOptions 'scopeModel') as |orgs|}} + {{#each orgs as |org|}} + + + {{org.key.displayName}} + + {{#each org.items as |project|}} + + {{project.displayName}} + + {{/each}} {{/each}} - {{/each}} - {{/let}} - - - + {{/let}} +
+
+
+ + + + {{/if}} diff --git a/ui/desktop/tests/acceptance/projects/sessions/index-test.js b/ui/desktop/tests/acceptance/projects/sessions/index-test.js index d3e345c18..8f2433e8e 100644 --- a/ui/desktop/tests/acceptance/projects/sessions/index-test.js +++ b/ui/desktop/tests/acceptance/projects/sessions/index-test.js @@ -373,8 +373,6 @@ module('Acceptance | projects | sessions | index', function (hooks) { resource: 'sessions', func: () => [instances.session2], }, - 'sessions', - 'targets', ); await visit(urls.globalSessions);