Dashboard Clean up and list view improvement. (#4675)

* refactor: initial setup

* refactor: created panelWrapper to separate panel data

* fix: type error

* fix: the dimension issue for graphs

* refactor: done with table value uplot panels

* refactor: done with logs panel component

* refactor: updated props for log panel component

* fix: query range duplicate issue for logs

* refactor: trace list view done

* fix: full view support

* refactor: done with edit mode for panels

* refactor: type and props

* refactor: reduce an extra api call on edit for list view

* refactor: done with full graph visibility handler

* refactor: removed commented code

* refactor: removed commented code

* fix: build failure

* refactor: updated service layer graphs

* refactor: updated top level oparation query key

* refactor: added drag select

* refactor: done with drag select in chart

* refactor: code cleanup

* refactor: legend should not need stage and run query
This commit is contained in:
Rajat Dabade 2024-04-02 16:40:41 +05:30 committed by GitHub
parent 7a7d814288
commit ec9dbb6853
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 1005 additions and 956 deletions

View File

@ -1,62 +1,60 @@
import './WidgetFullView.styles.scss'; import './WidgetFullView.styles.scss';
import { SyncOutlined } from '@ant-design/icons'; import { LoadingOutlined, SyncOutlined } from '@ant-design/icons';
import { Button } from 'antd'; import { Button, Spin } from 'antd';
import cx from 'classnames'; import cx from 'classnames';
import { ToggleGraphProps } from 'components/Graph/types'; import { ToggleGraphProps } from 'components/Graph/types';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import TimePreference from 'components/TimePreferenceDropDown'; import TimePreference from 'components/TimePreferenceDropDown';
import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import GridPanelSwitch from 'container/GridPanelSwitch';
import { import {
timeItems, timeItems,
timePreferance, timePreferance,
} from 'container/NewWidget/RightContainer/timeItems'; } from 'container/NewWidget/RightContainer/timeItems';
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
import { useChartMutable } from 'hooks/useChartMutable'; import { useChartMutable } from 'hooks/useChartMutable';
import { useIsDarkMode } from 'hooks/useDarkMode'; import useUrlQuery from 'hooks/useUrlQuery';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; import GetMinMax from 'lib/getMinMax';
import history from 'lib/history';
import { useDashboard } from 'providers/Dashboard/Dashboard'; import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useCallback, useEffect, useRef, useState } from 'react'; 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 { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import uPlot from 'uplot'; import { getGraphType } from 'utils/getGraphType';
import { getSortedSeriesData } from 'utils/getSortedSeriesData'; import { getSortedSeriesData } from 'utils/getSortedSeriesData';
import { getTimeRange } from 'utils/getTimeRange';
import { getLocalStorageGraphVisibilityState } from '../utils'; import { getLocalStorageGraphVisibilityState } from '../utils';
import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants'; import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants';
import GraphManager from './GraphManager';
import { GraphContainer, TimeContainer } from './styles'; import { GraphContainer, TimeContainer } from './styles';
import { FullViewProps } from './types'; import { FullViewProps } from './types';
function FullView({ function FullView({
widget, widget,
fullViewOptions = true, fullViewOptions = true,
onClickHandler,
name,
version, version,
originalName, originalName,
yAxisUnit,
onDragSelect,
isDependedDataLoaded = false, isDependedDataLoaded = false,
onToggleModelHandler, onToggleModelHandler,
parentChartRef,
}: FullViewProps): JSX.Element { }: FullViewProps): JSX.Element {
const { selectedTime: globalSelectedTime } = useSelector< const { selectedTime: globalSelectedTime } = useSelector<
AppState, AppState,
GlobalReducer GlobalReducer
>((state) => state.globalTime); >((state) => state.globalTime);
const dispatch = useDispatch();
const urlQuery = useUrlQuery();
const location = useLocation();
const fullViewRef = useRef<HTMLDivElement>(null); const fullViewRef = useRef<HTMLDivElement>(null);
const [chartOptions, setChartOptions] = useState<uPlot.Options>();
const { selectedDashboard, isDashboardLocked } = useDashboard(); const { selectedDashboard, isDashboardLocked } = useDashboard();
const getSelectedTime = useCallback( const getSelectedTime = useCallback(
@ -74,24 +72,70 @@ function FullView({
const updatedQuery = useStepInterval(widget?.query); const updatedQuery = useStepInterval(widget?.query);
const response = useGetQueryRange( const [requestData, setRequestData] = useState<GetQueryResultsProps>(() => {
{ if (widget.panelTypes !== PANEL_TYPES.LIST) {
return {
selectedTime: selectedTime.enum, selectedTime: selectedTime.enum,
graphType: graphType: getGraphType(widget.panelTypes),
widget.panelTypes === PANEL_TYPES.BAR
? PANEL_TYPES.TIME_SERIES
: widget.panelTypes,
query: updatedQuery, query: updatedQuery,
globalSelectedInterval: globalSelectedTime, globalSelectedInterval: globalSelectedTime,
variables: getDashboardVariables(selectedDashboard?.data.variables), variables: getDashboardVariables(selectedDashboard?.data.variables),
};
}
updatedQuery.builder.queryData[0].pageSize = 10;
return {
query: updatedQuery,
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
globalSelectedInterval: globalSelectedTime,
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, selectedDashboard?.data?.version || version || DEFAULT_ENTITY_VERSION,
{ {
queryKey: `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`, queryKey: [widget?.query, widget?.panelTypes, requestData, version],
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 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< const [graphsVisibilityStates, setGraphsVisibilityStates] = useState<
boolean[] boolean[]
>(Array(response.data?.payload.data.result.length).fill(true)); >(Array(response.data?.payload.data.result.length).fill(true));
@ -118,60 +162,6 @@ function FullView({
response.data.payload.data.result = sortedSeriesData; response.data.payload.data.result = sortedSeriesData;
} }
const chartData = getUPlotChartData(response?.data?.payload, widget.fillSpans);
const isDarkMode = useIsDarkMode();
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(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(() => { useEffect(() => {
graphsVisibilityStates?.forEach((e, i) => { graphsVisibilityStates?.forEach((e, i) => {
fullViewChartRef?.current?.toggleGraph(i, e); fullViewChartRef?.current?.toggleGraph(i, e);
@ -180,7 +170,7 @@ function FullView({
const isListView = widget.panelTypes === PANEL_TYPES.LIST; const isListView = widget.panelTypes === PANEL_TYPES.LIST;
if (response.isFetching) { if (response.isLoading && widget.panelTypes !== PANEL_TYPES.LIST) {
return <Spinner height="100%" size="large" tip="Loading..." />; return <Spinner height="100%" size="large" tip="Loading..." />;
} }
@ -189,6 +179,9 @@ function FullView({
<div className="full-view-header-container"> <div className="full-view-header-container">
{fullViewOptions && ( {fullViewOptions && (
<TimeContainer $panelType={widget.panelTypes}> <TimeContainer $panelType={widget.panelTypes}>
{response.isFetching && (
<Spin spinning indicator={<LoadingOutlined spin />} />
)}
<TimePreference <TimePreference
selectedTime={selectedTime} selectedTime={selectedTime}
setSelectedTime={setSelectedTime} setSelectedTime={setSelectedTime}
@ -214,47 +207,24 @@ function FullView({
})} })}
ref={fullViewRef} ref={fullViewRef}
> >
{chartOptions && (
<GraphContainer <GraphContainer
style={{ style={{
height: isListView ? '100%' : '90%', height: isListView ? '100%' : '90%',
}} }}
isGraphLegendToggleAvailable={canModifyChart} isGraphLegendToggleAvailable={canModifyChart}
> >
<GridPanelSwitch <PanelWrapper
panelType={widget.panelTypes} queryResponse={response}
data={chartData} widget={widget}
options={chartOptions} setRequestData={setRequestData}
onClickHandler={onClickHandler} isFullViewMode
name={name} onToggleModelHandler={onToggleModelHandler}
yAxisUnit={yAxisUnit} setGraphVisibility={setGraphsVisibilityStates}
graphVisibility={graphsVisibilityStates}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
panelData={response.data?.payload.data.newResult.data.result || []}
query={widget.query}
ref={fullViewChartRef}
thresholds={widget.thresholds}
selectedLogFields={widget.selectedLogFields}
dataSource={widget.query.builder.queryData[0].dataSource}
selectedTracesFields={widget.selectedTracesFields}
selectedTime={selectedTime}
/> />
</GraphContainer> </GraphContainer>
)}
</div> </div>
{canModifyChart && chartOptions && !isDashboardLocked && (
<GraphManager
data={chartData}
name={originalName}
options={chartOptions}
yAxisUnit={yAxisUnit}
onToggleModelHandler={onToggleModelHandler}
setGraphsVisibilityStates={setGraphsVisibilityStates}
graphsVisibilityStates={graphsVisibilityStates}
lineChartRef={fullViewChartRef}
parentChartRef={parentChartRef}
/>
)}
</div> </div>
); );
} }

View File

@ -18,6 +18,7 @@ export const NotFoundContainer = styled.div`
export const TimeContainer = styled.div<Props>` export const TimeContainer = styled.div<Props>`
display: flex; display: flex;
justify-content: flex-end; justify-content: flex-end;
align-items: center;
${({ $panelType }): FlattenSimpleInterpolation => ${({ $panelType }): FlattenSimpleInterpolation =>
$panelType === PANEL_TYPES.TABLE $panelType === PANEL_TYPES.TABLE
? css` ? css`

View File

@ -53,10 +53,8 @@ export interface FullViewProps {
version?: string; version?: string;
originalName: string; originalName: string;
yAxisUnit?: string; yAxisUnit?: string;
onDragSelect: (start: number, end: number) => void;
isDependedDataLoaded?: boolean; isDependedDataLoaded?: boolean;
onToggleModelHandler?: GraphManagerProps['onToggleModelHandler']; onToggleModelHandler?: GraphManagerProps['onToggleModelHandler'];
parentChartRef: GraphManagerProps['lineChartRef'];
} }
export interface GraphManagerProps extends UplotProps { export interface GraphManagerProps extends UplotProps {

View File

@ -6,7 +6,7 @@ import { ToggleGraphProps } from 'components/Graph/types';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import GridPanelSwitch from 'container/GridPanelSwitch'; import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';
import useUrlQuery from 'hooks/useUrlQuery'; import useUrlQuery from 'hooks/useUrlQuery';
@ -33,23 +33,20 @@ import FullView from './FullView';
import { Modal } from './styles'; import { Modal } from './styles';
import { WidgetGraphComponentProps } from './types'; import { WidgetGraphComponentProps } from './types';
import { getLocalStorageGraphVisibilityState } from './utils'; import { getLocalStorageGraphVisibilityState } from './utils';
// import { getLocalStorageGraphVisibilityState } from './utils';
function WidgetGraphComponent({ function WidgetGraphComponent({
widget, widget,
queryResponse, queryResponse,
errorMessage, errorMessage,
name,
version, version,
threshold, threshold,
headerMenuList, headerMenuList,
isWarning, isWarning,
data, isFetchingResponse,
options, setRequestData,
graphVisibiltyState,
onClickHandler, onClickHandler,
onDragSelect, onDragSelect,
setGraphVisibility,
isFetchingResponse,
}: WidgetGraphComponentProps): JSX.Element { }: WidgetGraphComponentProps): JSX.Element {
const [deleteModal, setDeleteModal] = useState(false); const [deleteModal, setDeleteModal] = useState(false);
const [hovered, setHovered] = useState(false); const [hovered, setHovered] = useState(false);
@ -61,12 +58,15 @@ function WidgetGraphComponent({
const isFullViewOpen = params.get(QueryParams.expandedWidgetId) === widget.id; const isFullViewOpen = params.get(QueryParams.expandedWidgetId) === widget.id;
const lineChartRef = useRef<ToggleGraphProps>(); const lineChartRef = useRef<ToggleGraphProps>();
const [graphVisibility, setGraphVisibility] = useState<boolean[]>(
Array(queryResponse.data?.payload?.data.result.length || 0).fill(true),
);
const graphRef = useRef<HTMLDivElement>(null); const graphRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
if (!lineChartRef.current) return; if (!lineChartRef.current) return;
graphVisibiltyState.forEach((state, index) => { graphVisibility.forEach((state, index) => {
lineChartRef.current?.toggleGraph(index, state); lineChartRef.current?.toggleGraph(index, state);
}); });
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@ -210,7 +210,7 @@ function WidgetGraphComponent({
graphVisibilityStates: localStoredVisibilityState, graphVisibilityStates: localStoredVisibilityState,
} = getLocalStorageGraphVisibilityState({ } = getLocalStorageGraphVisibilityState({
apiResponse: queryResponse.data.payload.data.result, apiResponse: queryResponse.data.payload.data.result,
name, name: widget.id,
}); });
setGraphVisibility(localStoredVisibilityState); setGraphVisibility(localStoredVisibilityState);
} }
@ -252,7 +252,7 @@ function WidgetGraphComponent({
onBlur={(): void => { onBlur={(): void => {
setHovered(false); setHovered(false);
}} }}
id={name} id={widget.id}
> >
<Modal <Modal
destroyOnClose destroyOnClose
@ -278,14 +278,12 @@ function WidgetGraphComponent({
className="widget-full-view" className="widget-full-view"
> >
<FullView <FullView
name={`${name}expanded`} name={`${widget.id}expanded`}
version={version} version={version}
originalName={name} originalName={widget.id}
widget={widget} widget={widget}
yAxisUnit={widget.yAxisUnit} yAxisUnit={widget.yAxisUnit}
onToggleModelHandler={onToggleModelHandler} onToggleModelHandler={onToggleModelHandler}
parentChartRef={lineChartRef}
onDragSelect={onDragSelect}
/> />
</Modal> </Modal>
@ -305,26 +303,22 @@ function WidgetGraphComponent({
isFetchingResponse={isFetchingResponse} isFetchingResponse={isFetchingResponse}
/> />
</div> </div>
{queryResponse.isLoading && <Skeleton />} {queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
<Skeleton />
)}
{(queryResponse.isSuccess || widget.panelTypes === PANEL_TYPES.LIST) && ( {(queryResponse.isSuccess || widget.panelTypes === PANEL_TYPES.LIST) && (
<div <div
className={cx('widget-graph-container', widget.panelTypes)} className={cx('widget-graph-container', widget.panelTypes)}
ref={graphRef} ref={graphRef}
> >
<GridPanelSwitch <PanelWrapper
panelType={widget.panelTypes} widget={widget}
data={data} queryResponse={queryResponse}
name={name} setRequestData={setRequestData}
ref={lineChartRef} setGraphVisibility={setGraphVisibility}
options={options} graphVisibility={graphVisibility}
yAxisUnit={widget.yAxisUnit}
onClickHandler={onClickHandler} onClickHandler={onClickHandler}
panelData={queryResponse.data?.payload?.data.newResult.data.result || []} onDragSelect={onDragSelect}
query={widget.query}
thresholds={widget.thresholds}
selectedLogFields={widget.selectedLogFields}
dataSource={widget.query.builder?.queryData[0]?.dataSource}
selectedTracesFields={widget.selectedTracesFields}
/> />
</div> </div>
)} )}

View File

@ -4,80 +4,43 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config'; import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange'; import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { useIntersectionObserver } from 'hooks/useIntersectionObserver'; import { useIntersectionObserver } from 'hooks/useIntersectionObserver';
import useUrlQuery from 'hooks/useUrlQuery';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import GetMinMax from 'lib/getMinMax'; import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import getTimeString from 'lib/getTimeString'; 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 isEmpty from 'lodash-es/isEmpty';
import _noop from 'lodash-es/noop';
import { useDashboard } from 'providers/Dashboard/Dashboard'; 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 { useDispatch, useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions'; import { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime'; import { GlobalReducer } from 'types/reducer/globalTime';
import { getGraphType } from 'utils/getGraphType'; import { getGraphType } from 'utils/getGraphType';
import { getSortedSeriesData } from 'utils/getSortedSeriesData'; import { getSortedSeriesData } from 'utils/getSortedSeriesData';
import { getTimeRange } from 'utils/getTimeRange';
import EmptyWidget from '../EmptyWidget'; import EmptyWidget from '../EmptyWidget';
import { MenuItemKeys } from '../WidgetHeader/contants'; import { MenuItemKeys } from '../WidgetHeader/contants';
import { GridCardGraphProps } from './types'; import { GridCardGraphProps } from './types';
import { getLocalStorageGraphVisibilityState } from './utils';
import WidgetGraphComponent from './WidgetGraphComponent'; import WidgetGraphComponent from './WidgetGraphComponent';
function GridCardGraph({ function GridCardGraph({
widget, widget,
name,
onClickHandler = _noop,
headerMenuList = [MenuItemKeys.View], headerMenuList = [MenuItemKeys.View],
isQueryEnabled, isQueryEnabled,
threshold, threshold,
variables, variables,
fillSpans = false,
version, version,
onClickHandler,
onDragSelect,
}: GridCardGraphProps): JSX.Element { }: GridCardGraphProps): JSX.Element {
const dispatch = useDispatch(); const dispatch = useDispatch();
const [errorMessage, setErrorMessage] = useState<string>(); const [errorMessage, setErrorMessage] = useState<string>();
const { toScrollWidgetId, setToScrollWidgetId } = useDashboard(); const { toScrollWidgetId, setToScrollWidgetId } = useDashboard();
const [minTimeScale, setMinTimeScale] = useState<number>();
const [maxTimeScale, setMaxTimeScale] = useState<number>();
const urlQuery = useUrlQuery();
const location = useLocation();
const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector< const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
AppState, AppState,
GlobalReducer GlobalReducer
>((state) => state.globalTime); >((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 handleBackNavigation = (): void => {
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);
const startTime = searchParams.get(QueryParams.startTime); const startTime = searchParams.get(QueryParams.startTime);
@ -127,19 +90,39 @@ function GridCardGraph({
const isEmptyWidget = const isEmptyWidget =
widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget); widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget);
const queryEnabledCondition = const queryEnabledCondition = isVisible && !isEmptyWidget && isQueryEnabled;
isVisible &&
!isEmptyWidget &&
isQueryEnabled &&
widget.panelTypes !== PANEL_TYPES.LIST;
const queryResponse = useGetQueryRange( const [requestData, setRequestData] = useState<GetQueryResultsProps>(() => {
{ if (widget.panelTypes !== PANEL_TYPES.LIST) {
return {
selectedTime: widget?.timePreferance, selectedTime: widget?.timePreferance,
graphType: getGraphType(widget.panelTypes), graphType: getGraphType(widget.panelTypes),
query: updatedQuery, query: updatedQuery,
globalSelectedInterval, globalSelectedInterval,
variables: getDashboardVariables(variables), 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(
{
...requestData,
variables: getDashboardVariables(variables),
selectedTime: 'GLOBAL_TIME',
globalSelectedInterval,
}, },
version || DEFAULT_ENTITY_VERSION, version || DEFAULT_ENTITY_VERSION,
{ {
@ -151,6 +134,7 @@ function GridCardGraph({
widget?.query, widget?.query,
widget?.panelTypes, widget?.panelTypes,
widget.timePreferance, widget.timePreferance,
requestData,
], ],
retry(failureCount, error): boolean { retry(failureCount, error): boolean {
if ( if (
@ -173,15 +157,6 @@ function GridCardGraph({
const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET; 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) { if (queryResponse.data && widget.panelTypes === PANEL_TYPES.BAR) {
const sortedSeriesData = getSortedSeriesData( const sortedSeriesData = getSortedSeriesData(
queryResponse.data?.payload.data.result, queryResponse.data?.payload.data.result,
@ -189,89 +164,29 @@ function GridCardGraph({
queryResponse.data.payload.data.result = sortedSeriesData; queryResponse.data.payload.data.result = sortedSeriesData;
} }
const chartData = getUPlotChartData(queryResponse?.data?.payload, fillSpans);
const isDarkMode = useIsDarkMode();
const menuList = const menuList =
widget.panelTypes === PANEL_TYPES.TABLE || widget.panelTypes === PANEL_TYPES.TABLE ||
widget.panelTypes === PANEL_TYPES.LIST widget.panelTypes === PANEL_TYPES.LIST
? headerMenuList.filter((menu) => menu !== MenuItemKeys.CreateAlerts) ? headerMenuList.filter((menu) => menu !== MenuItemKeys.CreateAlerts)
: headerMenuList; : headerMenuList;
const [graphVisibility, setGraphVisibility] = useState<boolean[]>(
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 ( return (
<div style={{ height: '100%', width: '100%' }} ref={graphRef}> <div style={{ height: '100%', width: '100%' }} ref={graphRef}>
{isEmptyLayout ? ( {isEmptyLayout ? (
<EmptyWidget /> <EmptyWidget />
) : ( ) : (
<WidgetGraphComponent <WidgetGraphComponent
data={chartData}
options={options}
widget={widget} widget={widget}
queryResponse={queryResponse} queryResponse={queryResponse}
errorMessage={errorMessage} errorMessage={errorMessage}
isWarning={false} isWarning={false}
name={name}
version={version} version={version}
onDragSelect={onDragSelect}
threshold={threshold} threshold={threshold}
headerMenuList={menuList} headerMenuList={menuList}
onClickHandler={onClickHandler}
graphVisibiltyState={graphVisibility}
setGraphVisibility={setGraphVisibility}
isFetchingResponse={queryResponse.isFetching} isFetchingResponse={queryResponse.isFetching}
setRequestData={setRequestData}
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
/> />
)} )}
</div> </div>

View File

@ -1,9 +1,9 @@
import { ToggleGraphProps } from 'components/Graph/types'; 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 { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react'; import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
import { UseQueryResult } from 'react-query'; 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 { Dashboard, Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import uPlot from 'uplot'; import uPlot from 'uplot';
@ -16,35 +16,32 @@ export interface GraphVisibilityLegendEntryProps {
legendEntry: LegendEntryProps[]; legendEntry: LegendEntryProps[];
} }
export interface WidgetGraphComponentProps extends UplotProps { export interface WidgetGraphComponentProps {
widget: Widgets; widget: Widgets;
queryResponse: UseQueryResult< queryResponse: UseQueryResult<
SuccessResponse<MetricRangePayloadProps> | ErrorResponse SuccessResponse<MetricRangePayloadProps, unknown>,
Error
>; >;
errorMessage: string | undefined; errorMessage: string | undefined;
name: string;
version?: string; version?: string;
onDragSelect: (start: number, end: number) => void;
onClickHandler?: OnClickPluginOpts['onClick'];
threshold?: ReactNode; threshold?: ReactNode;
headerMenuList: MenuItemKeys[]; headerMenuList: MenuItemKeys[];
isWarning: boolean; isWarning: boolean;
graphVisibiltyState: boolean[];
setGraphVisibility: Dispatch<SetStateAction<boolean[]>>;
isFetchingResponse: boolean; isFetchingResponse: boolean;
setRequestData?: Dispatch<SetStateAction<GetQueryResultsProps>>;
onClickHandler?: OnClickPluginOpts['onClick'];
onDragSelect: (start: number, end: number) => void;
} }
export interface GridCardGraphProps { export interface GridCardGraphProps {
widget: Widgets; widget: Widgets;
name: string;
onDragSelect?: (start: number, end: number) => void;
onClickHandler?: OnClickPluginOpts['onClick'];
threshold?: ReactNode; threshold?: ReactNode;
headerMenuList?: WidgetGraphComponentProps['headerMenuList']; headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
onClickHandler?: OnClickPluginOpts['onClick'];
isQueryEnabled: boolean; isQueryEnabled: boolean;
variables?: Dashboard['data']['variables']; variables?: Dashboard['data']['variables'];
fillSpans?: boolean;
version?: string; version?: string;
onDragSelect: (start: number, end: number) => void;
} }
export interface GetGraphVisibilityStateOnLegendClickProps { export interface GetGraphVisibilityStateOnLegendClickProps {

View File

@ -3,20 +3,25 @@ import './GridCardLayout.styles.scss';
import { PlusOutlined } from '@ant-design/icons'; import { PlusOutlined } from '@ant-design/icons';
import { Tooltip } from 'antd'; import { Tooltip } from 'antd';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { themeColors } from 'constants/theme'; import { themeColors } from 'constants/theme';
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
import useComponentPermission from 'hooks/useComponentPermission'; import useComponentPermission from 'hooks/useComponentPermission';
import { useIsDarkMode } from 'hooks/useDarkMode'; import { useIsDarkMode } from 'hooks/useDarkMode';
import { useNotifications } from 'hooks/useNotifications'; import { useNotifications } from 'hooks/useNotifications';
import useUrlQuery from 'hooks/useUrlQuery';
import history from 'lib/history';
import isEqual from 'lodash-es/isEqual'; import isEqual from 'lodash-es/isEqual';
import { FullscreenIcon } from 'lucide-react'; import { FullscreenIcon } from 'lucide-react';
import { useDashboard } from 'providers/Dashboard/Dashboard'; 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 { FullScreen, useFullScreenHandle } from 'react-full-screen';
import { Layout } from 'react-grid-layout'; import { Layout } from 'react-grid-layout';
import { useTranslation } from 'react-i18next'; 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 { AppState } from 'store/reducers';
import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
@ -45,6 +50,8 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
} = useDashboard(); } = useDashboard();
const { data } = selectedDashboard || {}; const { data } = selectedDashboard || {};
const handle = useFullScreenHandle(); const handle = useFullScreenHandle();
const { pathname } = useLocation();
const dispatch = useDispatch();
const { widgets, variables } = data || {}; const { widgets, variables } = data || {};
@ -61,6 +68,7 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
const updateDashboardMutation = useUpdateDashboard(); const updateDashboardMutation = useUpdateDashboard();
const { notifications } = useNotifications(); const { notifications } = useNotifications();
const urlQuery = useUrlQuery();
let permissions: ComponentTypes[] = ['save_layout', 'add_panel']; 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(() => { useEffect(() => {
if ( if (
dashboardLayout && dashboardLayout &&
@ -200,11 +225,10 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
> >
<GridCard <GridCard
widget={currentWidget || ({ id, query: {} } as Widgets)} widget={currentWidget || ({ id, query: {} } as Widgets)}
name={currentWidget?.id || ''}
headerMenuList={widgetActions} headerMenuList={widgetActions}
variables={variables} variables={variables}
fillSpans={currentWidget?.fillSpans}
version={selectedDashboard?.data?.version} version={selectedDashboard?.data?.version}
onDragSelect={onDragSelect}
/> />
</Card> </Card>
</CardContainer> </CardContainer>

View File

@ -1,10 +1,8 @@
import { ToggleGraphProps } from 'components/Graph/types'; import { ToggleGraphProps } from 'components/Graph/types';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { getComponentForPanelType } from 'constants/panelTypes'; import { getComponentForPanelType } from 'constants/panelTypes';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config'; import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
import { FC, forwardRef, memo, useMemo } from 'react'; import { FC, forwardRef, memo, useMemo } from 'react';
import { DataSource } from 'types/common/queryBuilder';
import { GridPanelSwitchProps, PropsTypePropsMap } from './types'; import { GridPanelSwitchProps, PropsTypePropsMap } from './types';
@ -21,10 +19,7 @@ const GridPanelSwitch = forwardRef<
query, query,
options, options,
thresholds, thresholds,
selectedLogFields,
selectedTracesFields,
dataSource, dataSource,
selectedTime,
}, },
ref, ref,
): JSX.Element | null => { ): JSX.Element | null => {
@ -46,20 +41,7 @@ const GridPanelSwitch = forwardRef<
query, query,
thresholds, thresholds,
}, },
[PANEL_TYPES.LIST]: [PANEL_TYPES.LIST]: null,
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.TRACE]: null, [PANEL_TYPES.TRACE]: null,
[PANEL_TYPES.BAR]: { [PANEL_TYPES.BAR]: {
data, data,
@ -70,19 +52,7 @@ const GridPanelSwitch = forwardRef<
}; };
return result; return result;
}, [ }, [data, options, ref, yAxisUnit, thresholds, panelData, query]);
data,
options,
ref,
yAxisUnit,
thresholds,
panelData,
query,
dataSource,
selectedLogFields,
selectedTime,
selectedTracesFields,
]);
const Component = getComponentForPanelType(panelType, dataSource) as FC< const Component = getComponentForPanelType(panelType, dataSource) as FC<
PropsTypePropsMap[typeof panelType] PropsTypePropsMap[typeof panelType]

View File

@ -2,9 +2,7 @@ import { StaticLineProps, ToggleGraphProps } from 'components/Graph/types';
import { UplotProps } from 'components/Uplot/Uplot'; import { UplotProps } from 'components/Uplot/Uplot';
import { GridTableComponentProps } from 'container/GridTableComponent/types'; import { GridTableComponentProps } from 'container/GridTableComponent/types';
import { GridValueComponentProps } from 'container/GridValueComponent/types'; import { GridValueComponentProps } from 'container/GridValueComponent/types';
import { LogsPanelComponentProps } from 'container/LogsPanelTable/LogsPanelComponent';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems'; import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import { TracesTableComponentProps } from 'container/TracesTableComponent/TracesTableComponent';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin'; import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { ForwardedRef } from 'react'; import { ForwardedRef } from 'react';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
@ -40,7 +38,7 @@ export type PropsTypePropsMap = {
[PANEL_TYPES.VALUE]: GridValueComponentProps; [PANEL_TYPES.VALUE]: GridValueComponentProps;
[PANEL_TYPES.TABLE]: GridTableComponentProps; [PANEL_TYPES.TABLE]: GridTableComponentProps;
[PANEL_TYPES.TRACE]: null; [PANEL_TYPES.TRACE]: null;
[PANEL_TYPES.LIST]: LogsPanelComponentProps | TracesTableComponentProps; [PANEL_TYPES.LIST]: null;
[PANEL_TYPES.BAR]: UplotProps & { [PANEL_TYPES.BAR]: UplotProps & {
ref: ForwardedRef<ToggleGraphProps | undefined>; ref: ForwardedRef<ToggleGraphProps | undefined>;
}; };

View File

@ -4,82 +4,53 @@ import { Table } from 'antd';
import LogDetail from 'components/LogDetail'; import LogDetail from 'components/LogDetail';
import { VIEW_TYPES } from 'components/LogDetail/constants'; import { VIEW_TYPES } from 'components/LogDetail/constants';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';
import { DEFAULT_ENTITY_VERSION } from 'constants/app'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { OPERATORS, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import Controls from 'container/Controls'; import Controls from 'container/Controls';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs'; import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
import { tableStyles } from 'container/TracesExplorer/ListView/styles'; import { tableStyles } from 'container/TracesExplorer/ListView/styles';
import { useActiveLog } from 'hooks/logs/useActiveLog'; import { useActiveLog } from 'hooks/logs/useActiveLog';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { Pagination } from 'hooks/queryPagination'; import { Pagination } from 'hooks/queryPagination';
import { useLogsData } from 'hooks/useLogsData'; import { useLogsData } from 'hooks/useLogsData';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults'; import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { FlatLogData } from 'lib/logs/flatLogData'; import { FlatLogData } from 'lib/logs/flatLogData';
import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { import {
Dispatch,
HTMLAttributes, HTMLAttributes,
SetStateAction,
useCallback, useCallback,
useEffect, useEffect,
useMemo, useMemo,
useState, useState,
} from 'react'; } from 'react';
import { useSelector } from 'react-redux'; import { UseQueryResult } from 'react-query';
import { AppState } from 'store/reducers'; import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { ILog } from 'types/api/logs/log'; import { ILog } from 'types/api/logs/log';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { GlobalReducer } from 'types/reducer/globalTime';
import { v4 as uuid } from 'uuid';
import { getLogPanelColumnsList } from './utils'; import { getLogPanelColumnsList, getNextOrPreviousItems } from './utils';
function LogsPanelComponent({ function LogsPanelComponent({
selectedLogsFields, widget,
query, setRequestData,
selectedTime, queryResponse,
}: LogsPanelComponentProps): JSX.Element { }: LogsPanelComponentProps): JSX.Element {
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
const [pagination, setPagination] = useState<Pagination>({ const [pagination, setPagination] = useState<Pagination>({
offset: 0, offset: 0,
limit: query.builder.queryData[0].limit || 0, limit: widget.query.builder.queryData[0].limit || 0,
});
const [requestData, setRequestData] = useState<GetQueryResultsProps>(() => {
const updatedQuery = { ...query };
updatedQuery.builder.queryData[0].pageSize = 10;
return {
query: updatedQuery,
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
globalSelectedInterval: globalSelectedTime,
tableParams: {
pagination,
},
};
}); });
useEffect(() => { useEffect(() => {
setRequestData({ setRequestData((prev) => ({
...requestData, ...prev,
globalSelectedInterval: globalSelectedTime,
tableParams: { tableParams: {
pagination, pagination,
}, },
}); }));
// eslint-disable-next-line react-hooks/exhaustive-deps }, [pagination, setRequestData]);
}, [pagination]);
const [pageSize, setPageSize] = useState<number>(10); const [pageSize, setPageSize] = useState<number>(10);
const { selectedDashboard } = useDashboard();
const handleChangePageSize = (value: number): void => { const handleChangePageSize = (value: number): void => {
setPagination({ setPagination({
@ -88,53 +59,35 @@ function LogsPanelComponent({
offset: value, offset: value,
}); });
setPageSize(value); setPageSize(value);
const newQueryData = { ...requestData.query }; setRequestData((prev) => {
const newQueryData = { ...prev.query };
newQueryData.builder.queryData[0].pageSize = value; newQueryData.builder.queryData[0].pageSize = value;
const newRequestData = { return {
...requestData, ...prev,
query: newQueryData, query: newQueryData,
tableParams: { tableParams: {
pagination, pagination: {
limit: 0,
offset: value,
},
}, },
}; };
setRequestData(newRequestData); });
}; };
const { data, isFetching, isError } = useGetQueryRange( const columns = getLogPanelColumnsList(widget.selectedLogFields);
{
...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 dataLength = 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 totalCount = useMemo(() => dataLength || 0, [dataLength]);
const [firstLog, setFirstLog] = useState<ILog>(); const [firstLog, setFirstLog] = useState<ILog>();
const [lastLog, setLastLog] = useState<ILog>(); const [lastLog, setLastLog] = useState<ILog>();
const { logs } = useLogsData({ const { logs } = useLogsData({
result: data?.payload.data.newResult.data.result, result: queryResponse.data?.payload.data.newResult.data.result,
panelType: PANEL_TYPES.LIST, panelType: PANEL_TYPES.LIST,
stagedQuery: query, stagedQuery: widget.query,
}); });
useEffect(() => { useEffect(() => {
@ -167,92 +120,86 @@ function LogsPanelComponent({
); );
const isOrderByTimeStamp = const isOrderByTimeStamp =
query.builder.queryData[0].orderBy.length > 0 && widget.query.builder.queryData[0].orderBy.length > 0 &&
query.builder.queryData[0].orderBy[0].columnName === 'timestamp'; widget.query.builder.queryData[0].orderBy[0].columnName === 'timestamp';
const handlePreviousPagination = (): void => { const handlePreviousPagination = (): void => {
if (isOrderByTimeStamp) { if (isOrderByTimeStamp) {
setRequestData({ setRequestData((prev) => ({
...requestData, ...prev,
query: { query: {
...requestData.query, ...prev.query,
builder: { builder: {
...requestData.query.builder, ...prev.query.builder,
queryData: [ queryData: [
{ {
...requestData.query.builder.queryData[0], ...prev.query.builder.queryData[0],
filters: { filters: {
...requestData.query.builder.queryData[0].filters, ...prev.query.builder.queryData[0].filters,
items: [ items: [
{ ...getNextOrPreviousItems(
id: uuid(), prev.query.builder.queryData[0].filters.items,
key: { 'PREV',
key: 'id', firstLog,
type: '', ),
dataType: DataTypes.String, ],
isColumn: true,
}, },
op: OPERATORS['>'], limit: 0,
value: firstLog?.id || '', offset: 0,
}, },
], ],
}, },
}, },
], }));
},
},
});
return;
} }
if (!isOrderByTimeStamp) {
setPagination({ setPagination({
...pagination, ...pagination,
limit: 0, limit: 0,
offset: pagination.offset - pageSize, offset: pagination.offset - pageSize,
}); });
}
}; };
const handleNextPagination = (): void => { const handleNextPagination = (): void => {
if (isOrderByTimeStamp) { if (isOrderByTimeStamp) {
setRequestData({ setRequestData((prev) => ({
...requestData, ...prev,
query: { query: {
...requestData.query, ...prev.query,
builder: { builder: {
...requestData.query.builder, ...prev.query.builder,
queryData: [ queryData: [
{ {
...requestData.query.builder.queryData[0], ...prev.query.builder.queryData[0],
filters: { filters: {
...requestData.query.builder.queryData[0].filters, ...prev.query.builder.queryData[0].filters,
items: [ items: [
{ ...getNextOrPreviousItems(
id: uuid(), prev.query.builder.queryData[0].filters.items,
key: { 'NEXT',
key: 'id', lastLog,
type: '', ),
dataType: DataTypes.String, ],
isColumn: true,
}, },
op: OPERATORS['<'], limit: 0,
value: lastLog?.id || '', offset: 0,
}, },
], ],
}, },
}, },
], }));
},
},
});
return;
} }
if (!isOrderByTimeStamp) {
setPagination({ setPagination({
...pagination, ...pagination,
limit: 0, limit: 0,
offset: pagination.offset + pageSize, offset: pagination.offset + pageSize,
}); });
}
}; };
if (isError) { if (queryResponse.isError) {
return <div>{SOMETHING_WENT_WRONG}</div>; return <div>{SOMETHING_WENT_WRONG}</div>;
} }
@ -265,19 +212,19 @@ function LogsPanelComponent({
tableLayout="fixed" tableLayout="fixed"
scroll={{ x: `calc(50vw - 10px)` }} scroll={{ x: `calc(50vw - 10px)` }}
sticky sticky
loading={isFetching} loading={queryResponse.isFetching}
style={tableStyles} style={tableStyles}
dataSource={flattenLogData} dataSource={flattenLogData}
columns={columns} columns={columns}
onRow={handleRow} onRow={handleRow}
/> />
</div> </div>
{!query.builder.queryData[0].limit && ( {!widget.query.builder.queryData[0].limit && (
<div className="controller"> <div className="controller">
<Controls <Controls
totalCount={totalCount} totalCount={totalCount}
perPageOptions={PER_PAGE_OPTIONS} perPageOptions={PER_PAGE_OPTIONS}
isLoading={isFetching} isLoading={queryResponse.isFetching}
offset={pagination.offset} offset={pagination.offset}
countPerPage={pageSize} countPerPage={pageSize}
handleNavigatePrevious={handlePreviousPagination} handleNavigatePrevious={handlePreviousPagination}
@ -301,13 +248,12 @@ function LogsPanelComponent({
} }
export type LogsPanelComponentProps = { export type LogsPanelComponentProps = {
selectedLogsFields: Widgets['selectedLogFields']; setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
query: Query; queryResponse: UseQueryResult<
selectedTime?: timePreferance; SuccessResponse<MetricRangePayloadProps, unknown>,
}; Error
>;
LogsPanelComponent.defaultProps = { widget: Widgets;
selectedTime: undefined,
}; };
export default LogsPanelComponent; export default LogsPanelComponent;

View File

@ -1,10 +1,15 @@
import { ColumnsType } from 'antd/es/table'; import { ColumnsType } from 'antd/es/table';
import { Typography } from 'antd/lib'; import { Typography } from 'antd/lib';
import { OPERATORS } from 'constants/queryBuilder';
// import Typography from 'antd/es/typography/Typography'; // import Typography from 'antd/es/typography/Typography';
import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { IField } from 'types/api/logs/fields'; 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 = ( export const getLogPanelColumnsList = (
selectedLogFields: Widgets['selectedLogFields'], selectedLogFields: Widgets['selectedLogFields'],
@ -36,3 +41,49 @@ export const getLogPanelColumnsList = (
return [...initialColumns, ...columns]; 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;
};

View File

@ -8,6 +8,7 @@ export const getWidgetQueryBuilder = ({
title = '', title = '',
panelTypes, panelTypes,
yAxisUnit = '', yAxisUnit = '',
fillSpans = false,
id, id,
}: GetWidgetQueryBuilderProps): Widgets => ({ }: GetWidgetQueryBuilderProps): Widgets => ({
description: '', description: '',
@ -24,4 +25,5 @@ export const getWidgetQueryBuilder = ({
softMin: null, softMin: null,
selectedLogFields: [], selectedLogFields: [],
selectedTracesFields: [], selectedTracesFields: [],
fillSpans,
}); });

View File

@ -70,6 +70,7 @@ function DBCall(): JSX.Element {
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'reqps', yAxisUnit: 'reqps',
id: SERVICE_CHART_ID.dbCallsRPS, id: SERVICE_CHART_ID.dbCallsRPS,
fillSpans: false,
}), }),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -89,7 +90,8 @@ function DBCall(): JSX.Element {
title: GraphTitle.DATABASE_CALLS_AVG_DURATION, title: GraphTitle.DATABASE_CALLS_AVG_DURATION,
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'ms', yAxisUnit: 'ms',
id: SERVICE_CHART_ID.dbCallsAvgDuration, id: GraphTitle.DATABASE_CALLS_AVG_DURATION,
fillSpans: true,
}), }),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -112,8 +114,6 @@ function DBCall(): JSX.Element {
<Card data-testid="database_call_rps"> <Card data-testid="database_call_rps">
<GraphContainer> <GraphContainer>
<Graph <Graph
fillSpans={false}
name="database_call_rps"
widget={databaseCallsRPSWidget} widget={databaseCallsRPSWidget}
onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
onGraphClickHandler(setSelectedTimeStamp)( onGraphClickHandler(setSelectedTimeStamp)(
@ -147,8 +147,6 @@ function DBCall(): JSX.Element {
<Card data-testid="database_call_avg_duration"> <Card data-testid="database_call_avg_duration">
<GraphContainer> <GraphContainer>
<Graph <Graph
fillSpans
name="database_call_avg_duration"
widget={databaseCallsAverageDurationWidget} widget={databaseCallsAverageDurationWidget}
headerMenuList={MENU_ITEMS} headerMenuList={MENU_ITEMS}
onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onClickHandler={(xValue, yValue, mouseX, mouseY): void => {

View File

@ -18,7 +18,7 @@ import { useParams } from 'react-router-dom';
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import { v4 as uuid } from 'uuid'; 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 { getWidgetQueryBuilder } from '../MetricsApplication.factory';
import { Card, GraphContainer, Row } from '../styles'; import { Card, GraphContainer, Row } from '../styles';
import { Button } from './styles'; import { Button } from './styles';
@ -60,7 +60,7 @@ function External(): JSX.Element {
title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE, title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: '%', yAxisUnit: '%',
id: SERVICE_CHART_ID.externalCallErrorPercentage, id: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
}), }),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -86,7 +86,8 @@ function External(): JSX.Element {
title: GraphTitle.EXTERNAL_CALL_DURATION, title: GraphTitle.EXTERNAL_CALL_DURATION,
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'ms', yAxisUnit: 'ms',
id: SERVICE_CHART_ID.externalCallDuration, id: GraphTitle.EXTERNAL_CALL_DURATION,
fillSpans: true,
}), }),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -108,7 +109,8 @@ function External(): JSX.Element {
title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS, title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'reqps', yAxisUnit: 'reqps',
id: SERVICE_CHART_ID.externalCallRPSByAddress, id: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
fillSpans: true,
}), }),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -130,7 +132,8 @@ function External(): JSX.Element {
title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS, title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'ms', yAxisUnit: 'ms',
id: SERVICE_CHART_ID.externalCallDurationByAddress, id: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
fillSpans: true,
}), }),
[servicename, tagFilterItems], [servicename, tagFilterItems],
); );
@ -155,9 +158,7 @@ function External(): JSX.Element {
<Card data-testid="external_call_error_percentage"> <Card data-testid="external_call_error_percentage">
<GraphContainer> <GraphContainer>
<Graph <Graph
fillSpans={false}
headerMenuList={MENU_ITEMS} headerMenuList={MENU_ITEMS}
name="external_call_error_percentage"
widget={externalCallErrorWidget} widget={externalCallErrorWidget}
onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
onGraphClickHandler(setSelectedTimeStamp)( onGraphClickHandler(setSelectedTimeStamp)(
@ -192,8 +193,6 @@ function External(): JSX.Element {
<Card data-testid="external_call_duration"> <Card data-testid="external_call_duration">
<GraphContainer> <GraphContainer>
<Graph <Graph
fillSpans
name="external_call_duration"
headerMenuList={MENU_ITEMS} headerMenuList={MENU_ITEMS}
widget={externalCallDurationWidget} widget={externalCallDurationWidget}
onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
@ -230,8 +229,6 @@ function External(): JSX.Element {
<Card data-testid="external_call_rps_by_address"> <Card data-testid="external_call_rps_by_address">
<GraphContainer> <GraphContainer>
<Graph <Graph
fillSpans
name="external_call_rps_by_address"
widget={externalCallRPSWidget} widget={externalCallRPSWidget}
headerMenuList={MENU_ITEMS} headerMenuList={MENU_ITEMS}
onClickHandler={(xValue, yValue, mouseX, mouseY): Promise<void> => onClickHandler={(xValue, yValue, mouseX, mouseY): Promise<void> =>
@ -267,10 +264,8 @@ function External(): JSX.Element {
<Card data-testid="external_call_duration_by_address"> <Card data-testid="external_call_duration_by_address">
<GraphContainer> <GraphContainer>
<Graph <Graph
name="external_call_duration_by_address"
widget={externalCallDurationAddressWidget} widget={externalCallDurationAddressWidget}
headerMenuList={MENU_ITEMS} headerMenuList={MENU_ITEMS}
fillSpans
onClickHandler={(xValue, yValue, mouseX, mouseY): void => { onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
onGraphClickHandler(setSelectedTimeStamp)( onGraphClickHandler(setSelectedTimeStamp)(
xValue, xValue,

View File

@ -19,13 +19,10 @@ import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { defaultTo } from 'lodash-es'; import { defaultTo } from 'lodash-es';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { useQuery } from 'react-query'; import { useQuery } from 'react-query';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch } from 'react-redux';
import { useLocation, useParams } from 'react-router-dom'; import { useLocation, useParams } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions'; import { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers';
import { EQueryType } from 'types/common/dashboard'; 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 { v4 as uuid } from 'uuid';
import { GraphTitle, SERVICE_CHART_ID } from '../constant'; import { GraphTitle, SERVICE_CHART_ID } from '../constant';
@ -49,9 +46,6 @@ import {
} from './util'; } from './util';
function Application(): JSX.Element { function Application(): JSX.Element {
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { servicename: encodedServiceName } = useParams<IServiceName>(); const { servicename: encodedServiceName } = useParams<IServiceName>();
const servicename = decodeURIComponent(encodedServiceName); const servicename = decodeURIComponent(encodedServiceName);
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0); const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
@ -59,10 +53,6 @@ function Application(): JSX.Element {
const { queries } = useResourceAttribute(); const { queries } = useResourceAttribute();
const urlQuery = useUrlQuery(); const urlQuery = useUrlQuery();
const selectedTags = useMemo(
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
[queries],
);
const isSpanMetricEnabled = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS) const isSpanMetricEnabled = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS)
?.active; ?.active;
@ -94,7 +84,7 @@ function Application(): JSX.Element {
isLoading: topLevelOperationsIsLoading, isLoading: topLevelOperationsIsLoading,
isError: topLevelOperationsIsError, isError: topLevelOperationsIsError,
} = useQuery<ServiceDataProps>({ } = useQuery<ServiceDataProps>({
queryKey: [servicename, minTime, maxTime, selectedTags], queryKey: [servicename],
queryFn: getTopLevelOperations, queryFn: getTopLevelOperations,
}); });
@ -116,9 +106,7 @@ function Application(): JSX.Element {
[servicename, topLevelOperations], [servicename, topLevelOperations],
); );
const operationPerSecWidget = useMemo( const operationPerSecWidget = getWidgetQueryBuilder({
() =>
getWidgetQueryBuilder({
query: { query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
@ -134,13 +122,9 @@ function Application(): JSX.Element {
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'ops', yAxisUnit: 'ops',
id: SERVICE_CHART_ID.rps, id: SERVICE_CHART_ID.rps,
}), });
[servicename, tagFilterItems, topLevelOperationsRoute],
);
const errorPercentageWidget = useMemo( const errorPercentageWidget = getWidgetQueryBuilder({
() =>
getWidgetQueryBuilder({
query: { query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
@ -156,9 +140,7 @@ function Application(): JSX.Element {
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: '%', yAxisUnit: '%',
id: SERVICE_CHART_ID.errorPercentage, id: SERVICE_CHART_ID.errorPercentage,
}), });
[servicename, tagFilterItems, topLevelOperationsRoute],
);
const onDragSelect = useCallback( const onDragSelect = useCallback(
(start: number, end: number) => { (start: number, end: number) => {

View File

@ -89,8 +89,6 @@ function ApDexMetrics({
return ( return (
<Graph <Graph
name="apdex"
fillSpans={false}
widget={apDexMetricsWidget} widget={apDexMetricsWidget}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('ApDex')} onClickHandler={handleGraphClick('ApDex')}

View File

@ -50,7 +50,6 @@ function ApDexTraces({
return ( return (
<Graph <Graph
name="apdex"
widget={apDexTracesWidget} widget={apDexTracesWidget}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('ApDex')} onClickHandler={handleGraphClick('ApDex')}

View File

@ -1,3 +1,4 @@
import { Skeleton } from 'antd';
import { ENTITY_VERSION_V4 } from 'constants/app'; import { ENTITY_VERSION_V4 } from 'constants/app';
import { FeatureKeys } from 'constants/features'; import { FeatureKeys } from 'constants/features';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
@ -46,9 +47,7 @@ function ServiceOverview({
[isSpanMetricEnable, queries], [isSpanMetricEnable, queries],
); );
const latencyWidget = useMemo( const latencyWidget = getWidgetQueryBuilder({
() =>
getWidgetQueryBuilder({
query: { query: {
queryType: EQueryType.QUERY_BUILDER, queryType: EQueryType.QUERY_BUILDER,
promql: [], promql: [],
@ -65,9 +64,7 @@ function ServiceOverview({
panelTypes: PANEL_TYPES.TIME_SERIES, panelTypes: PANEL_TYPES.TIME_SERIES,
yAxisUnit: 'ns', yAxisUnit: 'ns',
id: SERVICE_CHART_ID.latency, id: SERVICE_CHART_ID.latency,
}), });
[servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems],
);
const isQueryEnabled = const isQueryEnabled =
!topLevelOperationsIsLoading && topLevelOperationsRoute.length > 0; !topLevelOperationsIsLoading && topLevelOperationsRoute.length > 0;
@ -88,15 +85,23 @@ function ServiceOverview({
</Button> </Button>
<Card data-testid="service_latency"> <Card data-testid="service_latency">
<GraphContainer> <GraphContainer>
{topLevelOperationsIsLoading && (
<Skeleton
style={{
height: '100%',
padding: '16px',
}}
/>
)}
{!topLevelOperationsIsLoading && (
<Graph <Graph
name="service_latency"
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
widget={latencyWidget} widget={latencyWidget}
onClickHandler={handleGraphClick('Service')} onClickHandler={handleGraphClick('Service')}
isQueryEnabled={isQueryEnabled} isQueryEnabled={isQueryEnabled}
fillSpans={false}
version={ENTITY_VERSION_V4} version={ENTITY_VERSION_V4}
/> />
)}
</GraphContainer> </GraphContainer>
</Card> </Card>
</> </>

View File

@ -1,4 +1,4 @@
import { Typography } from 'antd'; import { Skeleton, Typography } from 'antd';
import axios from 'axios'; import axios from 'axios';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';
import { ENTITY_VERSION_V4 } from 'constants/app'; import { ENTITY_VERSION_V4 } from 'constants/app';
@ -27,15 +27,23 @@ function TopLevelOperation({
</Typography> </Typography>
) : ( ) : (
<GraphContainer> <GraphContainer>
{topLevelOperationsIsLoading && (
<Skeleton
style={{
height: '100%',
padding: '16px',
}}
/>
)}
{!topLevelOperationsIsLoading && (
<Graph <Graph
fillSpans={false}
name={name}
widget={widget} widget={widget}
onClickHandler={handleGraphClick(opName)} onClickHandler={handleGraphClick(opName)}
onDragSelect={onDragSelect} onDragSelect={onDragSelect}
isQueryEnabled={!topLevelOperationsIsLoading} isQueryEnabled={!topLevelOperationsIsLoading}
version={ENTITY_VERSION_V4} version={ENTITY_VERSION_V4}
/> />
)}
</GraphContainer> </GraphContainer>
)} )}
</Card> </Card>

View File

@ -13,7 +13,7 @@ export const Card = styled(CardComponent)`
} }
.ant-card-body { .ant-card-body {
height: calc(100% - 40px); height: 100%;
padding: 0; padding: 0;
} }
`; `;
@ -40,7 +40,7 @@ export const ColErrorContainer = styled(ColComponent)`
export const GraphContainer = styled.div` export const GraphContainer = styled.div`
min-height: calc(40vh - 40px); min-height: calc(40vh - 40px);
height: calc(100% - 40px); height: 100%;
`; `;
export const GraphTitle = styled(Typography)` export const GraphTitle = styled(Typography)`

View File

@ -10,6 +10,7 @@ export interface GetWidgetQueryBuilderProps {
panelTypes: Widgets['panelTypes']; panelTypes: Widgets['panelTypes'];
yAxisUnit?: Widgets['yAxisUnit']; yAxisUnit?: Widgets['yAxisUnit'];
id?: Widgets['id']; id?: Widgets['id'];
fillSpans?: Widgets['fillSpans'];
} }
export interface NavigateToTraceProps { export interface NavigateToTraceProps {

View File

@ -2,14 +2,11 @@ import './QuerySection.styles.scss';
import { Button, Tabs, Tooltip, Typography } from 'antd'; import { Button, Tabs, Tooltip, Typography } from 'antd';
import TextToolTip from 'components/TextToolTip'; import TextToolTip from 'components/TextToolTip';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts'; import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
import { WidgetGraphProps } from 'container/NewWidget/types';
import { QueryBuilder } from 'container/QueryBuilder'; import { QueryBuilder } from 'container/QueryBuilder';
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces'; import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys'; import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl'; import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval'; import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
@ -22,9 +19,12 @@ import {
getSelectedWidgetIndex, getSelectedWidgetIndex,
} from 'providers/Dashboard/util'; } from 'providers/Dashboard/util';
import { useCallback, useEffect, useMemo } from 'react'; import { useCallback, useEffect, useMemo } from 'react';
import { UseQueryResult } from 'react-query';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers'; import { AppState } from 'store/reducers';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { EQueryType } from 'types/common/dashboard'; import { EQueryType } from 'types/common/dashboard';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
@ -35,7 +35,7 @@ import PromQLQueryContainer from './QueryBuilder/promQL';
function QuerySection({ function QuerySection({
selectedGraph, selectedGraph,
selectedTime, queryResponse,
}: QueryProps): JSX.Element { }: QueryProps): JSX.Element {
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder(); const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
const urlQuery = useUrlQuery(); const urlQuery = useUrlQuery();
@ -51,14 +51,6 @@ function QuerySection({
const { selectedDashboard, setSelectedDashboard } = useDashboard(); const { selectedDashboard, setSelectedDashboard } = useDashboard();
const getWidgetQueryRange = useGetWidgetQueryRange(
{
graphType: selectedGraph,
selectedTime: selectedTime.enum,
},
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
);
const { widgets } = selectedDashboard?.data || {}; const { widgets } = selectedDashboard?.data || {};
const getWidget = useCallback(() => { const getWidget = useCallback(() => {
@ -233,7 +225,7 @@ function QuerySection({
<span style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}> <span style={{ display: 'flex', gap: '1rem', alignItems: 'center' }}>
<TextToolTip text="This will temporarily save the current query and graph state. This will persist across tab change" /> <TextToolTip text="This will temporarily save the current query and graph state. This will persist across tab change" />
<Button <Button
loading={getWidgetQueryRange.isFetching} loading={queryResponse.isFetching}
type="primary" type="primary"
onClick={handleRunQuery} onClick={handleRunQuery}
className="stage-run-query" className="stage-run-query"
@ -251,7 +243,10 @@ function QuerySection({
interface QueryProps { interface QueryProps {
selectedGraph: PANEL_TYPES; selectedGraph: PANEL_TYPES;
selectedTime: WidgetGraphProps['selectedTime']; queryResponse: UseQueryResult<
SuccessResponse<MetricRangePayloadProps, unknown>,
Error
>;
} }
export default QuerySection; export default QuerySection;

View File

@ -1,12 +1,9 @@
import { Card, Typography } from 'antd'; import { Card, Typography } from 'antd';
import Spinner from 'components/Spinner'; import Spinner from 'components/Spinner';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { WidgetGraphProps } from 'container/NewWidget/types'; import { WidgetGraphContainerProps } from 'container/NewWidget/types';
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange'; // import useUrlQuery from 'hooks/useUrlQuery';
import useUrlQuery from 'hooks/useUrlQuery'; // import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { getGraphType } from 'utils/getGraphType';
import { getSortedSeriesData } from 'utils/getSortedSeriesData'; import { getSortedSeriesData } from 'utils/getSortedSeriesData';
import { NotFoundContainer } from './styles'; import { NotFoundContainer } from './styles';
@ -14,58 +11,36 @@ import WidgetGraph from './WidgetGraphs';
function WidgetGraphContainer({ function WidgetGraphContainer({
selectedGraph, selectedGraph,
yAxisUnit, queryResponse,
selectedTime, setRequestData,
thresholds, selectedWidget,
fillSpans = false, }: WidgetGraphContainerProps): JSX.Element {
softMax, if (queryResponse.data && selectedGraph === PANEL_TYPES.BAR) {
softMin,
selectedLogFields,
selectedTracesFields,
}: WidgetGraphProps): JSX.Element {
const { selectedDashboard } = useDashboard();
const { widgets = [] } = selectedDashboard?.data || {};
const params = useUrlQuery();
const widgetId = params.get('widgetId');
const selectedWidget = widgets.find((e) => e.id === widgetId);
const getWidgetQueryRange = useGetWidgetQueryRange(
{
graphType: getGraphType(selectedGraph),
selectedTime: selectedTime.enum,
},
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
);
if (getWidgetQueryRange.data && selectedGraph === PANEL_TYPES.BAR) {
const sortedSeriesData = getSortedSeriesData( const sortedSeriesData = getSortedSeriesData(
getWidgetQueryRange.data?.payload.data.result, queryResponse.data?.payload.data.result,
); );
getWidgetQueryRange.data.payload.data.result = sortedSeriesData; // eslint-disable-next-line no-param-reassign
queryResponse.data.payload.data.result = sortedSeriesData;
} }
if (selectedWidget === undefined) { if (selectedWidget === undefined) {
return <Card>Invalid widget</Card>; return <Card>Invalid widget</Card>;
} }
if (getWidgetQueryRange.error) { if (queryResponse?.error) {
return ( return (
<NotFoundContainer> <NotFoundContainer>
<Typography>{getWidgetQueryRange.error.message}</Typography> <Typography>{queryResponse.error.message}</Typography>
</NotFoundContainer> </NotFoundContainer>
); );
} }
if (getWidgetQueryRange.isLoading) { if (queryResponse.isLoading && selectedGraph !== PANEL_TYPES.LIST) {
return <Spinner size="large" tip="Loading..." />; return <Spinner size="large" tip="Loading..." />;
} }
if ( if (
selectedGraph !== PANEL_TYPES.LIST && selectedGraph !== PANEL_TYPES.LIST &&
getWidgetQueryRange.data?.payload.data.result.length === 0 queryResponse.data?.payload.data.result.length === 0
) { ) {
return ( return (
<NotFoundContainer> <NotFoundContainer>
@ -75,7 +50,7 @@ function WidgetGraphContainer({
} }
if ( if (
selectedGraph === PANEL_TYPES.LIST && selectedGraph === PANEL_TYPES.LIST &&
getWidgetQueryRange.data?.payload.data.newResult.data.result.length === 0 queryResponse.data?.payload.data.newResult.data.result.length === 0
) { ) {
return ( return (
<NotFoundContainer> <NotFoundContainer>
@ -86,17 +61,9 @@ function WidgetGraphContainer({
return ( return (
<WidgetGraph <WidgetGraph
yAxisUnit={yAxisUnit || ''}
getWidgetQueryRange={getWidgetQueryRange}
selectedWidget={selectedWidget} selectedWidget={selectedWidget}
thresholds={thresholds} queryResponse={queryResponse}
fillSpans={fillSpans} setRequestData={setRequestData}
softMax={softMax}
softMin={softMin}
selectedLogFields={selectedLogFields}
selectedTracesFields={selectedTracesFields}
selectedTime={selectedTime}
selectedGraph={selectedGraph}
/> />
); );
} }

View File

@ -1,98 +1,35 @@
import { QueryParams } from 'constants/query'; import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder'; import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
import GridPanelSwitch from 'container/GridPanelSwitch';
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config'; import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import useUrlQuery from 'hooks/useUrlQuery'; import useUrlQuery from 'hooks/useUrlQuery';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import GetMinMax from 'lib/getMinMax'; import GetMinMax from 'lib/getMinMax';
import getTimeString from 'lib/getTimeString'; import getTimeString from 'lib/getTimeString';
import history from 'lib/history'; import history from 'lib/history';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions'; import {
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData'; Dispatch,
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; SetStateAction,
useCallback,
useEffect,
useRef,
} from 'react';
import { UseQueryResult } from 'react-query'; import { UseQueryResult } from 'react-query';
import { useDispatch, useSelector } from 'react-redux'; import { useDispatch } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions'; import { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers';
import { SuccessResponse } from 'types/api'; import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { GlobalReducer } from 'types/reducer/globalTime';
import { getTimeRange } from 'utils/getTimeRange';
function WidgetGraph({ function WidgetGraph({
getWidgetQueryRange,
selectedWidget, selectedWidget,
yAxisUnit, queryResponse,
thresholds, setRequestData,
fillSpans,
softMax,
softMin,
selectedLogFields,
selectedTracesFields,
selectedTime,
selectedGraph,
}: WidgetGraphProps): JSX.Element { }: WidgetGraphProps): JSX.Element {
const { stagedQuery, currentQuery } = useQueryBuilder();
const { minTime, maxTime, selectedTime: globalSelectedInterval } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
const [minTimeScale, setMinTimeScale] = useState<number>();
const [maxTimeScale, setMaxTimeScale] = useState<number>();
const location = useLocation();
useEffect((): void => {
const { startTime, endTime } = getTimeRange(getWidgetQueryRange);
setMinTimeScale(startTime);
setMaxTimeScale(endTime);
}, [getWidgetQueryRange, maxTime, minTime, globalSelectedInterval]);
const graphRef = useRef<HTMLDivElement>(null); const graphRef = useRef<HTMLDivElement>(null);
const containerDimensions = useResizeObserver(graphRef);
const chartData = getUPlotChartData(
getWidgetQueryRange?.data?.payload,
fillSpans,
);
const isDarkMode = useIsDarkMode();
const params = useUrlQuery();
const widgetId = params.get('widgetId');
const dispatch = useDispatch(); const dispatch = useDispatch();
const urlQuery = useUrlQuery();
const onDragSelect = useCallback( const location = useLocation();
(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,
]);
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 handleBackNavigation = (): void => {
const searchParams = new URLSearchParams(window.location.search); const searchParams = new URLSearchParams(window.location.search);
@ -114,6 +51,28 @@ function WidgetGraph({
} }
}; };
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],
);
useEffect(() => { useEffect(() => {
window.addEventListener('popstate', handleBackNavigation); window.addEventListener('popstate', handleBackNavigation);
@ -123,79 +82,25 @@ function WidgetGraph({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, []); }, []);
const options = useMemo(
() =>
getUPlotChartOptions({
id: widgetId || 'legend_widget',
yAxisUnit,
apiResponse: getWidgetQueryRange?.data?.payload,
dimensions: containerDimensions,
isDarkMode,
onDragSelect,
thresholds,
fillSpans,
minTimeScale,
maxTimeScale,
softMax,
softMin,
panelType: selectedGraph,
currentQuery,
}),
[
widgetId,
yAxisUnit,
getWidgetQueryRange?.data?.payload,
containerDimensions,
isDarkMode,
onDragSelect,
thresholds,
fillSpans,
minTimeScale,
maxTimeScale,
softMax,
softMin,
selectedGraph,
currentQuery,
],
);
return ( return (
<div ref={graphRef} style={{ height: '100%' }}> <div ref={graphRef} style={{ height: '100%' }}>
<GridPanelSwitch <PanelWrapper
data={chartData} widget={selectedWidget}
options={options} queryResponse={queryResponse}
panelType={selectedWidget.panelTypes} setRequestData={setRequestData}
name={widgetId || 'legend_widget'} onDragSelect={onDragSelect}
yAxisUnit={yAxisUnit}
panelData={
getWidgetQueryRange.data?.payload.data.newResult.data.result || []
}
query={stagedQuery || selectedWidget.query}
thresholds={thresholds}
selectedLogFields={selectedLogFields}
selectedTracesFields={selectedTracesFields}
dataSource={currentQuery.builder.queryData[0].dataSource}
selectedTime={selectedTime}
/> />
</div> </div>
); );
} }
interface WidgetGraphProps { interface WidgetGraphProps {
thresholds: ThresholdProps[];
yAxisUnit: string;
selectedWidget: Widgets; selectedWidget: Widgets;
fillSpans: boolean; queryResponse: UseQueryResult<
getWidgetQueryRange: UseQueryResult<
SuccessResponse<MetricRangePayloadProps, unknown>, SuccessResponse<MetricRangePayloadProps, unknown>,
Error Error
>; >;
softMax: number | null; setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
softMin: number | null;
selectedLogFields: Widgets['selectedLogFields'];
selectedTracesFields: Widgets['selectedTracesFields'];
selectedTime: timePreferance;
selectedGraph: PANEL_TYPES;
} }
export default WidgetGraph; export default WidgetGraph;

View File

@ -1,47 +1,20 @@
import { InfoCircleOutlined } from '@ant-design/icons'; import { InfoCircleOutlined } from '@ant-design/icons';
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { Card } from 'container/GridCardLayout/styles'; import { Card } from 'container/GridCardLayout/styles';
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder'; import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import useUrlQuery from 'hooks/useUrlQuery';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { memo } from 'react'; import { memo } from 'react';
import { getGraphType } from 'utils/getGraphType';
import { WidgetGraphProps } from '../../types'; import { WidgetGraphContainerProps } from '../../types';
import PlotTag from './PlotTag'; import PlotTag from './PlotTag';
import { AlertIconContainer, Container } from './styles'; import { AlertIconContainer, Container } from './styles';
import WidgetGraphComponent from './WidgetGraphContainer'; import WidgetGraphComponent from './WidgetGraphContainer';
function WidgetGraph({ function WidgetGraph({
selectedGraph, selectedGraph,
yAxisUnit, queryResponse,
selectedTime, setRequestData,
thresholds, selectedWidget,
fillSpans, }: WidgetGraphContainerProps): JSX.Element {
softMax,
softMin,
selectedLogFields,
selectedTracesFields,
}: WidgetGraphProps): JSX.Element {
const { currentQuery } = useQueryBuilder(); const { currentQuery } = useQueryBuilder();
const { selectedDashboard } = useDashboard();
const { widgets = [] } = selectedDashboard?.data || {};
const params = useUrlQuery();
const widgetId = params.get('widgetId');
const selectedWidget = widgets.find((e) => e.id === widgetId);
const getWidgetQueryRange = useGetWidgetQueryRange(
{
graphType: getGraphType(selectedGraph),
selectedTime: selectedTime.enum,
},
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
);
if (selectedWidget === undefined) { if (selectedWidget === undefined) {
return <Card $panelType={selectedGraph}>Invalid widget</Card>; return <Card $panelType={selectedGraph}>Invalid widget</Card>;
@ -50,22 +23,17 @@ function WidgetGraph({
return ( return (
<Container $panelType={selectedGraph}> <Container $panelType={selectedGraph}>
<PlotTag queryType={currentQuery.queryType} panelType={selectedGraph} /> <PlotTag queryType={currentQuery.queryType} panelType={selectedGraph} />
{getWidgetQueryRange.error && ( {queryResponse.error && (
<AlertIconContainer color="red" title={getWidgetQueryRange.error.message}> <AlertIconContainer color="red" title={queryResponse.error.message}>
<InfoCircleOutlined /> <InfoCircleOutlined />
</AlertIconContainer> </AlertIconContainer>
)} )}
<WidgetGraphComponent <WidgetGraphComponent
thresholds={thresholds}
selectedTime={selectedTime}
selectedGraph={selectedGraph} selectedGraph={selectedGraph}
yAxisUnit={yAxisUnit} queryResponse={queryResponse}
fillSpans={fillSpans} setRequestData={setRequestData}
softMax={softMax} selectedWidget={selectedWidget}
softMin={softMin}
selectedLogFields={selectedLogFields}
selectedTracesFields={selectedTracesFields}
/> />
</Container> </Container>
); );

View File

@ -1,5 +1,16 @@
import { PANEL_TYPES } from 'constants/queryBuilder'; import { DEFAULT_ENTITY_VERSION } from 'constants/app';
import { memo } from 'react'; import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { memo, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { GlobalReducer } from 'types/reducer/globalTime';
import { getGraphType } from 'utils/getGraphType';
import { WidgetGraphProps } from '../types'; import { WidgetGraphProps } from '../types';
import ExplorerColumnsRenderer from './ExplorerColumnsRenderer'; import ExplorerColumnsRenderer from './ExplorerColumnsRenderer';
@ -9,32 +20,81 @@ import WidgetGraph from './WidgetGraph';
function LeftContainer({ function LeftContainer({
selectedGraph, selectedGraph,
yAxisUnit,
selectedTime,
thresholds,
fillSpans,
softMax,
softMin,
selectedLogFields, selectedLogFields,
setSelectedLogFields, setSelectedLogFields,
selectedTracesFields, selectedTracesFields,
setSelectedTracesFields, setSelectedTracesFields,
selectedWidget,
}: WidgetGraphProps): JSX.Element { }: WidgetGraphProps): JSX.Element {
const { stagedQuery, redirectWithQueryBuilderData } = useQueryBuilder();
const { selectedDashboard } = useDashboard();
const { selectedTime: globalSelectedInterval } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
const [requestData, setRequestData] = useState<GetQueryResultsProps>(() => {
if (selectedWidget && selectedWidget.panelTypes !== PANEL_TYPES.LIST) {
return {
selectedTime: selectedWidget?.timePreferance,
graphType: getGraphType(selectedWidget.panelTypes),
query: stagedQuery || initialQueriesMap.metrics,
globalSelectedInterval,
variables: getDashboardVariables(selectedDashboard?.data.variables),
};
}
const updatedQuery = { ...(stagedQuery || initialQueriesMap.metrics) };
updatedQuery.builder.queryData[0].pageSize = 10;
redirectWithQueryBuilderData(updatedQuery);
return {
query: updatedQuery,
graphType: PANEL_TYPES.LIST,
selectedTime: 'GLOBAL_TIME',
globalSelectedInterval,
tableParams: {
pagination: {
offset: 0,
limit: updatedQuery.builder.queryData[0].limit || 0,
},
},
};
});
useEffect(() => {
if (stagedQuery) {
setRequestData((prev) => ({
...prev,
query: stagedQuery,
}));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [stagedQuery]);
const queryResponse = useGetQueryRange(
requestData,
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
{
enabled: !!stagedQuery,
retry: false,
queryKey: [
REACT_QUERY_KEY.GET_QUERY_RANGE,
globalSelectedInterval,
requestData,
],
},
);
return ( return (
<> <>
<WidgetGraph <WidgetGraph
thresholds={thresholds}
selectedTime={selectedTime}
selectedGraph={selectedGraph} selectedGraph={selectedGraph}
yAxisUnit={yAxisUnit} queryResponse={queryResponse}
fillSpans={fillSpans} setRequestData={setRequestData}
softMax={softMax} selectedWidget={selectedWidget}
softMin={softMin}
selectedLogFields={selectedLogFields}
selectedTracesFields={selectedTracesFields}
/> />
<QueryContainer> <QueryContainer>
<QuerySection selectedTime={selectedTime} selectedGraph={selectedGraph} /> <QuerySection selectedGraph={selectedGraph} queryResponse={queryResponse} />
{selectedGraph === PANEL_TYPES.LIST && ( {selectedGraph === PANEL_TYPES.LIST && (
<ExplorerColumnsRenderer <ExplorerColumnsRenderer
selectedLogFields={selectedLogFields} selectedLogFields={selectedLogFields}

View File

@ -81,7 +81,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
return widgets?.find((e) => e.id === widgetId); return widgets?.find((e) => e.id === widgetId);
}, [query, widgets]); }, [query, widgets]);
const selectedWidget = getWidget(); const [selectedWidget, setSelectedWidget] = useState(getWidget());
const [title, setTitle] = useState<string>( const [title, setTitle] = useState<string>(
selectedWidget?.title?.toString() || '', selectedWidget?.title?.toString() || '',
@ -129,6 +129,44 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
: selectedWidget?.softMax || 0, : selectedWidget?.softMax || 0,
); );
useEffect(() => {
setSelectedWidget((prev) => {
if (!prev) {
return prev;
}
return {
...prev,
query: currentQuery,
title,
description,
isStacked: stacked,
opacity,
nullZeroValues: selectedNullZeroValue,
yAxisUnit,
thresholds,
softMin,
softMax,
fillSpans: isFillSpans,
selectedLogFields,
selectedTracesFields,
};
});
}, [
currentQuery,
description,
isFillSpans,
opacity,
selectedLogFields,
selectedNullZeroValue,
selectedTracesFields,
softMax,
softMin,
stacked,
thresholds,
title,
yAxisUnit,
]);
const closeModal = (): void => { const closeModal = (): void => {
setSaveModal(false); setSaveModal(false);
setDiscardModal(false); setDiscardModal(false);
@ -194,21 +232,21 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
...preWidgets, ...preWidgets,
{ {
...(selectedWidget || ({} as Widgets)), ...(selectedWidget || ({} as Widgets)),
description, description: selectedWidget?.description || '',
timePreferance: selectedTime.enum, timePreferance: selectedTime.enum,
isStacked: stacked, isStacked: selectedWidget?.isStacked || false,
opacity, opacity: selectedWidget?.opacity || '1',
nullZeroValues: selectedNullZeroValue, nullZeroValues: selectedWidget?.nullZeroValues || 'zero',
title, title: selectedWidget?.title,
yAxisUnit, yAxisUnit: selectedWidget?.yAxisUnit,
panelTypes: graphType, panelTypes: graphType,
query: currentQuery, query: currentQuery,
thresholds, thresholds: selectedWidget?.thresholds,
softMin, softMin: selectedWidget?.softMin || 0,
softMax, softMax: selectedWidget?.softMax || 0,
fillSpans: isFillSpans, fillSpans: selectedWidget?.fillSpans,
selectedLogFields, selectedLogFields: selectedWidget?.selectedLogFields || [],
selectedTracesFields, selectedTracesFields: selectedWidget?.selectedTracesFields || [],
}, },
...afterWidgets, ...afterWidgets,
], ],
@ -234,21 +272,9 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
selectedDashboard, selectedDashboard,
preWidgets, preWidgets,
selectedWidget, selectedWidget,
description,
selectedTime.enum, selectedTime.enum,
stacked,
opacity,
selectedNullZeroValue,
title,
yAxisUnit,
graphType, graphType,
currentQuery, currentQuery,
thresholds,
softMin,
softMax,
isFillSpans,
selectedLogFields,
selectedTracesFields,
afterWidgets, afterWidgets,
updateDashboardMutation, updateDashboardMutation,
setSelectedDashboard, setSelectedDashboard,
@ -358,19 +384,16 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
<PanelContainer> <PanelContainer>
<LeftContainerWrapper flex={5}> <LeftContainerWrapper flex={5}>
{selectedWidget && (
<LeftContainer <LeftContainer
selectedTime={selectedTime}
selectedGraph={graphType} selectedGraph={graphType}
yAxisUnit={yAxisUnit}
thresholds={thresholds}
fillSpans={isFillSpans}
softMax={softMax}
softMin={softMin}
selectedLogFields={selectedLogFields} selectedLogFields={selectedLogFields}
setSelectedLogFields={setSelectedLogFields} setSelectedLogFields={setSelectedLogFields}
selectedTracesFields={selectedTracesFields} selectedTracesFields={selectedTracesFields}
setSelectedTracesFields={setSelectedTracesFields} setSelectedTracesFields={setSelectedTracesFields}
selectedWidget={selectedWidget}
/> />
)}
</LeftContainerWrapper> </LeftContainerWrapper>
<RightContainerWrapper flex={1}> <RightContainerWrapper flex={1}>

View File

@ -1,9 +1,10 @@
import { PANEL_TYPES } from 'constants/queryBuilder'; import { PANEL_TYPES } from 'constants/queryBuilder';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction } from 'react';
import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { ThresholdProps } from './RightContainer/Threshold/types';
import { timePreferance } from './RightContainer/timeItems';
export interface NewWidgetProps { export interface NewWidgetProps {
selectedGraph: PANEL_TYPES; selectedGraph: PANEL_TYPES;
@ -11,15 +12,23 @@ export interface NewWidgetProps {
fillSpans: Widgets['fillSpans']; fillSpans: Widgets['fillSpans'];
} }
export interface WidgetGraphProps extends NewWidgetProps { export interface WidgetGraphProps {
selectedTime: timePreferance;
thresholds: ThresholdProps[];
softMin: number | null;
softMax: number | null;
selectedLogFields: Widgets['selectedLogFields']; selectedLogFields: Widgets['selectedLogFields'];
setSelectedLogFields?: Dispatch<SetStateAction<Widgets['selectedLogFields']>>; setSelectedLogFields?: Dispatch<SetStateAction<Widgets['selectedLogFields']>>;
selectedTracesFields: Widgets['selectedTracesFields']; selectedTracesFields: Widgets['selectedTracesFields'];
setSelectedTracesFields?: Dispatch< setSelectedTracesFields?: Dispatch<
SetStateAction<Widgets['selectedTracesFields']> SetStateAction<Widgets['selectedTracesFields']>
>; >;
selectedWidget: Widgets;
selectedGraph: PANEL_TYPES;
} }
export type WidgetGraphContainerProps = {
queryResponse: UseQueryResult<
SuccessResponse<MetricRangePayloadProps, unknown>,
Error
>;
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
selectedGraph: PANEL_TYPES;
selectedWidget: Widgets;
};

View File

@ -0,0 +1,37 @@
import LogsPanelComponent from 'container/LogsPanelTable/LogsPanelComponent';
import TracesTableComponent from 'container/TracesTableComponent/TracesTableComponent';
import { DataSource } from 'types/common/queryBuilder';
import { PanelWrapperProps } from './panelWrapper.types';
function ListPanelWrapper({
widget,
queryResponse,
setRequestData,
}: PanelWrapperProps): JSX.Element {
const dataSource = widget.query.builder?.queryData[0]?.dataSource;
if (!setRequestData) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <></>;
}
if (dataSource === DataSource.LOGS) {
return (
<LogsPanelComponent
widget={widget}
queryResponse={queryResponse}
setRequestData={setRequestData}
/>
);
}
return (
<TracesTableComponent
widget={widget}
queryResponse={queryResponse}
setRequestData={setRequestData}
/>
);
}
export default ListPanelWrapper;

View File

@ -0,0 +1,40 @@
import { FC } from 'react';
import { PanelTypeVsPanelWrapper } from './constants';
import { PanelWrapperProps } from './panelWrapper.types';
function PanelWrapper({
widget,
queryResponse,
setRequestData,
isFullViewMode,
setGraphVisibility,
graphVisibility,
onToggleModelHandler,
onClickHandler,
onDragSelect,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
widget.panelTypes
] as FC<PanelWrapperProps>;
if (!Component) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <></>;
}
return (
<Component
widget={widget}
queryResponse={queryResponse}
setRequestData={setRequestData}
isFullViewMode={isFullViewMode}
setGraphVisibility={setGraphVisibility}
graphVisibility={graphVisibility}
onToggleModelHandler={onToggleModelHandler}
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
/>
);
}
export default PanelWrapper;

View File

@ -0,0 +1,24 @@
import GridTableComponent from 'container/GridTableComponent';
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
import { PanelWrapperProps } from './panelWrapper.types';
function TablePanelWrapper({
widget,
queryResponse,
}: PanelWrapperProps): JSX.Element {
const panelData =
queryResponse.data?.payload?.data.newResult.data.result || [];
const { thresholds } = widget;
return (
<GridTableComponent
data={panelData}
query={widget.query}
thresholds={thresholds}
// eslint-disable-next-line react/jsx-props-no-spreading
{...GRID_TABLE_CONFIG}
/>
);
}
export default TablePanelWrapper;

View File

@ -0,0 +1,141 @@
import { ToggleGraphProps } from 'components/Graph/types';
import Uplot from 'components/Uplot';
import { PANEL_TYPES } from 'constants/queryBuilder';
import GraphManager from 'container/GridCardLayout/GridCard/FullView/GraphManager';
import { getLocalStorageGraphVisibilityState } from 'container/GridCardLayout/GridCard/utils';
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import _noop from 'lodash-es/noop';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useEffect, useMemo, useRef, useState } from 'react';
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
import { getTimeRange } from 'utils/getTimeRange';
import { PanelWrapperProps } from './panelWrapper.types';
function UplotPanelWrapper({
queryResponse,
widget,
isFullViewMode,
setGraphVisibility,
graphVisibility,
onToggleModelHandler,
onClickHandler,
onDragSelect,
}: PanelWrapperProps): JSX.Element {
const { toScrollWidgetId, setToScrollWidgetId } = useDashboard();
const isDarkMode = useIsDarkMode();
const lineChartRef = useRef<ToggleGraphProps>();
const graphRef = useRef<HTMLDivElement>(null);
const [minTimeScale, setMinTimeScale] = useState<number>();
const [maxTimeScale, setMaxTimeScale] = useState<number>();
const { currentQuery } = useQueryBuilder();
useEffect(() => {
if (toScrollWidgetId === widget.id) {
graphRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
graphRef.current?.focus();
setToScrollWidgetId('');
}
}, [toScrollWidgetId, setToScrollWidgetId, widget.id]);
useEffect((): void => {
const { startTime, endTime } = getTimeRange(queryResponse);
setMinTimeScale(startTime);
setMaxTimeScale(endTime);
}, [queryResponse]);
const containerDimensions = useResizeObserver(graphRef);
useEffect(() => {
const {
graphVisibilityStates: localStoredVisibilityState,
} = getLocalStorageGraphVisibilityState({
apiResponse: queryResponse.data?.payload.data.result || [],
name: widget.id,
});
if (setGraphVisibility) {
setGraphVisibility(localStoredVisibilityState);
}
}, [queryResponse.data?.payload.data.result, setGraphVisibility, widget.id]);
if (queryResponse.data && widget.panelTypes === PANEL_TYPES.BAR) {
const sortedSeriesData = getSortedSeriesData(
queryResponse.data?.payload.data.result,
);
// eslint-disable-next-line no-param-reassign
queryResponse.data.payload.data.result = sortedSeriesData;
}
const chartData = getUPlotChartData(
queryResponse?.data?.payload,
widget.fillSpans,
);
const options = useMemo(
() =>
getUPlotChartOptions({
id: widget?.id,
apiResponse: queryResponse.data?.payload,
dimensions: containerDimensions,
isDarkMode,
onDragSelect,
yAxisUnit: widget?.yAxisUnit,
onClickHandler: onClickHandler || _noop,
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,
currentQuery,
}),
[
widget?.id,
widget?.yAxisUnit,
widget.thresholds,
widget.softMax,
widget.softMin,
widget.panelTypes,
queryResponse.data?.payload,
containerDimensions,
isDarkMode,
onDragSelect,
onClickHandler,
minTimeScale,
maxTimeScale,
graphVisibility,
setGraphVisibility,
currentQuery,
],
);
return (
<div style={{ height: '100%', width: '100%' }} ref={graphRef}>
<Uplot options={options} data={chartData} ref={lineChartRef} />
{isFullViewMode && setGraphVisibility && (
<GraphManager
data={chartData}
name={widget.id}
options={options}
yAxisUnit={widget.yAxisUnit}
onToggleModelHandler={onToggleModelHandler}
setGraphsVisibilityStates={setGraphVisibility}
graphsVisibilityStates={graphVisibility}
lineChartRef={lineChartRef}
/>
)}
</div>
);
}
export default UplotPanelWrapper;

View File

@ -0,0 +1,21 @@
import GridValueComponent from 'container/GridValueComponent';
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
import { PanelWrapperProps } from './panelWrapper.types';
function ValuePanelWrapper({
widget,
queryResponse,
}: PanelWrapperProps): JSX.Element {
const { yAxisUnit, thresholds } = widget;
const data = getUPlotChartData(queryResponse?.data?.payload);
return (
<GridValueComponent
data={data}
yAxisUnit={yAxisUnit}
thresholds={thresholds}
/>
);
}
export default ValuePanelWrapper;

View File

@ -0,0 +1,16 @@
import { PANEL_TYPES } from 'constants/queryBuilder';
import ListPanelWrapper from './ListPanelWrapper';
import TablePanelWrapper from './TablePanelWrapper';
import UplotPanelWrapper from './UplotPanelWrapper';
import ValuePanelWrapper from './ValuePanelWrapper';
export const PanelTypeVsPanelWrapper = {
[PANEL_TYPES.TIME_SERIES]: UplotPanelWrapper,
[PANEL_TYPES.TABLE]: TablePanelWrapper,
[PANEL_TYPES.LIST]: ListPanelWrapper,
[PANEL_TYPES.VALUE]: ValuePanelWrapper,
[PANEL_TYPES.TRACE]: null,
[PANEL_TYPES.EMPTY_WIDGET]: null,
[PANEL_TYPES.BAR]: UplotPanelWrapper,
};

View File

@ -0,0 +1,22 @@
import { WidgetGraphComponentProps } from 'container/GridCardLayout/GridCard/types';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { Dispatch, SetStateAction } from 'react';
import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
export type PanelWrapperProps = {
queryResponse: UseQueryResult<
SuccessResponse<MetricRangePayloadProps, unknown>,
Error
>;
widget: Widgets;
setRequestData?: WidgetGraphComponentProps['setRequestData'];
isFullViewMode?: boolean;
onToggleModelHandler?: () => void;
graphVisibility?: boolean[];
setGraphVisibility?: Dispatch<SetStateAction<boolean[]>>;
onClickHandler?: OnClickPluginOpts['onClick'];
onDragSelect: (start: number, end: number) => void;
};

View File

@ -1,12 +1,8 @@
import './TracesTableComponent.styles.scss'; import './TracesTableComponent.styles.scss';
import { Table } from 'antd'; import { Table } from 'antd';
// import { ResizeTable } from 'components/ResizeTable';
import { SOMETHING_WENT_WRONG } from 'constants/api'; import { SOMETHING_WENT_WRONG } from 'constants/api';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import Controls from 'container/Controls'; import Controls from 'container/Controls';
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs'; import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
import { tableStyles } from 'container/TracesExplorer/ListView/styles'; import { tableStyles } from 'container/TracesExplorer/ListView/styles';
import { import {
@ -14,76 +10,51 @@ import {
getTraceLink, getTraceLink,
transformDataWithDate, transformDataWithDate,
} from 'container/TracesExplorer/ListView/utils'; } from 'container/TracesExplorer/ListView/utils';
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
import { Pagination } from 'hooks/queryPagination'; import { Pagination } from 'hooks/queryPagination';
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables'; import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
import history from 'lib/history'; import history from 'lib/history';
import { RowData } from 'lib/query/createTableColumnsFromQuery'; import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { useDashboard } from 'providers/Dashboard/Dashboard'; import {
import { HTMLAttributes, useCallback, useMemo, useState } from 'react'; Dispatch,
import { useSelector } from 'react-redux'; HTMLAttributes,
import { AppState } from 'store/reducers'; SetStateAction,
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { UseQueryResult } from 'react-query';
import { SuccessResponse } from 'types/api';
import { Widgets } from 'types/api/dashboard/getAll'; import { Widgets } from 'types/api/dashboard/getAll';
import { Query } from 'types/api/queryBuilder/queryBuilderData'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { GlobalReducer } from 'types/reducer/globalTime';
function TracesTableComponent({ function TracesTableComponent({
selectedTracesFields, widget,
query, queryResponse,
version, setRequestData,
selectedTime,
}: TracesTableComponentProps): JSX.Element { }: TracesTableComponentProps): JSX.Element {
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
AppState,
GlobalReducer
>((state) => state.globalTime);
const [pagination, setPagination] = useState<Pagination>({ const [pagination, setPagination] = useState<Pagination>({
offset: 0, offset: 0,
limit: 10, limit: 10,
}); });
const { selectedDashboard } = useDashboard(); useEffect(() => {
setRequestData((prev) => ({
const { data, isFetching, isError } = useGetQueryRange( ...prev,
{
query,
graphType: PANEL_TYPES.LIST,
selectedTime: selectedTime?.enum || 'GLOBAL_TIME',
globalSelectedInterval: globalSelectedTime,
params: {
dataSource: 'traces',
},
tableParams: { tableParams: {
pagination, pagination,
selectColumns: selectedTracesFields,
}, },
variables: getDashboardVariables(selectedDashboard?.data.variables), }));
}, }, [pagination, setRequestData]);
version,
{
queryKey: [
REACT_QUERY_KEY.GET_QUERY_RANGE,
globalSelectedTime,
maxTime,
minTime,
query,
pagination,
selectedTracesFields?.length,
selectedTime?.enum,
selectedDashboard?.data.variables,
],
enabled: !!query && !!selectedTracesFields?.length,
},
);
const columns = getListColumns(selectedTracesFields || []); const columns = getListColumns(widget.selectedTracesFields || []);
const dataLength = 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 totalCount = useMemo(() => dataLength || 0, [dataLength]);
const queryTableDataResult = data?.payload.data.newResult.data.result; const queryTableDataResult =
queryResponse.data?.payload.data.newResult.data.result;
const queryTableData = useMemo(() => queryTableDataResult || [], [ const queryTableData = useMemo(() => queryTableDataResult || [], [
queryTableDataResult, queryTableDataResult,
]); ]);
@ -108,7 +79,7 @@ function TracesTableComponent({
[], [],
); );
if (isError) { if (queryResponse.isError) {
return <div>{SOMETHING_WENT_WRONG}</div>; return <div>{SOMETHING_WENT_WRONG}</div>;
} }
@ -119,7 +90,7 @@ function TracesTableComponent({
pagination={false} pagination={false}
tableLayout="fixed" tableLayout="fixed"
scroll={{ x: true }} scroll={{ x: true }}
loading={isFetching} loading={queryResponse.isFetching}
style={tableStyles} style={tableStyles}
dataSource={transformedQueryTableData} dataSource={transformedQueryTableData}
columns={columns} columns={columns}
@ -131,7 +102,7 @@ function TracesTableComponent({
<Controls <Controls
totalCount={totalCount} totalCount={totalCount}
perPageOptions={PER_PAGE_OPTIONS} perPageOptions={PER_PAGE_OPTIONS}
isLoading={isFetching} isLoading={queryResponse.isFetching}
offset={pagination.offset} offset={pagination.offset}
countPerPage={pagination.limit} countPerPage={pagination.limit}
handleNavigatePrevious={(): void => { handleNavigatePrevious={(): void => {
@ -160,14 +131,12 @@ function TracesTableComponent({
} }
export type TracesTableComponentProps = { export type TracesTableComponentProps = {
selectedTracesFields: Widgets['selectedTracesFields']; queryResponse: UseQueryResult<
query: Query; SuccessResponse<MetricRangePayloadProps, unknown>,
version: string; Error
selectedTime?: timePreferance; >;
}; widget: Widgets;
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
TracesTableComponent.defaultProps = {
selectedTime: undefined,
}; };
export default TracesTableComponent; export default TracesTableComponent;