mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-06-04 11:25:52 +08:00
feat: better x-axis labels
This commit is contained in:
parent
12970d6975
commit
1bf8e6bef6
@ -27,6 +27,7 @@ import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
|
||||
import { ITimeRange, useXAxisTimeUnit } from './xAxisConfig';
|
||||
Chart.register(
|
||||
LineElement,
|
||||
PointElement,
|
||||
@ -59,7 +60,11 @@ const Graph = ({
|
||||
const chartRef = useRef<HTMLCanvasElement>(null);
|
||||
const currentTheme = isDarkMode ? 'dark' : 'light';
|
||||
|
||||
// const [tooltipVisible, setTooltipVisible] = useState<boolean>(false);
|
||||
/**
|
||||
* Computes the relevant time unit for x axis by analyzing the time stamp data
|
||||
*/
|
||||
const xAxisTimeUnit = useXAxisTimeUnit(data);
|
||||
|
||||
const lineChartRef = useRef<Chart>();
|
||||
|
||||
const getGridColor = useCallback(() => {
|
||||
@ -109,7 +114,18 @@ const Graph = ({
|
||||
date: chartjsAdapter,
|
||||
},
|
||||
time: {
|
||||
unit: 'minute',
|
||||
unit: xAxisTimeUnit?.unitName || 'minute',
|
||||
stepSize: xAxisTimeUnit?.stepSize || 1,
|
||||
displayFormats: {
|
||||
millisecond: 'hh:mm:ss',
|
||||
second: 'hh:mm:ss',
|
||||
minute: 'HH:mm',
|
||||
hour: 'MM/dd HH:mm',
|
||||
day: 'MM/dd',
|
||||
week: 'MM/dd',
|
||||
month: 'yy-MM',
|
||||
year: 'yy',
|
||||
},
|
||||
},
|
||||
type: 'time',
|
||||
},
|
||||
|
113
frontend/src/components/Graph/xAxisConfig.ts
Normal file
113
frontend/src/components/Graph/xAxisConfig.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import { Chart, TimeUnit } from 'chart.js';
|
||||
import { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
interface IAxisTimeUint {
|
||||
unitName: TimeUnit;
|
||||
multiplier: number;
|
||||
}
|
||||
|
||||
interface IAxisTimeConfig {
|
||||
unitName: TimeUnit;
|
||||
stepSize: number;
|
||||
}
|
||||
|
||||
export interface ITimeRange {
|
||||
minTime: number | null;
|
||||
maxTime: number | null;
|
||||
}
|
||||
|
||||
const TIME_UNITS: IAxisTimeUint[] = [
|
||||
{
|
||||
unitName: 'millisecond',
|
||||
multiplier: 1,
|
||||
},
|
||||
{
|
||||
unitName: 'second',
|
||||
multiplier: 1 / 1e3,
|
||||
},
|
||||
{
|
||||
unitName: 'minute',
|
||||
multiplier: 1 / (1e3 * 60),
|
||||
},
|
||||
{
|
||||
unitName: 'hour',
|
||||
multiplier: 1 / (1e3 * 60 * 60),
|
||||
},
|
||||
{
|
||||
unitName: 'day',
|
||||
multiplier: 1 / (1e3 * 60 * 60 * 24),
|
||||
},
|
||||
{
|
||||
unitName: 'week',
|
||||
multiplier: 1 / (1e3 * 60 * 60 * 24 * 7),
|
||||
},
|
||||
{
|
||||
unitName: 'month',
|
||||
multiplier: 1 / (1e3 * 60 * 60 * 24 * 30),
|
||||
},
|
||||
{
|
||||
unitName: 'year',
|
||||
multiplier: 1 / (1e3 * 60 * 60 * 24 * 365),
|
||||
},
|
||||
];
|
||||
|
||||
export const useXAxisTimeUnit = (data: Chart['data']): IAxisTimeConfig => {
|
||||
let localTime: ITimeRange;
|
||||
{
|
||||
let minTime = Number.POSITIVE_INFINITY;
|
||||
let maxTime = Number.NEGATIVE_INFINITY;
|
||||
data?.labels?.forEach((timeStamp: any) => {
|
||||
timeStamp = Date.parse(timeStamp);
|
||||
minTime = Math.min(timeStamp, minTime);
|
||||
maxTime = Math.max(timeStamp, maxTime);
|
||||
});
|
||||
|
||||
localTime = {
|
||||
minTime: minTime === Number.POSITIVE_INFINITY ? null : minTime,
|
||||
maxTime: maxTime === Number.NEGATIVE_INFINITY ? null : maxTime,
|
||||
};
|
||||
}
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
const { maxTime, minTime } = useMemo(() => {
|
||||
if (localTime && localTime.maxTime && localTime.minTime) {
|
||||
return {
|
||||
minTime: localTime.minTime,
|
||||
maxTime: localTime.maxTime,
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
minTime: globalTime.minTime / 1e6,
|
||||
maxTime: globalTime.maxTime / 1e6,
|
||||
};
|
||||
}
|
||||
}, [globalTime, localTime]);
|
||||
// debugger;
|
||||
return convertTimeRange(minTime, maxTime);
|
||||
};
|
||||
|
||||
const convertTimeRange = (start: number, end: number): IAxisTimeConfig => {
|
||||
const MIN_INTERVALS = 6;
|
||||
const range = end - start;
|
||||
let relevantTimeUnit = TIME_UNITS[1];
|
||||
let stepSize = 1;
|
||||
for (let idx = TIME_UNITS.length - 1; idx >= 0; idx--) {
|
||||
const timeUnit = TIME_UNITS[idx];
|
||||
const units = range * timeUnit.multiplier;
|
||||
const steps = units / MIN_INTERVALS;
|
||||
if (steps >= 1) {
|
||||
relevantTimeUnit = timeUnit;
|
||||
stepSize = steps;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
unitName: relevantTimeUnit.unitName,
|
||||
stepSize: Math.floor(stepSize) || 1,
|
||||
};
|
||||
};
|
@ -82,7 +82,6 @@ const FullView = ({
|
||||
};
|
||||
|
||||
const queryMinMax = getMinMax(selectedTime.enum);
|
||||
|
||||
const response = await Promise.all(
|
||||
widget.query
|
||||
.filter((e) => e.query.length !== 0)
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ActiveElement, Chart, ChartData, ChartEvent } from 'chart.js';
|
||||
import Graph from 'components/Graph';
|
||||
import { getTimeRange, ITimeRange } from 'components/Graph/xAxisConfig';
|
||||
import { METRICS_PAGE_QUERY_PARAM } from 'constants/query';
|
||||
import ROUTES from 'constants/routes';
|
||||
import FullView from 'container/GridGraphLayout/Graph/FullView';
|
||||
@ -34,8 +35,7 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
|
||||
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
||||
|
||||
history.replace(
|
||||
`${
|
||||
ROUTES.TRACE
|
||||
`${ROUTES.TRACE
|
||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["ok","error"]}&filterToFetchData=["duration","status","serviceName"]&userSelectedFilter={"status":["error","ok"],"serviceName":["${servicename}"]}&isSelectedFilterSkipped=true`,
|
||||
);
|
||||
};
|
||||
@ -88,8 +88,7 @@ const Application = ({ getWidget }: DashboardProps): JSX.Element => {
|
||||
urlParams.set(METRICS_PAGE_QUERY_PARAM.endTime, tPlusOne.toString());
|
||||
|
||||
history.replace(
|
||||
`${
|
||||
ROUTES.TRACE
|
||||
`${ROUTES.TRACE
|
||||
}?${urlParams.toString()}&selected={"serviceName":["${servicename}"],"status":["error"]}&filterToFetchData=["duration","status","serviceName"]&userSelectedFilter={"status":["error"],"serviceName":["${servicename}"]}&isSelectedFilterSkipped=true`,
|
||||
);
|
||||
};
|
||||
|
@ -27,7 +27,6 @@ export const getChartData = (
|
||||
data: [],
|
||||
type: 'line',
|
||||
};
|
||||
|
||||
const chartLabels: ChartData<'line'>['labels'] = [];
|
||||
|
||||
Object.keys(allDataPoints).forEach((timestamp) => {
|
||||
|
@ -1,13 +1,13 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Typography } from 'antd';
|
||||
import Graph from 'components/Graph';
|
||||
import Spinner from 'components/Spinner';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { TraceReducer } from 'types/reducer/trace';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { Container } from './styles';
|
||||
import { Typography } from 'antd';
|
||||
|
||||
import { getChartData, getChartDataforGroupBy } from './config';
|
||||
import { Container } from './styles';
|
||||
|
||||
const TraceGraph = () => {
|
||||
const { spansGraph, selectedGroupBy } = useSelector<AppState, TraceReducer>(
|
||||
|
Loading…
x
Reference in New Issue
Block a user