import { getColorsForCategories } from "@/src/features/dashboard/utils/getColorsForCategories";
import { compactNumberFormatter } from "@/src/utils/numbers";
import { cn } from "@/src/utils/tailwind";
import { AreaChart, type CustomTooltipProps, LineChart } from "@tremor/react";
import { Tooltip } from "@/src/features/dashboard/components/Tooltip";
import {
dashboardDateRangeAggregationSettings,
type DashboardDateRangeAggregationOption,
} from "@/src/utils/date-range-utils";
import { useMemo } from "react";
export type TimeSeriesChartDataPoint = {
ts: number;
values: { label: string; value?: number }[];
};
export function BaseTimeSeriesChart(props: {
className?: string;
agg: DashboardDateRangeAggregationOption;
data: TimeSeriesChartDataPoint[];
showLegend?: boolean;
connectNulls?: boolean;
valueFormatter?: (value: number) => string;
chartType?: "line" | "area";
}) {
const labels = new Set(
props.data.flatMap((d) => d.values.map((v) => v.label)),
);
type ChartInput = { timestamp: string } & {
[key: string]: number | undefined;
};
function transformArray(array: TimeSeriesChartDataPoint[]): ChartInput[] {
return array.map((item) => {
const outputObject: ChartInput = {
timestamp: convertDate(item.ts, props.agg),
} as ChartInput;
item.values.forEach((valueObject) => {
outputObject[valueObject.label] = valueObject.value;
});
return outputObject;
});
}
const convertDate = (
date: number,
agg: DashboardDateRangeAggregationOption,
) => {
const showMinutes = ["minute", "hour"].includes(
dashboardDateRangeAggregationSettings[agg].dateTrunc ?? "",
);
if (showMinutes) {
return new Date(date).toLocaleTimeString("en-US", {
year: "2-digit",
month: "numeric",
day: "numeric",
hour: "2-digit",
minute: "2-digit",
});
}
return new Date(date).toLocaleDateString("en-US", {
year: "2-digit",
month: "numeric",
day: "numeric",
});
};
const ChartComponent = props.chartType === "area" ? AreaChart : LineChart;
const TooltipComponent = (tooltipProps: CustomTooltipProps) => (
);
const colors = getColorsForCategories(Array.from(labels));
// Calculate dynamic maxValue based on the maximum value in the data plus 10%
const dynamicMaxValue = useMemo(() => {
if (props.data.length === 0) return undefined;
const maxValue = Math.max(
...props.data.flatMap((point) => point.values.map((v) => v.value ?? 0)),
);
if (maxValue <= 0) return undefined;
// Add 10% buffer
const bufferedValue = maxValue * 1.1;
// Get order of magnitude and rounding goal
const magnitude = Math.floor(Math.log10(bufferedValue));
const roundTo = Math.max(1, Math.pow(10, magnitude) / 5);
// Round up to the next multiple of roundTo
return Math.ceil(bufferedValue / roundTo) * roundTo;
}, [props.data]);
return (
{}}
enableLegendSlider={true}
customTooltip={TooltipComponent}
maxValue={dynamicMaxValue}
/>
);
}