diff --git a/frontend/.eslintrc.js b/frontend/.eslintrc.js index 540b1ded70..5034915c00 100644 --- a/frontend/.eslintrc.js +++ b/frontend/.eslintrc.js @@ -86,6 +86,7 @@ module.exports = { }, ], 'import/no-extraneous-dependencies': ['error', { devDependencies: true }], + 'no-plusplus': 'off', 'jsx-a11y/label-has-associated-control': [ 'error', { @@ -109,7 +110,6 @@ module.exports = { // eslint rules need to remove '@typescript-eslint/no-shadow': 'off', 'import/no-cycle': 'off', - 'prettier/prettier': [ 'error', {}, diff --git a/frontend/src/components/Uplot/uplot.scss b/frontend/src/components/Uplot/Uplot.styles.scss similarity index 68% rename from frontend/src/components/Uplot/uplot.scss rename to frontend/src/components/Uplot/Uplot.styles.scss index 55e681cb70..448ef56b18 100644 --- a/frontend/src/components/Uplot/uplot.scss +++ b/frontend/src/components/Uplot/Uplot.styles.scss @@ -13,3 +13,11 @@ height: 100%; width: 100%; } + +.uplot-no-data { + position: relative; + display: flex; + width: 100%; + flex-direction: column; + gap: 8px; +} diff --git a/frontend/src/components/Uplot/Uplot.tsx b/frontend/src/components/Uplot/Uplot.tsx index 84b3d1bb9b..05f050a87c 100644 --- a/frontend/src/components/Uplot/Uplot.tsx +++ b/frontend/src/components/Uplot/Uplot.tsx @@ -1,8 +1,9 @@ /* eslint-disable sonarjs/cognitive-complexity */ -import './uplot.scss'; +import './Uplot.styles.scss'; import { Typography } from 'antd'; import { ToggleGraphProps } from 'components/Graph/types'; +import { LineChart } from 'lucide-react'; import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback'; import { forwardRef, @@ -127,6 +128,16 @@ const Uplot = forwardRef( } }, [data, resetScales, create]); + if (data && data[0] && data[0]?.length === 0) { + return ( +
+ + + No Data +
+ ); + } + return (
diff --git a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx index 3551c3a87a..7e22e3a11c 100644 --- a/frontend/src/container/FormAlertRules/ChartPreview/index.tsx +++ b/frontend/src/container/FormAlertRules/ChartPreview/index.tsx @@ -10,7 +10,7 @@ import { useIsDarkMode } from 'hooks/useDarkMode'; import { useResizeObserver } from 'hooks/useDimensions'; import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; -import { useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; @@ -18,6 +18,7 @@ import { AlertDef } from 'types/api/alerts/def'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; import { GlobalReducer } from 'types/reducer/globalTime'; +import { getTimeRange } from 'utils/getTimeRange'; import { ChartContainer, FailedMessageContainer } from './styles'; import { getThresholdLabel } from './utils'; @@ -49,9 +50,13 @@ function ChartPreview({ }: ChartPreviewProps): JSX.Element | null { const { t } = useTranslation('alerts'); const threshold = alertDef?.condition.target || 0; - const { minTime, maxTime } = useSelector( - (state) => state.globalTime, - ); + const [minTimeScale, setMinTimeScale] = useState(); + const [maxTimeScale, setMaxTimeScale] = useState(); + + const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< + AppState, + GlobalReducer + >((state) => state.globalTime); const canQuery = useMemo((): boolean => { if (!query || query == null) { @@ -101,6 +106,13 @@ function ChartPreview({ const graphRef = useRef(null); + useEffect((): void => { + const { startTime, endTime } = getTimeRange(queryResponse); + + setMinTimeScale(startTime); + setMaxTimeScale(endTime); + }, [maxTime, minTime, globalSelectedInterval, queryResponse]); + const chartData = getUPlotChartData(queryResponse?.data?.payload); const containerDimensions = useResizeObserver(graphRef); @@ -117,6 +129,8 @@ function ChartPreview({ yAxisUnit, apiResponse: queryResponse?.data?.payload, dimensions: containerDimensions, + minTimeScale, + maxTimeScale, isDarkMode, thresholds: [ { @@ -141,6 +155,8 @@ function ChartPreview({ yAxisUnit, queryResponse?.data?.payload, containerDimensions, + minTimeScale, + maxTimeScale, isDarkMode, threshold, t, diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx index 3bd60c40ba..db42625f1d 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx @@ -23,6 +23,7 @@ import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; import { GlobalReducer } from 'types/reducer/globalTime'; import uPlot from 'uplot'; +import { getTimeRange } from 'utils/getTimeRange'; import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants'; import GraphManager from './GraphManager'; @@ -92,6 +93,21 @@ function FullView({ const isDarkMode = useIsDarkMode(); + const [minTimeScale, setMinTimeScale] = useState(); + const [maxTimeScale, setMaxTimeScale] = useState(); + + const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< + AppState, + GlobalReducer + >((state) => state.globalTime); + + useEffect((): void => { + const { startTime, endTime } = getTimeRange(response); + + setMinTimeScale(startTime); + setMaxTimeScale(endTime); + }, [maxTime, minTime, globalSelectedInterval, response]); + useEffect(() => { if (!response.isFetching && fullViewRef.current) { const width = fullViewRef.current?.clientWidth @@ -114,6 +130,8 @@ function FullView({ graphsVisibilityStates, setGraphsVisibilityStates, thresholds: widget.thresholds, + minTimeScale, + maxTimeScale, }); setChartOptions(newChartOptions); diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx index 2129220427..29abaa9685 100644 --- a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx @@ -1,4 +1,5 @@ import { Skeleton, Typography } from 'antd'; +import cx from 'classnames'; import { ToggleGraphProps } from 'components/Graph/types'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { QueryParams } from 'constants/query'; @@ -298,7 +299,10 @@ function WidgetGraphComponent({
{queryResponse.isLoading && } {queryResponse.isSuccess && ( -
+
(); const { toScrollWidgetId, setToScrollWidgetId } = useDashboard(); + const [minTimeScale, setMinTimeScale] = useState(); + const [maxTimeScale, setMaxTimeScale] = useState(); const onDragSelect = useCallback( (start: number, end: number): void => { @@ -62,16 +65,16 @@ function GridCardGraph({ } }, [toScrollWidgetId, setToScrollWidgetId, widget.id]); - const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< - AppState, - GlobalReducer - >((state) => state.globalTime); - const updatedQuery = useStepInterval(widget?.query); 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, @@ -103,6 +106,13 @@ function GridCardGraph({ const containerDimensions = useResizeObserver(graphRef); + useEffect((): void => { + const { startTime, endTime } = getTimeRange(queryResponse); + + setMinTimeScale(startTime); + setMaxTimeScale(endTime); + }, [maxTime, minTime, globalSelectedInterval, queryResponse]); + const chartData = getUPlotChartData(queryResponse?.data?.payload, fillSpans); const isDarkMode = useIsDarkMode(); @@ -123,6 +133,8 @@ function GridCardGraph({ yAxisUnit: widget?.yAxisUnit, onClickHandler, thresholds: widget.thresholds, + minTimeScale, + maxTimeScale, }), [ widget?.id, @@ -133,6 +145,8 @@ function GridCardGraph({ isDarkMode, onDragSelect, onClickHandler, + minTimeScale, + maxTimeScale, ], ); diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss index 08de391e4c..ab90890541 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss +++ b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss @@ -5,3 +5,11 @@ border: none !important; } } + +.widget-graph-container { + height: 100%; + + &.graph { + height: calc(100% - 30px); + } +} diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss b/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss index e03f176570..40d138e7df 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss +++ b/frontend/src/container/GridCardLayout/WidgetHeader/WidgetHeader.styles.scss @@ -2,9 +2,12 @@ display: flex; justify-content: space-between; align-items: center; - height: 30px; + height: 40px; width: 100%; padding: 0.5rem; + box-sizing: border-box; + font-size: 14px; + font-weight: 600; } .widget-header-title { @@ -19,6 +22,10 @@ visibility: hidden; border: none; box-shadow: none; + cursor: pointer; + font: 14px; + font-weight: 600; + padding: 8px; } .widget-header-hover { diff --git a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx index 0fb2f90ead..b70e16585c 100644 --- a/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx +++ b/frontend/src/container/GridCardLayout/WidgetHeader/index.tsx @@ -10,7 +10,7 @@ import { MoreOutlined, WarningOutlined, } from '@ant-design/icons'; -import { Button, Dropdown, MenuProps, Tooltip, Typography } from 'antd'; +import { Dropdown, MenuProps, Tooltip, Typography } from 'antd'; import Spinner from 'components/Spinner'; import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; @@ -199,9 +199,7 @@ function WidgetHeader({ )} -