From cbce1b1847aa952b8983164bab886e9db23255d1 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Tue, 16 Jan 2024 17:35:07 +0530 Subject: [PATCH 1/3] feat: [GH-4325]: update the URL time query params when zoom in and zoom out of charts --- .../src/container/GridCardLayout/GridCard/index.tsx | 13 ++++++++++++- .../container/MetricsApplication/Tabs/Overview.tsx | 12 ++++++++++-- .../LeftContainer/WidgetGraph/WidgetGraphs.tsx | 11 ++++++++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 1b388e0802..6dfe9a081b 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -1,10 +1,13 @@ +import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; import { useIntersectionObserver } from 'hooks/useIntersectionObserver'; +import useUrlQuery from 'hooks/useUrlQuery'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; +import history from 'lib/history'; import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import isEmpty from 'lodash-es/isEmpty'; @@ -12,6 +15,7 @@ import _noop from 'lodash-es/noop'; import { useDashboard } from 'providers/Dashboard/Dashboard'; import { memo, 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 { GlobalReducer } from 'types/reducer/globalTime'; @@ -37,17 +41,24 @@ function GridCardGraph({ const { toScrollWidgetId, setToScrollWidgetId } = useDashboard(); const [minTimeScale, setMinTimeScale] = useState(); const [maxTimeScale, setMaxTimeScale] = useState(); + const urlQuery = useUrlQuery(); + const location = useLocation(); const onDragSelect = useCallback( (start: number, end: number): void => { const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); + urlQuery.set(QueryParams.startTime, startTimestamp.toString()); + urlQuery.set(QueryParams.endTime, endTimestamp.toString()); + const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; + history.replace(generatedUrl); + if (startTimestamp !== endTimestamp) { dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); } }, - [dispatch], + [dispatch, location.pathname, urlQuery], ); const graphRef = useRef(null); diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index b73b78411c..36db03b567 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -13,6 +13,7 @@ import { convertRawQueriesToTraceSelectedTags, resourceAttributesToTagFilterItems, } from 'hooks/useResourceAttribute/utils'; +import useUrlQuery from 'hooks/useUrlQuery'; import history from 'lib/history'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { useCallback, useMemo, useState } from 'react'; @@ -52,8 +53,10 @@ function Application(): JSX.Element { ); const { servicename } = useParams(); const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); - const { search } = useLocation(); + const { search, pathname } = useLocation(); const { queries } = useResourceAttribute(); + const urlQuery = useUrlQuery(); + const selectedTags = useMemo( () => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [], [queries], @@ -157,11 +160,16 @@ function Application(): JSX.Element { const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); + urlQuery.set(QueryParams.startTime, startTimestamp.toString()); + urlQuery.set(QueryParams.endTime, endTimestamp.toString()); + const generatedUrl = `${pathname}?${urlQuery.toString()}`; + history.replace(generatedUrl); + if (startTimestamp !== endTimestamp) { dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); } }, - [dispatch], + [dispatch, pathname, urlQuery], ); const onErrorTrackHandler = (timestamp: number): (() => void) => (): void => { diff --git a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx index 08b65fa9c1..e544b7bbf0 100644 --- a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx @@ -1,14 +1,17 @@ +import { QueryParams } from 'constants/query'; import GridPanelSwitch from 'container/GridPanelSwitch'; import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; import useUrlQuery from 'hooks/useUrlQuery'; +import history from 'lib/history'; import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { UseQueryResult } from 'react-query'; 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'; @@ -35,6 +38,7 @@ function WidgetGraph({ const [minTimeScale, setMinTimeScale] = useState(); const [maxTimeScale, setMaxTimeScale] = useState(); + const location = useLocation(); useEffect((): void => { const { startTime, endTime } = getTimeRange(getWidgetQueryRange); @@ -65,11 +69,16 @@ function WidgetGraph({ const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); + params.set(QueryParams.startTime, startTimestamp.toString()); + params.set(QueryParams.endTime, endTimestamp.toString()); + const generatedUrl = `${location.pathname}?${params.toString()}`; + history.replace(generatedUrl); + if (startTimestamp !== endTimestamp) { dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); } }, - [dispatch], + [dispatch, location.pathname, params], ); const options = useMemo( From 54038b8ddf688d0e93e98463e42b2287444b7628 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 17 Jan 2024 13:57:05 +0530 Subject: [PATCH 2/3] feat: handle back btn changes --- .../GridCardLayout/GridCard/index.tsx | 55 +++++++++++++++---- .../WidgetGraph/WidgetGraphs.tsx | 47 ++++++++++++++-- .../TopNav/DateTimeSelection/index.tsx | 4 +- 3 files changed, 88 insertions(+), 18 deletions(-) diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 6dfe9a081b..0616f20bdb 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -1,5 +1,6 @@ import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; +import dayjs from 'dayjs'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useIsDarkMode } from 'hooks/useDarkMode'; @@ -7,6 +8,8 @@ import { useResizeObserver } from 'hooks/useDimensions'; import { useIntersectionObserver } from 'hooks/useIntersectionObserver'; import useUrlQuery from 'hooks/useUrlQuery'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; +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'; @@ -43,24 +46,61 @@ function GridCardGraph({ const [maxTimeScale, setMaxTimeScale] = useState(); const urlQuery = useUrlQuery(); const location = useLocation(); + const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< + AppState, + GlobalReducer + >((state) => state.globalTime); const onDragSelect = useCallback( (start: number, end: number): void => { const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); - urlQuery.set(QueryParams.startTime, startTimestamp.toString()); - urlQuery.set(QueryParams.endTime, endTimestamp.toString()); - const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; - history.replace(generatedUrl); - 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()); + 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); + + if (startTime && endTime && startTime !== endTime) { + console.log(startTime, endTime); + const startDate = dayjs(new Date(parseInt(getTimeString(startTime), 10))); + const endDate = dayjs(new Date(parseInt(getTimeString(endTime), 10))); + + dispatch( + UpdateTimeInterval('custom', [ + startDate.toDate().getTime() || 0, + endDate.toDate().getTime() || 0, + ]), + ); + } + }; + + useEffect(() => { + window.addEventListener('popstate', handleBackNavigation); + + return (): void => { + window.removeEventListener('popstate', handleBackNavigation); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const graphRef = useRef(null); const isVisible = useIntersectionObserver(graphRef, undefined, true); @@ -81,11 +121,6 @@ function GridCardGraph({ const isEmptyWidget = widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget); - const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< - AppState, - GlobalReducer - >((state) => state.globalTime); - const queryResponse = useGetQueryRange( { selectedTime: widget?.timePreferance, diff --git a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx index e544b7bbf0..c91636dd0c 100644 --- a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx @@ -1,10 +1,13 @@ import { QueryParams } from 'constants/query'; import GridPanelSwitch from 'container/GridPanelSwitch'; import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; +import dayjs from 'dayjs'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; 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'; @@ -68,19 +71,51 @@ function WidgetGraph({ (start: number, end: number): void => { const startTimestamp = Math.trunc(start); const endTimestamp = Math.trunc(end); - - params.set(QueryParams.startTime, startTimestamp.toString()); - params.set(QueryParams.endTime, endTimestamp.toString()); - const generatedUrl = `${location.pathname}?${params.toString()}`; - history.replace(generatedUrl); - if (startTimestamp !== endTimestamp) { dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp])); } + + const { maxTime, minTime } = GetMinMax('custom', [ + startTimestamp, + endTimestamp, + ]); + + params.set(QueryParams.startTime, minTime.toString()); + params.set(QueryParams.endTime, maxTime.toString()); + const generatedUrl = `${location.pathname}?${params.toString()}`; + history.push(generatedUrl); }, [dispatch, location.pathname, params], ); + const handleBackNavigation = (): void => { + const searchParams = new URLSearchParams(window.location.search); + const startTime = searchParams.get(QueryParams.startTime); + const endTime = searchParams.get(QueryParams.endTime); + + if (startTime && endTime && startTime !== endTime) { + console.log(startTime, endTime); + const startDate = dayjs(new Date(parseInt(getTimeString(startTime), 10))); + const endDate = dayjs(new Date(parseInt(getTimeString(endTime), 10))); + + dispatch( + UpdateTimeInterval('custom', [ + startDate.toDate().getTime() || 0, + endDate.toDate().getTime() || 0, + ]), + ); + } + }; + + useEffect(() => { + window.addEventListener('popstate', handleBackNavigation); + + return (): void => { + window.removeEventListener('popstate', handleBackNavigation); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + const options = useMemo( () => getUPlotChartOptions({ diff --git a/frontend/src/container/TopNav/DateTimeSelection/index.tsx b/frontend/src/container/TopNav/DateTimeSelection/index.tsx index 0f0e47d7df..bbed038be8 100644 --- a/frontend/src/container/TopNav/DateTimeSelection/index.tsx +++ b/frontend/src/container/TopNav/DateTimeSelection/index.tsx @@ -194,7 +194,7 @@ function DateTimeSelection({ urlQuery.set(QueryParams.startTime, minTime.toString()); urlQuery.set(QueryParams.endTime, maxTime.toString()); const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; - history.replace(generatedUrl); + history.push(generatedUrl); } if (!stagedQuery) { @@ -231,7 +231,7 @@ function DateTimeSelection({ endTimeMoment?.toDate().getTime().toString(), ); const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; - history.replace(generatedUrl); + history.push(generatedUrl); } } } From 512fcda33d3b42a1289aeac27a9770bc57495600 Mon Sep 17 00:00:00 2001 From: Vikrant Gupta Date: Wed, 17 Jan 2024 14:04:38 +0530 Subject: [PATCH 3/3] fix: address review comments --- frontend/src/container/GridCardLayout/GridCard/index.tsx | 9 ++------- .../NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx | 9 ++------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 0616f20bdb..cf8d106224 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -1,6 +1,5 @@ import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; -import dayjs from 'dayjs'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useIsDarkMode } from 'hooks/useDarkMode'; @@ -79,14 +78,10 @@ function GridCardGraph({ const endTime = searchParams.get(QueryParams.endTime); if (startTime && endTime && startTime !== endTime) { - console.log(startTime, endTime); - const startDate = dayjs(new Date(parseInt(getTimeString(startTime), 10))); - const endDate = dayjs(new Date(parseInt(getTimeString(endTime), 10))); - dispatch( UpdateTimeInterval('custom', [ - startDate.toDate().getTime() || 0, - endDate.toDate().getTime() || 0, + parseInt(getTimeString(startTime), 10), + parseInt(getTimeString(endTime), 10), ]), ); } diff --git a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx index c91636dd0c..f4a8ed1b22 100644 --- a/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/WidgetGraph/WidgetGraphs.tsx @@ -1,7 +1,6 @@ import { QueryParams } from 'constants/query'; import GridPanelSwitch from 'container/GridPanelSwitch'; import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types'; -import dayjs from 'dayjs'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; @@ -94,14 +93,10 @@ function WidgetGraph({ const endTime = searchParams.get(QueryParams.endTime); if (startTime && endTime && startTime !== endTime) { - console.log(startTime, endTime); - const startDate = dayjs(new Date(parseInt(getTimeString(startTime), 10))); - const endDate = dayjs(new Date(parseInt(getTimeString(endTime), 10))); - dispatch( UpdateTimeInterval('custom', [ - startDate.toDate().getTime() || 0, - endDate.toDate().getTime() || 0, + parseInt(getTimeString(startTime), 10), + parseInt(getTimeString(endTime), 10), ]), ); }