fix: 🐛 Add refresh button back (#2124)
This commit is contained in:
1 parent
4d3c993b0f
commit
06cc64e6af
7 files changed
+233
-164
No files matched your search
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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<void>}
|
||||
*/
|
||||
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');
|
||||
}
|
||||
}
|
||||
@@ -5,54 +5,60 @@
|
||||
|
||||
{{#if (or @model.sessions @model.allSessions)}}
|
||||
{{#if @model.isClientDaemonRunning}}
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.target.title'}}
|
||||
@itemOptions={{this.availableTargets}}
|
||||
@checkedItems={{this.targets}}
|
||||
@applyFilter={{fn this.applyFilter 'targets'}}
|
||||
@isSearchable={{true}}
|
||||
@listPosition='bottom-left'
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'form.status.label'}}
|
||||
@itemOptions={{this.sessionStatusOptions}}
|
||||
@checkedItems={{this.status}}
|
||||
@applyFilter={{fn this.applyFilter 'status'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.scope.title'}}
|
||||
@itemOptions={{this.availableScopes}}
|
||||
@checkedItems={{this.scopes}}
|
||||
@applyFilter={{fn this.applyFilter 'scopes'}}
|
||||
@isSearchable={{true}}
|
||||
as |FD selectItem itemOptions|
|
||||
>
|
||||
{{#let (group-by itemOptions 'scopeModel') as |orgs|}}
|
||||
{{#each orgs as |org|}}
|
||||
<FD.Generic>
|
||||
<FlightIcon @name='org' />
|
||||
{{org.key.displayName}}
|
||||
</FD.Generic>
|
||||
{{#each org.items as |project|}}
|
||||
<FD.Checkbox
|
||||
@value={{project.id}}
|
||||
checked={{includes project.id this.scopes}}
|
||||
{{on 'change' selectItem}}
|
||||
>
|
||||
{{project.displayName}}
|
||||
</FD.Checkbox>
|
||||
<div class='search-filtering-toolbar'>
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.target.title'}}
|
||||
@itemOptions={{this.availableTargets}}
|
||||
@checkedItems={{this.targets}}
|
||||
@applyFilter={{fn this.applyFilter 'targets'}}
|
||||
@isSearchable={{true}}
|
||||
@listPosition='bottom-left'
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'form.status.label'}}
|
||||
@itemOptions={{this.sessionStatusOptions}}
|
||||
@checkedItems={{this.status}}
|
||||
@applyFilter={{fn this.applyFilter 'status'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.scope.title'}}
|
||||
@itemOptions={{this.availableScopes}}
|
||||
@checkedItems={{this.scopes}}
|
||||
@applyFilter={{fn this.applyFilter 'scopes'}}
|
||||
@isSearchable={{true}}
|
||||
as |FD selectItem itemOptions|
|
||||
>
|
||||
{{#let (group-by itemOptions 'scopeModel') as |orgs|}}
|
||||
{{#each orgs as |org|}}
|
||||
<FD.Generic>
|
||||
<FlightIcon @name='org' />
|
||||
{{org.key.displayName}}
|
||||
</FD.Generic>
|
||||
{{#each org.items as |project|}}
|
||||
<FD.Checkbox
|
||||
@value={{project.id}}
|
||||
checked={{includes project.id this.scopes}}
|
||||
{{on 'change' selectItem}}
|
||||
>
|
||||
{{project.displayName}}
|
||||
</FD.Checkbox>
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
{{/let}}
|
||||
</Dropdown>
|
||||
</S.Generic>
|
||||
</Hds::SegmentedGroup>
|
||||
{{/let}}
|
||||
</Dropdown>
|
||||
</S.Generic>
|
||||
</Hds::SegmentedGroup>
|
||||
<span>
|
||||
<ToolbarRefresher @onClick={{route-action 'refreshAll'}} />
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<FilterTags @filters={{this.filters}} />
|
||||
{{/if}}
|
||||
{{#if @model.sessions}}
|
||||
|
||||
@@ -5,60 +5,65 @@
|
||||
|
||||
{{#if this.showFilters}}
|
||||
{{#if @model.isClientDaemonRunning}}
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.TextInput
|
||||
@value={{this.search}}
|
||||
@type='search'
|
||||
placeholder={{t 'actions.search'}}
|
||||
aria-label={{t 'actions.search'}}
|
||||
{{on 'input' this.handleSearchInput}}
|
||||
/>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.target.titles.active-sessions'}}
|
||||
@itemOptions={{this.availableSessionOptions}}
|
||||
@checkedItems={{this.availableSessions}}
|
||||
@applyFilter={{fn this.applyFilter 'availableSessions'}}
|
||||
<div class='search-filtering-toolbar'>
|
||||
<Hds::SegmentedGroup as |S|>
|
||||
<S.TextInput
|
||||
@value={{this.search}}
|
||||
@type='search'
|
||||
placeholder={{t 'actions.search'}}
|
||||
aria-label={{t 'actions.search'}}
|
||||
{{on 'input' this.handleSearchInput}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'form.type.label'}}
|
||||
@itemOptions={{this.targetTypeOptions}}
|
||||
@checkedItems={{this.types}}
|
||||
@applyFilter={{fn this.applyFilter 'types'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.scope.title'}}
|
||||
@itemOptions={{this.availableScopes}}
|
||||
@checkedItems={{this.scopes}}
|
||||
@applyFilter={{fn this.applyFilter 'scopes'}}
|
||||
@isSearchable={{true}}
|
||||
as |FD selectItem itemOptions|
|
||||
>
|
||||
{{#let (group-by itemOptions 'scopeModel') as |orgs|}}
|
||||
{{#each orgs as |org|}}
|
||||
<FD.Generic>
|
||||
<FlightIcon @name='org' />
|
||||
{{org.key.displayName}}
|
||||
</FD.Generic>
|
||||
{{#each org.items as |project|}}
|
||||
<FD.Checkbox
|
||||
@value={{project.id}}
|
||||
data-test-checkbox={{project.name}}
|
||||
checked={{includes project.id this.scopes}}
|
||||
{{on 'change' selectItem}}
|
||||
>
|
||||
{{project.displayName}}
|
||||
</FD.Checkbox>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.target.titles.active-sessions'}}
|
||||
@itemOptions={{this.availableSessionOptions}}
|
||||
@checkedItems={{this.availableSessions}}
|
||||
@applyFilter={{fn this.applyFilter 'availableSessions'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'form.type.label'}}
|
||||
@itemOptions={{this.targetTypeOptions}}
|
||||
@checkedItems={{this.types}}
|
||||
@applyFilter={{fn this.applyFilter 'types'}}
|
||||
/>
|
||||
</S.Generic>
|
||||
<S.Generic>
|
||||
<Dropdown
|
||||
@name={{t 'resources.scope.title'}}
|
||||
@itemOptions={{this.availableScopes}}
|
||||
@checkedItems={{this.scopes}}
|
||||
@applyFilter={{fn this.applyFilter 'scopes'}}
|
||||
@isSearchable={{true}}
|
||||
as |FD selectItem itemOptions|
|
||||
>
|
||||
{{#let (group-by itemOptions 'scopeModel') as |orgs|}}
|
||||
{{#each orgs as |org|}}
|
||||
<FD.Generic>
|
||||
<FlightIcon @name='org' />
|
||||
{{org.key.displayName}}
|
||||
</FD.Generic>
|
||||
{{#each org.items as |project|}}
|
||||
<FD.Checkbox
|
||||
@value={{project.id}}
|
||||
data-test-checkbox={{project.name}}
|
||||
checked={{includes project.id this.scopes}}
|
||||
{{on 'change' selectItem}}
|
||||
>
|
||||
{{project.displayName}}
|
||||
</FD.Checkbox>
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
{{/each}}
|
||||
{{/let}}
|
||||
</Dropdown>
|
||||
</S.Generic>
|
||||
</Hds::SegmentedGroup>
|
||||
{{/let}}
|
||||
</Dropdown>
|
||||
</S.Generic>
|
||||
</Hds::SegmentedGroup>
|
||||
<span>
|
||||
<ToolbarRefresher @onClick={{route-action 'refreshAll'}} />
|
||||
</span>
|
||||
</div>
|
||||
<FilterTags @filters={{this.filters}} />
|
||||
{{/if}}
|
||||
|
||||
|
||||
@@ -373,8 +373,6 @@ module('Acceptance | projects | sessions | index', function (hooks) {
|
||||
resource: 'sessions',
|
||||
func: () => [instances.session2],
|
||||
},
|
||||
'sessions',
|
||||
'targets',
|
||||
);
|
||||
await visit(urls.globalSessions);
|
||||
|
||||
|
||||
Reference in new issue
Block a user