From b7753e39b5f6f0bbff6f103d277ad1dafbd89e0f Mon Sep 17 00:00:00 2001 From: Andreas Hochsteger Date: Fri, 18 Dec 2020 11:17:56 +0100 Subject: [PATCH 01/31] feat: Port nodes table filter to zwavejs2mqtt --- src/components/ControlPanel.vue | 90 +----- src/components/nodes-table/filter-options.vue | 145 +++++++++ src/components/nodes-table/index.vue | 141 +++++++++ src/components/nodes-table/nodes-table.css | 26 ++ src/components/nodes-table/nodes-table.js | 240 ++++++++++++++ src/modules/NodeCollection.js | 91 ++++++ src/modules/NodeCollection.test.js | 295 ++++++++++++++++++ 7 files changed, 950 insertions(+), 78 deletions(-) create mode 100644 src/components/nodes-table/filter-options.vue create mode 100644 src/components/nodes-table/index.vue create mode 100644 src/components/nodes-table/nodes-table.css create mode 100644 src/components/nodes-table/nodes-table.js create mode 100644 src/modules/NodeCollection.js create mode 100644 src/modules/NodeCollection.test.js diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue index a2be460e326..7ef53c5cbd7 100644 --- a/src/components/ControlPanel.vue +++ b/src/components/ControlPanel.vue @@ -68,54 +68,11 @@ - - - + Node @@ -666,6 +623,7 @@ import ValueID from '@/components/ValueId' import AnsiUp from 'ansi_up' import DialogSceneValue from '@/components/dialogs/DialogSceneValue' +import NodesTable from '@/components/nodes-table' import { socketEvents, inboundEvents as socketActions } from '@/plugins/socket' const ansiUp = new AnsiUp() @@ -679,7 +637,8 @@ export default { }, components: { ValueID, - DialogSceneValue + DialogSceneValue, + NodesTable }, computed: { scenesWithId () { @@ -691,9 +650,6 @@ export default { dialogTitle () { return this.editedIndex === -1 ? 'New Value' : 'Edit Value' }, - tableNodes () { - return this.showHidden ? this.nodes : this.nodes.filter(n => !n.failed) - }, hassDevices () { var devices = [] if (this.selectedNode && this.selectedNode.hassDevices) { @@ -723,9 +679,6 @@ export default { } }, watch: { - nodeTableItems (val) { - localStorage.setItem('nodes_itemsPerPage', val) - }, dialogValue (val) { val || this.closeDialog() }, @@ -778,7 +731,6 @@ export default { debugActive: false, selectedScene: null, cnt_status: 'Unknown', - nodeTableItems: 10, newScene: '', scene_values: [], dialogValue: false, @@ -879,20 +831,6 @@ export default { locError: null, newLoc: '', selectedNode: null, - headers: [ - { text: 'ID', value: 'id' }, - { text: 'Manufacturer', value: 'manufacturer' }, - { text: 'Product', value: 'productDescription' }, - { text: 'Product code', value: 'product' }, - { text: 'Name', value: 'name' }, - { text: 'Location', value: 'loc' }, - { text: 'Secure', value: 'isSecure' }, - { text: 'Beaming', value: 'isBeaming' }, - { text: 'Failed', value: 'failed' }, - { text: 'Status', value: 'status' }, - { text: 'Interview stage', value: 'interviewStage' }, - { text: 'Last Active', value: 'lastActive' } - ], rules: { required: value => { var valid = false @@ -916,13 +854,13 @@ export default { return match[0] !== name ? 'Only a-zA-Z0-9_- chars are allowed' : null }, - selectNode (item) { - if (!item) return + selectNode ({ node }) { + if (!node) return - if (this.selectedNode === item) { + if (this.selectedNode === node) { this.selectedNode = null } else { - this.selectedNode = this.nodes.find(n => n.id === item.id) + this.selectedNode = this.nodes.find(n => n.node_id === node.node_id) } }, getValue (v) { @@ -1412,10 +1350,6 @@ export default { mounted () { var self = this - const itemsPerPage = parseInt(localStorage.getItem('nodes_itemsPerPage')) - - this.nodeTableItems = !isNaN(itemsPerPage) ? itemsPerPage : 10 - this.socket.on(socketEvents.controller, data => { self.cnt_status = data }) diff --git a/src/components/nodes-table/filter-options.vue b/src/components/nodes-table/filter-options.vue new file mode 100644 index 00000000000..e8b6c046bc9 --- /dev/null +++ b/src/components/nodes-table/filter-options.vue @@ -0,0 +1,145 @@ + + + diff --git a/src/components/nodes-table/index.vue b/src/components/nodes-table/index.vue new file mode 100644 index 00000000000..47bac678904 --- /dev/null +++ b/src/components/nodes-table/index.vue @@ -0,0 +1,141 @@ + + + diff --git a/src/components/nodes-table/nodes-table.css b/src/components/nodes-table/nodes-table.css new file mode 100644 index 00000000000..7e7c2e85134 --- /dev/null +++ b/src/components/nodes-table/nodes-table.css @@ -0,0 +1,26 @@ +.td-large { + text-overflow: ellipsis; + white-space: nowrap; + overflow-x: hidden; + max-width: 20em; +} + +.td-medium { + text-overflow: ellipsis; + white-space: nowrap; + overflow-x: hidden; + max-width: 15em; +} + +.td-small { + text-overflow: ellipsis; + white-space: nowrap; + overflow-x: hidden; + max-width: 10em; +} + +.v-chip { + text-overflow: ellipsis; + white-space: nowrap; + overflow-x: hidden; +} diff --git a/src/components/nodes-table/nodes-table.js b/src/components/nodes-table/nodes-table.js new file mode 100644 index 00000000000..1420c88e21c --- /dev/null +++ b/src/components/nodes-table/nodes-table.js @@ -0,0 +1,240 @@ +import { NodeCollection } from '@/modules/NodeCollection' +import filterOptions from '@/components/nodes-table/filter-options.vue' + +export default { + props: { + nodes: Array, + showHidden: Boolean + }, + components: { + filterOptions + }, + data: () => ({ + nodeTableItems: 10, + selectedNode: undefined, + filters: {}, + headers: [ + { text: 'ID', value: 'id' }, + { text: 'Manufacturer', value: 'manufacturer' }, + { text: 'Product', value: 'productDescription' }, + { text: 'Product code', value: 'product' }, + { text: 'Name', value: 'name' }, + { text: 'Location', value: 'loc' }, + { text: 'Secure', value: 'isSecure' }, + { text: 'Beaming', value: 'isBeaming' }, + { text: 'Failed', value: 'failed' }, + { text: 'Status', value: 'status' }, + { text: 'Interview stage', value: 'interviewStage' }, + { text: 'Last Active', value: 'lastActive' } + ] + }), + methods: { + initFilters() { + return { + id: { type: 'number' }, + manufacturer: { type: 'string' }, + productDescription: { type: 'string' }, + product: { type: 'string' }, + name: { type: 'string' }, + loc: { type: 'string' }, + isSecure: { type: 'boolean' }, + isBeaming: { type: 'boolean' }, + failed: { type: 'boolean' }, + status: { type: 'string' }, + interviewStage: { type: 'string' }, + lastActive: { type: 'date' } + } + }, + resetFilter() { + this.filters = this.initFilters() + }, + nodeSelected(node) { + this.selectedNode = node + this.$emit('node-selected', { node }) + }, + productName(node) { + const manufacturer = node.manufacturer ? ` (${node.manufacturer})` : '' + return node.ready ? `${node.product}${manufacturer}` : '' + } + }, + mounted() { + this.filters = this.initFilters() + const itemsPerPage = parseInt(localStorage.getItem('nodes_itemsPerPage')) + this.nodeTableItems = !isNaN(itemsPerPage) ? itemsPerPage : 10 + }, + watch: { + nodeTableItems(val) { + localStorage.setItem('nodes_itemsPerPage', val) + } + }, + computed: { + nodeCollection() { + return new NodeCollection(this.nodes) + }, + relevantNodes() { + return this.nodeCollection.filter('failed', failed => { + return this.showHidden ? true : !failed + }) + }, + filteredNodes() { + return this.relevantNodes + .betweenNumber( + 'id', + this.filters.id ? this.filters.id.min : null, + this.filters.id ? this.filters.id.max : null + ) + .equalsAny( + 'id', + this.filters.id + ? this.filters.id.selections + ? this.filters.id.selections + : [] + : [] + ) + .contains( + ['manufacturer'], + this.filters.manufacturer ? this.filters.manufacturer.search : '' + ) + .equalsAny( + 'manufacturer', + this.filters.manufacturer + ? this.filters.manufacturer.selections + ? this.filters.manufacturer.selections + : [] + : [] + ) + .contains( + ['productDescription'], + this.filters.productDescription ? this.filters.productDescription.search : '' + ) + .equalsAny( + 'productDescription', + this.filters.productDescription + ? this.filters.productDescription.selections + ? this.filters.productDescription.selections + : [] + : [] + ) + .contains( + ['productLabel'], + this.filters.product ? this.filters.product.search : '' + ) + .equalsAny( + 'productLabel', + this.filters.product + ? this.filters.product.selections + ? this.filters.product.selections + : [] + : [] + ) + .contains( + ['name'], + this.filters.name ? this.filters.name.search : '' + ) + .equalsAny( + 'name', + this.filters.name + ? this.filters.name.selections + ? this.filters.name.selections + : [] + : [] + ) + .contains( + ['loc'], + this.filters.loc ? this.filters.loc.search : '' + ) + .equalsAny( + 'loc', + this.filters.loc + ? this.filters.loc.selections + ? this.filters.loc.selections + : [] + : [] + ) + .equals( + 'isSecure', + this.filters.isSecure ? this.filters.isSecure.bool : null + ) + .equalsAny( + 'isSecure', + this.filters.isSecure + ? this.filters.isSecure.selections + ? this.filters.isSecure.selections + : [] + : [] + ) + .equals( + 'isBeaming', + this.filters.isBeaming ? this.filters.isBeaming.bool : null + ) + .equalsAny( + 'isBeaming', + this.filters.isBeaming + ? this.filters.isBeaming.selections + ? this.filters.isBeaming.selections + : [] + : [] + ) + .equals( + 'failed', + this.filters.failed ? this.filters.failed.bool : null + ) + .equalsAny( + 'failed', + this.filters.failed + ? this.filters.failed.selections + ? this.filters.failed.selections + : [] + : [] + ) + .contains( + ['status'], + this.filters.status ? this.filters.status.search : '' + ) + .equalsAny( + 'status', + this.filters.status + ? this.filters.status.selections + ? this.filters.status.selections + : [] + : [] + ) + .contains( + ['interviewStage'], + this.filters.interviewStage ? this.filters.interviewStage.search : '' + ) + .equalsAny( + 'interviewStage', + this.filters.interviewStage + ? this.filters.interviewStage.selections + ? this.filters.interviewStage.selections + : [] + : [] + ) + .betweenDate( + 'lastActive', + this.filters.lastActive ? this.filters.lastActive.min : null, + this.filters.lastActive ? this.filters.lastActive.max : null + ) + }, + values() { + return { + 'id': this.relevantNodes.values('id'), + 'manufacturer': this.relevantNodes.values('manufacturer'), + 'productDescription': this.relevantNodes.values('productDescription'), + 'product': this.relevantNodes.values('productLabel'), + 'name': this.relevantNodes.values('name'), + 'loc': this.relevantNodes.values('loc'), + 'isSecure': this.relevantNodes.values('isSecure'), + 'isBeaming': this.relevantNodes.values('isBeaming'), + 'failed': this.relevantNodes.values('failed'), + 'status': this.relevantNodes.values('status'), + 'interviewStage': this.relevantNodes.values('interviewStage'), + 'lastActive': this.relevantNodes.values('lastActive') + } + }, + tableNodes() { + return this.filteredNodes.nodes + } + } +} diff --git a/src/modules/NodeCollection.js b/src/modules/NodeCollection.js new file mode 100644 index 00000000000..3f11fd8dcf0 --- /dev/null +++ b/src/modules/NodeCollection.js @@ -0,0 +1,91 @@ +export class NodeCollection { + constructor(nodes) { + this.nodes = nodes + } + + _isUndefined(value) { + return value === undefined || value === null || value === '' + } + + _strValue(str, caseSensitive) { + return caseSensitive ? `${str}` : `${str}`.toLowerCase() + } + + _createStringFilter(filterValue, caseSensitive) { + if (this._isUndefined(filterValue)) { + filterValue = '' + } + const strFilter = this._strValue(filterValue, caseSensitive) + return value => this._strValue(value, caseSensitive).indexOf(strFilter) >= 0 + } + + _filterByProps(node, properties, filter) { + const mergedProps = [properties].reduce( + (merged, prop) => merged.concat(prop), + [] + ) + return mergedProps.find(prop => filter(node[prop])) + } + + filter(properties, filter) { + const filtered = this.nodes.filter(node => + this._filterByProps(node, properties, filter) + ) + return new NodeCollection(filtered) + } + + contains(properties, value, caseSensitive = false) { + return this.filter( + properties, + this._createStringFilter(value, caseSensitive) + ) + } + + equals(properties, value) { + return this.filter( + properties, + nodeValue => this._isUndefined(value) || value === nodeValue + ) + } + + betweenNumber(properties, minValue, maxValue) { + return this.filter( + properties, + nodeValue => + (this._isUndefined(minValue) || minValue <= nodeValue) && + (this._isUndefined(maxValue) || maxValue >= nodeValue) + ) + } + + betweenDate(properties, minValue, maxValue) { + return this.filter(properties, nodeValue => { + const nodeValueTime = new Date(nodeValue).getTime() + return ( + (this._isUndefined(minValue) || + new Date(minValue).getTime() <= nodeValueTime) && + (this._isUndefined(maxValue) || + new Date(maxValue).getTime() >= nodeValueTime) + ) + }) + } + + equalsAny(properties, values) { + return this.filter( + properties, + nodeValue => values.length === 0 || values.indexOf(nodeValue) >= 0 + ) + } + + values(property) { + const uniqueMap = {} + this.nodes.forEach(node => { + const strVal = this._strValue(node[property]) + uniqueMap[strVal] = uniqueMap[strVal] || node[property] + }) + return Object.keys(uniqueMap) + .sort() + .map(key => uniqueMap[key]) + } +} + +export default NodeCollection diff --git a/src/modules/NodeCollection.test.js b/src/modules/NodeCollection.test.js new file mode 100644 index 00000000000..3856836a68f --- /dev/null +++ b/src/modules/NodeCollection.test.js @@ -0,0 +1,295 @@ +import chai from 'chai' +import { NodeCollection } from './NodeCollection' + +describe('NodeCollection', () => { + describe('#constructor', () => { + it('uses the nodes passed in as the collection nodes', () => { + const collection = new NodeCollection([{ id: 1 }]) + chai.expect(collection.nodes).to.eql([{ id: 1 }]) + }) + }) + describe('#filter', () => { + const isOdd = num => num % 2 + it('returns nodes with the property matching the filter', () => { + const collection = new NodeCollection([ + { id: 1 }, + { id: 2 }, + { id: 3 }, + { id: 4 } + ]) + const filtered = collection.filter('id', isOdd) + chai.expect(filtered.nodes).to.eql([{ id: 1 }, { id: 3 }]) + }) + it('returns nodes with any of the properties matching the filter', () => { + const collection = new NodeCollection([ + { id: 1, value: 2 }, + { id: 2, value: 1 }, + { id: 3, value: 2 }, + { id: 4, value: 2 } + ]) + const filtered = collection.filter(['id', 'value'], isOdd) + chai.expect(filtered.nodes).to.eql([ + { id: 1, value: 2 }, + { id: 2, value: 1 }, + { id: 3, value: 2 } + ]) + }) + }) + describe('#contains', () => { + const stringCollection = new NodeCollection([ + { id: 'pippo' }, + { id: 'paRanza' }, + { id: 'PipPo' }, + { id: 'Ames' } + ]) + + it('returns nodes with the properties containing the value', () => { + const collection = new NodeCollection([ + { id: 100 }, + { id: '210' }, + { id: 20 }, + { id: '300' } + ]) + const filtered = collection.contains('id', '10') + chai.expect(filtered.nodes).to.eql([{ id: 100 }, { id: '210' }]) + }) + it('matches values over multiple properties', () => { + const collection = new NodeCollection([ + { id: 100, name: 'sample' }, + { id: '210', name: 'trinity' }, + { id: 20, name: '10 packs' }, + { id: '300', name: 'fazuoli' } + ]) + const filtered = collection.contains(['id', 'name'], '10') + chai.expect(filtered.nodes).to.eql([ + { id: 100, name: 'sample' }, + { id: '210', name: 'trinity' }, + { id: 20, name: '10 packs' } + ]) + }) + it('is case insensitive by default', () => { + const filtered = stringCollection.contains('id', 'piPPo') + chai.expect(filtered.nodes).to.eql([{ id: 'pippo' }, { id: 'PipPo' }]) + }) + it('accepts a case sensitive flag', () => { + const filtered = stringCollection.contains('id', 'PipPo', true) + chai.expect(filtered.nodes).to.eql([{ id: 'PipPo' }]) + }) + }) + describe('#equals', () => { + it('returns nodes with the properties with equal value', () => { + const collection = new NodeCollection([ + { id: 10 }, + { id: '10' }, + { id: 20 }, + { id: '20' } + ]) + const filtered = collection.equals('id', 10) + chai.expect(filtered.nodes).to.eql([{ id: 10 }]) + }) + it('works over multiple properties', () => { + const collection = new NodeCollection([ + { id: 10, sample: '20' }, + { id: '10', sample: 30 }, + { id: 20, sample: '10' }, + { id: '20', sample: 10 } + ]) + const filtered = collection.equals(['id', 'sample'], 10) + chai.expect(filtered.nodes).to.eql([ + { id: 10, sample: '20' }, + { id: '20', sample: 10 } + ]) + }) + }) + describe('#equalsAny', () => { + it('returns all nodes when values has no elements', () => { + const collection = new NodeCollection([ + { id: 10 }, + { id: '10' }, + { id: 20 }, + { id: '20' } + ]) + const filtered = collection.equalsAny('id', []) + chai + .expect(filtered.nodes) + .to.eql([{ id: 10 }, { id: '10' }, { id: 20 }, { id: '20' }]) + }) + it('returns nodes with the properties equal to any of the values', () => { + const collection = new NodeCollection([ + { id: 10 }, + { id: '10' }, + { id: 20 }, + { id: '20' } + ]) + const filtered = collection.equalsAny('id', [10, '20']) + chai.expect(filtered.nodes).to.eql([{ id: 10 }, { id: '20' }]) + }) + it('works over multiple properties', () => { + const collection = new NodeCollection([ + { id: 10, sample: 20 }, + { id: '10', sample: '20' }, + { id: 20, sample: '10' }, + { id: '20', sample: 'zdub' } + ]) + const filtered = collection.equalsAny(['id', 'sample'], [10, '20']) + chai.expect(filtered.nodes).to.eql([ + { id: 10, sample: 20 }, + { id: '10', sample: '20' }, + { id: '20', sample: 'zdub' } + ]) + }) + }) + describe('#betweenNumber', () => { + it('returns all values if min/max are undefined', () => { + const collection = new NodeCollection([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + const filtered = collection.betweenNumber('id', undefined, undefined) + chai.expect(filtered.nodes).to.eql([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + }) + it('returns all values if min/max are null', () => { + const collection = new NodeCollection([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + const filtered = collection.betweenNumber('id', null, null) + chai.expect(filtered.nodes).to.eql([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + }) + it('returns all values that are greater or equal a min value', () => { + const collection = new NodeCollection([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + const filtered = collection.betweenNumber('id', 20, null) + chai.expect(filtered.nodes).to.eql([ + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + }) + it('returns all values that are less than or equal a max value', () => { + const collection = new NodeCollection([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + const filtered = collection.betweenNumber('id', null, 20) + chai.expect(filtered.nodes).to.eql([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 } + ]) + }) + it('returns all values that between or equal a min and a max value', () => { + const collection = new NodeCollection([ + { id: 10, sample: 10 }, + { id: 20, sample: 20 }, + { id: 30, sample: 30 } + ]) + const filtered = collection.betweenNumber('id', 15, 25) + chai.expect(filtered.nodes).to.eql([{ id: 20, sample: 20 }]) + }) + }) + describe('#betweenDate', () => { + it('returns all date values if min/max are undefined', () => { + const collection = new NodeCollection([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + const filtered = collection.betweenDate( + 'lastActive', + undefined, + undefined + ) + chai.expect(filtered.nodes).to.eql([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + }) + it('returns all date values if min/max are null', () => { + const collection = new NodeCollection([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + const filtered = collection.betweenDate('lastActive', null, null) + chai.expect(filtered.nodes).to.eql([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + }) + it('returns all date values that are greater or equal a min date value', () => { + const collection = new NodeCollection([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + const filtered = collection.betweenDate( + 'lastActive', + new Date(2020, 11, 10, 0, 0), + null + ) + chai.expect(filtered.nodes).to.eql([ + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + }) + it('returns all date values that are less than or equal a max date value', () => { + const collection = new NodeCollection([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + const filtered = collection.betweenDate( + 'lastActive', + null, + new Date(2020, 11, 10, 0, 0) + ) + chai.expect(filtered.nodes).to.eql([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) } + ]) + }) + it('returns all date values that between or equal a min and a max date value', () => { + const collection = new NodeCollection([ + { id: 10, lastActive: new Date(2020, 11, 9, 0, 0) }, + { id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }, + { id: 30, lastActive: new Date(2020, 11, 11, 0, 0) } + ]) + const filtered = collection.betweenDate( + 'lastActive', + new Date(2020, 11, 9, 12, 0), + new Date(2020, 11, 10, 12, 0) + ) + chai + .expect(filtered.nodes) + .to.eql([{ id: 20, lastActive: new Date(2020, 11, 10, 0, 0) }]) + }) + }) + describe('#values', () => { + it('returns a sorted list of unique values for a property - case ignored', () => { + const collection = new NodeCollection([ + { name: 'Giacomo' }, + { name: 'GiaCOMO' }, + { name: 'Birretta' }, + { name: 10 }, + { name: 'giacomo' }, + { name: 10 } + ]) + chai.expect(collection.values('name')).to.eql([10, 'Birretta', 'Giacomo']) + }) + }) +}) From c9821ad5efdeeb7438b7e2eac34193df9d638906 Mon Sep 17 00:00:00 2001 From: Andreas Hochsteger Date: Fri, 18 Dec 2020 11:19:34 +0100 Subject: [PATCH 02/31] fix: Lint errors --- src/components/nodes-table/nodes-table.js | 46 +++++++++++------------ src/modules/NodeCollection.js | 24 ++++++------ 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/src/components/nodes-table/nodes-table.js b/src/components/nodes-table/nodes-table.js index 1420c88e21c..207b342766e 100644 --- a/src/components/nodes-table/nodes-table.js +++ b/src/components/nodes-table/nodes-table.js @@ -29,7 +29,7 @@ export default { ] }), methods: { - initFilters() { + initFilters () { return { id: { type: 'number' }, manufacturer: { type: 'string' }, @@ -45,38 +45,38 @@ export default { lastActive: { type: 'date' } } }, - resetFilter() { + resetFilter () { this.filters = this.initFilters() }, - nodeSelected(node) { + nodeSelected (node) { this.selectedNode = node this.$emit('node-selected', { node }) }, - productName(node) { + productName (node) { const manufacturer = node.manufacturer ? ` (${node.manufacturer})` : '' return node.ready ? `${node.product}${manufacturer}` : '' } }, - mounted() { + mounted () { this.filters = this.initFilters() const itemsPerPage = parseInt(localStorage.getItem('nodes_itemsPerPage')) this.nodeTableItems = !isNaN(itemsPerPage) ? itemsPerPage : 10 }, watch: { - nodeTableItems(val) { + nodeTableItems (val) { localStorage.setItem('nodes_itemsPerPage', val) } }, computed: { - nodeCollection() { + nodeCollection () { return new NodeCollection(this.nodes) }, - relevantNodes() { + relevantNodes () { return this.nodeCollection.filter('failed', failed => { return this.showHidden ? true : !failed }) }, - filteredNodes() { + filteredNodes () { return this.relevantNodes .betweenNumber( 'id', @@ -217,23 +217,23 @@ export default { this.filters.lastActive ? this.filters.lastActive.max : null ) }, - values() { + values () { return { - 'id': this.relevantNodes.values('id'), - 'manufacturer': this.relevantNodes.values('manufacturer'), - 'productDescription': this.relevantNodes.values('productDescription'), - 'product': this.relevantNodes.values('productLabel'), - 'name': this.relevantNodes.values('name'), - 'loc': this.relevantNodes.values('loc'), - 'isSecure': this.relevantNodes.values('isSecure'), - 'isBeaming': this.relevantNodes.values('isBeaming'), - 'failed': this.relevantNodes.values('failed'), - 'status': this.relevantNodes.values('status'), - 'interviewStage': this.relevantNodes.values('interviewStage'), - 'lastActive': this.relevantNodes.values('lastActive') + id: this.relevantNodes.values('id'), + manufacturer: this.relevantNodes.values('manufacturer'), + productDescription: this.relevantNodes.values('productDescription'), + product: this.relevantNodes.values('productLabel'), + name: this.relevantNodes.values('name'), + loc: this.relevantNodes.values('loc'), + isSecure: this.relevantNodes.values('isSecure'), + isBeaming: this.relevantNodes.values('isBeaming'), + failed: this.relevantNodes.values('failed'), + status: this.relevantNodes.values('status'), + interviewStage: this.relevantNodes.values('interviewStage'), + lastActive: this.relevantNodes.values('lastActive') } }, - tableNodes() { + tableNodes () { return this.filteredNodes.nodes } } diff --git a/src/modules/NodeCollection.js b/src/modules/NodeCollection.js index 3f11fd8dcf0..ddc9d41784b 100644 --- a/src/modules/NodeCollection.js +++ b/src/modules/NodeCollection.js @@ -1,17 +1,17 @@ export class NodeCollection { - constructor(nodes) { + constructor (nodes) { this.nodes = nodes } - _isUndefined(value) { + _isUndefined (value) { return value === undefined || value === null || value === '' } - _strValue(str, caseSensitive) { + _strValue (str, caseSensitive) { return caseSensitive ? `${str}` : `${str}`.toLowerCase() } - _createStringFilter(filterValue, caseSensitive) { + _createStringFilter (filterValue, caseSensitive) { if (this._isUndefined(filterValue)) { filterValue = '' } @@ -19,7 +19,7 @@ export class NodeCollection { return value => this._strValue(value, caseSensitive).indexOf(strFilter) >= 0 } - _filterByProps(node, properties, filter) { + _filterByProps (node, properties, filter) { const mergedProps = [properties].reduce( (merged, prop) => merged.concat(prop), [] @@ -27,28 +27,28 @@ export class NodeCollection { return mergedProps.find(prop => filter(node[prop])) } - filter(properties, filter) { + filter (properties, filter) { const filtered = this.nodes.filter(node => this._filterByProps(node, properties, filter) ) return new NodeCollection(filtered) } - contains(properties, value, caseSensitive = false) { + contains (properties, value, caseSensitive = false) { return this.filter( properties, this._createStringFilter(value, caseSensitive) ) } - equals(properties, value) { + equals (properties, value) { return this.filter( properties, nodeValue => this._isUndefined(value) || value === nodeValue ) } - betweenNumber(properties, minValue, maxValue) { + betweenNumber (properties, minValue, maxValue) { return this.filter( properties, nodeValue => @@ -57,7 +57,7 @@ export class NodeCollection { ) } - betweenDate(properties, minValue, maxValue) { + betweenDate (properties, minValue, maxValue) { return this.filter(properties, nodeValue => { const nodeValueTime = new Date(nodeValue).getTime() return ( @@ -69,14 +69,14 @@ export class NodeCollection { }) } - equalsAny(properties, values) { + equalsAny (properties, values) { return this.filter( properties, nodeValue => values.length === 0 || values.indexOf(nodeValue) >= 0 ) } - values(property) { + values (property) { const uniqueMap = {} this.nodes.forEach(node => { const strVal = this._strValue(node[property]) From 5a86567f3c2a29818be36aa51a5b43e75fca55f3 Mon Sep 17 00:00:00 2001 From: Andreas Hochsteger Date: Fri, 18 Dec 2020 11:34:21 +0100 Subject: [PATCH 03/31] fix: Lint errors --- src/components/nodes-table/index.vue | 9 ++------- src/components/nodes-table/nodes-table.js | 19 ++++++------------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/components/nodes-table/index.vue b/src/components/nodes-table/index.vue index 47bac678904..29d50d236bc 100644 --- a/src/components/nodes-table/index.vue +++ b/src/components/nodes-table/index.vue @@ -15,10 +15,7 @@ > - - - - - - - - - - - - -