-
- {{if this.flags.isHvdManaged "For data period:" "For billing period:"}}
+
+ {{#if (and this.version.isEnterprise @billingStartTime)}}
+ {{! Enterprise should always have a @billingStartTime but as a fallback allow the user to query dates manually. }}
+ {{! A dropdown renders in Clients::DateRange to query specific dates if we have a @billingStartTime }}
+
+ {{if this.flags.isHvdManaged "For data period: " "For billing period: "}}
{{this.formattedStartDate}}
-
{{this.formattedEndDate}}
-
- {{/if}}
-
- {{#if this.showCommunity}}
+ {{else if (and @startTimestamp @endTimestamp)}}
+ {{! If these timestamps exist an initial query has already been made using the modal in Clients::DateRange }}
Client counting period
-
Specify a date range to view within that timeframe. Click 'Edit' to
- choose a different date range.
+
Click 'Edit' to select a different date range.
{{this.formattedStartDate}}
-
diff --git a/ui/app/components/clients/page-header.js b/ui/app/components/clients/page-header.js
index 20204f28f6..f110c723b0 100644
--- a/ui/app/components/clients/page-header.js
+++ b/ui/app/components/clients/page-header.js
@@ -95,10 +95,6 @@ export default class ClientsPageHeaderComponent extends Component {
return `clients_export${ns}${csvDateRange}`;
}
- get showCommunity() {
- return this.version.isCommunity && !!this.formattedStartDate && !!this.formattedEndDate;
- }
-
async getExportData() {
const adapter = this.store.adapterFor('clients/activity');
const { startTimestamp, endTimestamp } = this.args;
diff --git a/ui/app/components/clients/page/counts.ts b/ui/app/components/clients/page/counts.ts
index 260eb395c7..fe60de6992 100644
--- a/ui/app/components/clients/page/counts.ts
+++ b/ui/app/components/clients/page/counts.ts
@@ -39,7 +39,10 @@ export default class ClientsCountsPageComponent extends Component {
}
get formattedBillingStartDate() {
- return this.args.config.billingStartTimestamp.toISOString();
+ if (this.args.config?.billingStartTimestamp) {
+ return this.args.config.billingStartTimestamp.toISOString();
+ }
+ return null;
}
// passed into page-header for the export modal alert
diff --git a/ui/app/components/clients/page/overview.ts b/ui/app/components/clients/page/overview.ts
index ead01fd548..1fcbeba765 100644
--- a/ui/app/components/clients/page/overview.ts
+++ b/ui/app/components/clients/page/overview.ts
@@ -28,7 +28,7 @@ export default class ClientsOverviewPageComponent extends Component {
get byMonthClients() {
// HVD clusters are billed differently and the monthly total is the important metric.
if (this.flags.isHvdManaged) {
- return this.args.activity.byMonth;
+ return this.args.activity.byMonth || [];
}
// For self-managed clusters only the new_clients per month are relevant because clients accumulate over a billing period.
// (Since "total" per month is not cumulative it's not a useful metric)
@@ -36,6 +36,7 @@ export default class ClientsOverviewPageComponent extends Component {
}
// Supplies data passed to dropdown filters (except months which is computed below )
+ @cached
get activityData() {
// If no month is selected the table displays all of the activity for the queried date range.
const selectedMonth = this.args.filterQueryParams.month;
@@ -49,9 +50,12 @@ export default class ClientsOverviewPageComponent extends Component {
@cached
get months() {
- return this.byMonthClients.map((m) => m.timestamp).reverse();
+ const timestamps = this.byMonthClients.map((m) => m.timestamp);
+ // display the most recent month at the top of the dropdown
+ return timestamps.reverse();
}
+ @cached
get tableData() {
if (this.activityData?.length) {
// Reset the `month` query param because it determines which dataset (see this.activityData)
diff --git a/ui/tests/integration/components/clients/date-range-test.js b/ui/tests/integration/components/clients/date-range-test.js
index fb267af3a0..ac5633802b 100644
--- a/ui/tests/integration/components/clients/date-range-test.js
+++ b/ui/tests/integration/components/clients/date-range-test.js
@@ -18,6 +18,7 @@ module('Integration | Component | clients/date-range', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
+ this.version = this.owner.lookup('service:version');
Sinon.replace(timestamp, 'now', Sinon.fake.returns(new Date('2018-04-03T14:15:30')));
this.now = timestamp.now();
this.startTimestamp = '2018-01-01T14:15:30';
@@ -36,6 +37,11 @@ module('Integration | Component | clients/date-range', function (hooks) {
};
});
+ test('it does not render if a start and end timestamp are already provided', async function (assert) {
+ await this.renderComponent();
+ assert.dom(DATE_RANGE.edit).doesNotExist('it does not render if timestamps are provided');
+ });
+
test('it formats modal inputs to ISO string timestamps', async function (assert) {
this.startTimestamp = undefined;
await this.renderComponent();
@@ -80,7 +86,7 @@ module('Integration | Component | clients/date-range', function (hooks) {
});
test('it does not allow the current month to be selected as a start date or as an end date', async function (assert) {
- this.owner.lookup('service:version').type = 'community';
+ this.version.type = 'community';
this.endTimestamp = undefined;
const currentMonth = format(timestamp.now(), 'yyyy-MM');
@@ -91,7 +97,7 @@ module('Integration | Component | clients/date-range', function (hooks) {
assert.dom(DATE_RANGE.validation).hasText('You cannot select the current month or beyond.');
await click(GENERAL.submitButton);
- assert.false(this.onChange.called);
+ assert.false(this.onChange.called, 'it does not call @onChange callback');
// This tests validation when the end date is the current month and start is valid.
// If start is current month and end is a valid prior selection, it will run into the validation error of start being after end date
@@ -99,7 +105,22 @@ module('Integration | Component | clients/date-range', function (hooks) {
await fillIn(DATE_RANGE.editDate('start'), '2018-01');
await fillIn(DATE_RANGE.editDate('end'), currentMonth);
await click(GENERAL.submitButton);
- assert.false(this.onChange.called);
+ assert.false(this.onChange.called, 'it does not call @onChange callback');
+ });
+
+ test('it allows the current month to be selected if enterprise and there is not a @billingStartTime', async function (assert) {
+ this.version.type = 'enterprise';
+ this.endTimestamp = undefined;
+ const currentMonth = format(timestamp.now(), 'yyyy-MM');
+
+ await this.renderComponent();
+ await click(DATE_RANGE.edit);
+ await fillIn(DATE_RANGE.editDate('start'), currentMonth);
+ await fillIn(DATE_RANGE.editDate('end'), currentMonth);
+
+ assert.dom(DATE_RANGE.validation).doesNotExist();
+ await click(GENERAL.submitButton);
+ assert.true(this.onChange.called, 'it calls @onChange callback');
});
module('enterprise', function (hooks) {
@@ -126,6 +147,19 @@ module('Integration | Component | clients/date-range', function (hooks) {
});
});
+ test('it renders date range modal if there are no timestamps provided', async function (assert) {
+ this.billingStartTime = '';
+ this.startTimestamp = '';
+ this.endTimestamp = '';
+ await this.renderComponent();
+ assert
+ .dom(DATE_RANGE.edit)
+ .exists('it renders button to open date range modal')
+ .hasText('Set date range');
+ await click(DATE_RANGE.edit);
+ assert.dom(DATE_RANGE.editModal).exists();
+ });
+
test('it updates toggle text when a new date is selected', async function (assert) {
this.onChange = ({ start_time }) => this.set('startTimestamp', start_time);
diff --git a/ui/tests/integration/components/clients/page-header-test.js b/ui/tests/integration/components/clients/page-header-test.js
index 5928196217..184546d957 100644
--- a/ui/tests/integration/components/clients/page-header-test.js
+++ b/ui/tests/integration/components/clients/page-header-test.js
@@ -25,6 +25,7 @@ module('Integration | Component | clients/page-header', function (hooks) {
this.downloadStub = Sinon.stub(this.owner.lookup('service:download'), 'download');
this.startTimestamp = '2022-06-01T23:00:11.050Z';
this.endTimestamp = '2022-12-01T23:00:11.050Z';
+ this.billingStartTime = this.startTimestamp;
this.upgradesDuringActivity = [];
this.noData = undefined;
this.server.post('/sys/capabilities-self', () =>
@@ -34,7 +35,7 @@ module('Integration | Component | clients/page-header', function (hooks) {
this.renderComponent = async () => {
return render(hbs`