From 68ca6cf59b1a3aaca9007c76f0425774e8930f7d Mon Sep 17 00:00:00 2001 From: Arnav Palnitkar Date: Fri, 17 Sep 2021 14:01:41 -0700 Subject: [PATCH 1/5] Added namespace search to client count - Used existing search select component for namespace search --- ui/app/components/clients/history.js | 60 ++++++++++++++++--- .../templates/components/clients/history.hbs | 34 ++++++++++- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/ui/app/components/clients/history.js b/ui/app/components/clients/history.js index cb4ba01fb32f..c52fd68e512b 100644 --- a/ui/app/components/clients/history.js +++ b/ui/app/components/clients/history.js @@ -1,8 +1,14 @@ import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; export default class HistoryComponent extends Component { max_namespaces = 10; + @tracked selectedNamespace = null; + + // Determine if we have client count data based on the current tab, + // since model is slightly different for current month vs history api get hasClientData() { if (this.args.tab === 'current') { return this.args.model.activity && this.args.model.activity.clients; @@ -10,15 +16,34 @@ export default class HistoryComponent extends Component { return this.args.model.activity && this.args.model.activity.total; } - get barChartDataset() { + // Show namespace graph only if we have more than 1 + get showGraphs() { if (!this.args.model.activity || !this.args.model.activity.byNamespace) { return null; } - let dataset = this.args.model.activity.byNamespace; - // Filter out root data - dataset = dataset.filter(item => { - return item.namespace_id !== 'root'; + return this.args.model.activity.byNamespace.length > 1; + } + + // Construct the namespace model for the search select component + get searchDataset() { + let dataList = this.cleanUpNamespaces(); + if (!dataList) { + return null; + } + return dataList.map(d => { + return { + name: d['namespace_id'], + id: d['namespace_path'] || 'root', + }; }); + } + + // Construct the namespace model for the car chart component + get barChartDataset() { + let dataset = this.cleanUpNamespaces(); + if (!dataset) { + return null; + } // Show only top 10 namespaces dataset = dataset.slice(0, this.max_namespaces); return dataset.map(d => { @@ -31,10 +56,31 @@ export default class HistoryComponent extends Component { }); } - get showGraphs() { + // Filter out root data + cleanUpNamespaces() { if (!this.args.model.activity || !this.args.model.activity.byNamespace) { return null; } - return this.args.model.activity.byNamespace.length > 1; + let namespaces = this.args.model.activity.byNamespace; + namespaces = namespaces.filter(item => { + return item.namespace_id !== 'root'; + }); + return namespaces; + } + + // Set the namespace from the search select picker + setNamespace(path) { + this.selectedNamespace = this.args.model.activity.byNamespace.find(ns => { + return ns.namespace_path === path; + }); + } + + @action + initNamespace(value) { + if (value && value.length) { + this.setNamespace(value[0]); + } else { + this.selectedNamespace = null; + } } } diff --git a/ui/app/templates/components/clients/history.hbs b/ui/app/templates/components/clients/history.hbs index 6936c11cd8b9..89083936e28f 100644 --- a/ui/app/templates/components/clients/history.hbs +++ b/ui/app/templates/components/clients/history.hbs @@ -141,7 +141,39 @@ }} /> -
+
+
+
+ + {{#if this.selectedNamespace}} +
+
+ +
+
+
+
+ +
+
+ +
+
+ {{else}} + + {{/if}} +
+
+
{{/if}} {{/unless}} From 0febf6cd72503ef9c80fa21490f8665086a9411c Mon Sep 17 00:00:00 2001 From: Arnav Palnitkar Date: Fri, 17 Sep 2021 14:10:38 -0700 Subject: [PATCH 2/5] Added changelog --- changelog/12577.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 changelog/12577.txt diff --git a/changelog/12577.txt b/changelog/12577.txt new file mode 100644 index 000000000000..7f69476931be --- /dev/null +++ b/changelog/12577.txt @@ -0,0 +1,3 @@ +```release-note:improvement +ui: namespace search in client count views +``` \ No newline at end of file From 9e8c229e6c15c7e92da7712a5f6d804db096ffed Mon Sep 17 00:00:00 2001 From: Arnav Palnitkar Date: Mon, 20 Sep 2021 15:38:56 -0700 Subject: [PATCH 3/5] Added download csv component - generate namespaces data in csv format - Show root in top 10 namespaces - Changed active direct tokens to non-entity tokens --- ui/app/components/clients/history.js | 66 ++++++++++++------- ui/app/styles/components/bar-chart.scss | 2 +- .../templates/components/clients/history.hbs | 18 +++-- ui/lib/core/addon/components/bar-chart.js | 2 +- ui/lib/core/addon/components/download-csv.js | 17 +++++ .../templates/components/download-csv.hbs | 3 + ui/lib/core/app/components/download-csv.js | 1 + 7 files changed, 75 insertions(+), 34 deletions(-) create mode 100644 ui/lib/core/addon/components/download-csv.js create mode 100644 ui/lib/core/addon/templates/components/download-csv.hbs create mode 100644 ui/lib/core/app/components/download-csv.js diff --git a/ui/app/components/clients/history.js b/ui/app/components/clients/history.js index c52fd68e512b..56e63507d578 100644 --- a/ui/app/components/clients/history.js +++ b/ui/app/components/clients/history.js @@ -18,37 +18,36 @@ export default class HistoryComponent extends Component { // Show namespace graph only if we have more than 1 get showGraphs() { - if (!this.args.model.activity || !this.args.model.activity.byNamespace) { - return null; - } - return this.args.model.activity.byNamespace.length > 1; + return ( + this.args.model.activity && + this.args.model.activity.byNamespace && + this.args.model.activity.byNamespace.length > 1 + ); } // Construct the namespace model for the search select component get searchDataset() { - let dataList = this.cleanUpNamespaces(); - if (!dataList) { + if (!this.args.model.activity || !this.args.model.activity.byNamespace) { return null; } + let dataList = this.args.model.activity.byNamespace; return dataList.map(d => { return { name: d['namespace_id'], - id: d['namespace_path'] || 'root', + id: d['namespace_path'] === '' ? 'root' : d['namespace_path'], }; }); } - // Construct the namespace model for the car chart component + // Construct the namespace model for the bar chart component get barChartDataset() { - let dataset = this.cleanUpNamespaces(); - if (!dataset) { + if (!this.args.model.activity || !this.args.model.activity.byNamespace) { return null; } - // Show only top 10 namespaces - dataset = dataset.slice(0, this.max_namespaces); + let dataset = this.args.model.activity.byNamespace.slice(0, this.max_namespaces); return dataset.map(d => { return { - label: d['namespace_path'], + label: d['namespace_path'] === '' ? 'root' : d['namespace_path'], non_entity_tokens: d['counts']['non_entity_tokens'], distinct_entities: d['counts']['distinct_entities'], total: d['counts']['clients'], @@ -56,29 +55,46 @@ export default class HistoryComponent extends Component { }); } - // Filter out root data - cleanUpNamespaces() { + // Create namespaces data for csv format + get getCsvData() { if (!this.args.model.activity || !this.args.model.activity.byNamespace) { return null; } - let namespaces = this.args.model.activity.byNamespace; - namespaces = namespaces.filter(item => { - return item.namespace_id !== 'root'; + let results = '', + namespaces = this.args.model.activity.byNamespace, + fields = ['Namespace path', 'Active clients', 'Unique entities', 'Non-entity tokens']; + + results = fields.join(',') + '\n'; + + namespaces.forEach(function(item) { + let path = item.namespace_path !== '' ? item.namespace_path : 'root', + total = item.counts.clients, + unique = item.counts.distinct_entities, + non_entity = item.counts.non_entity_tokens; + + results += path + ',' + total + ',' + unique + ',' + non_entity + '\n'; }); - return namespaces; + return results; } - // Set the namespace from the search select picker - setNamespace(path) { - this.selectedNamespace = this.args.model.activity.byNamespace.find(ns => { + // Get the namespace by matching the path from the namespace list + getNamespace(path) { + return this.args.model.activity.byNamespace.find(ns => { + if (path === 'root') { + return ns.namespace_path === ''; + } return ns.namespace_path === path; }); } @action - initNamespace(value) { - if (value && value.length) { - this.setNamespace(value[0]); + selectNamespace(value) { + // In case of search select component, value returned is an array + if (Array.isArray(value)) { + this.selectedNamespace = this.getNamespace(value[0]); + } else if (typeof value === 'object') { + // While D3 bar selection returns an object + this.selectedNamespace = this.getNamespace(value.label); } else { this.selectedNamespace = null; } diff --git a/ui/app/styles/components/bar-chart.scss b/ui/app/styles/components/bar-chart.scss index 44458d04c58c..1b9484ee23d1 100644 --- a/ui/app/styles/components/bar-chart.scss +++ b/ui/app/styles/components/bar-chart.scss @@ -31,7 +31,7 @@ } .header-right { - text-align: center; + text-align: right; > button { font-size: $size-8; diff --git a/ui/app/templates/components/clients/history.hbs b/ui/app/templates/components/clients/history.hbs index 89083936e28f..9e9526eae16a 100644 --- a/ui/app/templates/components/clients/history.hbs +++ b/ui/app/templates/components/clients/history.hbs @@ -101,10 +101,10 @@
@@ -119,7 +119,7 @@
+ > + +
@@ -148,9 +151,10 @@ @id="namespaces" @labelClass="title is-5" @disallowNewItems={{true}} - @onChange={{action "initNamespace"}} + @onChange={{action this.selectNamespace}} @label="Single namespace" @options={{or this.searchDataset []}} + @searchField="namespace_path" @selectLimit={{1}} /> {{#if this.selectedNamespace}} @@ -164,7 +168,7 @@
- +
{{else}} diff --git a/ui/lib/core/addon/components/bar-chart.js b/ui/lib/core/addon/components/bar-chart.js index d4da82844f5d..230adbeb2752 100644 --- a/ui/lib/core/addon/components/bar-chart.js +++ b/ui/lib/core/addon/components/bar-chart.js @@ -218,7 +218,7 @@ class BarChartComponent extends Component { .style('top', `${event.pageY - 155}px`) .text( `${Math.round((chartData.total * 100) / totalCount)}% of total client counts: - ${chartData.non_entity_tokens} active tokens, ${chartData.distinct_entities} unique entities. + ${chartData.non_entity_tokens} non-entity tokens, ${chartData.distinct_entities} unique entities. ` ); }); diff --git a/ui/lib/core/addon/components/download-csv.js b/ui/lib/core/addon/components/download-csv.js new file mode 100644 index 000000000000..f618b3ba553f --- /dev/null +++ b/ui/lib/core/addon/components/download-csv.js @@ -0,0 +1,17 @@ +import Component from '@glimmer/component'; +import layout from '../templates/components/download-csv'; +import { setComponentTemplate } from '@ember/component'; +import { action } from '@ember/object'; + +class DownloadCsvComponent extends Component { + @action + downloadCsv() { + let hiddenElement = document.createElement('a'); + hiddenElement.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURI(this.args.csvData)); + hiddenElement.setAttribute('target', '_blank'); + hiddenElement.setAttribute('download', this.args.fileName); + hiddenElement.click(); + } +} + +export default setComponentTemplate(layout, DownloadCsvComponent); diff --git a/ui/lib/core/addon/templates/components/download-csv.hbs b/ui/lib/core/addon/templates/components/download-csv.hbs new file mode 100644 index 000000000000..71344d834b02 --- /dev/null +++ b/ui/lib/core/addon/templates/components/download-csv.hbs @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/ui/lib/core/app/components/download-csv.js b/ui/lib/core/app/components/download-csv.js new file mode 100644 index 000000000000..23ea50f57649 --- /dev/null +++ b/ui/lib/core/app/components/download-csv.js @@ -0,0 +1 @@ +export { default } from 'core/components/download-csv'; From da2aea243e2e76114a0a7448c827ae8594f6fe4e Mon Sep 17 00:00:00 2001 From: Arnav Palnitkar Date: Wed, 22 Sep 2021 09:41:50 -0700 Subject: [PATCH 4/5] Added test for checking graph render --- .../templates/components/clients/history.hbs | 2 +- .../components/clients-history-test.js | 22 +++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/ui/app/templates/components/clients/history.hbs b/ui/app/templates/components/clients/history.hbs index 9e9526eae16a..055a663597d7 100644 --- a/ui/app/templates/components/clients/history.hbs +++ b/ui/app/templates/components/clients/history.hbs @@ -130,7 +130,7 @@
{{#if this.showGraphs}}
-
+
Date: Wed, 22 Sep 2021 10:18:17 -0700 Subject: [PATCH 5/5] Added documentation for the download csv component --- ui/app/templates/components/clients/history.hbs | 2 +- ui/lib/core/addon/components/download-csv.js | 16 +++++++++++++++- .../addon/templates/components/download-csv.hbs | 2 +- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ui/app/templates/components/clients/history.hbs b/ui/app/templates/components/clients/history.hbs index 055a663597d7..c22f23d7326a 100644 --- a/ui/app/templates/components/clients/history.hbs +++ b/ui/app/templates/components/clients/history.hbs @@ -141,7 +141,7 @@ (hash key='distinct_entities' label='Unique entities') }} > - +
diff --git a/ui/lib/core/addon/components/download-csv.js b/ui/lib/core/addon/components/download-csv.js index f618b3ba553f..76b58641f83f 100644 --- a/ui/lib/core/addon/components/download-csv.js +++ b/ui/lib/core/addon/components/download-csv.js @@ -3,13 +3,27 @@ import layout from '../templates/components/download-csv'; import { setComponentTemplate } from '@ember/component'; import { action } from '@ember/object'; +/** + * @module DownloadCsv + * Download csv component is used to display a link which initiates a csv file download of the data provided by it's parent component. + * + * @example + * ```js + * + * ``` + * + * @param {string} label - Label for the download link button + * @param {string} csvData - Data in csv format + * @param {string} fileName - Custom name for the downloaded file + * + */ class DownloadCsvComponent extends Component { @action downloadCsv() { let hiddenElement = document.createElement('a'); hiddenElement.setAttribute('href', 'data:text/csv;charset=utf-8,' + encodeURI(this.args.csvData)); hiddenElement.setAttribute('target', '_blank'); - hiddenElement.setAttribute('download', this.args.fileName); + hiddenElement.setAttribute('download', this.args.fileName || 'vault-data.csv'); hiddenElement.click(); } } diff --git a/ui/lib/core/addon/templates/components/download-csv.hbs b/ui/lib/core/addon/templates/components/download-csv.hbs index 71344d834b02..2e4809e4ce88 100644 --- a/ui/lib/core/addon/templates/components/download-csv.hbs +++ b/ui/lib/core/addon/templates/components/download-csv.hbs @@ -1,3 +1,3 @@ \ No newline at end of file