diff --git a/.changelog/18607.txt b/.changelog/18607.txt new file mode 100644 index 000000000..90733578b --- /dev/null +++ b/.changelog/18607.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: change the State filter on clients page to split out eligibility and drain status +``` diff --git a/ui/app/components/client-node-row.js b/ui/app/components/client-node-row.js index 137513629..fca7a9a4d 100644 --- a/ui/app/components/client-node-row.js +++ b/ui/app/components/client-node-row.js @@ -57,37 +57,31 @@ export default class ClientNodeRow extends Component.extend( @watchRelationship('allocations') watch; - @computed('node.compositeStatus') + @computed('node.status') get nodeStatusColor() { - let compositeStatus = this.get('node.compositeStatus'); - - if (compositeStatus === 'draining') { - return 'neutral'; - } else if (compositeStatus === 'ineligible') { + let status = this.get('node.status'); + if (status === 'disconnected') { return 'warning'; - } else if (compositeStatus === 'down') { + } else if (status === 'down') { return 'critical'; - } else if (compositeStatus === 'ready') { + } else if (status === 'ready') { return 'success'; - } else if (compositeStatus === 'initializing') { + } else if (status === 'initializing') { return 'neutral'; } else { return 'neutral'; } } - @computed('node.compositeStatus') + @computed('node.status') get nodeStatusIcon() { - let compositeStatus = this.get('node.compositeStatus'); - - if (compositeStatus === 'draining') { - return 'minus-circle'; - } else if (compositeStatus === 'ineligible') { + let status = this.get('node.status'); + if (status === 'disconnected') { return 'skip'; - } else if (compositeStatus === 'down') { + } else if (status === 'down') { return 'x-circle'; - } else if (compositeStatus === 'ready') { + } else if (status === 'ready') { return 'check-circle'; - } else if (compositeStatus === 'initializing') { + } else if (status === 'initializing') { return 'entry-point'; } else { return ''; diff --git a/ui/app/controllers/clients/index.js b/ui/app/controllers/clients/index.js index b7f38375f..ea2bc84bd 100644 --- a/ui/app/controllers/clients/index.js +++ b/ui/app/controllers/clients/index.js @@ -3,6 +3,8 @@ * SPDX-License-Identifier: BUSL-1.1 */ +// @ts-check + /* eslint-disable ember/no-incorrect-calls-with-inline-anonymous-functions */ import { alias, readOnly } from '@ember/object/computed'; import { inject as service } from '@ember/service'; @@ -45,9 +47,6 @@ export default class IndexController extends Controller.extend( { qpClass: 'class', }, - { - qpState: 'state', - }, { qpDatacenter: 'dc', }, @@ -62,6 +61,110 @@ export default class IndexController extends Controller.extend( }, ]; + filterFunc = (node) => { + return node.isEligible; + }; + + clientFilterToggles = { + state: [ + { + label: 'initializing', + qp: 'state_initializing', + default: true, + filter: (node) => node.status === 'initializing', + }, + { + label: 'ready', + qp: 'state_ready', + default: true, + filter: (node) => node.status === 'ready', + }, + { + label: 'down', + qp: 'state_down', + default: true, + filter: (node) => node.status === 'down', + }, + { + label: 'disconnected', + qp: 'state_disconnected', + default: true, + filter: (node) => node.status === 'disconnected', + }, + ], + eligibility: [ + { + label: 'eligible', + qp: 'eligibility_eligible', + default: true, + filter: (node) => node.isEligible, + }, + { + label: 'ineligible', + qp: 'eligibility_ineligible', + default: true, + filter: (node) => !node.isEligible, + }, + ], + drainStatus: [ + { + label: 'draining', + qp: 'drain_status_draining', + default: true, + filter: (node) => node.isDraining, + }, + { + label: 'not draining', + qp: 'drain_status_not_draining', + default: true, + filter: (node) => !node.isDraining, + }, + ], + }; + + @computed( + 'state_initializing', + 'state_ready', + 'state_down', + 'state_disconnected', + 'eligibility_eligible', + 'eligibility_ineligible', + 'drain_status_draining', + 'drain_status_not_draining', + 'allToggles.[]' + ) + get activeToggles() { + return this.allToggles.filter((t) => this[t.qp]); + } + + get allToggles() { + return Object.values(this.clientFilterToggles).reduce( + (acc, filters) => acc.concat(filters), + [] + ); + } + + // eslint-disable-next-line ember/classic-decorator-hooks + constructor() { + super(...arguments); + this.addDynamicQueryParams(); + } + + addDynamicQueryParams() { + this.clientFilterToggles.state.forEach((filter) => { + this.queryParams.push({ [filter.qp]: filter.qp }); + this.set(filter.qp, filter.default); + }); + this.clientFilterToggles.eligibility.forEach((filter) => { + this.queryParams.push({ [filter.qp]: filter.qp }); + this.set(filter.qp, filter.default); + }); + this.clientFilterToggles.drainStatus.forEach((filter) => { + this.queryParams.push({ [filter.qp]: filter.qp }); + this.set(filter.qp, filter.default); + }); + } + currentPage = 1; @readOnly('userSettings.pageSize') pageSize; @@ -74,14 +177,12 @@ export default class IndexController extends Controller.extend( } qpClass = ''; - qpState = ''; qpDatacenter = ''; qpVersion = ''; qpVolume = ''; qpNodePool = ''; @selection('qpClass') selectionClass; - @selection('qpState') selectionState; @selection('qpDatacenter') selectionDatacenter; @selection('qpVersion') selectionVersion; @selection('qpVolume') selectionVolume; @@ -105,18 +206,6 @@ export default class IndexController extends Controller.extend( return classes.sort().map((dc) => ({ key: dc, label: dc })); } - @computed - get optionsState() { - return [ - { key: 'initializing', label: 'Initializing' }, - { key: 'ready', label: 'Ready' }, - { key: 'down', label: 'Down' }, - { key: 'ineligible', label: 'Ineligible' }, - { key: 'draining', label: 'Draining' }, - { key: 'disconnected', label: 'Disconnected' }, - ]; - } - @computed('nodes.[]', 'selectionDatacenter') get optionsDatacenter() { const datacenters = Array.from( @@ -195,35 +284,50 @@ export default class IndexController extends Controller.extend( } @computed( + 'clientFilterToggles', + 'drain_status_draining', + 'drain_status_not_draining', + 'eligibility_eligible', + 'eligibility_ineligible', 'nodes.[]', 'selectionClass', - 'selectionState', 'selectionDatacenter', 'selectionNodePool', 'selectionVersion', - 'selectionVolume' + 'selectionVolume', + 'state_disconnected', + 'state_down', + 'state_initializing', + 'state_ready' ) get filteredNodes() { const { selectionClass: classes, - selectionState: states, selectionDatacenter: datacenters, selectionNodePool: nodePools, selectionVersion: versions, selectionVolume: volumes, } = this; - const onlyIneligible = states.includes('ineligible'); - const onlyDraining = states.includes('draining'); + let nodes = this.nodes; - // states is a composite of node status and other node states - const statuses = states.without('ineligible').without('draining'); + // new QP style filtering + for (let category in this.clientFilterToggles) { + nodes = nodes.filter((node) => { + let includeNode = false; + for (let filter of this.clientFilterToggles[category]) { + if (this[filter.qp] && filter.filter(node)) { + includeNode = true; + break; + } + } + return includeNode; + }); + } - return this.nodes.filter((node) => { + return nodes.filter((node) => { if (classes.length && !classes.includes(node.get('nodeClass'))) return false; - if (statuses.length && !statuses.includes(node.get('status'))) - return false; if (datacenters.length && !datacenters.includes(node.get('datacenter'))) return false; if (versions.length && !versions.includes(node.get('version'))) @@ -237,9 +341,6 @@ export default class IndexController extends Controller.extend( return false; } - if (onlyIneligible && node.get('isEligible')) return false; - if (onlyDraining && !node.get('isDraining')) return false; - return true; }); } @@ -254,6 +355,16 @@ export default class IndexController extends Controller.extend( this.set(queryParam, serialize(selection)); } + @action + handleFilterChange(queryParamValue, option, queryParamLabel) { + if (queryParamValue.includes(option)) { + queryParamValue.removeObject(option); + } else { + queryParamValue.addObject(option); + } + this.set(queryParamLabel, serialize(queryParamValue)); + } + @action gotoNode(node) { this.transitionToRoute('clients.client', node); diff --git a/ui/app/styles/core/table.scss b/ui/app/styles/core/table.scss index 1b70c3c0f..4af22275c 100644 --- a/ui/app/styles/core/table.scss +++ b/ui/app/styles/core/table.scss @@ -110,6 +110,12 @@ white-space: nowrap; } + &.node-status-badges { + .hds-badge__text { + white-space: nowrap; + } + } + &.is-narrow { padding: 1.25em 0 1.25em 0.5em; diff --git a/ui/app/templates/clients/index.hbs b/ui/app/templates/clients/index.hbs index ba61341d9..5394ff2af 100644 --- a/ui/app/templates/clients/index.hbs +++ b/ui/app/templates/clients/index.hbs @@ -18,52 +18,190 @@ /> {{/if}} -
+ {{#each this.optionsVolume key="label" as |option|}} +