diff --git a/ui/app/styles/components/stat-text.scss b/ui/app/styles/components/stat-text.scss index efd76fa621..129771435f 100644 --- a/ui/app/styles/components/stat-text.scss +++ b/ui/app/styles/components/stat-text.scss @@ -9,6 +9,7 @@ .stat-label { font-size: $size-5; font-weight: $font-weight-semibold; + margin-bottom: $spacing-xxs; line-height: inherit; } .stat-text { diff --git a/ui/app/templates/components/clients/history.hbs b/ui/app/templates/components/clients/history.hbs index 9d9e0a9039..8963e9f619 100644 --- a/ui/app/templates/components/clients/history.hbs +++ b/ui/app/templates/components/clients/history.hbs @@ -138,13 +138,14 @@ @title="Top 10 Namespaces" @description="Each namespace's client count includes clients in child namespaces." @dataset={{this.barChartDataset}} + @tooltipData={{or @model.activity.clients @model.activity.total.clients}} @onClick={{action this.selectNamespace}} @mapLegend={{array (hash key='non_entity_tokens' label='Non-entity tokens') (hash key='distinct_entities' label='Unique entities') }} > - +
@@ -186,4 +187,4 @@ {{/unless}} {{/if}}
-{{/if}} \ No newline at end of file +{{/if}} diff --git a/ui/app/templates/components/pricing-metrics-dates.hbs b/ui/app/templates/components/pricing-metrics-dates.hbs index 9b565d5fee..826405fbdc 100644 --- a/ui/app/templates/components/pricing-metrics-dates.hbs +++ b/ui/app/templates/components/pricing-metrics-dates.hbs @@ -38,7 +38,7 @@
{{#if (and resultStart resultEnd)}}

- {{date-format resultStart "MMM dd, yyyy"}} through {{date-format resultEnd "MMM dd, yyyy"}} + {{date-format resultStart "MMM dd, yyyy" dateOnly=true}} through {{date-format resultEnd "MMM dd, yyyy" dateOnly=true}}

{{/if}}
diff --git a/ui/lib/core/addon/components/bar-chart.js b/ui/lib/core/addon/components/bar-chart.js index f875fcd769..bcc49e074f 100644 --- a/ui/lib/core/addon/components/bar-chart.js +++ b/ui/lib/core/addon/components/bar-chart.js @@ -19,6 +19,7 @@ * @param {string} title - title of the chart * @param {array} mapLegend - array of objects with key names 'key' and 'label' for the map legend * @param {object} dataset - dataset for the chart + * @param {array} tooltipData - misc. information needed to display tooltip (i.e. total clients from query params) * @param {string} [description] - description of the chart * @param {string} [labelKey=label] - labelKey is the key name in the dataset passed in that corresponds to the value labeling the y-axis * @param {function} [onClick] - takes function from parent and passes it to click event on data bars @@ -74,10 +75,10 @@ class BarChartComponent extends Component { } @action - renderBarChart(element, data) { + renderBarChart(element, args) { let elementId = guidFor(element); - let [dataset] = data; - let totalCount = dataset.reduce((prevValue, currValue) => prevValue + currValue.total, 0); + let dataset = args[0]; + let totalCount = args[1]; let handleClick = this.args.onClick; let labelKey = this.labelKey; let stackFunction = stack().keys(this.mapLegend.map(l => l.key)); @@ -140,7 +141,7 @@ class BarChartComponent extends Component { .append('rect') .attr('class', 'data-bar') .style('cursor', 'pointer') - .attr('width', chartData => `${xScale(chartData[1] - chartData[0] - 5)}%`) + .attr('width', chartData => `${xScale(chartData[1] - chartData[0]) - 0.25}%`) .attr('height', yScale.bandwidth()) .attr('x', chartData => `${xScale(chartData[0])}%`) .attr('y', ({ data }) => yScale(data[labelKey])) @@ -213,8 +214,8 @@ class BarChartComponent extends Component { select('.chart-tooltip') .style('opacity', 1) .style('max-width', '200px') - .style('left', `${event.pageX - 400}px`) - .style('top', `${event.pageY - 150}px`) + .style('left', `${event.pageX - 325}px`) + .style('top', `${event.pageY - 140}px`) .text( `${Math.round((chartData.total * 100) / totalCount)}% of total client counts: ${chartData.non_entity_tokens} non-entity tokens, ${chartData.distinct_entities} unique entities. @@ -263,7 +264,7 @@ class BarChartComponent extends Component { .on('mousemove', function(chartData) { if (chartData.label.length >= CHAR_LIMIT) { select('.chart-tooltip') - .style('left', `${event.pageX - 400}px`) + .style('left', `${event.pageX - 300}px`) .style('top', `${event.pageY - 100}px`) .text(`${chartData.label}`) .style('max-width', 'fit-content'); diff --git a/ui/lib/core/addon/helpers/date-format.js b/ui/lib/core/addon/helpers/date-format.js index 9f50d0c8e1..f2f8f9ec4b 100644 --- a/ui/lib/core/addon/helpers/date-format.js +++ b/ui/lib/core/addon/helpers/date-format.js @@ -1,11 +1,16 @@ import { helper } from '@ember/component/helper'; import { format, parseISO } from 'date-fns'; -export function dateFormat([date, style], { isFormatted = false }) { +export function dateFormat([date, style], { isFormatted = false, dateOnly = false }) { // see format breaking in upgrade to date-fns 2.x https://github.com/date-fns/date-fns/blob/master/CHANGELOG.md#changed-5 if (isFormatted) { return format(new Date(date), style); } + // when date is in '2021-09-01T00:00:00Z' format + // remove hours so date displays unaffected by timezone + if (dateOnly && typeof date === 'string') { + date = date.split('T')[0]; + } let number = typeof date === 'string' ? parseISO(date) : date; if (!number) { return; diff --git a/ui/lib/core/addon/helpers/format-number.js b/ui/lib/core/addon/helpers/format-number.js index a782e37de3..1d863c5c27 100644 --- a/ui/lib/core/addon/helpers/format-number.js +++ b/ui/lib/core/addon/helpers/format-number.js @@ -1,6 +1,9 @@ import { helper } from '@ember/component/helper'; export function formatNumber([number]) { + if (typeof number !== 'number') { + return number; + } // formats a number according to the locale return new Intl.NumberFormat().format(number); } diff --git a/ui/lib/core/addon/templates/components/bar-chart.hbs b/ui/lib/core/addon/templates/components/bar-chart.hbs index 584aa02e05..6ac736d2cd 100644 --- a/ui/lib/core/addon/templates/components/bar-chart.hbs +++ b/ui/lib/core/addon/templates/components/bar-chart.hbs @@ -19,8 +19,8 @@
diff --git a/ui/lib/core/addon/templates/components/stat-text.hbs b/ui/lib/core/addon/templates/components/stat-text.hbs index 9d4bb916d1..1e50a78358 100644 --- a/ui/lib/core/addon/templates/components/stat-text.hbs +++ b/ui/lib/core/addon/templates/components/stat-text.hbs @@ -3,5 +3,5 @@ {{#if @subText}}
{{@subText}}
{{/if}} -
{{@value}}
+
{{format-number @value}}
diff --git a/ui/mirage/config.js b/ui/mirage/config.js index 81cb56c563..b3a2b0df7a 100644 --- a/ui/mirage/config.js +++ b/ui/mirage/config.js @@ -31,6 +31,218 @@ export default function() { }; }); + this.get('/sys/internal/counters/activity/monthly', function() { + return { + data: { + by_namespace: [ + { + namespace_id: 'Z4Rzh', + namespace_path: 'namespace1/', + counts: { + distinct_entities: 867, + non_entity_tokens: 939, + clients: 1806, + }, + }, + { + namespace_id: 'DcgzU', + namespace_path: 'namespace17/', + counts: { + distinct_entities: 966, + non_entity_tokens: 550, + clients: 1516, + }, + }, + { + namespace_id: '5SWT8', + namespace_path: 'namespacelonglonglong4/', + counts: { + distinct_entities: 996, + non_entity_tokens: 417, + clients: 1413, + }, + }, + { + namespace_id: 'XGu7R', + namespace_path: 'namespace12/', + counts: { + distinct_entities: 829, + non_entity_tokens: 540, + clients: 1369, + }, + }, + { + namespace_id: 'yHcL9', + namespace_path: 'namespace11/', + counts: { + distinct_entities: 563, + non_entity_tokens: 705, + clients: 1268, + }, + }, + { + namespace_id: 'F0xGm', + namespace_path: 'namespace10/', + counts: { + distinct_entities: 925, + non_entity_tokens: 255, + clients: 1180, + }, + }, + { + namespace_id: 'aJuQG', + namespace_path: 'namespace9/', + counts: { + distinct_entities: 935, + non_entity_tokens: 239, + clients: 1174, + }, + }, + { + namespace_id: 'bw5UO', + namespace_path: 'namespace6/', + counts: { + distinct_entities: 810, + non_entity_tokens: 363, + clients: 1173, + }, + }, + { + namespace_id: 'IeyJp', + namespace_path: 'namespace14/', + counts: { + distinct_entities: 774, + non_entity_tokens: 392, + clients: 1166, + }, + }, + { + namespace_id: 'Uc0o8', + namespace_path: 'namespace16/', + counts: { + distinct_entities: 408, + non_entity_tokens: 743, + clients: 1151, + }, + }, + { + namespace_id: 'R6L40', + namespace_path: 'namespace2/', + counts: { + distinct_entities: 292, + non_entity_tokens: 736, + clients: 1028, + }, + }, + { + namespace_id: 'Rqa3W', + namespace_path: 'namespace13/', + counts: { + distinct_entities: 160, + non_entity_tokens: 803, + clients: 963, + }, + }, + { + namespace_id: 'MSgZE', + namespace_path: 'namespace7/', + counts: { + distinct_entities: 201, + non_entity_tokens: 657, + clients: 858, + }, + }, + { + namespace_id: 'kxU4t', + namespace_path: 'namespacelonglonglong3/', + counts: { + distinct_entities: 742, + non_entity_tokens: 26, + clients: 768, + }, + }, + { + namespace_id: '5xKya', + namespace_path: 'namespace15/', + counts: { + distinct_entities: 663, + non_entity_tokens: 19, + clients: 682, + }, + }, + { + namespace_id: '5KxXA', + namespace_path: 'namespace18anotherlong/', + counts: { + distinct_entities: 470, + non_entity_tokens: 196, + clients: 666, + }, + }, + { + namespace_id: 'AAidI', + namespace_path: 'namespace20/', + counts: { + distinct_entities: 429, + non_entity_tokens: 60, + clients: 489, + }, + }, + { + namespace_id: 'BCl56', + namespace_path: 'namespace8/', + counts: { + distinct_entities: 61, + non_entity_tokens: 201, + clients: 262, + }, + }, + { + namespace_id: 'yYNw2', + namespace_path: 'namespace19/', + counts: { + distinct_entities: 165, + non_entity_tokens: 85, + clients: 250, + }, + }, + { + namespace_id: 'root', + namespace_path: '', + counts: { + distinct_entities: 67, + non_entity_tokens: 9, + clients: 76, + }, + }, + ], + distinct_entities: 11323, + non_entity_tokens: 7935, + clients: 19258, + }, + }; + }); + + this.get('/sys/health', function() { + return { + initialized: true, + sealed: false, + standby: false, + license: { + expiry: '2021-05-12T23:20:50.52Z', + state: 'stored', + }, + performance_standby: false, + replication_performance_mode: 'disabled', + replication_dr_mode: 'disabled', + server_time_utc: 1622562585, + version: '1.9.0+ent', + cluster_name: 'vault-cluster-e779cd7c', + cluster_id: '5f20f5ab-acea-0481-787e-71ec2ff5a60b', + last_wal: 121, + }; + }); + this.get('/sys/license/status', function() { return { data: { @@ -60,25 +272,5 @@ export default function() { }; }); - this.get('/sys/health', function() { - return { - initialized: true, - sealed: false, - standby: false, - license: { - expiry: '2021-05-12T23:20:50.52Z', - state: 'stored', - }, - performance_standby: false, - replication_performance_mode: 'disabled', - replication_dr_mode: 'disabled', - server_time_utc: 1622562585, - version: '1.9.0+ent', - cluster_name: 'vault-cluster-e779cd7c', - cluster_id: '5f20f5ab-acea-0481-787e-71ec2ff5a60b', - last_wal: 121, - }; - }); - this.passthrough(); } diff --git a/ui/tests/integration/components/stat-text-test.js b/ui/tests/integration/components/stat-text-test.js index 2c1f2bf61a..554c5dbf91 100644 --- a/ui/tests/integration/components/stat-text-test.js +++ b/ui/tests/integration/components/stat-text-test.js @@ -1,6 +1,6 @@ import { module, test } from 'qunit'; import { setupRenderingTest } from 'ember-qunit'; -import { render } from '@ember/test-helpers'; +import { render, settled } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; module('Integration | Component | StatText', function(hooks) { @@ -14,16 +14,21 @@ module('Integration | Component | StatText', function(hooks) { test('it renders passed in attributes', async function(assert) { this.set('label', 'A Label'); - this.set('value', '9,999'); + this.set('value', 'A value'); this.set('size', 'l'); this.set('subText', 'This is my description'); await render( hbs`` ); - assert.dom('.stat-label').hasText(this.label, 'renders label'); assert.dom('.stat-text').hasText(this.subText, 'renders subtext'); - assert.dom('.stat-value').hasText(this.value, 'renders value'); + assert.dom('.stat-value').hasText(this.value, 'renders a non-integer value'); + + this.set('value', 604099); + await settled(); + + let formattedNumber = '604,099'; + assert.dom('.stat-value').hasText(formattedNumber, 'renders correctly formatted integer value'); }); }); diff --git a/ui/tests/integration/helpers/date-format-test.js b/ui/tests/integration/helpers/date-format-test.js index 644874d4a4..029d0dd53a 100644 --- a/ui/tests/integration/helpers/date-format-test.js +++ b/ui/tests/integration/helpers/date-format-test.js @@ -53,4 +53,14 @@ module('Integration | Helper | date-format', function(hooks) { ); assert.dom('[data-test-date-format]').includesText(format(formattedDate, 'MMMM dd, yyyy hh:mm:ss a')); }); + + test('displays correct date when timestamp is in ISO 8601 format', async function(assert) { + let timestampDate = '2021-09-01T00:00:00Z'; + this.set('timestampDate', timestampDate); + + await render( + hbs`

Date: {{date-format timestampDate 'MMM dd, yyyy' dateOnly=true}}

` + ); + assert.dom('[data-test-date-format]').includesText('Date: Sep 01, 2021'); + }); });