vault/ui/app/components/clients/charts/vertical-bar-basic.ts
claire bontempo 009702cae0
UI: Convert client count utils to typescript (#26262)
* cleanup namespaceArrayToObject method

* WIP typescript conversion

* WIP typescripted destructured block

* slowly making progress....

* WIP move all types to util type file, separate returns in formatByMonths

* namespaceArrayToObject is working?!?

* fix mirage handler not generating months when queries are after upgrade

* wow, the types are actually working omg

* add comments and update client-count-utils test

* delete old js file

* remove types from activity model

* remove comment

* reuse totalclients type to minimize places we add types

* commit file with type changes for git diff

* delete util file again

* address PR feedback and move type declarations to util file

* remove unused types

* update tests, use client helper in dashboard clients test

* remove typo

* make modifications with updated combined activity response from the backend
2024-04-09 20:53:16 +00:00

99 lines
2.6 KiB
TypeScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { BAR_WIDTH, formatNumbers } from 'vault/utils/chart-helpers';
import { formatNumber } from 'core/helpers/format-number';
import { parseAPITimestamp } from 'core/utils/date-formatters';
import type { MonthlyChartData } from 'vault/vault/charts/client-counts';
import type { TotalClients } from 'core/utils/client-count-utils';
interface Args {
data: MonthlyChartData[];
dataKey: string;
chartTitle: string;
chartHeight?: number;
}
interface ChartData {
x: string;
y: number | null;
tooltip: string;
legendX: string;
legendY: string;
}
/**
* @module VerticalBarBasic
* Renders a vertical bar chart of counts fora single data point (@dataKey) over time.
*
* @example
<Clients::Charts::VerticalBarBasic
@chartTitle="Secret Sync client counts"
@data={{this.model}}
@dataKey="secret_syncs"
@showTable={{true}}
@chartHeight={{200}}
/>
*/
export default class VerticalBarBasic extends Component<Args> {
barWidth = BAR_WIDTH;
@tracked activeDatum: ChartData | null = null;
get chartHeight() {
return this.args.chartHeight || 190;
}
get chartData() {
return this.args.data.map((d): ChartData => {
const xValue = d.timestamp as string;
const yValue = (d[this.args.dataKey as keyof TotalClients] as number) ?? null;
return {
x: parseAPITimestamp(xValue, 'M/yy') as string,
y: yValue,
tooltip:
yValue === null ? 'No data' : `${formatNumber([yValue])} ${this.args.dataKey.replace(/_/g, ' ')}`,
legendX: parseAPITimestamp(xValue, 'MMMM yyyy') as string,
legendY: (yValue ?? 'No data').toString(),
};
});
}
get yDomain() {
const counts: number[] = this.chartData
.map((d) => d.y)
.flatMap((num) => (typeof num === 'number' ? [num] : []));
const max = Math.max(...counts);
// if max is <=4, hardcode 4 which is the y-axis tickCount so y-axes are not decimals
return [0, max <= 4 ? 4 : max];
}
get xDomain() {
const months = this.chartData.map((d) => d.x);
return new Set(months);
}
// TEMPLATE HELPERS
barOffset = (bandwidth: number) => {
return (bandwidth - this.barWidth) / 2;
};
tooltipX = (original: number, bandwidth: number) => {
return (original + bandwidth / 2).toString();
};
tooltipY = (original: number) => {
if (!original) return `0`;
return `${original}`;
};
formatTicksY = (num: number): string => {
return formatNumbers(num) || num.toString();
};
}