mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-12 07:21:31 +08:00
194 lines
5.4 KiB
TypeScript
194 lines
5.4 KiB
TypeScript
import './TimeSeriesView.styles.scss';
|
|
|
|
import Uplot from 'components/Uplot';
|
|
import { QueryParams } from 'constants/query';
|
|
import EmptyLogsSearch from 'container/EmptyLogsSearch/EmptyLogsSearch';
|
|
import LogsError from 'container/LogsError/LogsError';
|
|
import { LogsLoading } from 'container/LogsLoading/LogsLoading';
|
|
import NoLogs from 'container/NoLogs/NoLogs';
|
|
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
|
import { TracesLoading } from 'container/TracesExplorer/TraceLoading/TraceLoading';
|
|
import { useIsDarkMode } from 'hooks/useDarkMode';
|
|
import useUrlQuery from 'hooks/useUrlQuery';
|
|
import GetMinMax from 'lib/getMinMax';
|
|
import getTimeString from 'lib/getTimeString';
|
|
import history from 'lib/history';
|
|
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
|
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
|
import { isEmpty } from 'lodash-es';
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
import { useLocation } from 'react-router-dom';
|
|
import { UpdateTimeInterval } from 'store/actions';
|
|
import { AppState } from 'store/reducers';
|
|
import { SuccessResponse } from 'types/api';
|
|
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
|
import { DataSource } from 'types/common/queryBuilder';
|
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
import { getTimeRange } from 'utils/getTimeRange';
|
|
|
|
import { Container } from './styles';
|
|
|
|
function TimeSeriesView({
|
|
data,
|
|
isLoading,
|
|
isError,
|
|
yAxisUnit,
|
|
isFilterApplied,
|
|
dataSource,
|
|
}: TimeSeriesViewProps): JSX.Element {
|
|
const graphRef = useRef<HTMLDivElement>(null);
|
|
|
|
const dispatch = useDispatch();
|
|
const urlQuery = useUrlQuery();
|
|
const location = useLocation();
|
|
|
|
const chartData = useMemo(() => getUPlotChartData(data?.payload), [
|
|
data?.payload,
|
|
]);
|
|
|
|
const isDarkMode = useIsDarkMode();
|
|
|
|
const width = graphRef.current?.clientWidth
|
|
? graphRef.current.clientWidth
|
|
: 700;
|
|
|
|
const height = graphRef.current?.clientWidth
|
|
? graphRef.current.clientHeight
|
|
: 300;
|
|
|
|
const [minTimeScale, setMinTimeScale] = useState<number>();
|
|
const [maxTimeScale, setMaxTimeScale] = useState<number>();
|
|
|
|
const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
|
|
AppState,
|
|
GlobalReducer
|
|
>((state) => state.globalTime);
|
|
|
|
useEffect((): void => {
|
|
const { startTime, endTime } = getTimeRange();
|
|
|
|
setMinTimeScale(startTime);
|
|
setMaxTimeScale(endTime);
|
|
}, [maxTime, minTime, globalSelectedInterval, data]);
|
|
|
|
const onDragSelect = useCallback(
|
|
(start: number, end: number): void => {
|
|
const startTimestamp = Math.trunc(start);
|
|
const endTimestamp = Math.trunc(end);
|
|
|
|
if (startTimestamp !== endTimestamp) {
|
|
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
|
}
|
|
|
|
const { maxTime, minTime } = GetMinMax('custom', [
|
|
startTimestamp,
|
|
endTimestamp,
|
|
]);
|
|
|
|
urlQuery.set(QueryParams.startTime, minTime.toString());
|
|
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
|
urlQuery.delete(QueryParams.relativeTime);
|
|
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
|
history.push(generatedUrl);
|
|
},
|
|
[dispatch, location.pathname, urlQuery],
|
|
);
|
|
|
|
const handleBackNavigation = (): void => {
|
|
const searchParams = new URLSearchParams(window.location.search);
|
|
const startTime = searchParams.get(QueryParams.startTime);
|
|
const endTime = searchParams.get(QueryParams.endTime);
|
|
const relativeTime = searchParams.get(
|
|
QueryParams.relativeTime,
|
|
) as CustomTimeType;
|
|
|
|
if (relativeTime) {
|
|
dispatch(UpdateTimeInterval(relativeTime));
|
|
} else if (startTime && endTime && startTime !== endTime) {
|
|
dispatch(
|
|
UpdateTimeInterval('custom', [
|
|
parseInt(getTimeString(startTime), 10),
|
|
parseInt(getTimeString(endTime), 10),
|
|
]),
|
|
);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
window.addEventListener('popstate', handleBackNavigation);
|
|
|
|
return (): void => {
|
|
window.removeEventListener('popstate', handleBackNavigation);
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
const chartOptions = getUPlotChartOptions({
|
|
onDragSelect,
|
|
yAxisUnit: yAxisUnit || '',
|
|
apiResponse: data?.payload,
|
|
dimensions: {
|
|
width,
|
|
height,
|
|
},
|
|
isDarkMode,
|
|
minTimeScale,
|
|
maxTimeScale,
|
|
softMax: null,
|
|
softMin: null,
|
|
});
|
|
|
|
return (
|
|
<Container>
|
|
{isError && <LogsError />}
|
|
<div
|
|
className="graph-container"
|
|
style={{ height: '100%', width: '100%' }}
|
|
ref={graphRef}
|
|
>
|
|
{isLoading &&
|
|
(dataSource === DataSource.LOGS ? <LogsLoading /> : <TracesLoading />)}
|
|
|
|
{chartData &&
|
|
chartData[0] &&
|
|
chartData[0]?.length === 0 &&
|
|
!isLoading &&
|
|
!isError &&
|
|
isFilterApplied && (
|
|
<EmptyLogsSearch dataSource={dataSource} panelType="TIME_SERIES" />
|
|
)}
|
|
|
|
{chartData &&
|
|
chartData[0] &&
|
|
chartData[0]?.length === 0 &&
|
|
!isLoading &&
|
|
!isError &&
|
|
!isFilterApplied && <NoLogs dataSource={dataSource} />}
|
|
|
|
{!isLoading &&
|
|
!isError &&
|
|
chartData &&
|
|
!isEmpty(chartData?.[0]) &&
|
|
chartOptions && <Uplot data={chartData} options={chartOptions} />}
|
|
</div>
|
|
</Container>
|
|
);
|
|
}
|
|
|
|
interface TimeSeriesViewProps {
|
|
data?: SuccessResponse<MetricRangePayloadProps>;
|
|
yAxisUnit?: string;
|
|
isLoading: boolean;
|
|
isError: boolean;
|
|
isFilterApplied: boolean;
|
|
dataSource: DataSource;
|
|
}
|
|
|
|
TimeSeriesView.defaultProps = {
|
|
data: undefined,
|
|
yAxisUnit: 'short',
|
|
};
|
|
|
|
export default TimeSeriesView;
|