09067b4eb7
There are two changes here, and some caveats/commentary: 1. The “State“ table column was actually sorting only by status. The state was not an actual property, just something calculated in each client row, as a product of status, isEligible, and isDraining. This PR adds isDraining as a component of compositeState so it can be used for sorting. 2. The Sortable mixin declares dependent keys that cause the sort to be live-updating, but only if the members of the array change, such as if a new client is added, but not if any of the sortable properties change. This PR adds a SortableFactory function that generates a mixin whose listSorted computed property includes dependent keys for the sortable properties, so the table will live-update if any of the sortable properties change, not just the array members. There’s a warning if you use SortableFactory without dependent keys and via the original Sortable interface, so we can eventually migrate away from it.
123 lines
3.8 KiB
JavaScript
123 lines
3.8 KiB
JavaScript
import { alias } from '@ember/object/computed';
|
|
import Controller, { inject as controller } from '@ember/controller';
|
|
import { computed } from '@ember/object';
|
|
import { scheduleOnce } from '@ember/runloop';
|
|
import intersection from 'lodash.intersection';
|
|
import SortableFactory from 'nomad-ui/mixins/sortable-factory';
|
|
import Searchable from 'nomad-ui/mixins/searchable';
|
|
import { serialize, deserializedQueryParam as selection } from 'nomad-ui/utils/qp-serialize';
|
|
|
|
export default Controller.extend(
|
|
SortableFactory(['id', 'name', 'compositeStatus', 'datacenter']),
|
|
Searchable,
|
|
{
|
|
clientsController: controller('clients'),
|
|
|
|
nodes: alias('model.nodes'),
|
|
agents: alias('model.agents'),
|
|
|
|
queryParams: {
|
|
currentPage: 'page',
|
|
searchTerm: 'search',
|
|
sortProperty: 'sort',
|
|
sortDescending: 'desc',
|
|
qpClass: 'class',
|
|
qpState: 'state',
|
|
qpDatacenter: 'dc',
|
|
},
|
|
|
|
currentPage: 1,
|
|
pageSize: 8,
|
|
|
|
sortProperty: 'modifyIndex',
|
|
sortDescending: true,
|
|
|
|
searchProps: computed(() => ['id', 'name', 'datacenter']),
|
|
|
|
qpClass: '',
|
|
qpState: '',
|
|
qpDatacenter: '',
|
|
|
|
selectionClass: selection('qpClass'),
|
|
selectionState: selection('qpState'),
|
|
selectionDatacenter: selection('qpDatacenter'),
|
|
|
|
optionsClass: computed('nodes.[]', function() {
|
|
const classes = Array.from(new Set(this.nodes.mapBy('nodeClass'))).compact();
|
|
|
|
// Remove any invalid node classes from the query param/selection
|
|
scheduleOnce('actions', () => {
|
|
this.set('qpClass', serialize(intersection(classes, this.selectionClass)));
|
|
});
|
|
|
|
return classes.sort().map(dc => ({ key: dc, label: dc }));
|
|
}),
|
|
|
|
optionsState: computed(() => [
|
|
{ key: 'initializing', label: 'Initializing' },
|
|
{ key: 'ready', label: 'Ready' },
|
|
{ key: 'down', label: 'Down' },
|
|
{ key: 'ineligible', label: 'Ineligible' },
|
|
{ key: 'draining', label: 'Draining' },
|
|
]),
|
|
|
|
optionsDatacenter: computed('nodes.[]', function() {
|
|
const datacenters = Array.from(new Set(this.nodes.mapBy('datacenter'))).compact();
|
|
|
|
// Remove any invalid datacenters from the query param/selection
|
|
scheduleOnce('actions', () => {
|
|
this.set('qpDatacenter', serialize(intersection(datacenters, this.selectionDatacenter)));
|
|
});
|
|
|
|
return datacenters.sort().map(dc => ({ key: dc, label: dc }));
|
|
}),
|
|
|
|
filteredNodes: computed(
|
|
'nodes.[]',
|
|
'selectionClass',
|
|
'selectionState',
|
|
'selectionDatacenter',
|
|
function() {
|
|
const {
|
|
selectionClass: classes,
|
|
selectionState: states,
|
|
selectionDatacenter: datacenters,
|
|
} = this;
|
|
|
|
const onlyIneligible = states.includes('ineligible');
|
|
const onlyDraining = states.includes('draining');
|
|
|
|
// states is a composite of node status and other node states
|
|
const statuses = states.without('ineligible').without('draining');
|
|
|
|
return this.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 (onlyIneligible && node.get('isEligible')) return false;
|
|
if (onlyDraining && !node.get('isDraining')) return false;
|
|
|
|
return true;
|
|
});
|
|
}
|
|
),
|
|
|
|
listToSort: alias('filteredNodes'),
|
|
listToSearch: alias('listSorted'),
|
|
sortedNodes: alias('listSearched'),
|
|
|
|
isForbidden: alias('clientsController.isForbidden'),
|
|
|
|
setFacetQueryParam(queryParam, selection) {
|
|
this.set(queryParam, serialize(selection));
|
|
},
|
|
|
|
actions: {
|
|
gotoNode(node) {
|
|
this.transitionToRoute('clients.client', node);
|
|
},
|
|
},
|
|
}
|
|
);
|