vault/ui/app/components/clients/history.js
claire bontempo b5868b3c19
UI/ Client count permissions empty states (#14313)
* fix no data empty states

* add comment

* handle error in component

* adds tests for empty state template

* tidy and fix tests

* Empty state for current tab (#14319)

* update ci.hcl to remove 1.6.x and add in 1.10.x (#14310)

* Fix autoseal health check race by passing metrics sink in CoreConfig (#14196)

* Add empty state for current tab, config off, no read permissions on config

Co-authored-by: Hridoy Roy <roy@hashicorp.com>
Co-authored-by: Scott Miller <smiller@hashicorp.com>

* update selector

* fix test

* remove helper

Co-authored-by: Chelsea Shaw <82459713+hashishaw@users.noreply.github.com>
Co-authored-by: Hridoy Roy <roy@hashicorp.com>
Co-authored-by: Scott Miller <smiller@hashicorp.com>
2022-03-02 10:44:41 -08:00

296 lines
9.8 KiB
JavaScript

import Component from '@glimmer/component';
import { action } from '@ember/object';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { isSameMonth, isAfter } from 'date-fns';
import getStorage from 'vault/lib/token-storage';
const INPUTTED_START_DATE = 'vault:ui-inputted-start-date';
export default class History extends Component {
@service store;
@service version;
arrayOfMonths = [
'January',
'February',
'March',
'April',
'May',
'June',
'July',
'August',
'September',
'October',
'November',
'December',
];
chartLegend = [
{ key: 'entity_clients', label: 'entity clients' },
{ key: 'non_entity_clients', label: 'non-entity clients' },
];
// FOR START DATE EDIT & MODAL //
months = Array.from({ length: 12 }, (item, i) => {
return new Date(0, i).toLocaleString('en-US', { month: 'long' });
});
years = Array.from({ length: 5 }, (item, i) => {
return new Date().getFullYear() - i;
});
currentDate = new Date();
currentYear = this.currentDate.getFullYear(); // integer of year
currentMonth = this.currentDate.getMonth(); // index of month
@tracked isEditStartMonthOpen = false;
@tracked startMonth = null;
@tracked startYear = null;
@tracked allowedMonthMax = 12;
@tracked disabledYear = null;
// FOR HISTORY COMPONENT //
// RESPONSE
@tracked endTimeFromResponse = this.args.model.endTimeFromResponse;
@tracked startTimeFromResponse = this.args.model.startTimeFromLicense; // ex: ['2021', 3] is April 2021 (0 indexed)
@tracked startTimeRequested = null;
@tracked queriedActivityResponse = null;
// VERSION/UPGRADE INFO
@tracked firstUpgradeVersion = this.args.model.versionHistory[0].id || null; // return 1.9.0 or earliest upgrade post 1.9.0
@tracked upgradeDate = this.args.model.versionHistory[0].timestampInstalled || null; // returns RFC3339 timestamp
// SEARCH SELECT
@tracked selectedNamespace = null;
@tracked namespaceArray = this.getActivityResponse.byNamespace
? this.getActivityResponse.byNamespace.map((namespace) => ({
name: namespace.label,
id: namespace.label,
}))
: [];
@tracked selectedAuthMethod = null;
@tracked authMethodOptions = [];
// TEMPLATE MESSAGING
@tracked noActivityDate = '';
@tracked responseRangeDiffMessage = null;
@tracked isLoadingQuery = false;
@tracked licenseStartIsCurrentMonth = this.args.model.activity?.isLicenseDateError || false;
@tracked errorObject = null;
get versionText() {
return this.version.isEnterprise
? {
label: 'Billing start month',
description:
'This date comes from your license, and defines when client counting starts. Without this starting point, the data shown is not reliable.',
title: 'No billing start date found',
message:
'In order to get the most from this data, please enter your billing period start month. This will ensure that the resulting data is accurate.',
}
: {
label: 'Client counting start date',
description:
'This date is when client counting starts. Without this starting point, the data shown is not reliable.',
title: 'No start date found',
message:
'In order to get the most from this data, please enter a start month above. Vault will calculate new clients starting from that month.',
};
}
// on init API response uses license start_date, getter updates when user queries dates
get getActivityResponse() {
return this.queriedActivityResponse || this.args.model.activity;
}
get hasAttributionData() {
if (this.selectedAuthMethod) return false;
if (this.selectedNamespace) {
return this.authMethodOptions.length > 0;
}
return !!this.totalClientsData && this.totalUsageCounts && this.totalUsageCounts.clients !== 0;
}
get startTimeDisplay() {
if (!this.startTimeFromResponse) {
return null;
}
let month = this.startTimeFromResponse[1];
let year = this.startTimeFromResponse[0];
return `${this.arrayOfMonths[month]} ${year}`;
}
get endTimeDisplay() {
if (!this.endTimeFromResponse) {
return null;
}
let month = this.endTimeFromResponse[1];
let year = this.endTimeFromResponse[0];
return `${this.arrayOfMonths[month]} ${year}`;
}
get filteredActivity() {
const namespace = this.selectedNamespace;
const auth = this.selectedAuthMethod;
if (!namespace && !auth) {
return this.getActivityResponse;
}
if (!auth) {
return this.getActivityResponse.byNamespace.find((ns) => ns.label === namespace);
}
return this.getActivityResponse.byNamespace
.find((ns) => ns.label === namespace)
.mounts?.find((mount) => mount.label === auth);
}
get isDateRange() {
return !isSameMonth(
new Date(this.getActivityResponse.startTime),
new Date(this.getActivityResponse.endTime)
);
}
// top level TOTAL client counts for given date range
get totalUsageCounts() {
return this.selectedNamespace ? this.filteredActivity : this.getActivityResponse.total;
}
// total client data for horizontal bar chart in attribution component
get totalClientsData() {
if (this.selectedNamespace) {
return this.filteredActivity?.mounts || null;
} else {
return this.getActivityResponse?.byNamespace;
}
}
get responseTimestamp() {
return this.getActivityResponse.responseTimestamp;
}
get countsIncludeOlderData() {
let firstUpgrade = this.args.model.versionHistory[0];
if (!firstUpgrade) {
return false;
}
let versionDate = new Date(firstUpgrade.timestampInstalled);
let startTimeFromResponseAsDateObject = new Date(
Number(this.startTimeFromResponse[0]),
this.startTimeFromResponse[1]
);
// compare against this startTimeFromResponse to show message or not.
return isAfter(versionDate, startTimeFromResponseAsDateObject) ? versionDate : false;
}
@action
async handleClientActivityQuery(month, year, dateType) {
this.isEditStartMonthOpen = false;
if (dateType === 'cancel') {
return;
}
// clicked "Current Billing period" in the calendar widget
if (dateType === 'reset') {
this.startTimeRequested = this.args.model.startTimeFromLicense;
this.endTimeRequested = null;
}
// clicked "Edit" Billing start month in History which opens a modal.
if (dateType === 'startTime') {
let monthIndex = this.arrayOfMonths.indexOf(month);
this.startTimeRequested = [year.toString(), monthIndex]; // ['2021', 0] (e.g. January 2021)
this.endTimeRequested = null;
}
// clicked "Custom End Month" from the calendar-widget
if (dateType === 'endTime') {
// use the currently selected startTime for your startTimeRequested.
this.startTimeRequested = this.startTimeFromResponse;
this.endTimeRequested = [year.toString(), month]; // endTime comes in as a number/index whereas startTime comes in as a month name. Hence the difference between monthIndex and month.
}
try {
this.isLoadingQuery = true;
let response = await this.store.queryRecord('clients/activity', {
start_time: this.startTimeRequested,
end_time: this.endTimeRequested,
});
if (response.id === 'no-data') {
// empty response (204) is the only time we want to update the displayed date with the requested time
this.startTimeFromResponse = this.startTimeRequested;
this.noActivityDate = this.startTimeDisplay;
} else {
// note: this.startTimeDisplay (getter) is updated by the @tracked startTimeFromResponse
this.startTimeFromResponse = response.formattedStartTime;
this.endTimeFromResponse = response.formattedEndTime;
this.storage().setItem(INPUTTED_START_DATE, this.startTimeFromResponse);
}
this.queriedActivityResponse = response;
this.licenseStartIsCurrentMonth = response.isLicenseDateError;
// compare if the response startTime comes after the requested startTime. If true throw a warning.
// only display if they selected a startTime
if (
dateType === 'startTime' &&
isAfter(
new Date(this.getActivityResponse.startTime),
new Date(this.startTimeRequested[0], this.startTimeRequested[1])
)
) {
this.responseRangeDiffMessage = `You requested data from ${month} ${year}. We only have data from ${this.startTimeDisplay}, and that is what is being shown here.`;
} else {
this.responseRangeDiffMessage = null;
}
} catch (e) {
this.errorObject = e;
return e;
} finally {
this.isLoadingQuery = false;
}
}
@action
handleCurrentBillingPeriod() {
this.handleClientActivityQuery(0, 0, 'reset');
}
@action
selectNamespace([value]) {
// value comes in as [namespace0]
this.selectedNamespace = value;
if (!value) {
this.authMethodOptions = [];
// on clear, also make sure auth method is cleared
this.selectedAuthMethod = null;
} else {
// Side effect: set auth namespaces
const mounts = this.filteredActivity.mounts?.map((mount) => ({
id: mount.label,
name: mount.label,
}));
this.authMethodOptions = mounts;
}
}
@action
setAuthMethod([authMount]) {
this.selectedAuthMethod = authMount;
}
// FOR START DATE MODAL
@action
selectStartMonth(month, event) {
this.startMonth = month;
// disables months if in the future
this.disabledYear = this.months.indexOf(month) >= this.currentMonth ? this.currentYear : null;
event.close();
}
@action
selectStartYear(year, event) {
this.startYear = year;
this.allowedMonthMax = year === this.currentYear ? this.currentMonth : 12;
event.close();
}
storage() {
return getStorage();
}
}