diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx index c24a731d5e..e4f14e8e19 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/index.tsx @@ -1,62 +1,60 @@ import './WidgetFullView.styles.scss'; -import { SyncOutlined } from '@ant-design/icons'; -import { Button } from 'antd'; +import { LoadingOutlined, SyncOutlined } from '@ant-design/icons'; +import { Button, Spin } from 'antd'; import cx from 'classnames'; import { ToggleGraphProps } from 'components/Graph/types'; import Spinner from 'components/Spinner'; import TimePreference from 'components/TimePreferenceDropDown'; import { DEFAULT_ENTITY_VERSION } from 'constants/app'; +import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; -import GridPanelSwitch from 'container/GridPanelSwitch'; import { timeItems, timePreferance, } from 'container/NewWidget/RightContainer/timeItems'; +import PanelWrapper from 'container/PanelWrapper/PanelWrapper'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useChartMutable } from 'hooks/useChartMutable'; -import { useIsDarkMode } from 'hooks/useDarkMode'; +import useUrlQuery from 'hooks/useUrlQuery'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; -import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; -import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; +import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults'; +import GetMinMax from 'lib/getMinMax'; +import history from 'lib/history'; import { useDashboard } from 'providers/Dashboard/Dashboard'; import { useCallback, useEffect, useRef, useState } from 'react'; -import { useSelector } from 'react-redux'; +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'; -import uPlot from 'uplot'; +import { getGraphType } from 'utils/getGraphType'; import { getSortedSeriesData } from 'utils/getSortedSeriesData'; -import { getTimeRange } from 'utils/getTimeRange'; import { getLocalStorageGraphVisibilityState } from '../utils'; import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants'; -import GraphManager from './GraphManager'; import { GraphContainer, TimeContainer } from './styles'; import { FullViewProps } from './types'; function FullView({ widget, fullViewOptions = true, - onClickHandler, - name, version, originalName, - yAxisUnit, - onDragSelect, isDependedDataLoaded = false, onToggleModelHandler, - parentChartRef, }: FullViewProps): JSX.Element { const { selectedTime: globalSelectedTime } = useSelector< AppState, GlobalReducer >((state) => state.globalTime); + const dispatch = useDispatch(); + const urlQuery = useUrlQuery(); + const location = useLocation(); const fullViewRef = useRef(null); - const [chartOptions, setChartOptions] = useState(); - const { selectedDashboard, isDashboardLocked } = useDashboard(); const getSelectedTime = useCallback( @@ -74,24 +72,70 @@ function FullView({ const updatedQuery = useStepInterval(widget?.query); - const response = useGetQueryRange( - { - selectedTime: selectedTime.enum, - graphType: - widget.panelTypes === PANEL_TYPES.BAR - ? PANEL_TYPES.TIME_SERIES - : widget.panelTypes, + const [requestData, setRequestData] = useState(() => { + if (widget.panelTypes !== PANEL_TYPES.LIST) { + return { + selectedTime: selectedTime.enum, + graphType: getGraphType(widget.panelTypes), + query: updatedQuery, + globalSelectedInterval: globalSelectedTime, + variables: getDashboardVariables(selectedDashboard?.data.variables), + }; + } + updatedQuery.builder.queryData[0].pageSize = 10; + return { query: updatedQuery, + graphType: PANEL_TYPES.LIST, + selectedTime: 'GLOBAL_TIME', globalSelectedInterval: globalSelectedTime, - variables: getDashboardVariables(selectedDashboard?.data.variables), - }, + tableParams: { + pagination: { + offset: 0, + limit: updatedQuery.builder.queryData[0].limit || 0, + }, + }, + }; + }); + + useEffect(() => { + setRequestData((prev) => ({ + ...prev, + selectedTime: selectedTime.enum, + })); + }, [selectedTime]); + + const response = useGetQueryRange( + requestData, selectedDashboard?.data?.version || version || DEFAULT_ENTITY_VERSION, { - queryKey: `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`, - enabled: !isDependedDataLoaded && widget.panelTypes !== PANEL_TYPES.LIST, // Internally both the list view panel has it's own query range api call, so we don't need to call it again + queryKey: [widget?.query, widget?.panelTypes, requestData, version], + enabled: !isDependedDataLoaded, + keepPreviousData: true, }, ); + 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()); + const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; + history.push(generatedUrl); + }, + [dispatch, location.pathname, urlQuery], + ); + const [graphsVisibilityStates, setGraphsVisibilityStates] = useState< boolean[] >(Array(response.data?.payload.data.result.length).fill(true)); @@ -118,60 +162,6 @@ function FullView({ response.data.payload.data.result = sortedSeriesData; } - const chartData = getUPlotChartData(response?.data?.payload, widget.fillSpans); - - 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 - ? fullViewRef.current.clientWidth - 45 - : 700; - - const height = fullViewRef.current?.clientWidth - ? fullViewRef.current.clientHeight - : 300; - - const newChartOptions = getUPlotChartOptions({ - id: originalName, - yAxisUnit: yAxisUnit || '', - apiResponse: response.data?.payload, - dimensions: { - height, - width, - }, - isDarkMode, - onDragSelect, - graphsVisibilityStates, - setGraphsVisibilityStates, - thresholds: widget.thresholds, - minTimeScale, - maxTimeScale, - softMax: widget.softMax === undefined ? null : widget.softMax, - softMin: widget.softMin === undefined ? null : widget.softMin, - panelType: widget.panelTypes, - }); - - setChartOptions(newChartOptions); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [response.isFetching, graphsVisibilityStates, fullViewRef.current]); - useEffect(() => { graphsVisibilityStates?.forEach((e, i) => { fullViewChartRef?.current?.toggleGraph(i, e); @@ -180,7 +170,7 @@ function FullView({ const isListView = widget.panelTypes === PANEL_TYPES.LIST; - if (response.isFetching) { + if (response.isLoading && widget.panelTypes !== PANEL_TYPES.LIST) { return ; } @@ -189,6 +179,9 @@ function FullView({
{fullViewOptions && ( + {response.isFetching && ( + } /> + )} - {chartOptions && ( - - - - )} + + +
- - {canModifyChart && chartOptions && !isDashboardLocked && ( - - )} ); } diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts index f963b31371..0133b1a49b 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/styles.ts @@ -18,6 +18,7 @@ export const NotFoundContainer = styled.div` export const TimeContainer = styled.div` display: flex; justify-content: flex-end; + align-items: center; ${({ $panelType }): FlattenSimpleInterpolation => $panelType === PANEL_TYPES.TABLE ? css` diff --git a/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts b/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts index 6b2e750ae9..7440278fd8 100644 --- a/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts +++ b/frontend/src/container/GridCardLayout/GridCard/FullView/types.ts @@ -53,10 +53,8 @@ export interface FullViewProps { version?: string; originalName: string; yAxisUnit?: string; - onDragSelect: (start: number, end: number) => void; isDependedDataLoaded?: boolean; onToggleModelHandler?: GraphManagerProps['onToggleModelHandler']; - parentChartRef: GraphManagerProps['lineChartRef']; } export interface GraphManagerProps extends UplotProps { diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx index 3dddc46884..505a1864bc 100644 --- a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx @@ -6,7 +6,7 @@ import { ToggleGraphProps } from 'components/Graph/types'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; -import GridPanelSwitch from 'container/GridPanelSwitch'; +import PanelWrapper from 'container/PanelWrapper/PanelWrapper'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { useNotifications } from 'hooks/useNotifications'; import useUrlQuery from 'hooks/useUrlQuery'; @@ -33,23 +33,20 @@ import FullView from './FullView'; import { Modal } from './styles'; import { WidgetGraphComponentProps } from './types'; import { getLocalStorageGraphVisibilityState } from './utils'; +// import { getLocalStorageGraphVisibilityState } from './utils'; function WidgetGraphComponent({ widget, queryResponse, errorMessage, - name, version, threshold, headerMenuList, isWarning, - data, - options, - graphVisibiltyState, + isFetchingResponse, + setRequestData, onClickHandler, onDragSelect, - setGraphVisibility, - isFetchingResponse, }: WidgetGraphComponentProps): JSX.Element { const [deleteModal, setDeleteModal] = useState(false); const [hovered, setHovered] = useState(false); @@ -61,12 +58,15 @@ function WidgetGraphComponent({ const isFullViewOpen = params.get(QueryParams.expandedWidgetId) === widget.id; const lineChartRef = useRef(); + const [graphVisibility, setGraphVisibility] = useState( + Array(queryResponse.data?.payload?.data.result.length || 0).fill(true), + ); const graphRef = useRef(null); useEffect(() => { if (!lineChartRef.current) return; - graphVisibiltyState.forEach((state, index) => { + graphVisibility.forEach((state, index) => { lineChartRef.current?.toggleGraph(index, state); }); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -210,7 +210,7 @@ function WidgetGraphComponent({ graphVisibilityStates: localStoredVisibilityState, } = getLocalStorageGraphVisibilityState({ apiResponse: queryResponse.data.payload.data.result, - name, + name: widget.id, }); setGraphVisibility(localStoredVisibilityState); } @@ -252,7 +252,7 @@ function WidgetGraphComponent({ onBlur={(): void => { setHovered(false); }} - id={name} + id={widget.id} > @@ -305,26 +303,22 @@ function WidgetGraphComponent({ isFetchingResponse={isFetchingResponse} /> - {queryResponse.isLoading && } + {queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && ( + + )} {(queryResponse.isSuccess || widget.panelTypes === PANEL_TYPES.LIST) && (
-
)} diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index d81e518222..363cba7f76 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -4,80 +4,43 @@ import { PANEL_TYPES } from 'constants/queryBuilder'; import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config'; 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 GetMinMax from 'lib/getMinMax'; +import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults'; 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/isEmpty'; -import _noop from 'lodash-es/noop'; import { useDashboard } from 'providers/Dashboard/Dashboard'; -import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { memo, useEffect, 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'; import { getGraphType } from 'utils/getGraphType'; import { getSortedSeriesData } from 'utils/getSortedSeriesData'; -import { getTimeRange } from 'utils/getTimeRange'; import EmptyWidget from '../EmptyWidget'; import { MenuItemKeys } from '../WidgetHeader/contants'; import { GridCardGraphProps } from './types'; -import { getLocalStorageGraphVisibilityState } from './utils'; import WidgetGraphComponent from './WidgetGraphComponent'; function GridCardGraph({ widget, - name, - onClickHandler = _noop, headerMenuList = [MenuItemKeys.View], isQueryEnabled, threshold, variables, - fillSpans = false, version, + onClickHandler, + onDragSelect, }: GridCardGraphProps): JSX.Element { const dispatch = useDispatch(); const [errorMessage, setErrorMessage] = useState(); const { toScrollWidgetId, setToScrollWidgetId } = useDashboard(); - const [minTimeScale, setMinTimeScale] = useState(); - 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); - - 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); @@ -127,19 +90,39 @@ function GridCardGraph({ const isEmptyWidget = widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget); - const queryEnabledCondition = - isVisible && - !isEmptyWidget && - isQueryEnabled && - widget.panelTypes !== PANEL_TYPES.LIST; + const queryEnabledCondition = isVisible && !isEmptyWidget && isQueryEnabled; + + const [requestData, setRequestData] = useState(() => { + if (widget.panelTypes !== PANEL_TYPES.LIST) { + return { + selectedTime: widget?.timePreferance, + graphType: getGraphType(widget.panelTypes), + query: updatedQuery, + globalSelectedInterval, + variables: getDashboardVariables(variables), + }; + } + updatedQuery.builder.queryData[0].pageSize = 10; + return { + query: updatedQuery, + graphType: PANEL_TYPES.LIST, + selectedTime: 'GLOBAL_TIME', + globalSelectedInterval, + tableParams: { + pagination: { + offset: 0, + limit: updatedQuery.builder.queryData[0].limit || 0, + }, + }, + }; + }); const queryResponse = useGetQueryRange( { - selectedTime: widget?.timePreferance, - graphType: getGraphType(widget.panelTypes), - query: updatedQuery, - globalSelectedInterval, + ...requestData, variables: getDashboardVariables(variables), + selectedTime: 'GLOBAL_TIME', + globalSelectedInterval, }, version || DEFAULT_ENTITY_VERSION, { @@ -151,6 +134,7 @@ function GridCardGraph({ widget?.query, widget?.panelTypes, widget.timePreferance, + requestData, ], retry(failureCount, error): boolean { if ( @@ -173,15 +157,6 @@ function GridCardGraph({ const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET; - const containerDimensions = useResizeObserver(graphRef); - - useEffect((): void => { - const { startTime, endTime } = getTimeRange(queryResponse); - - setMinTimeScale(startTime); - setMaxTimeScale(endTime); - }, [maxTime, minTime, globalSelectedInterval, queryResponse]); - if (queryResponse.data && widget.panelTypes === PANEL_TYPES.BAR) { const sortedSeriesData = getSortedSeriesData( queryResponse.data?.payload.data.result, @@ -189,89 +164,29 @@ function GridCardGraph({ queryResponse.data.payload.data.result = sortedSeriesData; } - const chartData = getUPlotChartData(queryResponse?.data?.payload, fillSpans); - - const isDarkMode = useIsDarkMode(); - const menuList = widget.panelTypes === PANEL_TYPES.TABLE || widget.panelTypes === PANEL_TYPES.LIST ? headerMenuList.filter((menu) => menu !== MenuItemKeys.CreateAlerts) : headerMenuList; - const [graphVisibility, setGraphVisibility] = useState( - Array(queryResponse.data?.payload?.data.result.length || 0).fill(true), - ); - - useEffect(() => { - const { - graphVisibilityStates: localStoredVisibilityState, - } = getLocalStorageGraphVisibilityState({ - apiResponse: queryResponse.data?.payload.data.result || [], - name, - }); - setGraphVisibility(localStoredVisibilityState); - }, [name, queryResponse.data?.payload.data.result]); - - const options = useMemo( - () => - getUPlotChartOptions({ - id: widget?.id, - apiResponse: queryResponse.data?.payload, - dimensions: containerDimensions, - isDarkMode, - onDragSelect, - yAxisUnit: widget?.yAxisUnit, - onClickHandler, - thresholds: widget.thresholds, - minTimeScale, - maxTimeScale, - softMax: widget.softMax === undefined ? null : widget.softMax, - softMin: widget.softMin === undefined ? null : widget.softMin, - graphsVisibilityStates: graphVisibility, - setGraphsVisibilityStates: setGraphVisibility, - panelType: widget.panelTypes, - }), - [ - widget?.id, - widget?.yAxisUnit, - widget.thresholds, - widget.softMax, - widget.softMin, - queryResponse.data?.payload, - containerDimensions, - isDarkMode, - onDragSelect, - onClickHandler, - minTimeScale, - maxTimeScale, - graphVisibility, - setGraphVisibility, - widget.panelTypes, - ], - ); - return (
{isEmptyLayout ? ( ) : ( )}
diff --git a/frontend/src/container/GridCardLayout/GridCard/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts index 9e527c143d..1235a26440 100644 --- a/frontend/src/container/GridCardLayout/GridCard/types.ts +++ b/frontend/src/container/GridCardLayout/GridCard/types.ts @@ -1,9 +1,9 @@ import { ToggleGraphProps } from 'components/Graph/types'; -import { UplotProps } from 'components/Uplot/Uplot'; +import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react'; import { UseQueryResult } from 'react-query'; -import { ErrorResponse, SuccessResponse } from 'types/api'; +import { SuccessResponse } from 'types/api'; import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import uPlot from 'uplot'; @@ -16,35 +16,32 @@ export interface GraphVisibilityLegendEntryProps { legendEntry: LegendEntryProps[]; } -export interface WidgetGraphComponentProps extends UplotProps { +export interface WidgetGraphComponentProps { widget: Widgets; queryResponse: UseQueryResult< - SuccessResponse | ErrorResponse + SuccessResponse, + Error >; errorMessage: string | undefined; - name: string; version?: string; - onDragSelect: (start: number, end: number) => void; - onClickHandler?: OnClickPluginOpts['onClick']; threshold?: ReactNode; headerMenuList: MenuItemKeys[]; isWarning: boolean; - graphVisibiltyState: boolean[]; - setGraphVisibility: Dispatch>; isFetchingResponse: boolean; + setRequestData?: Dispatch>; + onClickHandler?: OnClickPluginOpts['onClick']; + onDragSelect: (start: number, end: number) => void; } export interface GridCardGraphProps { widget: Widgets; - name: string; - onDragSelect?: (start: number, end: number) => void; - onClickHandler?: OnClickPluginOpts['onClick']; threshold?: ReactNode; headerMenuList?: WidgetGraphComponentProps['headerMenuList']; + onClickHandler?: OnClickPluginOpts['onClick']; isQueryEnabled: boolean; variables?: Dashboard['data']['variables']; - fillSpans?: boolean; version?: string; + onDragSelect: (start: number, end: number) => void; } export interface GetGraphVisibilityStateOnLegendClickProps { diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.tsx b/frontend/src/container/GridCardLayout/GridCardLayout.tsx index 16ae0eeaa5..b84e88b292 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.tsx +++ b/frontend/src/container/GridCardLayout/GridCardLayout.tsx @@ -3,20 +3,25 @@ import './GridCardLayout.styles.scss'; import { PlusOutlined } from '@ant-design/icons'; import { Tooltip } from 'antd'; import { SOMETHING_WENT_WRONG } from 'constants/api'; +import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { themeColors } from 'constants/theme'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import useComponentPermission from 'hooks/useComponentPermission'; import { useIsDarkMode } from 'hooks/useDarkMode'; import { useNotifications } from 'hooks/useNotifications'; +import useUrlQuery from 'hooks/useUrlQuery'; +import history from 'lib/history'; import isEqual from 'lodash-es/isEqual'; import { FullscreenIcon } from 'lucide-react'; import { useDashboard } from 'providers/Dashboard/Dashboard'; -import { useEffect, useState } from 'react'; +import { useCallback, useEffect, useState } from 'react'; import { FullScreen, useFullScreenHandle } from 'react-full-screen'; import { Layout } from 'react-grid-layout'; import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { useLocation } from 'react-router-dom'; +import { UpdateTimeInterval } from 'store/actions'; import { AppState } from 'store/reducers'; import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; import AppReducer from 'types/reducer/app'; @@ -45,6 +50,8 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element { } = useDashboard(); const { data } = selectedDashboard || {}; const handle = useFullScreenHandle(); + const { pathname } = useLocation(); + const dispatch = useDispatch(); const { widgets, variables } = data || {}; @@ -61,6 +68,7 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element { const updateDashboardMutation = useUpdateDashboard(); const { notifications } = useNotifications(); + const urlQuery = useUrlQuery(); let permissions: ComponentTypes[] = ['save_layout', 'add_panel']; @@ -126,6 +134,23 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element { } }; + const onDragSelect = useCallback( + (start: number, end: number) => { + 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, pathname, urlQuery], + ); + useEffect(() => { if ( dashboardLayout && @@ -200,11 +225,10 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element { > diff --git a/frontend/src/container/GridPanelSwitch/index.tsx b/frontend/src/container/GridPanelSwitch/index.tsx index 18d4e7a63a..d8e526fecd 100644 --- a/frontend/src/container/GridPanelSwitch/index.tsx +++ b/frontend/src/container/GridPanelSwitch/index.tsx @@ -1,10 +1,8 @@ import { ToggleGraphProps } from 'components/Graph/types'; -import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { getComponentForPanelType } from 'constants/panelTypes'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config'; import { FC, forwardRef, memo, useMemo } from 'react'; -import { DataSource } from 'types/common/queryBuilder'; import { GridPanelSwitchProps, PropsTypePropsMap } from './types'; @@ -21,10 +19,7 @@ const GridPanelSwitch = forwardRef< query, options, thresholds, - selectedLogFields, - selectedTracesFields, dataSource, - selectedTime, }, ref, ): JSX.Element | null => { @@ -46,20 +41,7 @@ const GridPanelSwitch = forwardRef< query, thresholds, }, - [PANEL_TYPES.LIST]: - dataSource === DataSource.LOGS - ? { - selectedLogsFields: selectedLogFields || [], - query, - version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3 - selectedTime, - } - : { - selectedTracesFields: selectedTracesFields || [], - query, - version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3 - selectedTime, - }, + [PANEL_TYPES.LIST]: null, [PANEL_TYPES.TRACE]: null, [PANEL_TYPES.BAR]: { data, @@ -70,19 +52,7 @@ const GridPanelSwitch = forwardRef< }; return result; - }, [ - data, - options, - ref, - yAxisUnit, - thresholds, - panelData, - query, - dataSource, - selectedLogFields, - selectedTime, - selectedTracesFields, - ]); + }, [data, options, ref, yAxisUnit, thresholds, panelData, query]); const Component = getComponentForPanelType(panelType, dataSource) as FC< PropsTypePropsMap[typeof panelType] diff --git a/frontend/src/container/GridPanelSwitch/types.ts b/frontend/src/container/GridPanelSwitch/types.ts index ea437bf9ff..e587d59717 100644 --- a/frontend/src/container/GridPanelSwitch/types.ts +++ b/frontend/src/container/GridPanelSwitch/types.ts @@ -2,9 +2,7 @@ import { StaticLineProps, ToggleGraphProps } from 'components/Graph/types'; import { UplotProps } from 'components/Uplot/Uplot'; import { GridTableComponentProps } from 'container/GridTableComponent/types'; import { GridValueComponentProps } from 'container/GridValueComponent/types'; -import { LogsPanelComponentProps } from 'container/LogsPanelTable/LogsPanelComponent'; import { timePreferance } from 'container/NewWidget/RightContainer/timeItems'; -import { TracesTableComponentProps } from 'container/TracesTableComponent/TracesTableComponent'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { ForwardedRef } from 'react'; import { Widgets } from 'types/api/dashboard/getAll'; @@ -40,7 +38,7 @@ export type PropsTypePropsMap = { [PANEL_TYPES.VALUE]: GridValueComponentProps; [PANEL_TYPES.TABLE]: GridTableComponentProps; [PANEL_TYPES.TRACE]: null; - [PANEL_TYPES.LIST]: LogsPanelComponentProps | TracesTableComponentProps; + [PANEL_TYPES.LIST]: null; [PANEL_TYPES.BAR]: UplotProps & { ref: ForwardedRef; }; diff --git a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx index 618f732595..59f78499b3 100644 --- a/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx +++ b/frontend/src/container/LogsPanelTable/LogsPanelComponent.tsx @@ -4,82 +4,53 @@ import { Table } from 'antd'; import LogDetail from 'components/LogDetail'; import { VIEW_TYPES } from 'components/LogDetail/constants'; import { SOMETHING_WENT_WRONG } from 'constants/api'; -import { DEFAULT_ENTITY_VERSION } from 'constants/app'; -import { OPERATORS, PANEL_TYPES } from 'constants/queryBuilder'; -import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; +import { PANEL_TYPES } from 'constants/queryBuilder'; import Controls from 'container/Controls'; -import { timePreferance } from 'container/NewWidget/RightContainer/timeItems'; import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs'; import { tableStyles } from 'container/TracesExplorer/ListView/styles'; import { useActiveLog } from 'hooks/logs/useActiveLog'; -import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { Pagination } from 'hooks/queryPagination'; import { useLogsData } from 'hooks/useLogsData'; -import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults'; import { FlatLogData } from 'lib/logs/flatLogData'; import { RowData } from 'lib/query/createTableColumnsFromQuery'; -import { useDashboard } from 'providers/Dashboard/Dashboard'; import { + Dispatch, HTMLAttributes, + SetStateAction, useCallback, useEffect, useMemo, useState, } from 'react'; -import { useSelector } from 'react-redux'; -import { AppState } from 'store/reducers'; +import { UseQueryResult } from 'react-query'; +import { SuccessResponse } from 'types/api'; import { Widgets } from 'types/api/dashboard/getAll'; import { ILog } from 'types/api/logs/log'; -import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; -import { Query } from 'types/api/queryBuilder/queryBuilderData'; -import { GlobalReducer } from 'types/reducer/globalTime'; -import { v4 as uuid } from 'uuid'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; -import { getLogPanelColumnsList } from './utils'; +import { getLogPanelColumnsList, getNextOrPreviousItems } from './utils'; function LogsPanelComponent({ - selectedLogsFields, - query, - selectedTime, + widget, + setRequestData, + queryResponse, }: LogsPanelComponentProps): JSX.Element { - const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector< - AppState, - GlobalReducer - >((state) => state.globalTime); - const [pagination, setPagination] = useState({ offset: 0, - limit: query.builder.queryData[0].limit || 0, - }); - - const [requestData, setRequestData] = useState(() => { - const updatedQuery = { ...query }; - updatedQuery.builder.queryData[0].pageSize = 10; - return { - query: updatedQuery, - graphType: PANEL_TYPES.LIST, - selectedTime: 'GLOBAL_TIME', - globalSelectedInterval: globalSelectedTime, - tableParams: { - pagination, - }, - }; + limit: widget.query.builder.queryData[0].limit || 0, }); useEffect(() => { - setRequestData({ - ...requestData, - globalSelectedInterval: globalSelectedTime, + setRequestData((prev) => ({ + ...prev, tableParams: { pagination, }, - }); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [pagination]); + })); + }, [pagination, setRequestData]); const [pageSize, setPageSize] = useState(10); - const { selectedDashboard } = useDashboard(); const handleChangePageSize = (value: number): void => { setPagination({ @@ -88,53 +59,35 @@ function LogsPanelComponent({ offset: value, }); setPageSize(value); - const newQueryData = { ...requestData.query }; - newQueryData.builder.queryData[0].pageSize = value; - const newRequestData = { - ...requestData, - query: newQueryData, - tableParams: { - pagination, - }, - }; - setRequestData(newRequestData); + setRequestData((prev) => { + const newQueryData = { ...prev.query }; + newQueryData.builder.queryData[0].pageSize = value; + return { + ...prev, + query: newQueryData, + tableParams: { + pagination: { + limit: 0, + offset: value, + }, + }, + }; + }); }; - const { data, isFetching, isError } = useGetQueryRange( - { - ...requestData, - globalSelectedInterval: globalSelectedTime, - selectedTime: selectedTime?.enum || 'GLOBAL_TIME', - variables: getDashboardVariables(selectedDashboard?.data.variables), - }, - DEFAULT_ENTITY_VERSION, - { - queryKey: [ - REACT_QUERY_KEY.GET_QUERY_RANGE, - globalSelectedTime, - maxTime, - minTime, - requestData, - pagination, - selectedDashboard?.data.variables, - ], - enabled: !!requestData.query && !!selectedLogsFields?.length, - }, - ); - - const columns = getLogPanelColumnsList(selectedLogsFields); + const columns = getLogPanelColumnsList(widget.selectedLogFields); const dataLength = - data?.payload?.data?.newResult?.data?.result[0]?.list?.length; + queryResponse.data?.payload?.data?.newResult?.data?.result[0]?.list?.length; const totalCount = useMemo(() => dataLength || 0, [dataLength]); const [firstLog, setFirstLog] = useState(); const [lastLog, setLastLog] = useState(); const { logs } = useLogsData({ - result: data?.payload.data.newResult.data.result, + result: queryResponse.data?.payload.data.newResult.data.result, panelType: PANEL_TYPES.LIST, - stagedQuery: query, + stagedQuery: widget.query, }); useEffect(() => { @@ -167,92 +120,86 @@ function LogsPanelComponent({ ); const isOrderByTimeStamp = - query.builder.queryData[0].orderBy.length > 0 && - query.builder.queryData[0].orderBy[0].columnName === 'timestamp'; + widget.query.builder.queryData[0].orderBy.length > 0 && + widget.query.builder.queryData[0].orderBy[0].columnName === 'timestamp'; const handlePreviousPagination = (): void => { if (isOrderByTimeStamp) { - setRequestData({ - ...requestData, + setRequestData((prev) => ({ + ...prev, query: { - ...requestData.query, + ...prev.query, builder: { - ...requestData.query.builder, + ...prev.query.builder, queryData: [ { - ...requestData.query.builder.queryData[0], + ...prev.query.builder.queryData[0], filters: { - ...requestData.query.builder.queryData[0].filters, + ...prev.query.builder.queryData[0].filters, items: [ - { - id: uuid(), - key: { - key: 'id', - type: '', - dataType: DataTypes.String, - isColumn: true, - }, - op: OPERATORS['>'], - value: firstLog?.id || '', - }, + ...getNextOrPreviousItems( + prev.query.builder.queryData[0].filters.items, + 'PREV', + firstLog, + ), ], }, + limit: 0, + offset: 0, }, ], }, }, - }); - return; + })); + } + if (!isOrderByTimeStamp) { + setPagination({ + ...pagination, + limit: 0, + offset: pagination.offset - pageSize, + }); } - setPagination({ - ...pagination, - limit: 0, - offset: pagination.offset - pageSize, - }); }; const handleNextPagination = (): void => { if (isOrderByTimeStamp) { - setRequestData({ - ...requestData, + setRequestData((prev) => ({ + ...prev, query: { - ...requestData.query, + ...prev.query, builder: { - ...requestData.query.builder, + ...prev.query.builder, queryData: [ { - ...requestData.query.builder.queryData[0], + ...prev.query.builder.queryData[0], filters: { - ...requestData.query.builder.queryData[0].filters, + ...prev.query.builder.queryData[0].filters, items: [ - { - id: uuid(), - key: { - key: 'id', - type: '', - dataType: DataTypes.String, - isColumn: true, - }, - op: OPERATORS['<'], - value: lastLog?.id || '', - }, + ...getNextOrPreviousItems( + prev.query.builder.queryData[0].filters.items, + 'NEXT', + lastLog, + ), ], }, + limit: 0, + offset: 0, }, ], }, }, - }); - return; + })); + } + if (!isOrderByTimeStamp) { + setPagination({ + ...pagination, + limit: 0, + offset: pagination.offset + pageSize, + }); } - setPagination({ - ...pagination, - limit: 0, - offset: pagination.offset + pageSize, - }); }; - if (isError) { + if (queryResponse.isError) { return
{SOMETHING_WENT_WRONG}
; } @@ -265,19 +212,19 @@ function LogsPanelComponent({ tableLayout="fixed" scroll={{ x: `calc(50vw - 10px)` }} sticky - loading={isFetching} + loading={queryResponse.isFetching} style={tableStyles} dataSource={flattenLogData} columns={columns} onRow={handleRow} /> - {!query.builder.queryData[0].limit && ( + {!widget.query.builder.queryData[0].limit && (
>; + queryResponse: UseQueryResult< + SuccessResponse, + Error + >; + widget: Widgets; }; export default LogsPanelComponent; diff --git a/frontend/src/container/LogsPanelTable/utils.tsx b/frontend/src/container/LogsPanelTable/utils.tsx index 46701e763b..a95442b7cc 100644 --- a/frontend/src/container/LogsPanelTable/utils.tsx +++ b/frontend/src/container/LogsPanelTable/utils.tsx @@ -1,10 +1,15 @@ import { ColumnsType } from 'antd/es/table'; import { Typography } from 'antd/lib'; +import { OPERATORS } from 'constants/queryBuilder'; // import Typography from 'antd/es/typography/Typography'; import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { ReactNode } from 'react'; import { Widgets } from 'types/api/dashboard/getAll'; import { IField } from 'types/api/logs/fields'; +import { ILog } from 'types/api/logs/log'; +import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; +import { v4 as uuid } from 'uuid'; export const getLogPanelColumnsList = ( selectedLogFields: Widgets['selectedLogFields'], @@ -36,3 +41,49 @@ export const getLogPanelColumnsList = ( return [...initialColumns, ...columns]; }; + +export const getNextOrPreviousItems = ( + items: TagFilterItem[], + direction: 'NEXT' | 'PREV', + log?: ILog, +): TagFilterItem[] => { + const nextItem = { + id: uuid(), + key: { + key: 'id', + type: '', + dataType: DataTypes.String, + isColumn: true, + }, + op: OPERATORS['<'], + value: log?.id || '', + }; + const prevItem = { + id: uuid(), + key: { + key: 'id', + type: '', + dataType: DataTypes.String, + isColumn: true, + }, + op: OPERATORS['>'], + value: log?.id || '', + }; + let index = items.findIndex((item) => item.op === OPERATORS['<']); + if (index === -1) { + index = items.findIndex((item) => item.op === OPERATORS['>']); + } + if (index === -1) { + if (direction === 'NEXT') { + return [...items, nextItem]; + } + return [...items, prevItem]; + } + const newItems = [...items]; + if (direction === 'NEXT') { + newItems[index] = nextItem; + } else { + newItems[index] = prevItem; + } + return newItems; +}; diff --git a/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts b/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts index b974972d70..672bc11812 100644 --- a/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts +++ b/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts @@ -8,6 +8,7 @@ export const getWidgetQueryBuilder = ({ title = '', panelTypes, yAxisUnit = '', + fillSpans = false, id, }: GetWidgetQueryBuilderProps): Widgets => ({ description: '', @@ -24,4 +25,5 @@ export const getWidgetQueryBuilder = ({ softMin: null, selectedLogFields: [], selectedTracesFields: [], + fillSpans, }); diff --git a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx index 0d84d45136..54c2115927 100644 --- a/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/DBCall.tsx @@ -70,6 +70,7 @@ function DBCall(): JSX.Element { panelTypes: PANEL_TYPES.TIME_SERIES, yAxisUnit: 'reqps', id: SERVICE_CHART_ID.dbCallsRPS, + fillSpans: false, }), [servicename, tagFilterItems], ); @@ -89,7 +90,8 @@ function DBCall(): JSX.Element { title: GraphTitle.DATABASE_CALLS_AVG_DURATION, panelTypes: PANEL_TYPES.TIME_SERIES, yAxisUnit: 'ms', - id: SERVICE_CHART_ID.dbCallsAvgDuration, + id: GraphTitle.DATABASE_CALLS_AVG_DURATION, + fillSpans: true, }), [servicename, tagFilterItems], ); @@ -112,8 +114,6 @@ function DBCall(): JSX.Element { { onGraphClickHandler(setSelectedTimeStamp)( @@ -147,8 +147,6 @@ function DBCall(): JSX.Element { { diff --git a/frontend/src/container/MetricsApplication/Tabs/External.tsx b/frontend/src/container/MetricsApplication/Tabs/External.tsx index 707675ec1d..62ce71b6b4 100644 --- a/frontend/src/container/MetricsApplication/Tabs/External.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/External.tsx @@ -18,7 +18,7 @@ import { useParams } from 'react-router-dom'; import { EQueryType } from 'types/common/dashboard'; import { v4 as uuid } from 'uuid'; -import { GraphTitle, legend, MENU_ITEMS, SERVICE_CHART_ID } from '../constant'; +import { GraphTitle, legend, MENU_ITEMS } from '../constant'; import { getWidgetQueryBuilder } from '../MetricsApplication.factory'; import { Card, GraphContainer, Row } from '../styles'; import { Button } from './styles'; @@ -60,7 +60,7 @@ function External(): JSX.Element { title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE, panelTypes: PANEL_TYPES.TIME_SERIES, yAxisUnit: '%', - id: SERVICE_CHART_ID.externalCallErrorPercentage, + id: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE, }), [servicename, tagFilterItems], ); @@ -86,7 +86,8 @@ function External(): JSX.Element { title: GraphTitle.EXTERNAL_CALL_DURATION, panelTypes: PANEL_TYPES.TIME_SERIES, yAxisUnit: 'ms', - id: SERVICE_CHART_ID.externalCallDuration, + id: GraphTitle.EXTERNAL_CALL_DURATION, + fillSpans: true, }), [servicename, tagFilterItems], ); @@ -108,7 +109,8 @@ function External(): JSX.Element { title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS, panelTypes: PANEL_TYPES.TIME_SERIES, yAxisUnit: 'reqps', - id: SERVICE_CHART_ID.externalCallRPSByAddress, + id: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS, + fillSpans: true, }), [servicename, tagFilterItems], ); @@ -130,7 +132,8 @@ function External(): JSX.Element { title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS, panelTypes: PANEL_TYPES.TIME_SERIES, yAxisUnit: 'ms', - id: SERVICE_CHART_ID.externalCallDurationByAddress, + id: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS, + fillSpans: true, }), [servicename, tagFilterItems], ); @@ -155,9 +158,7 @@ function External(): JSX.Element { { onGraphClickHandler(setSelectedTimeStamp)( @@ -192,8 +193,6 @@ function External(): JSX.Element { { @@ -230,8 +229,6 @@ function External(): JSX.Element { => @@ -267,10 +264,8 @@ function External(): JSX.Element { { onGraphClickHandler(setSelectedTimeStamp)( xValue, diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx index 1fde8d0919..cfaf339d2e 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview.tsx @@ -19,13 +19,10 @@ import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { defaultTo } from 'lodash-es'; import { useCallback, useMemo, useState } from 'react'; import { useQuery } from 'react-query'; -import { useDispatch, useSelector } from 'react-redux'; +import { useDispatch } from 'react-redux'; import { useLocation, useParams } from 'react-router-dom'; import { UpdateTimeInterval } from 'store/actions'; -import { AppState } from 'store/reducers'; import { EQueryType } from 'types/common/dashboard'; -import { GlobalReducer } from 'types/reducer/globalTime'; -import { Tags } from 'types/reducer/trace'; import { v4 as uuid } from 'uuid'; import { GraphTitle, SERVICE_CHART_ID } from '../constant'; @@ -49,9 +46,6 @@ import { } from './util'; function Application(): JSX.Element { - const { maxTime, minTime } = useSelector( - (state) => state.globalTime, - ); const { servicename: encodedServiceName } = useParams(); const servicename = decodeURIComponent(encodedServiceName); const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); @@ -59,10 +53,6 @@ function Application(): JSX.Element { const { queries } = useResourceAttribute(); const urlQuery = useUrlQuery(); - const selectedTags = useMemo( - () => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [], - [queries], - ); const isSpanMetricEnabled = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS) ?.active; @@ -94,7 +84,7 @@ function Application(): JSX.Element { isLoading: topLevelOperationsIsLoading, isError: topLevelOperationsIsError, } = useQuery({ - queryKey: [servicename, minTime, maxTime, selectedTags], + queryKey: [servicename], queryFn: getTopLevelOperations, }); @@ -116,49 +106,41 @@ function Application(): JSX.Element { [servicename, topLevelOperations], ); - const operationPerSecWidget = useMemo( - () => - getWidgetQueryBuilder({ - query: { - queryType: EQueryType.QUERY_BUILDER, - promql: [], - builder: operationPerSec({ - servicename, - tagFilterItems, - topLevelOperations: topLevelOperationsRoute, - }), - clickhouse_sql: [], - id: uuid(), - }, - title: GraphTitle.RATE_PER_OPS, - panelTypes: PANEL_TYPES.TIME_SERIES, - yAxisUnit: 'ops', - id: SERVICE_CHART_ID.rps, + const operationPerSecWidget = getWidgetQueryBuilder({ + query: { + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: operationPerSec({ + servicename, + tagFilterItems, + topLevelOperations: topLevelOperationsRoute, }), - [servicename, tagFilterItems, topLevelOperationsRoute], - ); + clickhouse_sql: [], + id: uuid(), + }, + title: GraphTitle.RATE_PER_OPS, + panelTypes: PANEL_TYPES.TIME_SERIES, + yAxisUnit: 'ops', + id: SERVICE_CHART_ID.rps, + }); - const errorPercentageWidget = useMemo( - () => - getWidgetQueryBuilder({ - query: { - queryType: EQueryType.QUERY_BUILDER, - promql: [], - builder: errorPercentage({ - servicename, - tagFilterItems, - topLevelOperations: topLevelOperationsRoute, - }), - clickhouse_sql: [], - id: uuid(), - }, - title: GraphTitle.ERROR_PERCENTAGE, - panelTypes: PANEL_TYPES.TIME_SERIES, - yAxisUnit: '%', - id: SERVICE_CHART_ID.errorPercentage, + const errorPercentageWidget = getWidgetQueryBuilder({ + query: { + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: errorPercentage({ + servicename, + tagFilterItems, + topLevelOperations: topLevelOperationsRoute, }), - [servicename, tagFilterItems, topLevelOperationsRoute], - ); + clickhouse_sql: [], + id: uuid(), + }, + title: GraphTitle.ERROR_PERCENTAGE, + panelTypes: PANEL_TYPES.TIME_SERIES, + yAxisUnit: '%', + id: SERVICE_CHART_ID.errorPercentage, + }); const onDragSelect = useCallback( (start: number, end: number) => { diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx index abfaf52d58..dcd01270ee 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/ApDex/ApDexMetrics.tsx @@ -89,8 +89,6 @@ function ApDexMetrics({ return ( - getWidgetQueryBuilder({ - query: { - queryType: EQueryType.QUERY_BUILDER, - promql: [], - builder: latency({ - servicename, - tagFilterItems, - isSpanMetricEnable, - topLevelOperationsRoute, - }), - clickhouse_sql: [], - id: uuid(), - }, - title: GraphTitle.LATENCY, - panelTypes: PANEL_TYPES.TIME_SERIES, - yAxisUnit: 'ns', - id: SERVICE_CHART_ID.latency, + const latencyWidget = getWidgetQueryBuilder({ + query: { + queryType: EQueryType.QUERY_BUILDER, + promql: [], + builder: latency({ + servicename, + tagFilterItems, + isSpanMetricEnable, + topLevelOperationsRoute, }), - [servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems], - ); + clickhouse_sql: [], + id: uuid(), + }, + title: GraphTitle.LATENCY, + panelTypes: PANEL_TYPES.TIME_SERIES, + yAxisUnit: 'ns', + id: SERVICE_CHART_ID.latency, + }); const isQueryEnabled = !topLevelOperationsIsLoading && topLevelOperationsRoute.length > 0; @@ -88,15 +85,23 @@ function ServiceOverview({ - + {topLevelOperationsIsLoading && ( + + )} + {!topLevelOperationsIsLoading && ( + + )} diff --git a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx index 1b0903f4fa..9238e82231 100644 --- a/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx +++ b/frontend/src/container/MetricsApplication/Tabs/Overview/TopLevelOperations.tsx @@ -1,4 +1,4 @@ -import { Typography } from 'antd'; +import { Skeleton, Typography } from 'antd'; import axios from 'axios'; import { SOMETHING_WENT_WRONG } from 'constants/api'; import { ENTITY_VERSION_V4 } from 'constants/app'; @@ -27,15 +27,23 @@ function TopLevelOperation({ ) : ( - + {topLevelOperationsIsLoading && ( + + )} + {!topLevelOperationsIsLoading && ( + + )} )} diff --git a/frontend/src/container/MetricsApplication/styles.ts b/frontend/src/container/MetricsApplication/styles.ts index 2e1ab9c8bf..8e396891e7 100644 --- a/frontend/src/container/MetricsApplication/styles.ts +++ b/frontend/src/container/MetricsApplication/styles.ts @@ -13,7 +13,7 @@ export const Card = styled(CardComponent)` } .ant-card-body { - height: calc(100% - 40px); + height: 100%; padding: 0; } `; @@ -40,7 +40,7 @@ export const ColErrorContainer = styled(ColComponent)` export const GraphContainer = styled.div` min-height: calc(40vh - 40px); - height: calc(100% - 40px); + height: 100%; `; export const GraphTitle = styled(Typography)` diff --git a/frontend/src/container/MetricsApplication/types.ts b/frontend/src/container/MetricsApplication/types.ts index 642bf0b057..d8464d1248 100644 --- a/frontend/src/container/MetricsApplication/types.ts +++ b/frontend/src/container/MetricsApplication/types.ts @@ -10,6 +10,7 @@ export interface GetWidgetQueryBuilderProps { panelTypes: Widgets['panelTypes']; yAxisUnit?: Widgets['yAxisUnit']; id?: Widgets['id']; + fillSpans?: Widgets['fillSpans']; } export interface NavigateToTraceProps { diff --git a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx index 9dfd8d0deb..7c9fca416e 100644 --- a/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx +++ b/frontend/src/container/NewWidget/LeftContainer/QuerySection/index.tsx @@ -2,14 +2,11 @@ import './QuerySection.styles.scss'; import { Button, Tabs, Tooltip, Typography } from 'antd'; import TextToolTip from 'components/TextToolTip'; -import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { PANEL_TYPES } from 'constants/queryBuilder'; import { QBShortcuts } from 'constants/shortcuts/QBShortcuts'; -import { WidgetGraphProps } from 'container/NewWidget/types'; import { QueryBuilder } from 'container/QueryBuilder'; import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces'; import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys'; -import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval'; @@ -22,9 +19,12 @@ import { getSelectedWidgetIndex, } from 'providers/Dashboard/util'; import { useCallback, useEffect, useMemo } from 'react'; +import { UseQueryResult } from 'react-query'; import { useSelector } from 'react-redux'; import { AppState } from 'store/reducers'; +import { SuccessResponse } from 'types/api'; import { Widgets } from 'types/api/dashboard/getAll'; +import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { EQueryType } from 'types/common/dashboard'; import AppReducer from 'types/reducer/app'; @@ -35,7 +35,7 @@ import PromQLQueryContainer from './QueryBuilder/promQL'; function QuerySection({ selectedGraph, - selectedTime, + queryResponse, }: QueryProps): JSX.Element { const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder(); const urlQuery = useUrlQuery(); @@ -51,14 +51,6 @@ function QuerySection({ const { selectedDashboard, setSelectedDashboard } = useDashboard(); - const getWidgetQueryRange = useGetWidgetQueryRange( - { - graphType: selectedGraph, - selectedTime: selectedTime.enum, - }, - selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION, - ); - const { widgets } = selectedDashboard?.data || {}; const getWidget = useCallback(() => { @@ -233,7 +225,7 @@ function QuerySection({