refactor: Update YAxisOptions interface to include a precision property

This commit is contained in:
Simon Larsen
2024-08-21 14:49:47 +01:00
parent 12e766e9d4
commit dbdca789d4
17 changed files with 1061 additions and 858 deletions

View File

@@ -65,11 +65,11 @@ export default class Queue {
}
public static getQueueInspectorRouter(): ExpressRouter {
const serverAdapter = new ExpressAdapter();
const serverAdapter: ExpressAdapter = new ExpressAdapter();
createBullBoard({
queues: [
...Object.values(QueueName).map((queueName) => {
...Object.values(QueueName).map((queueName: QueueName) => {
return new BullMQAdapter(this.getQueue(queueName));
}),
],

View File

@@ -1138,8 +1138,6 @@ export default class OneUptimeDate {
);
}
public static getDateWithCustomTime(data: {
hours: number;
minutes: number;

View File

@@ -1,3 +1,3 @@
export default interface ChartDataPoint {
[x: string]: number | string;
}
export default interface ChartDataPoint {
[x: string]: number | string;
}

View File

@@ -1,8 +1,12 @@
// Tremor Raw chartColors [v0.1.0]
export type ColorUtility = "bg" | "stroke" | "fill" | "text"
export type ColorUtility = "bg" | "stroke" | "fill" | "text";
export const chartColors = {
export const chartColors: {
[color: string]: {
[key in ColorUtility]: string;
};
} = {
blue: {
bg: "bg-blue-500",
stroke: "stroke-blue-500",
@@ -69,28 +73,24 @@ export const chartColors = {
fill: "fill-rose-500",
text: "text-rose-500",
},
} as const satisfies {
[color: string]: {
[key in ColorUtility]: string
}
}
};
export type AvailableChartColorsKeys = keyof typeof chartColors
export type AvailableChartColorsKeys = keyof typeof chartColors;
export const AvailableChartColors: AvailableChartColorsKeys[] = Object.keys(
chartColors,
) as Array<AvailableChartColorsKeys>
) as Array<AvailableChartColorsKeys>;
export const constructCategoryColors = (
categories: string[],
colors: AvailableChartColorsKeys[],
): Map<string, AvailableChartColorsKeys> => {
const categoryColors = new Map<string, AvailableChartColorsKeys>()
const categoryColors = new Map<string, AvailableChartColorsKeys>();
categories.forEach((category, index) => {
categoryColors.set(category, colors[index % colors.length]!)
})
return categoryColors
}
categoryColors.set(category, colors[index % colors.length]!);
});
return categoryColors;
};
export const getColorClassName = (
color: AvailableChartColorsKeys,
@@ -101,6 +101,6 @@ export const getColorClassName = (
stroke: "stroke-gray-500",
fill: "fill-gray-500",
text: "text-gray-500",
}
return chartColors[color]?.[type] ?? fallbackColor[type]
}
};
return chartColors[color]?.[type] ?? fallbackColor[type];
};

View File

@@ -1,8 +1,8 @@
// Tremor Raw cx [v0.0.0]
import clsx, { type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
import clsx, { type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cx(...args: ClassValue[]) {
return twMerge(clsx(...args))
}
export function cx(...args: ClassValue[]): string {
return twMerge(clsx(...args));
}

View File

@@ -1,11 +1,11 @@
// Tremor Raw getYAxisDomain [v0.0.0]
export const getYAxisDomain = (
autoMinValue: boolean,
minValue: number | undefined,
maxValue: number | undefined,
) => {
const minDomain = autoMinValue ? "auto" : (minValue ?? 0)
const maxDomain = maxValue ?? "auto"
return [minDomain, maxDomain]
}
export const getYAxisDomain: (autoMinValue: boolean, minValue: number | undefined, maxValue: number | undefined) => (number | "auto")[] = (
autoMinValue: boolean,
minValue: number | undefined,
maxValue: number | undefined,
): (number | "auto")[] => {
const minDomain: number | "auto" = autoMinValue ? "auto" : minValue ?? 0;
const maxDomain: number | "auto" = maxValue ?? "auto";
return [minDomain, maxDomain];
};

View File

@@ -1,19 +1,19 @@
// Tremor Raw hasOnlyOneValueForKey [v0.1.0]
export function hasOnlyOneValueForKey(
array: any[],
keyToCheck: string,
): boolean {
const val: any[] = []
for (const obj of array) {
if (Object.prototype.hasOwnProperty.call(obj, keyToCheck)) {
val.push(obj[keyToCheck])
if (val.length > 1) {
return false
}
array: any[],
keyToCheck: string,
): boolean {
const val: any[] = [];
for (const obj of array) {
if (Object.prototype.hasOwnProperty.call(obj, keyToCheck)) {
val.push(obj[keyToCheck]);
if (val.length > 1) {
return false;
}
}
return true
}
}
return true;
}

View File

@@ -1,15 +1,15 @@
// Tremor Raw useOnWindowResize [v0.0.0]
import * as React from "react";
import * as React from "react"
export const useOnWindowResize = (handler: { (): void }) => {
export const useOnWindowResize: (handler: () => void) => void = (handler: () => void): void => {
React.useEffect(() => {
const handleResize = () => {
handler()
}
handleResize()
window.addEventListener("resize", handleResize)
const handleResize: () => void = () => {
handler();
};
handleResize();
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize)
}, [handler])
}
return () => {
window.removeEventListener("resize", handleResize);
};
}, [handler]);
};

View File

@@ -7,7 +7,6 @@ import ChartCurve from "../Types/ChartCurve";
import ChartDataPoint from "../ChartLibrary/Types/ChartDataPoint";
import DataPointUtil from "../Utils/DataPoint";
export interface ComponentProps {
data: Array<SeriesPoint>;
xAxis: XAxis;
@@ -19,6 +18,9 @@ export interface ComponentProps {
const LineChartElement: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
if (!props.sync) {
return <></>;
}
const [records, setRecords] = React.useState<Array<ChartDataPoint>>([]);
@@ -27,7 +29,6 @@ const LineChartElement: FunctionComponent<ComponentProps> = (
});
useEffect(() => {
if (!props.data || props.data.length === 0) {
setRecords([]);
}
@@ -35,14 +36,12 @@ const LineChartElement: FunctionComponent<ComponentProps> = (
const records: Array<ChartDataPoint> = DataPointUtil.getChartDataPoints({
seriesPoints: props.data,
xAxis: props.xAxis,
yAxis: props.yAxis,
});
setRecords(records);
}, [props.data]);
return (
<LineChart
className="h-80"
@@ -50,14 +49,21 @@ const LineChartElement: FunctionComponent<ComponentProps> = (
tickGap={1}
index={"Time"}
categories={categories}
colors={["indigo", "rose", "emerald", "amber", "cyan", "gray", "pink", "lime", "fuchsia"]}
colors={[
"indigo",
"rose",
"emerald",
"amber",
"cyan",
"gray",
"pink",
"lime",
"fuchsia",
]}
valueFormatter={props.yAxis.options.formatter || undefined}
showTooltip={true}
connectNulls={true}
yAxisWidth={60}
onValueChange={(v) => {
return console.log(v);
}}
/>
);
};

View File

@@ -2,7 +2,7 @@ import XAxisMaxMin from "./XAxisMaxMin";
import XAxisType from "./XAxisType";
export enum XAxisAggregateType {
Average = "Average",
Average = "Average",
Sum = "Sum",
Max = "Max",
Min = "Min",

View File

@@ -13,7 +13,7 @@ export interface YAxisOptions {
min: YAxisMaxMin;
max: YAxisMaxMin;
formatter: (value: number) => string;
precision:
precision: YAxisPrecision;
}
export default interface YAxis {

View File

@@ -1,6 +1,4 @@
/// ChartDataPoint is in the format of:
/// ChartDataPoint is in the format of:
// {
// date: "Feb 22",
// SolarPanels: 2756,
@@ -15,109 +13,176 @@ import XAxisMaxMin from "../Types/XAxis/XAxisMaxMin";
import YAxis, { YAxisPrecision } from "../Types/YAxis/YAxis";
import XAxisUtil from "./XAxis";
interface SeriesData {
sum: number;
count: number;
max: number;
min: number;
}
export default class DataPointUtil {
public static getChartDataPoints(data: {
seriesPoints: Array<SeriesPoints>;
xAxis: XAxis;
yAxis: YAxis;
}): Array<ChartDataPoint> {
public static getChartDataPoints(data: {
seriesPoints: Array<SeriesPoints>;
xAxis: XAxis;
yAxis: YAxis;
}): Array<ChartDataPoint> {
const { xAxisLegend, intervals, formatter } = this.initializeXAxisData(
data.xAxis,
);
const arrayOfData: ChartDataPoint[] = this.initializeArrayOfData(
intervals,
xAxisLegend,
formatter,
);
const seriesDataMap: {
[key: string]: SeriesData;
} = this.processSeriesData(
data.seriesPoints,
arrayOfData,
xAxisLegend,
formatter,
data.xAxis.options.aggregateType,
);
this.formatSeriesData(
arrayOfData,
seriesDataMap,
data.yAxis.options.precision,
);
return arrayOfData;
}
const xAxisMax: XAxisMaxMin = data.xAxis.options.max;
const xAxisMin: XAxisMaxMin = data.xAxis.options.min;
const xAxisLegend: string = data.xAxis.legend;
const intervals: Array<Date> = XAxisUtil.getPrecisionIntervals({
xAxisMax: xAxisMax,
xAxisMin: xAxisMin
});
const formatter: (value: Date) => string = XAxisUtil.getFormatter({
xAxisMax: xAxisMax,
xAxisMin: xAxisMin
});
const arrayOfData: Array<ChartDataPoint> = [];
// format all the intervals.
for (const interval of intervals) {
const dataPoint: ChartDataPoint = {};
dataPoint[xAxisLegend] = formatter(interval);
arrayOfData.push(dataPoint);
}
interface SeriesData {
sum: number;
count: number;
max: number;
min: number;
}
// Initialize a new data structure to store sum, count, max, and min for each series
const seriesDataMap: { [key: string]: SeriesData } = {};
// now we need to add the data points.
for (const series of data.seriesPoints) {
for (const dataPoint of series.data) {
const date: Date = dataPoint.x;
const value: number = dataPoint.y;
const formattedDate: string = formatter(date);
for (const chartDataPoint of arrayOfData) {
if (chartDataPoint[xAxisLegend] === formattedDate) {
// Initialize series data if it doesn't exist
if (!seriesDataMap[series.seriesName]) {
seriesDataMap[series.seriesName] = { sum: 0, count: 0, max: Number.NEGATIVE_INFINITY, min: Number.POSITIVE_INFINITY };
}
// Update sum, count, max, and min
seriesDataMap[series.seriesName]!.sum += value;
seriesDataMap[series.seriesName]!.count += 1;
seriesDataMap[series.seriesName]!.max = Math.max(seriesDataMap[series.seriesName]!.max, value);
seriesDataMap[series.seriesName]!.min = Math.min(seriesDataMap[series.seriesName]!.min, value);
// Calculate the average, sum, max, or min based on the aggregate type
if (data.xAxis.options.aggregateType === XAxisAggregateType.Average) {
chartDataPoint[series.seriesName] = seriesDataMap[series.seriesName]!.sum / seriesDataMap[series.seriesName]!.count;
} else if (data.xAxis.options.aggregateType === XAxisAggregateType.Sum) {
chartDataPoint[series.seriesName] = seriesDataMap[series.seriesName]!.sum;
} else if (data.xAxis.options.aggregateType === XAxisAggregateType.Max) {
chartDataPoint[series.seriesName] = seriesDataMap[series.seriesName]!.max;
} else if (data.xAxis.options.aggregateType === XAxisAggregateType.Min) {
chartDataPoint[series.seriesName] = seriesDataMap[series.seriesName]!.min;
} else {
throw new BadDataException("Aggregate type not supported.");
}
if (chartDataPoint[series.seriesName] && typeof chartDataPoint[series.seriesName] === "number") {
// Format the series data based on yAxis precision
const yAxisPrecision = data.yAxis.options.precision;
switch (yAxisPrecision) {
case YAxisPrecision.NoDecimals:
chartDataPoint[series.seriesName] = parseFloat((chartDataPoint[series.seriesName]! as number).toFixed(0));
break;
case YAxisPrecision.OneDecimal:
chartDataPoint[series.seriesName] = parseFloat(((chartDataPoint[series.seriesName]! as number).toFixed(1)));
break;
case YAxisPrecision.TwoDecimals:
chartDataPoint[series.seriesName] = parseFloat(((chartDataPoint[series.seriesName]! as number).toFixed(2)));
break;
case YAxisPrecision.ThreeDecimals:
chartDataPoint[series.seriesName] = parseFloat(((chartDataPoint[series.seriesName]! as number).toFixed(3)))
break;
default:
throw new BadDataException("YAxis precision not supported.");
}
}
}
}
}
}
return arrayOfData;
private static initializeXAxisData(xAxis: XAxis): {
xAxisMax: XAxisMaxMin;
xAxisMin: XAxisMaxMin;
xAxisLegend: string;
intervals: Array<Date>;
formatter: (value: Date) => string;
} {
const xAxisMax: XAxisMaxMin = xAxis.options.max;
const xAxisMin: XAxisMaxMin = xAxis.options.min;
const xAxisLegend: string = xAxis.legend;
const intervals: Array<Date> = XAxisUtil.getPrecisionIntervals({
xAxisMax,
xAxisMin,
});
const formatter: (value: Date) => string = XAxisUtil.getFormatter({
xAxisMax,
xAxisMin,
});
return { xAxisMax, xAxisMin, xAxisLegend, intervals, formatter };
}
private static initializeArrayOfData(
intervals: Array<Date>,
xAxisLegend: string,
formatter: (value: Date) => string,
): Array<ChartDataPoint> {
const arrayOfData: Array<ChartDataPoint> = [];
for (const interval of intervals) {
const dataPoint: ChartDataPoint = {};
dataPoint[xAxisLegend] = formatter(interval);
arrayOfData.push(dataPoint);
}
}
return arrayOfData;
}
private static processSeriesData(
seriesPoints: Array<SeriesPoints>,
arrayOfData: Array<ChartDataPoint>,
xAxisLegend: string,
formatter: (value: Date) => string,
aggregateType: XAxisAggregateType,
): { [key: string]: SeriesData } {
const seriesDataMap: { [key: string]: SeriesData } = {};
for (const series of seriesPoints) {
for (const dataPoint of series.data) {
const date: Date = dataPoint.x;
const value: number = dataPoint.y;
const formattedDate: string = formatter(date);
for (const chartDataPoint of arrayOfData) {
if (chartDataPoint[xAxisLegend] === formattedDate) {
if (!seriesDataMap[series.seriesName]) {
seriesDataMap[series.seriesName] = {
sum: 0,
count: 0,
max: Number.NEGATIVE_INFINITY,
min: Number.POSITIVE_INFINITY,
};
}
seriesDataMap[series.seriesName]!.sum += value;
seriesDataMap[series.seriesName]!.count += 1;
seriesDataMap[series.seriesName]!.max = Math.max(
seriesDataMap[series.seriesName]!.max,
value,
);
seriesDataMap[series.seriesName]!.min = Math.min(
seriesDataMap[series.seriesName]!.min,
value,
);
chartDataPoint[series.seriesName] = this.calculateAggregate(
seriesDataMap[series.seriesName]!,
aggregateType,
);
}
}
}
}
return seriesDataMap;
}
private static calculateAggregate(
seriesData: SeriesData,
aggregateType: XAxisAggregateType,
): number {
switch (aggregateType) {
case XAxisAggregateType.Average:
return seriesData.sum / seriesData.count;
case XAxisAggregateType.Sum:
return seriesData.sum;
case XAxisAggregateType.Max:
return seriesData.max;
case XAxisAggregateType.Min:
return seriesData.min;
default:
throw new BadDataException("Aggregate type not supported.");
}
}
private static formatSeriesData(
arrayOfData: Array<ChartDataPoint>,
seriesDataMap: { [key: string]: SeriesData },
yAxisPrecision: YAxisPrecision,
): void {
for (const chartDataPoint of arrayOfData) {
for (const seriesName in seriesDataMap) {
if (
chartDataPoint[seriesName] &&
typeof chartDataPoint[seriesName] === "number"
) {
chartDataPoint[seriesName] = this.formatValue(
chartDataPoint[seriesName] as number,
yAxisPrecision,
);
}
}
}
}
private static formatValue(
value: number,
yAxisPrecision: YAxisPrecision,
): number {
switch (yAxisPrecision) {
case YAxisPrecision.NoDecimals:
return parseFloat(value.toFixed(0));
case YAxisPrecision.OneDecimal:
return parseFloat(value.toFixed(1));
case YAxisPrecision.TwoDecimals:
return parseFloat(value.toFixed(2));
case YAxisPrecision.ThreeDecimals:
return parseFloat(value.toFixed(3));
default:
throw new BadDataException("YAxis precision not supported.");
}
}
}

View File

@@ -3,207 +3,265 @@ import XAxisMaxMin from "../Types/XAxis/XAxisMaxMin";
import XAxisPrecision from "../Types/XAxis/XAxisPrecision";
export default class XAxisUtil {
public static getPrecision(data: {
xAxisMin: XAxisMaxMin,
xAxisMax: XAxisMaxMin,
}): XAxisPrecision {
if (typeof data.xAxisMax === "number" || typeof data.xAxisMin === "number") {
// number not yet supported.
throw new NotImplementedException();
}
const startDate: Date = data.xAxisMin;
const endDate: Date = data.xAxisMax;
const totalMilliseconds = endDate.getTime() - startDate.getTime();
const totalSeconds = totalMilliseconds / 1000;
const totalMinutes = totalSeconds / 60;
const totalHours = totalMinutes / 60;
const totalDays = totalHours / 24;
const totalWeeks = totalDays / 7;
const totalMonths = totalDays / 30;
if (totalSeconds <= 50) return XAxisPrecision.EVERY_SECOND;
if (totalSeconds <= 250) return XAxisPrecision.EVERY_FIVE_SECONDS;
if (totalSeconds <= 500) return XAxisPrecision.EVERY_TEN_SECONDS;
if (totalSeconds <= 1500) return XAxisPrecision.EVERY_THIRTY_SECONDS;
if (totalMinutes <= 50) return XAxisPrecision.EVERY_MINUTE;
if (totalMinutes <= 250) return XAxisPrecision.EVERY_FIVE_MINUTES;
if (totalMinutes <= 500) return XAxisPrecision.EVERY_TEN_MINUTES;
if (totalMinutes <= 1500) return XAxisPrecision.EVERY_THIRTY_MINUTES;
if (totalHours <= 50) return XAxisPrecision.EVERY_HOUR;
if (totalHours <= 100) return XAxisPrecision.EVERY_TWO_HOURS;
if (totalHours <= 150) return XAxisPrecision.EVERY_THREE_HOURS;
if (totalHours <= 300) return XAxisPrecision.EVERY_SIX_HOURS;
if (totalHours <= 600) return XAxisPrecision.EVERY_TWELVE_HOURS;
if (totalDays <= 50) return XAxisPrecision.EVERY_DAY;
if (totalDays <= 100) return XAxisPrecision.EVERY_TWO_DAYS;
if (totalWeeks <= 50) return XAxisPrecision.EVERY_WEEK;
if (totalWeeks <= 100) return XAxisPrecision.EVERY_TWO_WEEKS;
if (totalMonths <= 50) return XAxisPrecision.EVERY_MONTH;
if (totalMonths <= 100) return XAxisPrecision.EVERY_TWO_MONTHS;
if (totalMonths <= 150) return XAxisPrecision.EVERY_THREE_MONTHS;
if (totalMonths <= 300) return XAxisPrecision.EVERY_SIX_MONTHS;
return XAxisPrecision.EVERY_YEAR;
public static getPrecision(data: {
xAxisMin: XAxisMaxMin;
xAxisMax: XAxisMaxMin;
}): XAxisPrecision {
if (
typeof data.xAxisMax === "number" ||
typeof data.xAxisMin === "number"
) {
// number not yet supported.
throw new NotImplementedException();
}
public static getPrecisionIntervals(data: {
xAxisMin: XAxisMaxMin,
xAxisMax: XAxisMaxMin,
}): Array<Date> {
const precision: XAxisPrecision = XAxisUtil.getPrecision(data);
const startDate: Date = data.xAxisMin as Date;
const endDate: Date = data.xAxisMax as Date;
if (typeof data.xAxisMax === "number" || typeof data.xAxisMin === "number") {
// number not yet supported.
throw new NotImplementedException();
}
const totalMilliseconds: number = endDate.getTime() - startDate.getTime();
const totalSeconds: number = totalMilliseconds / 1000;
const totalMinutes: number = totalSeconds / 60;
const totalHours: number = totalMinutes / 60;
const totalDays: number = totalHours / 24;
const totalWeeks: number = totalDays / 7;
const totalMonths: number = totalDays / 30;
const startDate: Date = new Date(data.xAxisMin);
const endDate: Date = new Date(data.xAxisMax);
const intervals: Array<Date> = [];
if (totalSeconds <= 100) {
return XAxisPrecision.EVERY_SECOND;
}
if (totalSeconds <= 500) {
return XAxisPrecision.EVERY_FIVE_SECONDS;
}
if (totalSeconds <= 1000) {
return XAxisPrecision.EVERY_TEN_SECONDS;
}
if (totalSeconds <= 3000) {
return XAxisPrecision.EVERY_THIRTY_SECONDS;
}
if (totalMinutes <= 100) {
return XAxisPrecision.EVERY_MINUTE;
}
if (totalMinutes <= 500) {
return XAxisPrecision.EVERY_FIVE_MINUTES;
}
if (totalMinutes <= 1000) {
return XAxisPrecision.EVERY_TEN_MINUTES;
}
if (totalMinutes <= 3000) {
return XAxisPrecision.EVERY_THIRTY_MINUTES;
}
if (totalHours <= 100) {
return XAxisPrecision.EVERY_HOUR;
}
if (totalHours <= 200) {
return XAxisPrecision.EVERY_TWO_HOURS;
}
if (totalHours <= 300) {
return XAxisPrecision.EVERY_THREE_HOURS;
}
if (totalHours <= 600) {
return XAxisPrecision.EVERY_SIX_HOURS;
}
if (totalHours <= 1200) {
return XAxisPrecision.EVERY_TWELVE_HOURS;
}
if (totalDays <= 100) {
return XAxisPrecision.EVERY_DAY;
}
if (totalDays <= 200) {
return XAxisPrecision.EVERY_TWO_DAYS;
}
if (totalWeeks <= 100) {
return XAxisPrecision.EVERY_WEEK;
}
if (totalWeeks <= 200) {
return XAxisPrecision.EVERY_TWO_WEEKS;
}
if (totalMonths <= 100) {
return XAxisPrecision.EVERY_MONTH;
}
if (totalMonths <= 200) {
return XAxisPrecision.EVERY_TWO_MONTHS;
}
if (totalMonths <= 300) {
return XAxisPrecision.EVERY_THREE_MONTHS;
}
if (totalMonths <= 600) {
return XAxisPrecision.EVERY_SIX_MONTHS;
}
return XAxisPrecision.EVERY_YEAR;
}
let currentDate = startDate;
public static getPrecisionIntervals(data: {
xAxisMin: XAxisMaxMin;
xAxisMax: XAxisMaxMin;
}): Array<Date> {
const precision: XAxisPrecision = XAxisUtil.getPrecision(data);
while (currentDate <= endDate) {
intervals.push(new Date(currentDate));
switch (precision) {
case XAxisPrecision.EVERY_SECOND:
currentDate.setSeconds(currentDate.getSeconds() + 1);
break;
case XAxisPrecision.EVERY_FIVE_SECONDS:
currentDate.setSeconds(currentDate.getSeconds() + 5);
break;
case XAxisPrecision.EVERY_TEN_SECONDS:
currentDate.setSeconds(currentDate.getSeconds() + 10);
break;
case XAxisPrecision.EVERY_THIRTY_SECONDS:
currentDate.setSeconds(currentDate.getSeconds() + 30);
break;
case XAxisPrecision.EVERY_MINUTE:
currentDate.setMinutes(currentDate.getMinutes() + 1);
break;
case XAxisPrecision.EVERY_FIVE_MINUTES:
currentDate.setMinutes(currentDate.getMinutes() + 5);
break;
case XAxisPrecision.EVERY_TEN_MINUTES:
currentDate.setMinutes(currentDate.getMinutes() + 10);
break;
case XAxisPrecision.EVERY_THIRTY_MINUTES:
currentDate.setMinutes(currentDate.getMinutes() + 30);
break;
case XAxisPrecision.EVERY_HOUR:
currentDate.setHours(currentDate.getHours() + 1);
break;
case XAxisPrecision.EVERY_TWO_HOURS:
currentDate.setHours(currentDate.getHours() + 2);
break;
case XAxisPrecision.EVERY_THREE_HOURS:
currentDate.setHours(currentDate.getHours() + 3);
break;
case XAxisPrecision.EVERY_SIX_HOURS:
currentDate.setHours(currentDate.getHours() + 6);
break;
case XAxisPrecision.EVERY_TWELVE_HOURS:
currentDate.setHours(currentDate.getHours() + 12);
break;
case XAxisPrecision.EVERY_DAY:
currentDate.setDate(currentDate.getDate() + 1);
break;
case XAxisPrecision.EVERY_TWO_DAYS:
currentDate.setDate(currentDate.getDate() + 2);
break;
case XAxisPrecision.EVERY_WEEK:
currentDate.setDate(currentDate.getDate() + 7);
break;
case XAxisPrecision.EVERY_TWO_WEEKS:
currentDate.setDate(currentDate.getDate() + 14);
break;
case XAxisPrecision.EVERY_MONTH:
currentDate.setMonth(currentDate.getMonth() + 1);
break;
case XAxisPrecision.EVERY_TWO_MONTHS:
currentDate.setMonth(currentDate.getMonth() + 2);
break;
case XAxisPrecision.EVERY_THREE_MONTHS:
currentDate.setMonth(currentDate.getMonth() + 3);
break;
case XAxisPrecision.EVERY_SIX_MONTHS:
currentDate.setMonth(currentDate.getMonth() + 6);
break;
case XAxisPrecision.EVERY_YEAR:
currentDate.setFullYear(currentDate.getFullYear() + 1);
break;
}
}
return intervals;
if (
typeof data.xAxisMax === "number" ||
typeof data.xAxisMin === "number"
) {
// number not yet supported.
throw new NotImplementedException();
}
public static getFormatter(data: {
xAxisMin: XAxisMaxMin,
xAxisMax: XAxisMaxMin,
}): (value: Date) => string {
const startDate: Date = new Date(data.xAxisMin as Date);
const endDate: Date = new Date(data.xAxisMax as Date);
const intervals: Array<Date> = [];
const precision: XAxisPrecision = XAxisUtil.getPrecision(data);
const currentDate: Date = new Date(startDate);
switch (precision) {
case XAxisPrecision.EVERY_SECOND:
case XAxisPrecision.EVERY_FIVE_SECONDS:
case XAxisPrecision.EVERY_TEN_SECONDS:
case XAxisPrecision.EVERY_THIRTY_SECONDS:
return (value: Date) => value.toISOString().substring(11, 19); // HH:mm:ss
case XAxisPrecision.EVERY_MINUTE:
case XAxisPrecision.EVERY_FIVE_MINUTES:
case XAxisPrecision.EVERY_TEN_MINUTES:
case XAxisPrecision.EVERY_THIRTY_MINUTES:
return (value: Date) => value.toISOString().substring(11, 16); // HH:mm
case XAxisPrecision.EVERY_HOUR:
case XAxisPrecision.EVERY_TWO_HOURS:
case XAxisPrecision.EVERY_THREE_HOURS:
case XAxisPrecision.EVERY_SIX_HOURS:
case XAxisPrecision.EVERY_TWELVE_HOURS:
return (value: Date) => {
const dateString = value.toISOString();
const day = dateString.substring(8, 10);
const month = value.toLocaleString('default', { month: 'short' });
const hour = dateString.substring(11, 13);
return `${day} ${month}, ${hour}:00`;
}; // DD MMM, HH:00
case XAxisPrecision.EVERY_DAY:
case XAxisPrecision.EVERY_TWO_DAYS:
return (value: Date) => {
const dateString = value.toISOString();
const day = dateString.substring(8, 10);
const month = value.toLocaleString('default', { month: 'short' });
return `${day} ${month}`;
}; // DD MMM
case XAxisPrecision.EVERY_WEEK:
case XAxisPrecision.EVERY_TWO_WEEKS:
return (value: Date) => {
const dateString = value.toISOString();
const day = dateString.substring(8, 10);
const month = value.toLocaleString('default', { month: 'short' });
return `${day} ${month}`;
}; // DD MMM
case XAxisPrecision.EVERY_MONTH:
case XAxisPrecision.EVERY_TWO_MONTHS:
case XAxisPrecision.EVERY_THREE_MONTHS:
case XAxisPrecision.EVERY_SIX_MONTHS:
return (value: Date) => {
const dateString = value.toISOString();
const day = dateString.substring(8, 10);
const year = dateString.substring(0, 4);
const month = value.toLocaleString('default', { month: 'short' });
return `${day} ${month} ${year}`;
}; // DD MMM
case XAxisPrecision.EVERY_YEAR:
return (value: Date) => value.toISOString().substring(0, 4); // YYYY
default:
throw new Error("Unsupported precision");
}
while (currentDate <= endDate) {
intervals.push(new Date(currentDate));
switch (precision) {
case XAxisPrecision.EVERY_SECOND:
currentDate.setSeconds(currentDate.getSeconds() + 1);
break;
case XAxisPrecision.EVERY_FIVE_SECONDS:
currentDate.setSeconds(currentDate.getSeconds() + 5);
break;
case XAxisPrecision.EVERY_TEN_SECONDS:
currentDate.setSeconds(currentDate.getSeconds() + 10);
break;
case XAxisPrecision.EVERY_THIRTY_SECONDS:
currentDate.setSeconds(currentDate.getSeconds() + 30);
break;
case XAxisPrecision.EVERY_MINUTE:
currentDate.setMinutes(currentDate.getMinutes() + 1);
break;
case XAxisPrecision.EVERY_FIVE_MINUTES:
currentDate.setMinutes(currentDate.getMinutes() + 5);
break;
case XAxisPrecision.EVERY_TEN_MINUTES:
currentDate.setMinutes(currentDate.getMinutes() + 10);
break;
case XAxisPrecision.EVERY_THIRTY_MINUTES:
currentDate.setMinutes(currentDate.getMinutes() + 30);
break;
case XAxisPrecision.EVERY_HOUR:
currentDate.setHours(currentDate.getHours() + 1);
break;
case XAxisPrecision.EVERY_TWO_HOURS:
currentDate.setHours(currentDate.getHours() + 2);
break;
case XAxisPrecision.EVERY_THREE_HOURS:
currentDate.setHours(currentDate.getHours() + 3);
break;
case XAxisPrecision.EVERY_SIX_HOURS:
currentDate.setHours(currentDate.getHours() + 6);
break;
case XAxisPrecision.EVERY_TWELVE_HOURS:
currentDate.setHours(currentDate.getHours() + 12);
break;
case XAxisPrecision.EVERY_DAY:
currentDate.setDate(currentDate.getDate() + 1);
break;
case XAxisPrecision.EVERY_TWO_DAYS:
currentDate.setDate(currentDate.getDate() + 2);
break;
case XAxisPrecision.EVERY_WEEK:
currentDate.setDate(currentDate.getDate() + 7);
break;
case XAxisPrecision.EVERY_TWO_WEEKS:
currentDate.setDate(currentDate.getDate() + 14);
break;
case XAxisPrecision.EVERY_MONTH:
currentDate.setMonth(currentDate.getMonth() + 1);
break;
case XAxisPrecision.EVERY_TWO_MONTHS:
currentDate.setMonth(currentDate.getMonth() + 2);
break;
case XAxisPrecision.EVERY_THREE_MONTHS:
currentDate.setMonth(currentDate.getMonth() + 3);
break;
case XAxisPrecision.EVERY_SIX_MONTHS:
currentDate.setMonth(currentDate.getMonth() + 6);
break;
case XAxisPrecision.EVERY_YEAR:
currentDate.setFullYear(currentDate.getFullYear() + 1);
break;
}
}
}
return intervals;
}
public static getFormatter(data: {
xAxisMin: XAxisMaxMin;
xAxisMax: XAxisMaxMin;
}): (value: Date) => string {
const precision: XAxisPrecision = XAxisUtil.getPrecision(data);
switch (precision) {
case XAxisPrecision.EVERY_SECOND:
case XAxisPrecision.EVERY_FIVE_SECONDS:
case XAxisPrecision.EVERY_TEN_SECONDS:
case XAxisPrecision.EVERY_THIRTY_SECONDS:
return (value: Date) => {
return value.toISOString().substring(11, 19);
}; // HH:mm:ss
case XAxisPrecision.EVERY_MINUTE:
case XAxisPrecision.EVERY_FIVE_MINUTES:
case XAxisPrecision.EVERY_TEN_MINUTES:
case XAxisPrecision.EVERY_THIRTY_MINUTES:
return (value: Date) => {
return value.toISOString().substring(11, 16);
}; // HH:mm
case XAxisPrecision.EVERY_HOUR:
case XAxisPrecision.EVERY_TWO_HOURS:
case XAxisPrecision.EVERY_THREE_HOURS:
case XAxisPrecision.EVERY_SIX_HOURS:
case XAxisPrecision.EVERY_TWELVE_HOURS:
return (value: Date) => {
const dateString: string = value.toISOString();
const day: string = dateString.substring(8, 10);
const month: string = value.toLocaleString("default", {
month: "short",
});
const hour: string = dateString.substring(11, 13);
return `${day} ${month}, ${hour}:00`;
}; // DD MMM, HH:00
case XAxisPrecision.EVERY_DAY:
case XAxisPrecision.EVERY_TWO_DAYS:
return (value: Date) => {
const dateString: string = value.toISOString();
const day: string = dateString.substring(8, 10);
const month: string = value.toLocaleString("default", {
month: "short",
});
return `${day} ${month}`;
}; // DD MMM
case XAxisPrecision.EVERY_WEEK:
case XAxisPrecision.EVERY_TWO_WEEKS:
return (value: Date) => {
const dateString: string = value.toISOString();
const day: string = dateString.substring(8, 10);
const month: string = value.toLocaleString("default", {
month: "short",
});
return `${day} ${month}`;
}; // DD MMM
case XAxisPrecision.EVERY_MONTH:
case XAxisPrecision.EVERY_TWO_MONTHS:
case XAxisPrecision.EVERY_THREE_MONTHS:
case XAxisPrecision.EVERY_SIX_MONTHS:
return (value: Date) => {
const dateString: string = value.toISOString();
const day: string = dateString.substring(8, 10);
const year: string = dateString.substring(0, 4);
const month: string = value.toLocaleString("default", {
month: "short",
});
return `${day} ${month} ${year}`;
}; // DD MMM
case XAxisPrecision.EVERY_YEAR:
return (value: Date) => {
return value.toISOString().substring(0, 4);
}; // YYYY
default:
throw new Error("Unsupported precision");
}
}
}

View File

@@ -29,7 +29,7 @@ import ModelAPI from "Common/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI";
import Metric from "Common/Models/AnalyticsModels/Metric";
import OneUptimeDate from "Common/Types/Date";
import { LIMIT_PER_PROJECT } from "Common/Types/Database/LimitMax";
import ComponentLoader from "Common/UI/Components/Compon\entLoader/ComponentLoader";
import ComponentLoader from "Common/UI/Components/ComponentLoader/ComponentLoader";
import ErrorMessage from "Common/UI/Components/ErrorMessage/ErrorMessage";
import ChartGroup, {
Chart,
@@ -66,7 +66,6 @@ export interface ComponentProps {
const MetricView: FunctionComponent<ComponentProps> = (
props: ComponentProps,
): ReactElement => {
const [xAxisType, setXAxisType] = useState<XAxisType>(XAxisType.Time);
const [chartStartDate, setChartStartDate] = useState<Date>(
@@ -135,7 +134,6 @@ const MetricView: FunctionComponent<ComponentProps> = (
return XAxisType.Date;
};
useEffect(() => {
fetchAggregatedResults().catch((err: Error) => {
setMetricResultsError(API.getFriendlyErrorMessage(err as Error));
@@ -158,29 +156,43 @@ const MetricView: FunctionComponent<ComponentProps> = (
continue;
}
let xAxisAggregationType = XAxisAggregateType.Average;
let xAxisAggregationType: XAxisAggregateType = XAxisAggregateType.Average;
if(queryConfig.metricQueryData.filterData.aggegationType === MetricsAggregationType.Sum) {
if (
queryConfig.metricQueryData.filterData.aggegationType ===
MetricsAggregationType.Sum
) {
xAxisAggregationType = XAxisAggregateType.Sum;
}
if(queryConfig.metricQueryData.filterData.aggegationType === MetricsAggregationType.Count) {
if (
queryConfig.metricQueryData.filterData.aggegationType ===
MetricsAggregationType.Count
) {
xAxisAggregationType = XAxisAggregateType.Sum;
}
if(queryConfig.metricQueryData.filterData.aggegationType === MetricsAggregationType.Max) {
if (
queryConfig.metricQueryData.filterData.aggegationType ===
MetricsAggregationType.Max
) {
xAxisAggregationType = XAxisAggregateType.Max;
}
if(queryConfig.metricQueryData.filterData.aggegationType === MetricsAggregationType.Min) {
if (
queryConfig.metricQueryData.filterData.aggegationType ===
MetricsAggregationType.Min
) {
xAxisAggregationType = XAxisAggregateType.Min;
}
if(queryConfig.metricQueryData.filterData.aggegationType === MetricsAggregationType.Avg) {
if (
queryConfig.metricQueryData.filterData.aggegationType ===
MetricsAggregationType.Avg
) {
xAxisAggregationType = XAxisAggregateType.Average;
}
const chart: Chart = {
id: index.toString(),
type: ChartType.LINE,

View File

@@ -14,9 +14,14 @@ import Probe from "Common/Models/DatabaseModels/Probe";
import DataPoint from "Common/UI/Components/Charts/Types/DataPoint";
import SeriesPoints from "Common/UI/Components/Charts/Types/SeriesPoints";
import YAxisType from "Common/UI/Components/Charts/Types/YAxis/YAxisType";
import YAxis, { YAxisPrecision } from "Common/UI/Components/Charts/Types/YAxis/YAxis";
import YAxis, {
YAxisPrecision,
} from "Common/UI/Components/Charts/Types/YAxis/YAxis";
import XAxisType from "Common/UI/Components/Charts/Types/XAxis/XAxisType";
import { XAxis, XAxisAggregateType } from "Common/UI/Components/Charts/Types/XAxis/XAxis";
import {
XAxis,
XAxisAggregateType,
} from "Common/UI/Components/Charts/Types/XAxis/XAxis";
import ChartCurve from "Common/UI/Components/Charts/Types/ChartCurve";
export class MonitorCharts {
@@ -259,31 +264,31 @@ export class MonitorCharts {
monitorMetricsByMinute: Array<MonitorMetricsByMinute>;
checkOn: CheckOn;
}): XAxis {
const startTime: Date =
data.monitorMetricsByMinute[0]?.createdAt! || undefined;
const startTime: Date =
data.monitorMetricsByMinute[0]!.createdAt! || undefined;
const endTime: Date =
data.monitorMetricsByMinute[data.monitorMetricsByMinute.length - 1]
?.createdAt! || undefined;
data.monitorMetricsByMinute[data.monitorMetricsByMinute.length - 1]!
.createdAt! || undefined;
let xAxisAggregationType: XAxisAggregateType = XAxisAggregateType.Average;
if(data.checkOn === CheckOn.ResponseStatusCode) {
if (data.checkOn === CheckOn.ResponseStatusCode) {
xAxisAggregationType = XAxisAggregateType.Max;
}
if(data.checkOn === CheckOn.IsOnline) {
if (data.checkOn === CheckOn.IsOnline) {
xAxisAggregationType = XAxisAggregateType.Min;
}
if(data.checkOn === CheckOn.ResponseTime) {
if (data.checkOn === CheckOn.ResponseTime) {
xAxisAggregationType = XAxisAggregateType.Average;
}
if(
if (
data.checkOn === CheckOn.DiskUsagePercent ||
data.checkOn === CheckOn.MemoryUsagePercent ||
data.checkOn === CheckOn.CPUUsagePercent
){
) {
xAxisAggregationType = XAxisAggregateType.Average;
}
@@ -309,7 +314,7 @@ export class MonitorCharts {
precision: YAxisPrecision.NoDecimals,
formatter: (value: number) => {
return `${value} ms`;
}
},
},
};
} else if (data.checkOn === CheckOn.ResponseStatusCode) {
@@ -322,7 +327,7 @@ export class MonitorCharts {
precision: YAxisPrecision.NoDecimals,
formatter: (value: number) => {
return `${value}`;
}
},
},
};
} else if (
@@ -339,7 +344,7 @@ export class MonitorCharts {
precision: YAxisPrecision.TwoDecimals,
formatter: (value: number) => {
return `${value}%`;
}
},
},
};
}
@@ -353,7 +358,7 @@ export class MonitorCharts {
precision: YAxisPrecision.NoDecimals,
formatter: (value: number) => {
return `${value}`;
}
},
},
};
}

View File

@@ -204,6 +204,7 @@ export default tseslint.config(
module: true,
__dirname: true,
exports: true,
"NodeJS": true
},
parserOptions: {
project: ["./tsconfig.json"], // Specify it only for TypeScript files