From 213838a021efa549353de09fa591479fff5be076 Mon Sep 17 00:00:00 2001 From: Chintan Sudani <46838508+csudani7@users.noreply.github.com> Date: Wed, 25 Jan 2023 20:31:42 +0530 Subject: [PATCH] fix: Chart loaders on reload and change of time interval at dashboard (#2068) * fix: Chart data logic on dashboard reloads * fix: linting issues * fix: added right side loader & css config * fix: loader condition change * fix: linting issues * fix: error state of API * fix: Resolved suggested changes * fix: Error state for API Failed * fix: Default loading state * fix: linting issues * fix: Suggested changes * feat: Added common hook for previous value * chore: usePrevious is made type safety * chore: chart data set is updated * chore: removed eslint rule * fix: commitlint issue on commit Co-authored-by: Vishal Sharma Co-authored-by: Palash Gupta Co-authored-by: Srikanth Chekuri --- frontend/.husky/commit-msg | 2 +- frontend/src/components/Spinner/index.tsx | 6 +- .../Graph/FullView/index.metricsBuilder.tsx | 42 +++---- .../GridGraphLayout/Graph/FullView/index.tsx | 40 ++++--- .../container/GridGraphLayout/Graph/index.tsx | 111 ++++++++++++------ .../GridGraphLayout/WidgetHeader/config.ts | 13 ++ .../GridGraphLayout/WidgetHeader/index.tsx | 52 +++++--- .../container/MetricsApplication/index.tsx | 4 +- frontend/src/hooks/usePreviousValue.ts | 13 ++ 9 files changed, 187 insertions(+), 96 deletions(-) create mode 100644 frontend/src/container/GridGraphLayout/WidgetHeader/config.ts create mode 100644 frontend/src/hooks/usePreviousValue.ts diff --git a/frontend/.husky/commit-msg b/frontend/.husky/commit-msg index 818ef8b9b5..be422b654c 100755 --- a/frontend/.husky/commit-msg +++ b/frontend/.husky/commit-msg @@ -1,4 +1,4 @@ #!/bin/sh . "$(dirname "$0")/_/husky.sh" -cd frontend && npm run commitlint +cd frontend && yarn run commitlint --edit $1 diff --git a/frontend/src/components/Spinner/index.tsx b/frontend/src/components/Spinner/index.tsx index 160bfb75f5..819e552442 100644 --- a/frontend/src/components/Spinner/index.tsx +++ b/frontend/src/components/Spinner/index.tsx @@ -4,9 +4,9 @@ import React from 'react'; import { SpinerStyle } from './styles'; -function Spinner({ size, tip, height }: SpinnerProps): JSX.Element { +function Spinner({ size, tip, height, style }: SpinnerProps): JSX.Element { return ( - + } /> ); @@ -16,11 +16,13 @@ interface SpinnerProps { size?: SpinProps['size']; tip?: SpinProps['tip']; height?: React.CSSProperties['height']; + style?: React.CSSProperties; } Spinner.defaultProps = { size: undefined, tip: undefined, height: undefined, + style: {}, }; export default Spinner; diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.metricsBuilder.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.metricsBuilder.tsx index 4c1912f471..e2ffff224e 100644 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.metricsBuilder.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.metricsBuilder.tsx @@ -9,7 +9,7 @@ import { } from 'container/NewWidget/RightContainer/timeItems'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; import getChartData from 'lib/getChartData'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { GetMetricQueryRange } from 'store/actions/dashboard/getQueryResults'; @@ -58,6 +58,18 @@ function FullView({ }), ); + const chartDataSet = useMemo( + () => + getChartData({ + queryData: [ + { + queryData: response?.data?.payload?.data?.result || [], + }, + ], + }), + [response], + ); + const isLoading = response.isLoading === true; if (isLoading) { @@ -86,25 +98,15 @@ function FullView({ )} ); diff --git a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx index b9c8ba7f38..ff3545bf92 100644 --- a/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/FullView/index.tsx @@ -15,7 +15,7 @@ import GetMaxMinTime from 'lib/getMaxMinTime'; import GetMinMax from 'lib/getMinMax'; import getStartAndEndTime from 'lib/getStartAndEndTime'; import getStep from 'lib/getStep'; -import React, { useCallback, useState } from 'react'; +import React, { useCallback, useMemo, useState } from 'react'; import { useQueries } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -115,6 +115,18 @@ function FullView({ })), ); + const chartDataSet = useMemo( + () => + getChartData({ + queryData: data.map((e) => ({ + query: e?.map((e) => e.query).join(' ') || '', + queryData: e?.map((e) => e.queryData) || [], + legend: e?.map((e) => e.legend).join('') || '', + })), + }), + [data], + ); + if (isLoading) { return ; } @@ -149,23 +161,15 @@ function FullView({ )} ({ - query: e?.map((e) => e.query).join(' ') || '', - queryData: e?.map((e) => e.queryData) || [], - legend: e?.map((e) => e.legend).join('') || '', - })), - }), - isStacked: widget.isStacked, - opacity: widget.opacity, - title: widget.title, - onClickHandler, - name, - yAxisUnit, - onDragSelect, - }} + GRAPH_TYPES={widget.panelTypes} + data={chartDataSet} + isStacked={widget.isStacked} + opacity={widget.opacity} + title={widget.title} + onClickHandler={onClickHandler} + name={name} + yAxisUnit={yAxisUnit} + onDragSelect={onDragSelect} /> ); diff --git a/frontend/src/container/GridGraphLayout/Graph/index.tsx b/frontend/src/container/GridGraphLayout/Graph/index.tsx index d88bfdc1ad..a47fc6b53d 100644 --- a/frontend/src/container/GridGraphLayout/Graph/index.tsx +++ b/frontend/src/container/GridGraphLayout/Graph/index.tsx @@ -1,6 +1,8 @@ import { Typography } from 'antd'; +import { ChartData } from 'chart.js'; import Spinner from 'components/Spinner'; import GridGraphComponent from 'container/GridGraphComponent'; +import usePreviousValue from 'hooks/usePreviousValue'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; import getChartData from 'lib/getChartData'; import isEmpty from 'lodash-es/isEmpty'; @@ -18,9 +20,7 @@ import { GetMetricQueryRange } from 'store/actions/dashboard/getQueryResults'; import { AppState } from 'store/reducers'; import AppActions from 'types/actions'; import { GlobalTime } from 'types/actions/globalTime'; -import { ErrorResponse, SuccessResponse } from 'types/api'; import { Widgets } from 'types/api/dashboard/getAll'; -import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import DashboardReducer from 'types/reducer/dashboards'; import { GlobalReducer } from 'types/reducer/globalTime'; @@ -28,7 +28,7 @@ import { LayoutProps } from '..'; import EmptyWidget from '../EmptyWidget'; import WidgetHeader from '../WidgetHeader'; import FullView from './FullView/index.metricsBuilder'; -import { ErrorContainer, FullViewContainer, Modal } from './styles'; +import { FullViewContainer, Modal } from './styles'; function GridCardGraph({ widget, @@ -39,6 +39,7 @@ function GridCardGraph({ setLayout, onDragSelect, }: GridCardGraphProps): JSX.Element { + const [errorMessage, setErrorMessage] = useState(''); const [hovered, setHovered] = useState(false); const [modal, setModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false); @@ -57,9 +58,7 @@ function GridCardGraph({ const selectedData = selectedDashboard?.data; const { variables } = selectedData; - const response = useQuery< - SuccessResponse | ErrorResponse - >( + const queryResponse = useQuery( [ `GetMetricsQueryRange-${widget.timePreferance}-${globalSelectedInterval}-${widget.id}`, { @@ -81,6 +80,11 @@ function GridCardGraph({ { keepPreviousData: true, refetchOnMount: false, + onError: (error) => { + if (error instanceof Error) { + setErrorMessage(error.message); + } + }, }, ); @@ -89,15 +93,15 @@ function GridCardGraph({ getChartData({ queryData: [ { - queryData: response?.data?.payload?.data?.result - ? response?.data?.payload?.data?.result - : [], + queryData: queryResponse?.data?.payload?.data?.result || [], }, ], }), - [response?.data?.payload], + [queryResponse], ); + const prevChartDataSetRef = usePreviousValue(chartData); + const onToggleModal = useCallback( (func: React.Dispatch>) => { func((value) => !value); @@ -154,39 +158,68 @@ function GridCardGraph({ const isEmptyLayout = widget?.id === 'empty' || isEmpty(widget); - if (response.isError && !isEmptyLayout) { + if (queryResponse.isError && !isEmptyLayout) { return ( - <> + {getModals()} -
- -
- - - {response.isError && 'Something went wrong'} - - + {!isEmpty(widget) && prevChartDataSetRef && ( + <> +
+ +
+ + + )} +
); } - if (response.isFetching) { + if (prevChartDataSetRef?.labels === undefined && queryResponse.isLoading) { return ( - <> - - - + + {!isEmpty(widget) && prevChartDataSetRef?.labels ? ( + <> +
+ +
+ + + ) : ( + + )} +
); } @@ -213,13 +246,15 @@ function GridCardGraph({ widget={widget} onView={handleOnView} onDelete={handleOnDelete} + queryResponse={queryResponse} + errorMessage={errorMessage} /> )} {!isEmptyLayout && getModals()} - {!isEmpty(widget) && !!response.data?.payload?.data?.result && ( + {!isEmpty(widget) && !!queryResponse.data?.payload && ( | ErrorResponse + >; + errorMessage: string | undefined; } function WidgetHeader({ title, @@ -34,6 +44,8 @@ function WidgetHeader({ onView, onDelete, parentHover, + queryResponse, + errorMessage, }: IWidgetHeaderProps): JSX.Element { const [localHover, setLocalHover] = useState(false); @@ -106,20 +118,30 @@ function WidgetHeader({ overlayStyle={{ minWidth: 100 }} placement="bottom" > - setLocalHover(true)} - onMouseOut={(): void => setLocalHover(false)} - hover={localHover} - > - e.preventDefault()}> - - {title} - - - - - - + <> + setLocalHover(true)} + onMouseOut={(): void => setLocalHover(false)} + hover={localHover} + > + e.preventDefault()}> + + {title} + + + + + + + {queryResponse.isFetching && !queryResponse.isError && ( + + )} + {queryResponse.isError && ( + + + + )} + ); } diff --git a/frontend/src/container/MetricsApplication/index.tsx b/frontend/src/container/MetricsApplication/index.tsx index 1465103dc0..af76ca95ae 100644 --- a/frontend/src/container/MetricsApplication/index.tsx +++ b/frontend/src/container/MetricsApplication/index.tsx @@ -1,6 +1,6 @@ import RouteTab from 'components/RouteTab'; import ROUTES from 'constants/routes'; -import React from 'react'; +import React, { memo } from 'react'; import { generatePath, useParams } from 'react-router-dom'; import { useLocation } from 'react-use'; @@ -85,4 +85,4 @@ function ServiceMetrics(): JSX.Element { ); } -export default React.memo(ServiceMetrics); +export default memo(ServiceMetrics); diff --git a/frontend/src/hooks/usePreviousValue.ts b/frontend/src/hooks/usePreviousValue.ts new file mode 100644 index 0000000000..dcda2f013c --- /dev/null +++ b/frontend/src/hooks/usePreviousValue.ts @@ -0,0 +1,13 @@ +import { useEffect, useRef } from 'react'; + +function usePreviousValue(value: T): T { + const ref = useRef(); + + useEffect(() => { + ref.current = value; + }, [value]); + + return ref.current as T; +} + +export default usePreviousValue;