mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-17 20:17:00 +02:00
* add timestamp to attribution * create usage stat component * updates stat text boxes * remove flex-header css * remove comment * add empty state if no data * update monthly serializer * remove empty state - unnecessary * change tab to 'history' * add usage stats to history view * change css styling for upcased grey subtitle * correctly exports namespace and auth data * close modal on download * test making a service? * fix monthly attrs * update csv content format * remove component and make downloadCsv a service * update function name * wip//add warning labels, fixing up current and history tabs * wip//clean up serializer fix with real data * fix link styling: * add conditionals for no data, add warning for 1.9 counting changes * naming comment * fix tooltip formatting * fix number format and consolidate actions * remove outdated test * add revokeObjectURL and rename variable * fix errors and empty state views when no activity data at all * fix end time error * fix comment * return truncating to serializer * PR review cleanup * return new object
205 lines
6.6 KiB
JavaScript
205 lines
6.6 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 } from 'date-fns';
|
|
|
|
export default class Dashboard extends Component {
|
|
arrayOfMonths = [
|
|
'January',
|
|
'February',
|
|
'March',
|
|
'April',
|
|
'May',
|
|
'June',
|
|
'July',
|
|
'August',
|
|
'September',
|
|
'October',
|
|
'November',
|
|
'December',
|
|
];
|
|
maxNamespaces = 10;
|
|
chartLegend = [
|
|
{ key: 'entity_clients', label: 'entity clients' },
|
|
{ key: 'non_entity_clients', label: 'non-entity clients' },
|
|
];
|
|
|
|
// needed for startTime modal picker
|
|
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;
|
|
});
|
|
|
|
@service store;
|
|
|
|
@tracked barChartSelection = false;
|
|
@tracked isEditStartMonthOpen = false;
|
|
@tracked responseRangeDiffMessage = null;
|
|
@tracked startTimeRequested = null;
|
|
@tracked startTimeFromResponse = this.args.model.startTimeFromLicense; // ex: ['2021', 3] is April 2021 (0 indexed)
|
|
@tracked endTimeFromResponse = this.args.model.endTimeFromResponse;
|
|
@tracked startMonth = null;
|
|
@tracked startYear = null;
|
|
@tracked selectedNamespace = null;
|
|
@tracked noActivityDate = '';
|
|
// @tracked selectedNamespace = 'namespace18anotherlong/'; // for testing namespace selection view with mirage
|
|
|
|
get startTimeDisplay() {
|
|
if (!this.startTimeFromResponse) {
|
|
// otherwise will return date of new Date(null)
|
|
return null;
|
|
}
|
|
let month = this.startTimeFromResponse[1];
|
|
let year = this.startTimeFromResponse[0];
|
|
return `${this.arrayOfMonths[month]} ${year}`;
|
|
}
|
|
|
|
get endTimeDisplay() {
|
|
if (!this.endTimeFromResponse) {
|
|
// otherwise will return date of new Date(null)
|
|
return null;
|
|
}
|
|
let month = this.endTimeFromResponse[1];
|
|
let year = this.endTimeFromResponse[0];
|
|
return `${this.arrayOfMonths[month]} ${year}`;
|
|
}
|
|
|
|
get isDateRange() {
|
|
return !isSameMonth(
|
|
new Date(this.args.model.activity.startTime),
|
|
new Date(this.args.model.activity.endTime)
|
|
);
|
|
}
|
|
|
|
// top level TOTAL client counts from response for given date range
|
|
get totalUsageCounts() {
|
|
return this.selectedNamespace
|
|
? this.filterByNamespace(this.selectedNamespace)
|
|
: this.args.model.activity?.total;
|
|
}
|
|
|
|
// by namespace client count data for date range
|
|
get byNamespaceActivity() {
|
|
return this.args.model.activity?.byNamespace || null;
|
|
}
|
|
|
|
// for horizontal bar chart in attribution component
|
|
get topTenChartData() {
|
|
if (this.selectedNamespace) {
|
|
let filteredNamespace = this.filterByNamespace(this.selectedNamespace);
|
|
return filteredNamespace.mounts
|
|
? this.filterByNamespace(this.selectedNamespace).mounts.slice(0, 10)
|
|
: null;
|
|
} else {
|
|
return this.byNamespaceActivity;
|
|
}
|
|
}
|
|
|
|
get responseTimestamp() {
|
|
return this.args.model.activity?.responseTimestamp;
|
|
}
|
|
// HELPERS
|
|
areArraysTheSame(a1, a2) {
|
|
return (
|
|
a1 === a2 ||
|
|
(a1 !== null &&
|
|
a2 !== null &&
|
|
a1.length === a2.length &&
|
|
a1
|
|
.map(function (val, idx) {
|
|
return val === a2[idx];
|
|
})
|
|
.reduce(function (prev, cur) {
|
|
return prev && cur;
|
|
}, true))
|
|
);
|
|
}
|
|
|
|
// ACTIONS
|
|
@action
|
|
async handleClientActivityQuery(month, year, dateType) {
|
|
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 Dashboard which opens a modal.
|
|
if (dateType === 'startTime') {
|
|
let monthIndex = this.arrayOfMonths.indexOf(month);
|
|
this.startTimeRequested = [year.toString(), monthIndex]; // ['2021', 0] (e.g. January 2021) // TODO CHANGE TO ARRAY
|
|
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 {
|
|
let response = await this.store.queryRecord('clients/activity', {
|
|
start_time: this.startTimeRequested,
|
|
end_time: this.endTimeRequested,
|
|
});
|
|
if (response.id === 'no-data') {
|
|
// empty response 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 this.startTimeFromResponse
|
|
this.startTimeFromResponse = response.formattedStartTime;
|
|
this.endTimeFromResponse = response.formattedEndTime;
|
|
}
|
|
// compare if the response and what you requested are the same. If they are not throw a warning.
|
|
// this only gets triggered if the data was returned, which does not happen if the user selects a startTime after for which we have data. That's an adapter error and is captured differently.
|
|
if (!this.areArraysTheSame(this.startTimeFromResponse, this.startTimeRequested)) {
|
|
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;
|
|
}
|
|
return response;
|
|
} catch (e) {
|
|
// ARG TODO handle error
|
|
}
|
|
}
|
|
|
|
@action
|
|
handleCurrentBillingPeriod() {
|
|
this.handleClientActivityQuery(0, 0, 'reset');
|
|
}
|
|
|
|
@action
|
|
selectNamespace(value) {
|
|
// In case of search select component, value returned is an array
|
|
if (Array.isArray(value)) {
|
|
this.selectedNamespace = this.getNamespace(value[0]);
|
|
this.barChartSelection = false;
|
|
} else if (typeof value === 'object') {
|
|
// While D3 bar selection returns an object
|
|
this.selectedNamespace = this.getNamespace(value.label);
|
|
this.barChartSelection = true;
|
|
}
|
|
}
|
|
|
|
@action
|
|
selectStartMonth(month) {
|
|
this.startMonth = month;
|
|
}
|
|
|
|
@action
|
|
selectStartYear(year) {
|
|
this.startYear = year;
|
|
}
|
|
|
|
// HELPERS
|
|
filterByNamespace(namespace) {
|
|
return this.byNamespaceActivity.find((ns) => ns.label === namespace);
|
|
}
|
|
}
|