[UI] VAULT-35614 review reporting phase 1 dashboard design changes (#30538)

* Update reporting addon

* Update usage page data mappings

Remove display name mapping per design review

* Update after feedback from design

Copywrite headers

* Remove the old name mapping test from the usage page

* Update tooltips to have periods

Update namespaces tooltip
This commit is contained in:
Jim Wright 2025-05-09 09:33:09 -07:00 committed by GitHub
parent 3e171f4318
commit 5ee33d47ba
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
41 changed files with 328 additions and 219 deletions

View File

@ -5,8 +5,6 @@
import Component from '@ember/component';
import { service } from '@ember/service';
import { allEngines } from 'vault/helpers/mountable-secret-engines';
import { allMethods } from 'vault/helpers/mountable-auth-methods';
import type FlagsService from 'vault/services/flags';
import type ApiService from 'vault/services/api';
@ -31,23 +29,47 @@ export default class UsagePage extends Component {
@service declare readonly flags: FlagsService;
handleFetchUsageData: getUsageDataFunction = async () => {
//TODO: Update client with typed response after the API is updated https://hashicorp.atlassian.net/browse/VAULT-35108
/**
* We get a partially typed response from the API client, but only 1 level deep.
* Casting the nested types here and falling back to defaults in the mappings.
* We should get typescript errors if top level interfaces in the API client or
* the vault-reporting addon change.
*/
const response = await this.api.sys.generateUtilizationReport();
const data = response as UsageDashboardData;
// Replace engine names with display names if available
allEngines().forEach((engine) => {
if (engine.type in data.secret_engines) {
data.secret_engines[engine.displayName] = data.secret_engines[engine.type] || 0;
delete data.secret_engines[engine.type];
}
});
// Replace auth method names with display names if available
allMethods().forEach((method) => {
if (method.type in data.auth_methods) {
data.auth_methods[method.displayName] = data.auth_methods[method.type] || 0;
delete data.auth_methods[method.type];
}
});
const leaseCountQuotas = response.leaseCountQuotas as UsageDashboardData['leaseCountQuotas'];
const replicationStatus = response.replicationStatus as UsageDashboardData['replicationStatus'];
const pki = response.pki as UsageDashboardData['pki'];
const secretSync = response.secretSync as UsageDashboardData['secretSync'];
const data: UsageDashboardData = {
authMethods: (response.authMethods as Record<string, number>) || {},
secretEngines: (response.secretEngines as Record<string, number>) || {},
leasesByAuthMethod: (response.leasesByAuthMethod as Record<string, number>) || {},
leaseCountQuotas: {
globalLeaseCountQuota: {
capacity: leaseCountQuotas?.globalLeaseCountQuota?.capacity || 0,
count: leaseCountQuotas?.globalLeaseCountQuota?.count || 0,
name: leaseCountQuotas?.globalLeaseCountQuota?.name || '',
},
totalLeaseCountQuotas: leaseCountQuotas?.totalLeaseCountQuotas || 0,
},
replicationStatus: {
drState: replicationStatus?.drState || 'disabled',
prState: replicationStatus?.prState || 'disabled',
drPrimary: replicationStatus?.drPrimary ?? false,
prPrimary: replicationStatus?.prPrimary ?? false,
},
kvv1Secrets: response.kvv1Secrets || 0,
kvv2Secrets: response.kvv2Secrets || 0,
namespaces: response.namespaces || 0,
pki: {
totalIssuers: pki?.totalIssuers || 0,
totalRoles: pki?.totalRoles || 0,
},
secretSync: {
totalDestinations: secretSync?.totalDestinations || 0,
},
};
return data as UsageDashboardData;
};
}

View File

@ -0,0 +1,27 @@
/**
* Copyright (c) HashiCorp, Inc.
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import sinon from 'sinon';
module('Integration | Component | usage | Page::Usage', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(async function () {
this.api = this.owner.lookup('service:api');
this.generateUtilizationReportStub = sinon.stub(this.api.sys, 'generateUtilizationReport').resolves({});
});
hooks.afterEach(function () {
this.generateUtilizationReportStub.restore();
});
test('it provides the correct fetch function to the dashboard component', async function (assert) {
await render(hbs`<Usage::Page />`);
assert.true(this.generateUtilizationReportStub.calledOnce, 'fetch function is called on render');
});
});

View File

@ -1,77 +0,0 @@
/**
* Copyright (c) HashiCorp, Inc.
*/
import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { findAll, render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import sinon from 'sinon';
module('Integration | Component | usage | Page::Usage', function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(async function () {
this.api = this.owner.lookup('service:api');
this.generateUtilizationReportStub = sinon.stub(this.api.sys, 'generateUtilizationReport').resolves({});
});
hooks.afterEach(function () {
this.generateUtilizationReportStub.restore();
});
test('it provides the correct fetch function to the dashboard component', async function (assert) {
await render(hbs`<Usage::Page />`);
assert.true(this.generateUtilizationReportStub.calledOnce, 'fetch function is called on render');
});
test('it remaps data to friendly names if available', async function (assert) {
this.generateUtilizationReportStub.resolves({
auth_methods: { alicloud: 2, cert: 2, userpass: 2, 'unknown-random-method': 1 },
kvv1_secrets: 15,
kvv2_secrets: 146,
lease_count_quotas: {
global_lease_count_quota: {
capacity: 300000,
count: 244121,
name: 'default',
},
total_lease_count_quotas: 2,
},
namespaces: 10,
secrets_sync: 79,
pki: { total_issuers: 2, total_roles: 6 },
replication_status: {
dr_primary: false,
dr_state: 'disabled',
pr_primary: false,
pr_state: 'enabled',
},
secret_engines: {
keymgmt: 5,
gcpkms: 10,
pki: 11,
'unknown-random-engine': 1,
},
});
await render(hbs`<Usage::Page />`);
const engineLabels = [...findAll('[data-test-dashboard-secret-engines] .axis g text')].map(
(label) => label.textContent
);
const authMethodLabels = [...findAll('[data-test-dashboard-auth-methods] .axis g text')].map(
(label) => label.textContent
);
assert.deepEqual(
engineLabels,
['PKI Certificates', 'Google Cloud KMS', 'Key Management', 'unknown-random-engine'],
'Engine labels are correct (sorted DESC)'
);
assert.deepEqual(
authMethodLabels,
['AliCloud', 'TLS Certificates', 'Username & Password', 'unknown-random-method'],
'Auth method labels are correct (sorted DESC)'
);
});
});

View File

@ -5,6 +5,7 @@
import Component from '@glimmer/component';
import { HdsLinkStandalone } from '@hashicorp/design-system-components/components';
import './title-row.scss';
import type { SafeString } from '@ember/template';
/**
* TitleRow Component
*
@ -16,7 +17,7 @@ export interface TitleRowSignature {
/** The main title text to display */
title: string;
/** Optional description text to display beneath the title */
description?: string;
description?: string | SafeString;
/** Custom text for the link (defaults to "View all") */
linkText?: string;
/** Icon to display with the link (defaults to "arrow-right") */

View File

@ -1 +1 @@
{"version":3,"file":"title-row.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/base/title-row.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAGL,iBAAiB,EAClB,MAAM,gDAAgD,CAAC;AACxD,OAAO,kBAAkB,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE;QACJ,qCAAqC;QACrC,KAAK,EAAE,MAAM,CAAC;QACd,6DAA6D;QAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,wDAAwD;QACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gEAAgE;QAChE,QAAQ,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACrC,gEAAgE;QAChE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gDAAgD;QAChD,UAAU,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;KACjC,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAChE,IAAI,OAAO,uBAEV;IAED,IAAI,QAAQ,WAEX;IAED,IAAI,OAAO,WAEV;IAED,IAAI,QAAQ,urTAEX;IAED,IAAI,UAAU,uBAEb;CAqDF"}
{"version":3,"file":"title-row.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/base/title-row.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAGL,iBAAiB,EAClB,MAAM,gDAAgD,CAAC;AACxD,OAAO,kBAAkB,CAAC;AAC1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE;QACJ,qCAAqC;QACrC,KAAK,EAAE,MAAM,CAAC;QACd,6DAA6D;QAC7D,WAAW,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;QAClC,wDAAwD;QACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gEAAgE;QAChE,QAAQ,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACrC,gEAAgE;QAChE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gDAAgD;QAChD,UAAU,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;KACjC,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,QAAS,SAAQ,SAAS,CAAC,iBAAiB,CAAC;IAChE,IAAI,OAAO,uBAEV;IAED,IAAI,QAAQ,WAEX;IAED,IAAI,OAAO,WAEV;IAED,IAAI,QAAQ,urTAEX;IAED,IAAI,UAAU,uBAEb;CAqDF"}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"cluster-replication.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/cluster-replication.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAO3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAEjE,OAAO,4BAA4B,CAAC;AAEpC,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE;QACJ,yBAAyB,EAAE,OAAO,CAAC;QACnC,qBAAqB,EAAE,yBAAyB,GAAG,UAAU,CAAC;QAC9D,oBAAoB,EAAE,OAAO,CAAC;QAC9B,gBAAgB,EAAE,yBAAyB,GAAG,UAAU,CAAC;KAC1D,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,SAAS,CAAC,2BAA2B,CAAC;IACpF,IAAI,qBAAqB,IAAI;QAC3B,IAAI,EAAE,OAAO,GAAG,GAAG,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;KAC9B,CAMA;IAED,IAAI,gBAAgB,IAAI;QACtB,IAAI,EAAE,OAAO,GAAG,GAAG,CAAC;QACpB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;KAC9B,CAMA;IAED,IAAI,oBAAoB,4BAEvB;IAED,IAAI,eAAe,4BAElB;CAyFF"}
{"version":3,"file":"cluster-replication.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/cluster-replication.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAO3C,OAAO,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AAIjE,OAAO,4BAA4B,CAAC;AAEpC,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE;QACJ,qBAAqB,EAAE,yBAAyB,GAAG,UAAU,CAAC;QAC9D,gBAAgB,EAAE,yBAAyB,GAAG,UAAU,CAAC;KAC1D,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,SAAS,CAAC,2BAA2B,CAAC;IACpF,QAAQ,WAAW,yBAAyB,GAAG,UAAU,4CAEvD;IAEF,IAAI,OAAO,YAKV;IAED,IAAI,WAAW,sGAQd;IAED,OAAO,WAAW,yBAAyB,GAAG,UAAU,osTAQtD;IAEF,QAAQ,WAAW,yBAAyB,GAAG,UAAU,oGAQvD;CAuDH"}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
{"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/counter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAO3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,gBAAgB,CAAC;AAExB,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,QAAQ,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,SAAS,CAAC,4BAA4B,CAAC;IACtF,IAAI,oBAAoB,+BAEvB;IACD,IAAI,KAAK,gCAUR;IAED,IAAI,IAAI,urTAEP;IAED,IAAI,SAAS,4BAKZ;CAqEF"}
{"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/counter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAO3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAE5D,OAAO,gBAAgB,CAAC;AAExB,MAAM,WAAW,4BAA4B;IAC3C,IAAI,EAAE;QACJ,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,IAAI,CAAC,EAAE,QAAQ,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,MAAM,CAAC,OAAO,OAAO,mBAAoB,SAAQ,SAAS,CAAC,4BAA4B,CAAC;IACtF,IAAI,oBAAoB,+BAEvB;IAED,IAAI,KAAK,gCAUR;IAED,IAAI,IAAI,urTAEP;IAED,IAAI,IAAI,uBAKP;CAuEF"}

View File

@ -1 +1 @@
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/dashboard/export.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAG3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE;QACJ,IAAI,CAAC,EAAE,kBAAkB,CAAC;KAC3B,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IACF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,SAAS,CAAC,wBAAwB,CAAC;;IAO9E,IAAI,4BAA4B,WAM/B;IAED,IAAI,2BAA2B,WA6D9B;CA2CF"}
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/dashboard/export.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAG3C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEzD,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE;QACJ,IAAI,CAAC,EAAE,kBAAkB,CAAC;KAC3B,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IACF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,SAAS,CAAC,wBAAwB,CAAC;;IAO9E,IAAI,4BAA4B,WAM/B;IAED,IAAI,2BAA2B,WA2D9B;CA2CF"}

View File

@ -0,0 +1,44 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { type Filter } from '../../utils/cel.ts';
export interface FilterFieldDefinition {
name: string;
label: string;
type: 'text' | 'multiselect' | 'number' | 'daterange';
options?: {
name: string;
value: string;
}[];
}
export interface FilterBarSignature {
Args: {
onFiltersApplied: (filters: Filter[]) => void;
appliedFilters: Filter[];
filterFieldDefinitions: FilterFieldDefinition[];
};
Blocks: {
default: [];
};
Element: HTMLElement;
}
export default class FilterBar extends Component<FilterBarSignature> {
updateFilters: (filters: Record<string, Filter>) => void;
handleClearFilters: () => void;
handleDismissFilter: (key: string) => void;
handleMultiselectChange: (name: string, event: Event) => void;
handleTextInputChange: (name: string, event: Event) => void;
handleNumberChange: (event: Event) => void;
handleDateRangeChange: (name: string, event: Event) => void;
isEqual: (a: string, b: string) => boolean;
isChecked: (name: string, value: string) => boolean;
getValue: (name: string) => string;
getOperator: (name: string) => "" | ">=" | "<=" | ">" | "<" | "=" | "!=" | "IN" | "NOT IN";
friendlyAppliedString: (appliedFilter: Filter) => string;
get appliedFilters(): Record<string, Filter>;
get appliedFiltersCount(): number;
get hasAppliedFilters(): boolean;
}
//# sourceMappingURL=filter-bar.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"filter-bar.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/filter-bar.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAG3C,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,QAAQ,GAAG,WAAW,CAAC;IACtD,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC7C;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE;QACJ,gBAAgB,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;QAC9C,cAAc,EAAE,MAAM,EAAE,CAAC;QACzB,sBAAsB,EAAE,qBAAqB,EAAE,CAAC;KACjD,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IACF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,SAAS,CAAC,kBAAkB,CAAC;IAClE,aAAa,YAAa,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,UAE9C;IAEF,kBAAkB,aAEhB;IAEF,mBAAmB,QAAS,MAAM,UAIhC;IAEF,uBAAuB,SAAU,MAAM,SAAS,KAAK,UA8BnD;IAEF,qBAAqB,SAAU,MAAM,SAAS,KAAK,UAajD;IAEF,kBAAkB,UAAW,KAAK,UAiBhC;IAEF,qBAAqB,SAAU,MAAM,SAAS,KAAK,UAcjD;IAEF,OAAO,MAAO,MAAM,KAAK,MAAM,aAE7B;IAEF,SAAS,SAAU,MAAM,SAAS,MAAM,aAGtC;IAEF,QAAQ,SAAU,MAAM,YAEtB;IAEF,WAAW,SAAU,MAAM,iEAEzB;IAEF,qBAAqB,kBAAmB,MAAM,YAM5C;IAEF,IAAI,cAAc,2BAgBjB;IAED,IAAI,mBAAmB,WAEtB;IAED,IAAI,iBAAiB,YAEpB;CAoSF"}

View File

@ -26,11 +26,15 @@ export interface GlobalLeaseSignature {
}
export default class GlobalLease extends Component<GlobalLeaseSignature> {
get percentage(): number;
get progressFillClass(): "ssu-global-lease__progress-fill--low" | "ssu-global-lease__progress-fill--medium" | "ssu-global-lease__progress-fill--high";
get progressFillClass(): "" | "ssu-global-lease__progress-fill--exceeded";
get formattedCount(): string;
get percentageString(): string;
get hasData(): boolean | 0 | undefined;
get description(): "Snapshot of global lease count quota consumption" | undefined;
get linkUrl(): "https://developer.hashicorp.com/vault/docs/enterprise/lease-count-quotas" | undefined;
get description(): import("@ember/template").SafeString | undefined;
get linkUrl(): "https://developer.hashicorp.com/vault/tutorials/operations/resource-quotas#global-default-lease-count-quota" | undefined;
get alert(): {
color: 'warning' | 'neutral';
description: string;
} | undefined;
}
//# sourceMappingURL=global-lease.d.ts.map

View File

@ -1 +1 @@
{"version":3,"file":"global-lease.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/global-lease.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAQ3C,OAAO,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4EAA4E,CAAC;AAE/H,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;QACZ;;;;;;;aAOK;QACL,KAAK,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;KAC1D,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,SAAS,CAAC,oBAAoB,CAAC;IACtE,IAAI,UAAU,WAIb;IAED,IAAI,iBAAiB,iIAQpB;IAED,IAAI,cAAc,WAWjB;IAED,IAAI,gBAAgB,WAEnB;IAED,IAAI,OAAO,4BAEV;IAED,IAAI,WAAW,mEAId;IAED,IAAI,OAAO,2FAIV;CAgHF"}
{"version":3,"file":"global-lease.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/global-lease.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAS3C,OAAO,qBAAqB,CAAC;AAE7B,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4EAA4E,CAAC;AAG/H,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE;QACJ,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;QACZ;;;;;;;aAOK;QACL,KAAK,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;KAC1D,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,SAAS,CAAC,oBAAoB,CAAC;IACtE,IAAI,UAAU,WAIb;IAED,IAAI,iBAAiB,qDAKpB;IAED,IAAI,cAAc,WAWjB;IAED,IAAI,gBAAgB,WAEnB;IAED,IAAI,OAAO,4BAEV;IAED,IAAI,WAAW,qDAMd;IAED,IAAI,OAAO,8HAIV;IAED,IAAI,KAAK,IACL;QAAE,KAAK,EAAE,SAAS,GAAG,SAAS,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GACrD,SAAS,CAgBZ;CAgIF"}

View File

@ -4,6 +4,7 @@
*/
import './horizontal-bar-chart.scss';
import Component from '@glimmer/component';
import { HdsLinkStandalone } from '@hashicorp/design-system-components/components';
import type { SimpleDatum } from '../../types/index.ts';
import type { HdsApplicationStateSignature } from '@hashicorp/design-system-components/components/hds/application-state/index';
export interface SSUReportingHorizontalBarChartSignature {
@ -11,7 +12,14 @@ export interface SSUReportingHorizontalBarChartSignature {
data: SimpleDatum[];
title: string;
description?: string;
/** Custom text for the link (defaults to "View all") */
linkText?: string;
/** Icon to display with the link (defaults to "arrow-right") */
linkIcon?: HdsLinkStandalone['icon'];
/** URL for the link - if not provided, no link will be shown */
linkUrl?: string;
/** Target for the link - defaults to "_self" */
linkTarget?: '_blank' | '_self';
};
Blocks: {
default: [];

View File

@ -1 +1 @@
{"version":3,"file":"horizontal-bar-chart.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/horizontal-bar-chart.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,6BAA6B,CAAC;AACrC,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAkB3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4EAA4E,CAAC;AAE/H,MAAM,WAAW,uCAAuC;IACtD,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW,EAAE,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;KAClB,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;QACZ;;;;;;;aAOK;QACL,KAAK,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;KAC1D,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,MAAM,CAAC,OAAO,OAAO,8BAA+B,SAAQ,SAAS,CAAC,uCAAuC,CAAC;IACnG,iBAAiB,SAAK;IAE/B,IAAI,OAAO,YAMV;IAED,IAAI,IAAI,kBAUP;IAED,IAAI,KAAK,WAIR;IAED,IAAI,SAAS,WAUZ;IAED,IAAI,OAAO,aAEV;IAED,IAAI,OAAO,aAEV;IAED,IAAI,WAAW,WAEd;IAED,IAAI,MAAM,aAET;IAED,IAAI,eAAe,WAElB;IAED,IAAI,qBAAqB,WAGxB;IAED,IAAI,kBAAkB,WAGrB;IAED,IAAI,WAAW,uBAId;IAED,IAAI,OAAO,uBAIV;IAED,SAAS,UAAW,MAAM,cAExB;IAEF,gBAAgB,gBAAiB,MAAM,UAErC;CAmKH"}
{"version":3,"file":"horizontal-bar-chart.d.ts","sourceRoot":"","sources":["../../../src/components/vault-reporting/horizontal-bar-chart.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,6BAA6B,CAAC;AACrC,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAE3C,OAAO,EAKL,iBAAiB,EAClB,MAAM,gDAAgD,CAAC;AAWxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,4EAA4E,CAAC;AAE/H,MAAM,WAAW,uCAAuC;IACtD,IAAI,EAAE;QACJ,IAAI,EAAE,WAAW,EAAE,CAAC;QACpB,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,wDAAwD;QACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,gEAAgE;QAChE,QAAQ,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACrC,gEAAgE;QAChE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,gDAAgD;QAChD,UAAU,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;KACjC,CAAC;IACF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;QACZ;;;;;;;aAOK;QACL,KAAK,EAAE,4BAA4B,CAAC,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC;KAC1D,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,MAAM,CAAC,OAAO,OAAO,8BAA+B,SAAQ,SAAS,CAAC,uCAAuC,CAAC;IACnG,iBAAiB,SAAK;IAE/B,IAAI,OAAO,YAMV;IAED,IAAI,IAAI,kBAUP;IAED,IAAI,KAAK,WAIR;IAED,IAAI,SAAS,WAUZ;IAED,IAAI,OAAO,aAEV;IAED,IAAI,OAAO,aAEV;IAED,IAAI,WAAW,WAEd;IAED,IAAI,MAAM,aAET;IAED,IAAI,eAAe,WAElB;IAED,IAAI,qBAAqB,WAGxB;IAED,IAAI,kBAAkB,WAGrB;IAED,IAAI,WAAW,uBAId;IAED,IAAI,OAAO,uBAIV;IAED,SAAS,UAAW,MAAM,cAExB;IAEF,gBAAgB,gBAAiB,MAAM,UAErC;CAoKH"}

View File

@ -14,6 +14,7 @@ interface CounterBlock {
suffix?: string;
link?: string;
emptyText?: string;
emptyLink?: string;
}
export interface SSUViewDashboardSignature {
Args: {
@ -31,7 +32,7 @@ export default class SSUViewDashboard extends Component<SSUViewDashboardSignatur
error?: unknown;
constructor(owner: unknown, args: SSUViewDashboardSignature['Args']);
fetchAllData: () => void;
getBarChartData: (map: Record<string, number>) => SimpleDatum[];
getBarChartData: (map: Record<string, number>, exclude?: string[]) => SimpleDatum[];
get isVaultDedicated(): boolean;
get kvSecretsTooltipMessage(): string;
get counters(): CounterBlock[];

View File

@ -1 +1 @@
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/views/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAE3C,OAAO,kBAAkB,CAAC;AAO1B,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAa5D,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE;QACJ,gBAAgB,EAAE,oBAAoB,CAAC;QACvC,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,SAAS,CAAC,yBAAyB,CAAC;IAEhF,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAG1B,eAAe,EAAE,MAAM,CAAM;IAG7B,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEJ,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC,MAAM,CAAC;IAKnE,YAAY,EAAE,MAAM,IAAI,CAWtB;IAEF,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,WAAW,EAAE,CAS7D;IAEF,IAAI,gBAAgB,IAAI,OAAO,CAE9B;IAED,IAAI,uBAAuB,IAAI,MAAM,CAgBpC;IAED,IAAI,QAAQ,IAAI,YAAY,EAAE,CAmC7B;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;CAqLF"}
{"version":3,"file":"dashboard.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/views/dashboard.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAE3C,OAAO,kBAAkB,CAAC;AAO1B,OAAO,KAAK,EACV,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AAExB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAU5D,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE;QACJ,gBAAgB,EAAE,oBAAoB,CAAC;QACvC,gBAAgB,EAAE,OAAO,CAAC;KAC3B,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AACD,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,SAAS,CAAC,yBAAyB,CAAC;IAEhF,IAAI,CAAC,EAAE,kBAAkB,CAAC;IAG1B,eAAe,EAAE,MAAM,CAAM;IAG7B,KAAK,CAAC,EAAE,OAAO,CAAC;gBAEJ,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,yBAAyB,CAAC,MAAM,CAAC;IAKnE,YAAY,EAAE,MAAM,IAAI,CAUtB;IAEF,eAAe,EAAE,CACf,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3B,OAAO,CAAC,EAAE,MAAM,EAAE,KACf,WAAW,EAAE,CAWhB;IAEF,IAAI,gBAAgB,IAAI,OAAO,CAE9B;IAED,IAAI,uBAAuB,IAAI,MAAM,CAepC;IAED,IAAI,QAAQ,IAAI,YAAY,EAAE,CAgC7B;IAED,IAAI,SAAS,IAAI,MAAM,CAEtB;CAkNF"}

View File

@ -0,0 +1,28 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { type FilterFieldDefinition } from '../filter-bar';
import './secret-inventory.scss';
import Component from '@glimmer/component';
import { type Filter } from '@hashicorp/vault-reporting/utils/cel';
export interface SecretInventorySignature {
Args: {
onFilterApplied: (value: string) => void;
filterString: string;
};
Blocks: {
default: [];
};
Element: HTMLElement;
}
export default class SecretInventory extends Component<SecretInventorySignature> {
quickFilters: {
label: string;
applyFilter: () => void;
}[];
filterFieldDefinitions: FilterFieldDefinition[];
handleApplyFilters: (filters: Filter[]) => void;
get appliedFilters(): Filter[];
}
//# sourceMappingURL=secret-inventory.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"secret-inventory.d.ts","sourceRoot":"","sources":["../../../../src/components/vault-reporting/views/secret-inventory.ts"],"names":[],"mappings":"AAAA;;;GAGG;AASH,OAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,yBAAyB,CAAC;AACjC,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAEL,KAAK,MAAM,EAEZ,MAAM,sCAAsC,CAAC;AAE9C,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE;QACJ,eAAe,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;QACzC,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;IAEF,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;IAEF,OAAO,EAAE,WAAW,CAAC;CACtB;AAQD,MAAM,CAAC,OAAO,OAAO,eAAgB,SAAQ,SAAS,CAAC,wBAAwB,CAAC;IAC9E,YAAY;;;QAyEV;IACF,sBAAsB,EAAE,qBAAqB,EAAE,CAgD7C;IACF,kBAAkB,YAAa,MAAM,EAAE,UAErC;IAEF,IAAI,cAAc,aAKjB;CA2EF"}

View File

@ -0,0 +1,17 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Service from '@ember/service';
export default class ReportingAnalytics extends Service {
get analytics(): {
trackEvent: (event: string, properties?: object, options?: object) => void;
} | undefined;
trackEvent(event: string, properties?: object, options?: object): void;
}
declare module '@ember/service' {
interface Registry {
reportingAnalytics: ReportingAnalytics;
}
}
//# sourceMappingURL=reporting-analytics.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"reporting-analytics.d.ts","sourceRoot":"","sources":["../../src/services/reporting-analytics.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,OAAO,MAAM,gBAAgB,CAAC;AAErC,MAAM,CAAC,OAAO,OAAO,kBAAmB,SAAQ,OAAO;IAGrD,IAAI,SAAS,IAEP;QACE,UAAU,EAAE,CACV,KAAK,EAAE,MAAM,EACb,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,KACb,IAAI,CAAC;KACX,GACD,SAAS,CACd;IAED,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM;CAUhE;AAGD,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,QAAQ;QAChB,kBAAkB,EAAE,kBAAkB,CAAC;KACxC;CACF"}

View File

@ -20,30 +20,33 @@ export declare enum REPLICATION_ENABLED_STATE {
}
export declare const REPLICATION_DISABLED_STATE = "disabled";
export interface UsageDashboardData {
auth_methods: Record<string, number>;
kvv1_secrets: number;
kvv2_secrets: number;
lease_count_quotas: {
global_lease_count_quota: {
authMethods: Record<string, number>;
leasesByAuthMethod: Record<string, number>;
kvv1Secrets: number;
kvv2Secrets: number;
leaseCountQuotas: {
globalLeaseCountQuota: {
capacity: number;
count: number;
name: string;
};
total_lease_count_quotas: number;
totalLeaseCountQuotas: number;
};
namespaces: number;
secrets_sync: number;
secretSync: {
totalDestinations: number;
};
pki: {
total_issuers: number;
total_roles: number;
totalIssuers: number;
totalRoles: number;
};
replication_status: {
dr_primary: boolean;
dr_state: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;
pr_primary: boolean;
pr_state: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;
replicationStatus: {
drPrimary: boolean;
drState: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;
prPrimary: boolean;
prState: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;
};
secret_engines: Record<string, number>;
secretEngines: Record<string, number>;
}
export type getUsageDataFunction = () => Promise<UsageDashboardData>;
export {};

View File

@ -1 +1 @@
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,aAAa,GAAG,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;AAChF,KAAK,aAAa,GAAG,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;AAChF,KAAK,iBAAiB,GAAG,GAAG,aAAa,IAAI,aAAa,EAAE,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,oBAAY,yBAAyB;IACnC,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,aAAa,kBAAkB;CAChC;AACD,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,kBAAkB,EAAE;QAClB,wBAAwB,EAAE;YACxB,QAAQ,EAAE,MAAM,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;QACF,wBAAwB,EAAE,MAAM,CAAC;KAClC,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE;QACH,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,kBAAkB,EAAE;QAClB,UAAU,EAAE,OAAO,CAAC;QACpB,QAAQ,EAAE,yBAAyB,GAAG,OAAO,0BAA0B,CAAC;QACxE,UAAU,EAAE,OAAO,CAAC;QACpB,QAAQ,EAAE,yBAAyB,GAAG,OAAO,0BAA0B,CAAC;KACzE,CAAC;IACF,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,KAAK,aAAa,GAAG,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;AAChF,KAAK,aAAa,GAAG,GAAG,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,IAAI,MAAM,GAAG,MAAM,EAAE,CAAC;AAChF,KAAK,iBAAiB,GAAG,GAAG,aAAa,IAAI,aAAa,EAAE,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,iBAAiB,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAMD,oBAAY,yBAAyB;IACnC,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,aAAa,kBAAkB;CAChC;AACD,eAAO,MAAM,0BAA0B,aAAa,CAAC;AAErD,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,kBAAkB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE;QAChB,qBAAqB,EAAE;YACrB,QAAQ,EAAE,MAAM,CAAC;YACjB,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;SACd,CAAC;QACF,qBAAqB,EAAE,MAAM,CAAC;KAC/B,CAAC;IACF,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE;QAAE,iBAAiB,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,GAAG,EAAE;QACH,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,iBAAiB,EAAE;QACjB,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,EAAE,yBAAyB,GAAG,OAAO,0BAA0B,CAAC;QACvE,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,EAAE,yBAAyB,GAAG,OAAO,0BAA0B,CAAC;KACxE,CAAC;IACF,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED,MAAM,MAAM,oBAAoB,GAAG,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC"}

View File

@ -0,0 +1,15 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
export interface Filter {
field: string;
operator: '>=' | '<=' | '>' | '<' | '=' | '!=' | 'IN' | 'NOT IN';
value: {
type: 'timestamp' | 'list' | 'string' | 'number';
value: unknown;
};
}
export declare const expressionToFilters: (expression: string) => Filter[];
export declare const filtersToExpression: (filters: Filter[]) => string;
//# sourceMappingURL=cel.d.ts.map

View File

@ -0,0 +1 @@
{"version":3,"file":"cel.d.ts","sourceRoot":"","sources":["../../src/utils/cel.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAsCH,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,QAAQ,CAAC;IACjE,KAAK,EAAE;QACL,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;QACjD,KAAK,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAiCD,eAAO,MAAM,mBAAmB,eAAgB,MAAM,aA4BrD,CAAC;AAEF,eAAO,MAAM,mBAAmB,YAAa,MAAM,EAAE,WAepD,CAAC"}

View File

@ -1 +1 @@
{"version":3,"file":"title-row.js","sources":["../../../../src/components/vault-reporting/base/title-row.gts"],"sourcesContent":["/**\n * Copyright (c) HashiCorp, Inc.\n * SPDX-License-Identifier: BUSL-1.1\n */\n\nimport Component from '@glimmer/component';\nimport {\n HdsTextDisplay,\n HdsTextBody,\n HdsLinkStandalone,\n} from '@hashicorp/design-system-components/components';\nimport './title-row.scss';\n\n/**\n * TitleRow Component\n *\n * A reusable component that displays a title with an optional description and link.\n * Used in dashboard cards to create consistent header styling.\n */\nexport interface TitleRowSignature {\n Args: {\n /** The main title text to display */\n title: string;\n /** Optional description text to display beneath the title */\n description?: string;\n /** Custom text for the link (defaults to \"View all\") */\n linkText?: string;\n /** Icon to display with the link (defaults to \"arrow-right\") */\n linkIcon?: HdsLinkStandalone['icon'];\n /** URL for the link - if not provided, no link will be shown */\n linkUrl?: string;\n /** Target for the link - defaults to \"_self\" */\n linkTarget?: '_blank' | '_self';\n };\n\n Blocks: {\n default: [];\n };\n\n Element: HTMLElement;\n}\n\nexport default class TitleRow extends Component<TitleRowSignature> {\n get hasLink() {\n return this.args.linkUrl;\n }\n\n get linkText() {\n return this.args.linkText || 'View all';\n }\n\n get linkUrl() {\n return this.args.linkUrl || '#';\n }\n\n get linkIcon() {\n return this.args.linkIcon || 'arrow-right';\n }\n\n get linkTarget() {\n return this.args.linkTarget || '_self';\n }\n\n <template>\n <div class=\"ssu-title-row\" data-test-dashboard-card-title-row>\n <div class=\"ssu-title-row__container\">\n <HdsTextDisplay data-test-dashboard-card-title @size=\"300\">\n {{@title}}\n </HdsTextDisplay>\n\n {{#if this.hasLink}}\n <HdsLinkStandalone\n data-test-dashboard-card-title-link\n class=\"ssu-title-row__container__link\"\n @text={{this.linkText}}\n @href={{this.linkUrl}}\n @icon={{this.linkIcon}}\n target={{this.linkTarget}}\n @iconPosition=\"trailing\"\n />\n {{/if}}\n </div>\n\n {{#if @description}}\n <HdsTextBody\n class=\"ssu-title-row__description\"\n data-test-dashboard-card-description\n >\n {{@description}}\n </HdsTextBody>\n {{/if}}\n </div>\n </template>\n}\n"],"names":["TitleRow","Component","hasLink","args","linkUrl","linkText","linkIcon","linkTarget","setComponentTemplate","precompileTemplate","strictMode","scope","HdsTextDisplay","HdsLinkStandalone","HdsTextBody"],"mappings":";;;;;AAAA;;;AAGC;AAuCc,MAAMA,iBAAiBC,SAAU,CAAA;EAC9C,IAAIC,OAAUA,GAAA;AACZ,IAAA,OAAO,IAAI,CAACC,IAAI,CAACC,OAAO;AAC1B;EAEA,IAAIC,QAAWA,GAAA;AACb,IAAA,OAAO,IAAI,CAACF,IAAI,CAACE,QAAQ,IAAI,UAAA;AAC/B;EAEA,IAAID,OAAUA,GAAA;AACZ,IAAA,OAAO,IAAI,CAACD,IAAI,CAACC,OAAO,IAAI,GAAA;AAC9B;EAEA,IAAIE,QAAWA,GAAA;AACb,IAAA,OAAO,IAAI,CAACH,IAAI,CAACG,QAAQ,IAAI,aAAA;AAC/B;EAEA,IAAIC,UAAaA,GAAA;AACf,IAAA,OAAO,IAAI,CAACJ,IAAI,CAACI,UAAU,IAAI,OAAA;AACjC;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CA6BA,4uBAAA,EAAA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,cAAA;QAAAC,iBAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
{"version":3,"file":"title-row.js","sources":["../../../../src/components/vault-reporting/base/title-row.gts"],"sourcesContent":["/**\n * Copyright (c) HashiCorp, Inc.\n * SPDX-License-Identifier: BUSL-1.1\n */\n\nimport Component from '@glimmer/component';\nimport {\n HdsTextDisplay,\n HdsTextBody,\n HdsLinkStandalone,\n} from '@hashicorp/design-system-components/components';\nimport './title-row.scss';\nimport type { SafeString } from '@ember/template';\n\n/**\n * TitleRow Component\n *\n * A reusable component that displays a title with an optional description and link.\n * Used in dashboard cards to create consistent header styling.\n */\nexport interface TitleRowSignature {\n Args: {\n /** The main title text to display */\n title: string;\n /** Optional description text to display beneath the title */\n description?: string | SafeString;\n /** Custom text for the link (defaults to \"View all\") */\n linkText?: string;\n /** Icon to display with the link (defaults to \"arrow-right\") */\n linkIcon?: HdsLinkStandalone['icon'];\n /** URL for the link - if not provided, no link will be shown */\n linkUrl?: string;\n /** Target for the link - defaults to \"_self\" */\n linkTarget?: '_blank' | '_self';\n };\n\n Blocks: {\n default: [];\n };\n\n Element: HTMLElement;\n}\n\nexport default class TitleRow extends Component<TitleRowSignature> {\n get hasLink() {\n return this.args.linkUrl;\n }\n\n get linkText() {\n return this.args.linkText || 'View all';\n }\n\n get linkUrl() {\n return this.args.linkUrl || '#';\n }\n\n get linkIcon() {\n return this.args.linkIcon || 'arrow-right';\n }\n\n get linkTarget() {\n return this.args.linkTarget || '_self';\n }\n\n <template>\n <div class=\"ssu-title-row\" data-test-dashboard-card-title-row>\n <div class=\"ssu-title-row__container\">\n <HdsTextDisplay data-test-dashboard-card-title @size=\"300\">\n {{@title}}\n </HdsTextDisplay>\n\n {{#if this.hasLink}}\n <HdsLinkStandalone\n data-test-dashboard-card-title-link\n class=\"ssu-title-row__container__link\"\n @text={{this.linkText}}\n @href={{this.linkUrl}}\n @icon={{this.linkIcon}}\n target={{this.linkTarget}}\n @iconPosition=\"trailing\"\n />\n {{/if}}\n </div>\n\n {{#if @description}}\n <HdsTextBody\n class=\"ssu-title-row__description\"\n data-test-dashboard-card-description\n >\n {{@description}}\n </HdsTextBody>\n {{/if}}\n </div>\n </template>\n}\n"],"names":["TitleRow","Component","hasLink","args","linkUrl","linkText","linkIcon","linkTarget","setComponentTemplate","precompileTemplate","strictMode","scope","HdsTextDisplay","HdsLinkStandalone","HdsTextBody"],"mappings":";;;;;AAAA;;;AAGC;AAwCc,MAAMA,iBAAiBC,SAAU,CAAA;EAC9C,IAAIC,OAAUA,GAAA;AACZ,IAAA,OAAO,IAAI,CAACC,IAAI,CAACC,OAAO;AAC1B;EAEA,IAAIC,QAAWA,GAAA;AACb,IAAA,OAAO,IAAI,CAACF,IAAI,CAACE,QAAQ,IAAI,UAAA;AAC/B;EAEA,IAAID,OAAUA,GAAA;AACZ,IAAA,OAAO,IAAI,CAACD,IAAI,CAACC,OAAO,IAAI,GAAA;AAC9B;EAEA,IAAIE,QAAWA,GAAA;AACb,IAAA,OAAO,IAAI,CAACH,IAAI,CAACG,QAAQ,IAAI,aAAA;AAC/B;EAEA,IAAIC,UAAaA,GAAA;AACf,IAAA,OAAO,IAAI,CAACJ,IAAI,CAACI,UAAU,IAAI,OAAA;AACjC;AAEA,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CA6BA,4uBAAA,EAAA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,cAAA;QAAAC,iBAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}

View File

@ -1,7 +1,7 @@
import Component from '@glimmer/component';
import { HdsBadge, HdsTextBody, HdsCardContainer } from '@hashicorp/design-system-components/components';
import TitleRow from './base/title-row.js';
import { REPLICATION_ENABLED_STATE } from '../../types/index.js';
import { htmlSafe } from '@ember/template';
import { precompileTemplate } from '@ember/template-compilation';
import { setComponentTemplate } from '@ember/component';
@ -10,36 +10,39 @@ import { setComponentTemplate } from '@ember/component';
* SPDX-License-Identifier: BUSL-1.1
*/
class ClusterReplication extends Component {
get disasterRecoveryBadge() {
return Object.values(REPLICATION_ENABLED_STATE).includes(this.args.disasterRecoveryState) ? {
icon: 'check',
text: 'Enabled',
color: 'success'
} : {
icon: 'x',
text: 'Not set up',
color: 'neutral'
getState = (state = 'disabled') => {
return state;
};
get isEmpty() {
return this.getState(this.args.disasterRecoveryState) === 'disabled' && this.getState(this.args.performanceState) === 'disabled';
}
get description() {
if (this.isEmpty) {
return htmlSafe('Enable <a class="hds-link-inline--color-secondary" href="https://developer.hashicorp.com/vault/docs/internals/replication" target="_blank" data-test-cluster-replication-description-link>replication</a> to replicate data across clusters.');
} else {
return 'Status of disaster recovery and performance replication.';
}
}
getIcon = (state = 'disabled') => {
const iconMap = {
disabled: 'x',
primary: 'check',
secondary: 'check',
bootstrapping: 'loading'
};
}
get performanceBadge() {
return Object.values(REPLICATION_ENABLED_STATE).includes(this.args.performanceState) ? {
icon: 'check',
text: 'Enabled',
color: 'success'
} : {
icon: 'x',
text: 'Not set up',
color: 'neutral'
return iconMap[state] || iconMap['disabled'];
};
getColor = (state = 'disabled') => {
const colorMap = {
disabled: 'neutral',
primary: 'success',
secondary: 'success',
bootstrapping: 'neutral'
};
}
get disasterRecoveryRole() {
return this.args.isDisasterRecoveryPrimary ? 'Primary' : 'Secondary';
}
get performanceRole() {
return this.args.isPerformancePrimary ? 'Primary' : 'Secondary';
}
return colorMap[state] || colorMap['disabled'];
};
static {
setComponentTemplate(precompileTemplate("\n <HdsCardContainer data-test-cluster-replication @hasBorder={{true}} class=\"ssu-cluster-replication\" ...attributes>\n <TitleRow @title=\"Cluster replication status\" @description=\"Check the status and health of Vault clusters\" @linkUrl=\"replication\" />\n\n <div class=\"ssu-cluster-replication__list-row\" data-test-cluster-replication-dr-row>\n <HdsTextBody @size=\"300\">\n Disaster Recovery\n <HdsBadge class=\"ssu-cluster-replication__list-row__badge\" data-test-cluster-replication-dr-badge @icon={{this.disasterRecoveryBadge.icon}} @text={{this.disasterRecoveryBadge.text}} @color={{this.disasterRecoveryBadge.color}} @type=\"outlined\" @size=\"small\" />\n </HdsTextBody>\n\n <HdsTextBody class=\"ssu-cluster-replication__list-row__role\" data-test-cluster-replication-dr-role @size=\"300\" @color=\"var(--token-color-palette-green-400)\">\n {{this.disasterRecoveryRole}}\n </HdsTextBody>\n </div>\n\n <div class=\"ssu-cluster-replication__list-row\" data-test-cluster-replication-perf-row>\n <HdsTextBody @size=\"300\">\n Performance\n <HdsBadge class=\"ssu-cluster-replication__list-row__badge\" data-test-cluster-replication-perf-badge @icon={{this.performanceBadge.icon}} @text={{this.performanceBadge.text}} @color={{this.performanceBadge.color}} @type=\"outlined\" @size=\"small\" />\n </HdsTextBody>\n\n <HdsTextBody class=\"ssu-cluster-replication__list-row__role\" data-test-cluster-replication-perf-role @size=\"300\" @color=\"var(--token-color-palette-green-400)\">\n {{this.performanceRole}}\n </HdsTextBody>\n </div>\n\n </HdsCardContainer>\n ", {
setComponentTemplate(precompileTemplate("\n <HdsCardContainer data-test-cluster-replication @hasBorder={{true}} class=\"ssu-cluster-replication\" ...attributes>\n <TitleRow @title=\"Cluster replication\" @description={{this.description}} @linkUrl=\"replication\" />\n\n <HdsTextBody @size=\"300\" data-test-cluster-replication-dr-row>\n Disaster Recovery\n <HdsBadge class=\"ssu-cluster-replication__list-row__badge\" data-test-cluster-replication-dr-badge @icon={{this.getIcon @disasterRecoveryState}} @text={{this.getState @disasterRecoveryState}} @color={{this.getColor @disasterRecoveryState}} @type=\"outlined\" @size=\"small\" />\n </HdsTextBody>\n\n <HdsTextBody @size=\"300\" data-test-cluster-replication-perf-row>\n Performance\n <HdsBadge class=\"ssu-cluster-replication__list-row__badge\" data-test-cluster-replication-perf-badge @icon={{this.getIcon @performanceState}} @text={{this.getState @performanceState}} @color={{this.getColor @performanceState}} @type=\"outlined\" @size=\"small\" />\n </HdsTextBody>\n\n </HdsCardContainer>\n ", {
strictMode: true,
scope: () => ({
HdsCardContainer,

File diff suppressed because one or more lines are too long

View File

@ -23,14 +23,14 @@ class SSUReportingCounter extends Component {
get icon() {
return this.args.icon || 'info';
}
get linkColor() {
if (this.shouldShowEmptyState) {
return 'secondary';
get link() {
if (this.shouldShowEmptyState && this.args.emptyLink) {
return this.args.emptyLink;
}
return 'primary';
return this.args.link;
}
static {
setComponentTemplate(precompileTemplate("\n <div ...attributes data-test-counter={{@title}} class=\"ssu-counter\">\n <div class=\"ssu-counter__title-row\">\n <HdsTextBody @weight=\"semibold\">{{@title}}\n {{#if @tooltipMessage}}\n <HdsTooltipButton data-test-counter-tooltip-button class=\"ssu-counter__title-row__tooltip\" @text={{@tooltipMessage}} aria-label=\"tooltip\" @isInline={{true}}>\n <HdsIcon @name=\"help\" @isInline={{true}} />\n </HdsTooltipButton>\n {{/if}}\n </HdsTextBody>\n </div>\n\n <HdsTextBody>\n {{#if @link}}\n <HdsLinkInline @href={{@link}} @color={{this.linkColor}} class=\"ssu-counter__link\">{{this.count}}\n </HdsLinkInline>\n {{else}}\n {{this.count}}\n {{/if}}\n </HdsTextBody>\n </div>\n ", {
setComponentTemplate(precompileTemplate("\n <div ...attributes data-test-counter={{@title}} class=\"ssu-counter\" aria-label=\"{{@title}} {{this.count}}\">\n <div class=\"ssu-counter__title-row\">\n <HdsTextBody @weight=\"semibold\" @size=\"200\" @color=\"primary\">{{@title}}\n {{#if @tooltipMessage}}\n <HdsTooltipButton data-test-counter-tooltip-button class=\"ssu-counter__title-row__tooltip\" @text={{@tooltipMessage}} aria-label=\"Tooltip for {{@title}}\" @isInline={{true}}>\n <HdsIcon @name=\"help\" @isInline={{true}} />\n </HdsTooltipButton>\n {{/if}}\n </HdsTextBody>\n </div>\n\n <HdsTextBody>\n {{#if this.link}}\n <HdsLinkInline @href={{this.link}} @color=\"secondary\" class=\"ssu-counter__link\" target=\"_self\">{{this.count}}\n </HdsLinkInline>\n {{else}}\n {{this.count}}\n {{/if}}\n </HdsTextBody>\n </div>\n ", {
strictMode: true,
scope: () => ({
HdsTextBody,

View File

@ -1 +1 @@
{"version":3,"file":"counter.js","sources":["../../../src/components/vault-reporting/counter.gts"],"sourcesContent":["/**\n * Copyright (c) HashiCorp, Inc.\n * SPDX-License-Identifier: BUSL-1.1\n */\n\nimport Component from '@glimmer/component';\nimport {\n HdsTextBody,\n HdsIcon,\n HdsTooltipButton,\n HdsLinkInline,\n} from '@hashicorp/design-system-components/components';\nimport type { IconName } from '@hashicorp/flight-icons/svg';\n\nimport './counter.scss';\n\nexport interface SSUReportingCounterSignature {\n Args: {\n count: number;\n title: string;\n tooltipMessage?: string;\n icon?: IconName;\n suffix?: string;\n link?: string;\n emptyText?: string;\n };\n\n Blocks: {\n default: [];\n };\n\n Element: HTMLElement;\n}\nexport default class SSUReportingCounter extends Component<SSUReportingCounterSignature> {\n get shouldShowEmptyState() {\n return this.args.count === 0 && this.args.emptyText;\n }\n get count() {\n if (this.shouldShowEmptyState) {\n return this.args.emptyText;\n }\n\n if (this.args.suffix) {\n return `${this.args.count} ${this.args.suffix}`;\n }\n\n return this.args.count;\n }\n\n get icon() {\n return this.args.icon || 'info';\n }\n\n get linkColor() {\n if (this.shouldShowEmptyState) {\n return 'secondary';\n }\n return 'primary';\n }\n\n <template>\n <div ...attributes data-test-counter={{@title}} class=\"ssu-counter\">\n <div class=\"ssu-counter__title-row\">\n <HdsTextBody @weight=\"semibold\">{{@title}}\n {{#if @tooltipMessage}}\n <HdsTooltipButton\n data-test-counter-tooltip-button\n class=\"ssu-counter__title-row__tooltip\"\n @text={{@tooltipMessage}}\n aria-label=\"tooltip\"\n @isInline={{true}}\n >\n <HdsIcon @name=\"help\" @isInline={{true}} />\n </HdsTooltipButton>\n {{/if}}\n </HdsTextBody>\n </div>\n\n <HdsTextBody>\n {{#if @link}}\n <HdsLinkInline\n @href={{@link}}\n @color={{this.linkColor}}\n class=\"ssu-counter__link\"\n >{{this.count}}\n </HdsLinkInline>\n {{else}}\n {{this.count}}\n {{/if}}\n </HdsTextBody>\n </div>\n </template>\n}\n"],"names":["SSUReportingCounter","Component","shouldShowEmptyState","args","count","emptyText","suffix","icon","linkColor","setComponentTemplate","precompileTemplate","strictMode","scope","HdsTextBody","HdsTooltipButton","HdsIcon","HdsLinkInline"],"mappings":";;;;;AAAA;;;AAGC;AA8Bc,MAAMA,4BAA4BC,SAAU,CAAA;EACzD,IAAIC,oBAAuBA,GAAA;AACzB,IAAA,OAAO,IAAI,CAACC,IAAI,CAACC,KAAK,KAAK,CAAA,IAAK,IAAI,CAACD,IAAI,CAACE,SAAS;AACrD;EACA,IAAID,KAAQA,GAAA;IACV,IAAI,IAAI,CAACF,oBAAoB,EAAE;AAC7B,MAAA,OAAO,IAAI,CAACC,IAAI,CAACE,SAAS;AAC5B;AAEA,IAAA,IAAI,IAAI,CAACF,IAAI,CAACG,MAAM,EAAE;AACpB,MAAA,OAAO,CAAG,EAAA,IAAI,CAACH,IAAI,CAACC,KAAK,CAAI,CAAA,EAAA,IAAI,CAACD,IAAI,CAACG,MAAM,CAAE,CAAA;AACjD;AAEA,IAAA,OAAO,IAAI,CAACH,IAAI,CAACC,KAAK;AACxB;EAEA,IAAIG,IAAOA,GAAA;AACT,IAAA,OAAO,IAAI,CAACJ,IAAI,CAACI,IAAI,IAAI,MAAA;AAC3B;EAEA,IAAIC,SAAYA,GAAA;IACd,IAAI,IAAI,CAACN,oBAAoB,EAAE;AAC7B,MAAA,OAAO,WAAA;AACT;AACA,IAAA,OAAO,SAAA;AACT;AAEA,EAAA;IAAAO,oBAAA,CAAAC,kBAAA,CA+BA,uzBAAA,EAAA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,WAAA;QAAAC,gBAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
{"version":3,"file":"counter.js","sources":["../../../src/components/vault-reporting/counter.gts"],"sourcesContent":["/**\n * Copyright (c) HashiCorp, Inc.\n * SPDX-License-Identifier: BUSL-1.1\n */\n\nimport Component from '@glimmer/component';\nimport {\n HdsTextBody,\n HdsIcon,\n HdsTooltipButton,\n HdsLinkInline,\n} from '@hashicorp/design-system-components/components';\nimport type { IconName } from '@hashicorp/flight-icons/svg';\n\nimport './counter.scss';\n\nexport interface SSUReportingCounterSignature {\n Args: {\n count: number;\n title: string;\n tooltipMessage?: string;\n icon?: IconName;\n suffix?: string;\n link?: string;\n emptyText?: string;\n emptyLink?: string;\n };\n\n Blocks: {\n default: [];\n };\n\n Element: HTMLElement;\n}\nexport default class SSUReportingCounter extends Component<SSUReportingCounterSignature> {\n get shouldShowEmptyState() {\n return this.args.count === 0 && this.args.emptyText;\n }\n\n get count() {\n if (this.shouldShowEmptyState) {\n return this.args.emptyText;\n }\n\n if (this.args.suffix) {\n return `${this.args.count} ${this.args.suffix}`;\n }\n\n return this.args.count;\n }\n\n get icon() {\n return this.args.icon || 'info';\n }\n\n get link() {\n if (this.shouldShowEmptyState && this.args.emptyLink) {\n return this.args.emptyLink;\n }\n return this.args.link;\n }\n\n <template>\n <div\n ...attributes\n data-test-counter={{@title}}\n class=\"ssu-counter\"\n aria-label=\"{{@title}} {{this.count}}\"\n >\n <div class=\"ssu-counter__title-row\">\n <HdsTextBody @weight=\"semibold\" @size=\"200\" @color=\"primary\">{{@title}}\n {{#if @tooltipMessage}}\n <HdsTooltipButton\n data-test-counter-tooltip-button\n class=\"ssu-counter__title-row__tooltip\"\n @text={{@tooltipMessage}}\n aria-label=\"Tooltip for {{@title}}\"\n @isInline={{true}}\n >\n <HdsIcon @name=\"help\" @isInline={{true}} />\n </HdsTooltipButton>\n {{/if}}\n </HdsTextBody>\n </div>\n\n <HdsTextBody>\n {{#if this.link}}\n <HdsLinkInline\n @href={{this.link}}\n @color=\"secondary\"\n class=\"ssu-counter__link\"\n target=\"_self\"\n >{{this.count}}\n </HdsLinkInline>\n {{else}}\n {{this.count}}\n {{/if}}\n </HdsTextBody>\n </div>\n </template>\n}\n"],"names":["SSUReportingCounter","Component","shouldShowEmptyState","args","count","emptyText","suffix","icon","link","emptyLink","setComponentTemplate","precompileTemplate","strictMode","scope","HdsTextBody","HdsTooltipButton","HdsIcon","HdsLinkInline"],"mappings":";;;;;AAAA;;;AAGC;AA+Bc,MAAMA,4BAA4BC,SAAU,CAAA;EACzD,IAAIC,oBAAuBA,GAAA;AACzB,IAAA,OAAO,IAAI,CAACC,IAAI,CAACC,KAAK,KAAK,CAAA,IAAK,IAAI,CAACD,IAAI,CAACE,SAAS;AACrD;EAEA,IAAID,KAAQA,GAAA;IACV,IAAI,IAAI,CAACF,oBAAoB,EAAE;AAC7B,MAAA,OAAO,IAAI,CAACC,IAAI,CAACE,SAAS;AAC5B;AAEA,IAAA,IAAI,IAAI,CAACF,IAAI,CAACG,MAAM,EAAE;AACpB,MAAA,OAAO,CAAG,EAAA,IAAI,CAACH,IAAI,CAACC,KAAK,CAAI,CAAA,EAAA,IAAI,CAACD,IAAI,CAACG,MAAM,CAAE,CAAA;AACjD;AAEA,IAAA,OAAO,IAAI,CAACH,IAAI,CAACC,KAAK;AACxB;EAEA,IAAIG,IAAOA,GAAA;AACT,IAAA,OAAO,IAAI,CAACJ,IAAI,CAACI,IAAI,IAAI,MAAA;AAC3B;EAEA,IAAIC,IAAOA,GAAA;IACT,IAAI,IAAI,CAACN,oBAAoB,IAAI,IAAI,CAACC,IAAI,CAACM,SAAS,EAAE;AACpD,MAAA,OAAO,IAAI,CAACN,IAAI,CAACM,SAAS;AAC5B;AACA,IAAA,OAAO,IAAI,CAACN,IAAI,CAACK,IAAI;AACvB;AAEA,EAAA;IAAAE,oBAAA,CAAAC,kBAAA,CAqCA,o6BAAA,EAAA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;QAAAC,WAAA;QAAAC,gBAAA;QAAAC,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}

View File

@ -25,7 +25,7 @@ class DashboardExport extends Component {
get dataAsDownloadableCSVString() {
const headers = ['Metric', 'Count/Breakdown'];
// Manually define rows as looping through the data does not leave the most legible structure
const rows = [headers, ['Child Namespaces', this.args?.data?.namespaces || 0], ['Total KV Secrets', (this.args.data?.kvv1_secrets || 0) + (this.args.data?.kvv2_secrets || 0)], ['KV V1 Secrets', this.args.data?.kvv1_secrets || 0], ['KV V2 Secrets', this.args.data?.kvv2_secrets || 0], ['Secret Syncs', this.args.data?.secrets_sync || 0], ['PKI Roles', this.args.data?.pki?.total_roles || 0], ...this.#getNestedRows(this.args.data?.secret_engines || {}, 'Secret Engine'), ...this.#getNestedRows(this.args.data?.auth_methods || {}, 'Auth Method'), ['Global Lease Count', this.args.data?.lease_count_quotas.global_lease_count_quota.count || 0], ['Global Lease Quota', this.args.data?.lease_count_quotas.global_lease_count_quota.capacity || 0], ['Cluster Disaster Recovery', this.args?.data?.replication_status.dr_state || '-'], ['Cluster Disaster Recovery Primary', this.args?.data?.replication_status.dr_primary ?? '-'], ['Cluster Performance', this.args?.data?.replication_status.pr_state || '-'], ['Cluster Performance Primary', this.args?.data?.replication_status.pr_primary ?? '-']];
const rows = [headers, ['Child Namespaces', this.args?.data?.namespaces || 0], ['Total KV Secrets', (this.args.data?.kvv1Secrets || 0) + (this.args.data?.kvv2Secrets || 0)], ['KV V1 Secrets', this.args.data?.kvv1Secrets || 0], ['KV V2 Secrets', this.args.data?.kvv2Secrets || 0], ['Secret Syncs', this.args.data?.secretSync?.totalDestinations || 0], ['PKI Roles', this.args.data?.pki?.totalRoles || 0], ...this.#getNestedRows(this.args.data?.secretEngines || {}, 'Secret Engine'), ...this.#getNestedRows(this.args.data?.authMethods || {}, 'Auth Method'), ['Global Lease Count', this.args.data?.leaseCountQuotas.globalLeaseCountQuota.count || 0], ['Global Lease Quota', this.args.data?.leaseCountQuotas.globalLeaseCountQuota.capacity || 0], ['Cluster Disaster Recovery', this.args?.data?.replicationStatus.drState || '-'], ['Cluster Disaster Recovery Primary', this.args?.data?.replicationStatus.drPrimary ?? '-'], ['Cluster Performance', this.args?.data?.replicationStatus.prState || '-'], ['Cluster Performance Primary', this.args?.data?.replicationStatus.prPrimary ?? '-']];
// Escape double quotes, quote cell content and separate with comma
const csvString = rows.map(row => row.map(cell => {
const escaped = String(cell).replace(/"/g, '""');

File diff suppressed because one or more lines are too long

View File

@ -1,7 +1,8 @@
import Component from '@glimmer/component';
import { HdsApplicationState, HdsTextDisplay, HdsCardContainer } from '@hashicorp/design-system-components/components';
import { HdsApplicationState, HdsAlert, HdsTextDisplay, HdsCardContainer } from '@hashicorp/design-system-components/components';
import TitleRow from './base/title-row.js';
import cssCustomProperty from '../../modifiers/css-custom-property.js';
import { htmlSafe } from '@ember/template';
import { precompileTemplate } from '@ember/template-compilation';
import { setComponentTemplate } from '@ember/component';
@ -18,13 +19,10 @@ class GlobalLease extends Component {
return Math.round(Math.min(count / quota * 100, 100));
}
get progressFillClass() {
if (this.percentage < 50) {
return 'ssu-global-lease__progress-fill--low';
if (this.percentage >= 100) {
return 'ssu-global-lease__progress-fill--exceeded';
}
if (this.percentage < 100) {
return 'ssu-global-lease__progress-fill--medium';
}
return 'ssu-global-lease__progress-fill--high';
return '';
}
get formattedCount() {
const formatter = new Intl.NumberFormat('en-US', {
@ -47,22 +45,37 @@ class GlobalLease extends Component {
}
get description() {
if (this.hasData) {
return 'Snapshot of global lease count quota consumption';
return htmlSafe('Total number of active <a class="hds-link-inline--color-secondary" href="https://developer.hashicorp.com/vault/docs/concepts/lease" target="_blank" data-test-global-lease-description-link>leases</a> for this quota.');
}
}
get linkUrl() {
if (this.hasData) {
return 'https://developer.hashicorp.com/vault/docs/enterprise/lease-count-quotas';
return 'https://developer.hashicorp.com/vault/tutorials/operations/resource-quotas#global-default-lease-count-quota';
}
}
get alert() {
if (this.percentage >= 100) {
return {
color: 'warning',
description: 'Global lease quota limit reached. If lease creation is blocked, reduce usage or increase the limit.'
};
}
if (this.percentage >= 95) {
return {
color: 'neutral',
description: 'Approaching quota limit. Reduce usage or increase the lease limit to avoid blocking new leases.'
};
}
}
static {
setComponentTemplate(precompileTemplate("\n <HdsCardContainer data-test-global-lease @hasBorder={{true}} class=\"ssu-global-lease\" {{cssCustomProperty \"--vault-reporting-global-lease-percentage\" this.percentageString}} ...attributes>\n <TitleRow @title=\"Global lease count quota\" @description={{this.description}} @linkText=\"Documentation\" @linkIcon=\"docs-link\" @linkUrl={{this.linkUrl}} @linkTarget=\"_blank\" />\n {{#if this.hasData}}\n <HdsTextDisplay class=\"ssu-global-lease__percentage-text\">{{this.percentage}}%</HdsTextDisplay>\n\n <div class=\"ssu-global-lease__progress-wrapper\">\n <div class=\"ssu-global-lease__progress-bar\">\n <div class=\"ssu-global-lease__progress-fill {{this.progressFillClass}}\" data-test-global-lease-fill></div>\n </div>\n <span class=\"ssu-global-lease__count-text\">\n <HdsTextDisplay @size=\"400\" @weight=\"medium\">\n {{this.formattedCount}}\n </HdsTextDisplay>\n </span>\n </div>\n {{else}}\n\n <HdsApplicationState data-test-global-lease-empty-state class=\"ssu-global-lease__empty-state\" as |A|>\n {{#if (has-block \"empty\")}}\n {{yield A to=\"empty\"}}\n {{else}}\n <A.Header data-test-global-lease-empty-state-title @title=\"None enforced\" />\n <A.Body data-test-global-lease-empty-state-description @text=\"Global lease count quota is disabled. Enable it to manage active leases.\" />\n\n <A.Footer as |F|>\n <F.LinkStandalone data-test-global-lease-empty-state-link @icon=\"docs-link\" @iconPosition=\"trailing\" @text=\"Global lease count quota\" @href=\"https://developer.hashicorp.com/vault/tutorials/operations/resource-quotas#global-default-lease-count-quota\" target=\"_blank\" />\n </A.Footer>\n {{/if}}\n </HdsApplicationState>\n {{/if}}\n\n </HdsCardContainer>\n ", {
setComponentTemplate(precompileTemplate("\n <HdsCardContainer data-test-global-lease @hasBorder={{true}} class=\"ssu-global-lease\" {{cssCustomProperty \"--vault-reporting-global-lease-percentage\" this.percentageString}} ...attributes>\n <TitleRow @title=\"Global lease count quota\" @description={{this.description}} @linkText=\"Documentation\" @linkIcon=\"docs-link\" @linkUrl={{this.linkUrl}} @linkTarget=\"_blank\" />\n {{#if this.hasData}}\n <HdsTextDisplay @size=\"300\" @weight=\"medium\" data-test-global-lease-percentage-text>{{this.percentage}}%</HdsTextDisplay>\n\n {{#if this.alert}}\n <HdsAlert data-test-global-lease-alert class=\"ssu-global-lease__alert\" @type=\"compact\" @color={{this.alert.color}} as |A|>\n <A.Description>{{this.alert.description}}</A.Description>\n </HdsAlert>\n {{/if}}\n\n <div class=\"ssu-global-lease__progress-wrapper\">\n <div class=\"ssu-global-lease__progress-bar\">\n <div class=\"ssu-global-lease__progress-fill {{this.progressFillClass}}\" data-test-global-lease-fill></div>\n </div>\n <span>\n <HdsTextDisplay @size=\"200\" @weight=\"semibold\" data-test-global-lease-count-text>\n {{this.formattedCount}}\n </HdsTextDisplay>\n </span>\n </div>\n {{else}}\n\n <HdsApplicationState data-test-global-lease-empty-state class=\"ssu-global-lease__empty-state\" as |A|>\n {{#if (has-block \"empty\")}}\n {{yield A to=\"empty\"}}\n {{else}}\n <A.Body data-test-global-lease-empty-state-description @text=\"Lease quotas enforce limits on active secrets and tokens. It's recommended to enable this to protect stability for this Vault cluster.\" />\n\n <A.Footer as |F|>\n <F.LinkStandalone data-test-global-lease-empty-state-link @icon=\"docs-link\" @iconPosition=\"trailing\" @text=\"Global lease count quota\" @href=\"https://developer.hashicorp.com/vault/tutorials/operations/resource-quotas#global-default-lease-count-quota\" target=\"_blank\" />\n </A.Footer>\n {{/if}}\n </HdsApplicationState>\n {{/if}}\n\n </HdsCardContainer>\n ", {
strictMode: true,
scope: () => ({
HdsCardContainer,
cssCustomProperty,
TitleRow,
HdsTextDisplay,
HdsAlert,
HdsApplicationState
})
}), this);

File diff suppressed because one or more lines are too long

View File

@ -99,7 +99,7 @@ class SSUReportingHorizontalBarChart extends Component {
this.xRangeOffsetWidth = offsetWidth;
};
static {
setComponentTemplate(precompileTemplate("\n <HdsCardContainer ...attributes class=\"ssu-horizontal-bar-chart__container\" @hasBorder={{true}}>\n <TitleRow @title={{@title}} @description={{this.description}} @linkUrl={{this.linkUrl}} />\n {{#if this.hasData}}\n {{!-- TODO: Figure out glint errors on lineal components --}}\n {{!-- @glint-expect-error --}}\n <LinealFluid class=\"ssu-horizontal-bar-chart__chart\" as |width|>\n <svg height={{this.rangeHeight}} width=\"100%\" {{axisOffset this.handleAxisOffset 8}} data-test-horizontal-bar-chart-svg>\n {{!-- We are using the stacked version of the HBars as there seems to be an issue in the non-stacked version for how the x position is calculated. --}}\n {{#let (scaleLinear range=(this.getXRange width) domain=this.xDomain) (scaleBand range=this.yRange domain=this.yDomain) (stackH data=this.data x=\"value\" y=\"label\" z=\"\") as |xScale yScale stacked|}}\n {{#if xScale.isValid}}\n <LinealAxis @scale={{yScale}} {{!-- @glint-expect-error --}} @orientation=\"left\" @includeDomain={{false}} />\n {{!-- TODO: Extra wrapper exists only for test attribute, figure out a better way --}}\n <g data-test-horizontal-bar-chart-bars>\n <LinealHBars @data={{stacked.data}} {{!-- @glint-expect-error --}} @x=\"x\" {{!-- @glint-expect-error --}} @y=\"y\" {{!-- @glint-expect-error --}} @height={{10}} @xScale={{xScale}} @yScale={{yScale}} />\n </g>\n <g>\n {{!-- @glint-expect-error --}}\n {{#each stacked.data as |dataset|}}\n {{#each dataset as |datum|}}\n <text class=\"ssu-horizontal-bar-chart__label\" {{!-- @glint-expect-error --}} y={{yScale.compute datum.y}} x={{xScale.compute datum.x}} dy=\"16px\" dx=\"8px\" data-test-horizontal-bar-chart-inline-count>\n {{datum.x}}\n </text>\n {{/each}}\n {{/each}}\n </g>\n {{/if}}\n {{/let}}\n </svg>\n </LinealFluid>\n <HdsSeparator class=\"ssu-horizontal-bar-chart__separator\" @spacing=\"0\" />\n <HdsTextBody class=\"ssu-horizontal-bar-chart__total\" @size=\"200\" @tag=\"p\" data-test-horizontal-bar-chart-total>\n Total:\n {{this.total}}\n </HdsTextBody>\n {{else}}\n\n <HdsApplicationState data-test-horizontal-bar-chart-empty-state class=\"ssu-horizontal-bar-chart__empty-state\" as |A|>\n {{#if (has-block \"empty\")}}\n {{yield A to=\"empty\"}}\n {{else}}\n <A.Header data-test-horizontal-bar-chart-empty-state-title @title={{this.emptyStateTitle}} />\n <A.Body data-test-horizontal-bar-chart-empty-state-description @text={{this.emptyStateDescription}} />\n {{#if @linkUrl}}\n <A.Footer as |F|>\n <F.LinkStandalone data-test-horizontal-bar-chart-empty-state-link @icon=\"plus\" @text={{this.emptyStateLinkText}} @href={{@linkUrl}} />\n </A.Footer>\n {{/if}}\n {{/if}}\n </HdsApplicationState>\n {{/if}}\n </HdsCardContainer>\n ", {
setComponentTemplate(precompileTemplate("\n <HdsCardContainer ...attributes class=\"ssu-horizontal-bar-chart__container\" @hasBorder={{true}}>\n <TitleRow @title={{@title}} @description={{this.description}} @linkUrl={{this.linkUrl}} @linkText={{@linkText}} @linkTarget={{@linkTarget}} @linkIcon={{@linkIcon}} />\n {{#if this.hasData}}\n {{!-- TODO: Figure out glint errors on lineal components --}}\n {{!-- @glint-expect-error --}}\n <LinealFluid class=\"ssu-horizontal-bar-chart__chart\" as |width|>\n <svg height={{this.rangeHeight}} width=\"100%\" {{axisOffset this.handleAxisOffset 8}} data-test-horizontal-bar-chart-svg>\n {{!-- We are using the stacked version of the HBars as there seems to be an issue in the non-stacked version for how the x position is calculated. --}}\n {{#let (scaleLinear range=(this.getXRange width) domain=this.xDomain) (scaleBand range=this.yRange domain=this.yDomain) (stackH data=this.data x=\"value\" y=\"label\" z=\"\") as |xScale yScale stacked|}}\n {{#if xScale.isValid}}\n <LinealAxis @scale={{yScale}} {{!-- @glint-expect-error --}} @orientation=\"left\" @includeDomain={{false}} />\n {{!-- TODO: Extra wrapper exists only for test attribute, figure out a better way --}}\n <g data-test-horizontal-bar-chart-bars>\n <LinealHBars @data={{stacked.data}} {{!-- @glint-expect-error --}} @x=\"x\" {{!-- @glint-expect-error --}} @y=\"y\" {{!-- @glint-expect-error --}} @height={{6}} @xScale={{xScale}} @yScale={{yScale}} />\n </g>\n <g>\n {{!-- @glint-expect-error --}}\n {{#each stacked.data as |dataset|}}\n {{#each dataset as |datum|}}\n <text class=\"ssu-horizontal-bar-chart__label\" {{!-- @glint-expect-error --}} y={{yScale.compute datum.y}} x={{xScale.compute datum.x}} dy=\"17.5px\" dx=\"8px\" data-test-horizontal-bar-chart-inline-count aria-label=\"{{datum.y}} {{datum.x}}\">\n {{datum.x}}\n </text>\n {{/each}}\n {{/each}}\n </g>\n {{/if}}\n {{/let}}\n </svg>\n </LinealFluid>\n <HdsSeparator class=\"ssu-horizontal-bar-chart__separator\" @spacing=\"0\" />\n <HdsTextBody class=\"ssu-horizontal-bar-chart__total\" @size=\"200\" @tag=\"p\" data-test-horizontal-bar-chart-total>\n Total:\n {{this.total}}\n </HdsTextBody>\n {{else}}\n\n <HdsApplicationState data-test-horizontal-bar-chart-empty-state class=\"ssu-horizontal-bar-chart__empty-state\" as |A|>\n {{#if (has-block \"empty\")}}\n {{yield A to=\"empty\"}}\n {{else}}\n <A.Header data-test-horizontal-bar-chart-empty-state-title @title={{this.emptyStateTitle}} />\n <A.Body data-test-horizontal-bar-chart-empty-state-description @text={{this.emptyStateDescription}} />\n {{#if @linkUrl}}\n <A.Footer as |F|>\n <F.LinkStandalone data-test-horizontal-bar-chart-empty-state-link @icon=\"plus\" @text={{this.emptyStateLinkText}} @href={{@linkUrl}} />\n </A.Footer>\n {{/if}}\n {{/if}}\n </HdsApplicationState>\n {{/if}}\n </HdsCardContainer>\n ", {
strictMode: true,
scope: () => ({
HdsCardContainer,

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,12 @@
import Component from '@glimmer/component';
import { concat } from '@ember/helper';
import { array } from '@ember/helper';
import SSUReportingCounter from '../counter.js';
import SSUReportingHorizontalBarChart from '../horizontal-bar-chart.js';
import GlobalLease from '../global-lease.js';
import ClusterReplication from '../cluster-replication.js';
import DashboardExport from '../dashboard/export.js';
import { tracked } from '@glimmer/tracking';
import { on } from '@ember/modifier';
import { HdsTextDisplay, HdsSeparator, HdsCardContainer, HdsAlert, HdsButton, HdsLinkInline, HdsBadge, HdsPageHeader } from '@hashicorp/design-system-components/components';
import { HdsCardContainer, HdsAlert, HdsLinkInline, HdsTextBody, HdsBadge, HdsPageHeader } from '@hashicorp/design-system-components/components';
import { precompileTemplate } from '@ember/template-compilation';
import { setComponentTemplate } from '@ember/component';
import { g, i } from 'decorator-transforms/runtime';
@ -40,87 +39,92 @@ class SSUViewDashboard extends Component {
this.error = undefined;
this.data = await this.args.onFetchUsageData();
this.lastUpdatedTime = new Intl.DateTimeFormat('en-US', {
dateStyle: 'medium',
timeStyle: 'medium'
}).format(new Date());
} catch (e) {
this.error = e;
}
};
getBarChartData = (map = {}) => {
getBarChartData = (map = {}, exclude = []) => {
return Object.entries(map).map(([label, value]) => {
return {
label,
value
};
}).filter(item => {
return !exclude?.includes(item.label);
});
};
get isVaultDedicated() {
return this.args.isVaultDedicated ?? true;
}
get kvSecretsTooltipMessage() {
const kvv1Secrets = this.data?.kvv1_secrets ?? 0;
const kvv2Secrets = this.data?.kvv2_secrets ?? 0;
const {
kvv1Secrets = 0,
kvv2Secrets = 0
} = this.data ?? {};
const kvv1Formatted = Intl.NumberFormat().format(kvv1Secrets);
const kvv2Formatted = Intl.NumberFormat().format(kvv2Secrets);
if (kvv1Secrets && kvv2Secrets) {
return `Combined count of ${kvv1Formatted} KV v1 secrets and ${kvv2Formatted} KV v2 secrets in this namespace`;
return `Combined count of ${kvv1Formatted} KV version 1 secrets and ${kvv2Formatted} KV version 2 secrets.`;
}
if (kvv1Secrets) {
return `Total number of ${kvv1Formatted} KV v1 secrets in this namespace`;
return `Total number of ${kvv1Formatted} KV version 1 secrets.`;
}
if (kvv2Secrets) {
return `Total number of ${kvv2Formatted} KV v2 secrets in this namespace`;
return `Total number of ${kvv2Formatted} KV version 2 secrets.`;
}
return '';
}
get counters() {
const {
kvv1Secrets = 0,
kvv2Secrets = 0
} = this.data ?? {};
return [{
title: 'Child namespaces',
tooltipMessage: this.isVaultDedicated ? 'Total number of direct child namespaces under the root/ namespace' : 'Total number of direct child namespaces under the admin/ namespace',
tooltipMessage: 'Total number of namespaces for this cluster.',
data: this.data?.namespaces ?? 0,
link: 'access/namespaces'
}, {
title: 'KV secrets',
tooltipMessage: this.kvSecretsTooltipMessage,
data: (this.data?.kvv1_secrets ?? 0) + (this.data?.kvv2_secrets ?? 0),
emptyText: 'No secrets stored'
data: kvv1Secrets + kvv2Secrets,
emptyText: 'No secrets stored',
emptyLink: 'secrets'
}, {
title: 'Secrets sync',
tooltipMessage: this.isVaultDedicated ? 'Total number of destinations (e.g. third-party integrations) synced with secrets from this namespace' : '',
data: this.data?.secrets_sync ?? 0,
tooltipMessage: 'Total number of destinations (e.g. third-party integrations) synced with secrets from this namespace.',
data: this.data?.secretSync?.totalDestinations ?? 0,
link: 'sync/secrets/overview',
emptyText: 'Not configured',
emptyText: 'Not activated',
suffix: 'destinations'
}, {
title: 'PKI roles',
tooltipMessage: this.isVaultDedicated ? 'Total number of PKI roles configured in this namespace' : '',
data: this.data?.pki?.total_roles ?? 0,
tooltipMessage: 'Total number of PKI roles configured.',
data: this.data?.pki?.totalRoles ?? 0,
emptyText: 'No roles created'
}];
}
get namespace() {
return this.isVaultDedicated ? 'Admin' : 'Root';
return this.isVaultDedicated ? 'admin' : 'root';
}
static {
setComponentTemplate(precompileTemplate("\n <div class=\"dashboard\">\n <HdsPageHeader as |PH|>\n <PH.Title>Vault Usage\n <HdsBadge class=\"dashboard__badge\" @size=\"small\" @type=\"inverted\" @text={{concat \"Namespace: \" this.namespace}} />\n </PH.Title>\n <PH.Description>\n {{#if this.lastUpdatedTime}}\n Last updated\n {{this.lastUpdatedTime}}.\n {{/if}}\n Don't see what you're looking for?\n <HdsLinkInline @icon=\"external-link\" @href=\"#\">Share feedback.</HdsLinkInline>\n </PH.Description>\n <PH.Actions>\n <HdsButton @text=\"Refresh\" @icon=\"reload\" @iconPosition=\"leading\" @color=\"secondary\" data-test-dashboard-refresh-button {{on \"click\" this.fetchAllData}} />\n <DashboardExport @data={{this.data}} />\n </PH.Actions>\n </HdsPageHeader>\n {{#if this.error}}\n <HdsAlert data-test-dashboard-error @type=\"inline\" @color=\"critical\" class=\"dashboard__error\" as |A|>\n <A.Title>Error</A.Title>\n <A.Description data-test-dashboard-error-description>An error\n occurred, please try again.</A.Description>\n </HdsAlert>\n {{/if}}\n {{#if this.data}}\n <HdsCardContainer @hasBorder={{true}} class=\"dashboard__counters\" data-test-dashboard-counters>\n {{#each this.counters as |counter|}}\n <ReportingCounter @title={{counter.title}} @tooltipMessage={{counter.tooltipMessage}} @count={{counter.data}} @icon={{counter.icon}} @suffix={{counter.suffix}} @link={{counter.link}} @emptyText={{counter.emptyText}} />\n {{/each}}\n </HdsCardContainer>\n <HdsSeparator />\n <HdsTextDisplay @tag=\"h2\" @size=\"400\" class=\"dashboard__inventory-header\">Resource inventory</HdsTextDisplay>\n <div class=\"dashboard__viz-blocks\">\n <div>\n <ReportingHorizontalBarChart @data={{this.getBarChartData this.data.secret_engines}} @title=\"Secret engines\" @description=\"Breakdown of secret engines for this namespace(s)\" @linkUrl=\"secrets\" class=\"dashboard__viz-block\" data-test-dashboard-secret-engines />\n <GlobalLease @count={{this.data.lease_count_quotas.global_lease_count_quota.count}} @quota={{this.data.lease_count_quotas.global_lease_count_quota.capacity}} class=\"dashboard__viz-block\" data-test-dashboard-lease-count />\n </div>\n\n <div>\n <ReportingHorizontalBarChart @data={{this.getBarChartData this.data.auth_methods}} @title=\"Authentication methods\" @description=\"Breakdown of authentication methods\" @linkUrl=\"access\" class=\"dashboard__viz-block\" data-test-dashboard-auth-methods />\n\n <ClusterReplication @isDisasterRecoveryPrimary={{this.data.replication_status.dr_primary}} @disasterRecoveryState={{this.data.replication_status.dr_state}} @isPerformancePrimary={{this.data.replication_status.pr_primary}} @performanceState={{this.data.replication_status.pr_state}} data-test-dashboard-cluster-replication />\n </div>\n </div>\n {{/if}}\n </div>\n ", {
setComponentTemplate(precompileTemplate("\n <div class=\"dashboard\">\n <HdsPageHeader as |PH|>\n <PH.Title>\n Vault Usage\n <HdsBadge class=\"dashboard__badge\" @size=\"medium\" @icon=\"org\" @color=\"neutral\" @text={{this.namespace}} />\n </PH.Title>\n <PH.Description class=\"dashboard__description\">\n {{#if this.lastUpdatedTime}}\n <HdsTextBody @tag=\"p\" @size=\"200\" @color=\"faint\">\n Updated today at\n {{this.lastUpdatedTime}}.\n\n </HdsTextBody>\n {{/if}}\n <HdsTextBody @tag=\"p\" @size=\"200\" @color=\"primary\">\n View and export your Vault usage. Don't see what you're looking for?\n <HdsLinkInline data-test-dashboard-survey-link @icon=\"external-link\" @href=\"https://hashicorp.sjc1.qualtrics.com/jfe/form/SV_bqhLeB3deLd2caa\" target=\"_blank\">Share feedback</HdsLinkInline>\n </HdsTextBody>\n </PH.Description>\n <PH.Actions>\n <DashboardExport @data={{this.data}} />\n </PH.Actions>\n </HdsPageHeader>\n {{#if this.error}}\n <HdsAlert data-test-dashboard-error @type=\"inline\" @color=\"critical\" class=\"dashboard__error\" as |A|>\n <A.Title>Error</A.Title>\n <A.Description data-test-dashboard-error-description>An error\n occurred, please try again.</A.Description>\n </HdsAlert>\n {{/if}}\n {{#if this.data}}\n <HdsCardContainer @hasBorder={{true}} {{!-- @glint-expect-error --}} @background=\"neutral-secondary\" class=\"dashboard__counters\" data-test-dashboard-counters>\n {{#each this.counters as |counter|}}\n <ReportingCounter @title={{counter.title}} @tooltipMessage={{counter.tooltipMessage}} @count={{counter.data}} @icon={{counter.icon}} @suffix={{counter.suffix}} @link={{counter.link}} @emptyText={{counter.emptyText}} @emptyLink={{counter.emptyLink}} />\n {{/each}}\n </HdsCardContainer>\n <div data-test-dashboard-viz-blocks class=\"dashboard__viz-blocks\">\n <div>\n <ReportingHorizontalBarChart @data={{this.getBarChartData this.data.authMethods}} @title=\"Authentication methods\" @description=\"Enabled authentication methods for this cluster.\" @linkUrl=\"access\" class=\"dashboard__viz-block\" data-test-dashboard-auth-methods />\n\n <ReportingHorizontalBarChart @data={{this.getBarChartData this.data.secretEngines (array \"system\" \"identity\")}} @title=\"Secret engines\" @description=\"Enabled secret engines for this cluster.\" @linkUrl=\"secrets\" class=\"dashboard__viz-block\" data-test-dashboard-secret-engines />\n\n <ClusterReplication @disasterRecoveryState={{this.data.replicationStatus.drState}} @performanceState={{this.data.replicationStatus.prState}} data-test-dashboard-cluster-replication />\n </div>\n\n <div>\n <ReportingHorizontalBarChart @data={{this.getBarChartData this.data.leasesByAuthMethod}} @title=\"Leases by authentication methods\" @description=\"Active leases issued per authentication method.\" @linkUrl=\"https://developer.hashicorp.com/vault/docs/concepts/auth#auth-leases\" @linkText=\"Documentation\" @linkIcon=\"docs-link\" @linkTarget=\"_blank\" class=\"dashboard__viz-block\" data-test-dashboard-leases-by-auth-method>\n <:empty as |A|>\n <A.Body @text=\"Lease are created when clients authenticate. Add an authentication method to monitor leases across this namespace.\" />\n <A.Footer as |F|>\n <F.LinkStandalone @icon=\"docs-link\" @text=\"Authentication leases\" @href=\"https://developer.hashicorp.com/vault/docs/concepts/auth#auth-leases\" />\n </A.Footer>\n </:empty>\n </ReportingHorizontalBarChart>\n <GlobalLease @count={{this.data.leaseCountQuotas.globalLeaseCountQuota.count}} @quota={{this.data.leaseCountQuotas.globalLeaseCountQuota.capacity}} class=\"dashboard__viz-block\" data-test-dashboard-lease-count />\n </div>\n\n </div>\n {{/if}}\n </div>\n ", {
strictMode: true,
scope: () => ({
HdsPageHeader,
HdsBadge,
concat,
HdsTextBody,
HdsLinkInline,
HdsButton,
on,
DashboardExport,
HdsAlert,
HdsCardContainer,
ReportingCounter: SSUReportingCounter,
HdsSeparator,
HdsTextDisplay,
ReportingHorizontalBarChart: SSUReportingHorizontalBarChart,
GlobalLease,
ClusterReplication
array,
ClusterReplication,
GlobalLease
})
}), this);
}

File diff suppressed because one or more lines are too long

View File

@ -2,4 +2,4 @@
* Copyright (c) HashiCorp, Inc.
*/
.ssu-cluster-replication{padding:16px;display:flex;flex-direction:column;justify-content:center;gap:1rem}.ssu-cluster-replication__list-row{display:flex;justify-content:space-between;align-items:center}.ssu-cluster-replication__list-row__badge{margin:0 .25rem}.ssu-cluster-replication__list-row__role{color:var(--token-color-palette-green-400)}.ssu-title-row{margin-bottom:1rem}.ssu-title-row__description{display:block;margin-top:.25rem;color:var(--token-color-foreground-faint)}.ssu-title-row__container{display:flex;justify-content:space-between}.ssu-title-row__container__link{padding:0}.ssu-counter{padding:16px}.ssu-counter__title-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.ssu-counter__title-row__tooltip{margin-left:3px;top:3px}.ssu-global-lease{padding:16px;display:flex;flex-direction:column;justify-content:center;gap:1rem}.ssu-global-lease__progress-wrapper{display:flex;align-items:center;gap:1rem;height:50px}.ssu-global-lease__progress-bar{flex:1;height:100%;background-color:var(--token-color-palette-neutral-300);overflow:hidden;font-size:40px;display:inline-block}@keyframes initialWidth{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}.ssu-global-lease__progress-fill{height:100%;width:var(--vault-reporting-global-lease-percentage);animation:1s ease-out initialWidth;transform-origin:left;transition:width 1s ease-out,background-color 1s ease-out}.ssu-global-lease__progress-fill--initial{background-color:var(--token-color-palette-gray-100)}.ssu-global-lease__progress-fill--low{background-color:var(--token-color-palette-green-200)}.ssu-global-lease__progress-fill--medium{background-color:var(--token-color-vault-brand)}.ssu-global-lease__progress-fill--high{background-color:var(--token-color-palette-red-200)}.ssu-global-lease__percentage-text{font-weight:400;font-size:2.7rem;line-height:2.3rem}.ssu-global-lease__count-text{font-size:.875rem}.ssu-global-lease__empty-state{min-width:66%;margin:16px auto}.dashboard{padding:16px}.dashboard__error{margin-top:16px}.dashboard__counters{display:grid;grid-template-columns:repeat(4, minmax(0, 1fr));gap:16px;margin:32px 0}.dashboard__viz-blocks{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:16px}.dashboard__viz-block{margin-bottom:16px}.dashboard__inventory-header{margin-bottom:16px}.dashboard__badge{margin-left:5px}.ssu-horizontal-bar-chart__container{padding:16px;display:flex;flex-direction:column;gap:16px;overflow:hidden}.ssu-horizontal-bar-chart__chart{box-sizing:border-box}.ssu-horizontal-bar-chart__chart svg{overflow:visible}.ssu-horizontal-bar-chart__chart rect{fill:var(--token-color-palette-blue-200);transform:translateY(7px);rx:5px;ry:5px}.ssu-horizontal-bar-chart__chart .axis line{display:none}.ssu-horizontal-bar-chart__separator{margin-top:auto}.ssu-horizontal-bar-chart__empty-state{min-width:66%;margin:16px auto}
.ssu-cluster-replication{padding:16px;display:flex;flex-direction:column;justify-content:center;gap:1rem}.ssu-cluster-replication__list-row__badge{margin:0 .25rem;text-transform:capitalize}.ssu-horizontal-bar-chart__container{padding:16px;overflow:hidden;font-size:13px}.ssu-horizontal-bar-chart__chart{box-sizing:border-box;margin-top:15px;color:var(--token-color-foreground-primary);fill:var(--token-color-foreground-primary)}.ssu-horizontal-bar-chart__chart svg{overflow:visible}.ssu-horizontal-bar-chart__chart rect{fill:var(--token-color-palette-blue-200);transform:translateY(10.5px);rx:3px;ry:3px}.ssu-horizontal-bar-chart__chart .axis line{display:none}.ssu-horizontal-bar-chart__separator{margin-bottom:10px}.ssu-horizontal-bar-chart__empty-state{min-width:66%;margin:16px auto}.ssu-horizontal-bar-chart__total{color:var(--token-color-foreground-primary)}.ssu-title-row{margin-bottom:8px}.ssu-title-row__description{display:block;margin-top:.25rem;color:var(--token-color-foreground-faint)}.ssu-title-row__container{display:flex;justify-content:space-between}.ssu-title-row__container__link{padding:0}.ssu-global-lease{padding:16px}.ssu-global-lease__progress-wrapper{display:flex;align-items:center;gap:1rem;height:15px}.ssu-global-lease__progress-bar{flex:1;height:100%;background-color:var(--token-color-palette-neutral-100);border:1.5px solid var(--token-color-palette-neutral-200);border-radius:4px;overflow:hidden;font-size:40px;display:inline-block}@keyframes initialWidth{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}.ssu-global-lease__progress-fill{height:100%;width:var(--vault-reporting-global-lease-percentage);animation:1s ease-out initialWidth;transform-origin:left;transition:width 1s ease-out,background-color 1s ease-out;background-color:var(--token-color-palette-neutral-300)}.ssu-global-lease__progress-fill--exceeded{background-color:var(--token-color-palette-red-100)}.ssu-global-lease__empty-state{min-width:66%;margin:16px auto;color:var(--token-color-foreground-faint)}.ssu-global-lease__alert{margin-bottom:8px}.dashboard{padding:16px}.dashboard__error{margin-top:16px}.dashboard__counters{display:grid;grid-template-columns:repeat(4, minmax(0, 1fr));gap:16px;margin:32px 0}.dashboard__viz-blocks{display:grid;grid-template-columns:repeat(2, minmax(0, 1fr));gap:16px}.dashboard__viz-block{margin-bottom:16px}.dashboard__badge{margin-left:5px}.dashboard__description p{margin-bottom:8px}.ssu-counter{padding:16px}.ssu-counter__title-row{display:flex;align-items:center;gap:8px;margin-bottom:8px}.ssu-counter__title-row__tooltip{margin-left:3px;top:3px}

View File

@ -1 +1 @@
{"version":3,"file":"index.js","sources":["../../src/types/index.ts"],"sourcesContent":["/**\n * Copyright (c) HashiCorp, Inc.\n * SPDX-License-Identifier: BUSL-1.1\n */\n\ntype ISODateString = `${number}${number}-${number}${number}-${number}${number}`;\ntype ISOTimeString = `${number}${number}:${number}${number}:${number}${number}`;\ntype ISODateTimeString = `${ISODateString}T${ISOTimeString}`;\n\nexport interface TimeSeriesDatum {\n date: ISODateTimeString;\n value: number;\n}\n\nexport interface SimpleDatum {\n value: number;\n label: string;\n}\n\n/*\n States for disaster recovery and performance\n (More states might be added once this is hooked up to the backend)\n*/\nexport enum REPLICATION_ENABLED_STATE {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n BOOTSTRAPPING = 'bootstrapping',\n}\nexport const REPLICATION_DISABLED_STATE = 'disabled';\n\nexport interface UsageDashboardData {\n auth_methods: Record<string, number>;\n kvv1_secrets: number;\n kvv2_secrets: number;\n lease_count_quotas: {\n global_lease_count_quota: {\n capacity: number;\n count: number;\n name: string;\n };\n total_lease_count_quotas: number;\n };\n namespaces: number;\n secrets_sync: number;\n pki: {\n total_issuers: number;\n total_roles: number;\n };\n replication_status: {\n dr_primary: boolean;\n dr_state: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;\n pr_primary: boolean;\n pr_state: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;\n };\n secret_engines: Record<string, number>;\n}\n\nexport type getUsageDataFunction = () => Promise<UsageDashboardData>;\n"],"names":["REPLICATION_ENABLED_STATE","REPLICATION_DISABLED_STATE"],"mappings":"AAAA;AACA;AACA;AACA;;AAgBA;AACA;AACA;AACA;AACYA,IAAAA,yBAAyB,0BAAzBA,yBAAyB,EAAA;EAAzBA,yBAAyB,CAAA,SAAA,CAAA,GAAA,SAAA;EAAzBA,yBAAyB,CAAA,WAAA,CAAA,GAAA,WAAA;EAAzBA,yBAAyB,CAAA,eAAA,CAAA,GAAA,eAAA;AAAA,EAAA,OAAzBA,yBAAyB;AAAA,CAAA,CAAA,EAAA;AAK9B,MAAMC,0BAA0B,GAAG;;;;"}
{"version":3,"file":"index.js","sources":["../../src/types/index.ts"],"sourcesContent":["/**\n * Copyright (c) HashiCorp, Inc.\n * SPDX-License-Identifier: BUSL-1.1\n */\n\ntype ISODateString = `${number}${number}-${number}${number}-${number}${number}`;\ntype ISOTimeString = `${number}${number}:${number}${number}:${number}${number}`;\ntype ISODateTimeString = `${ISODateString}T${ISOTimeString}`;\n\nexport interface TimeSeriesDatum {\n date: ISODateTimeString;\n value: number;\n}\n\nexport interface SimpleDatum {\n value: number;\n label: string;\n}\n\n/*\n States for disaster recovery and performance\n (More states might be added once this is hooked up to the backend)\n*/\nexport enum REPLICATION_ENABLED_STATE {\n PRIMARY = 'primary',\n SECONDARY = 'secondary',\n BOOTSTRAPPING = 'bootstrapping',\n}\nexport const REPLICATION_DISABLED_STATE = 'disabled';\n\nexport interface UsageDashboardData {\n authMethods: Record<string, number>;\n leasesByAuthMethod: Record<string, number>;\n kvv1Secrets: number;\n kvv2Secrets: number;\n leaseCountQuotas: {\n globalLeaseCountQuota: {\n capacity: number;\n count: number;\n name: string;\n };\n totalLeaseCountQuotas: number;\n };\n namespaces: number;\n secretSync: { totalDestinations: number };\n pki: {\n totalIssuers: number;\n totalRoles: number;\n };\n replicationStatus: {\n drPrimary: boolean;\n drState: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;\n prPrimary: boolean;\n prState: REPLICATION_ENABLED_STATE | typeof REPLICATION_DISABLED_STATE;\n };\n secretEngines: Record<string, number>;\n}\n\nexport type getUsageDataFunction = () => Promise<UsageDashboardData>;\n"],"names":["REPLICATION_ENABLED_STATE","REPLICATION_DISABLED_STATE"],"mappings":"AAAA;AACA;AACA;AACA;;AAgBA;AACA;AACA;AACA;AACYA,IAAAA,yBAAyB,0BAAzBA,yBAAyB,EAAA;EAAzBA,yBAAyB,CAAA,SAAA,CAAA,GAAA,SAAA;EAAzBA,yBAAyB,CAAA,WAAA,CAAA,GAAA,WAAA;EAAzBA,yBAAyB,CAAA,eAAA,CAAA,GAAA,eAAA;AAAA,EAAA,OAAzBA,yBAAyB;AAAA,CAAA,CAAA,EAAA;AAK9B,MAAMC,0BAA0B,GAAG;;;;"}