mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-24 14:44:24 +08:00
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:
parent
7a7d814288
commit
ec9dbb6853
@ -1,62 +1,60 @@
|
||||
import './WidgetFullView.styles.scss';
|
||||
|
||||
import { SyncOutlined } from '@ant-design/icons';
|
||||
import { Button } from 'antd';
|
||||
import { LoadingOutlined, SyncOutlined } from '@ant-design/icons';
|
||||
import { Button, Spin } from 'antd';
|
||||
import cx from 'classnames';
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import Spinner from 'components/Spinner';
|
||||
import TimePreference from 'components/TimePreferenceDropDown';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import {
|
||||
timeItems,
|
||||
timePreferance,
|
||||
} from 'container/NewWidget/RightContainer/timeItems';
|
||||
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import { useChartMutable } from 'hooks/useChartMutable';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import history from 'lib/history';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import uPlot from 'uplot';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
||||
import { getTimeRange } from 'utils/getTimeRange';
|
||||
|
||||
import { getLocalStorageGraphVisibilityState } from '../utils';
|
||||
import { PANEL_TYPES_VS_FULL_VIEW_TABLE } from './contants';
|
||||
import GraphManager from './GraphManager';
|
||||
import { GraphContainer, TimeContainer } from './styles';
|
||||
import { FullViewProps } from './types';
|
||||
|
||||
function FullView({
|
||||
widget,
|
||||
fullViewOptions = true,
|
||||
onClickHandler,
|
||||
name,
|
||||
version,
|
||||
originalName,
|
||||
yAxisUnit,
|
||||
onDragSelect,
|
||||
isDependedDataLoaded = false,
|
||||
onToggleModelHandler,
|
||||
parentChartRef,
|
||||
}: FullViewProps): JSX.Element {
|
||||
const { selectedTime: globalSelectedTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
const dispatch = useDispatch();
|
||||
const urlQuery = useUrlQuery();
|
||||
const location = useLocation();
|
||||
|
||||
const fullViewRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const [chartOptions, setChartOptions] = useState<uPlot.Options>();
|
||||
|
||||
const { selectedDashboard, isDashboardLocked } = useDashboard();
|
||||
|
||||
const getSelectedTime = useCallback(
|
||||
@ -74,24 +72,70 @@ function FullView({
|
||||
|
||||
const updatedQuery = useStepInterval(widget?.query);
|
||||
|
||||
const response = useGetQueryRange(
|
||||
{
|
||||
selectedTime: selectedTime.enum,
|
||||
graphType:
|
||||
widget.panelTypes === PANEL_TYPES.BAR
|
||||
? PANEL_TYPES.TIME_SERIES
|
||||
: widget.panelTypes,
|
||||
const [requestData, setRequestData] = useState<GetQueryResultsProps>(() => {
|
||||
if (widget.panelTypes !== PANEL_TYPES.LIST) {
|
||||
return {
|
||||
selectedTime: selectedTime.enum,
|
||||
graphType: getGraphType(widget.panelTypes),
|
||||
query: updatedQuery,
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
};
|
||||
}
|
||||
updatedQuery.builder.queryData[0].pageSize = 10;
|
||||
return {
|
||||
query: updatedQuery,
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
tableParams: {
|
||||
pagination: {
|
||||
offset: 0,
|
||||
limit: updatedQuery.builder.queryData[0].limit || 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setRequestData((prev) => ({
|
||||
...prev,
|
||||
selectedTime: selectedTime.enum,
|
||||
}));
|
||||
}, [selectedTime]);
|
||||
|
||||
const response = useGetQueryRange(
|
||||
requestData,
|
||||
selectedDashboard?.data?.version || version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: `FullViewGetMetricsQueryRange-${selectedTime.enum}-${globalSelectedTime}-${widget.id}`,
|
||||
enabled: !isDependedDataLoaded && widget.panelTypes !== PANEL_TYPES.LIST, // Internally both the list view panel has it's own query range api call, so we don't need to call it again
|
||||
queryKey: [widget?.query, widget?.panelTypes, requestData, version],
|
||||
enabled: !isDependedDataLoaded,
|
||||
keepPreviousData: true,
|
||||
},
|
||||
);
|
||||
|
||||
const onDragSelect = useCallback(
|
||||
(start: number, end: number): void => {
|
||||
const startTimestamp = Math.trunc(start);
|
||||
const endTimestamp = Math.trunc(end);
|
||||
|
||||
if (startTimestamp !== endTimestamp) {
|
||||
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
||||
}
|
||||
|
||||
const { maxTime, minTime } = GetMinMax('custom', [
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
]);
|
||||
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.push(generatedUrl);
|
||||
},
|
||||
[dispatch, location.pathname, urlQuery],
|
||||
);
|
||||
|
||||
const [graphsVisibilityStates, setGraphsVisibilityStates] = useState<
|
||||
boolean[]
|
||||
>(Array(response.data?.payload.data.result.length).fill(true));
|
||||
@ -118,60 +162,6 @@ function FullView({
|
||||
response.data.payload.data.result = sortedSeriesData;
|
||||
}
|
||||
|
||||
const chartData = getUPlotChartData(response?.data?.payload, widget.fillSpans);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const [minTimeScale, setMinTimeScale] = useState<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(() => {
|
||||
graphsVisibilityStates?.forEach((e, i) => {
|
||||
fullViewChartRef?.current?.toggleGraph(i, e);
|
||||
@ -180,7 +170,7 @@ function FullView({
|
||||
|
||||
const isListView = widget.panelTypes === PANEL_TYPES.LIST;
|
||||
|
||||
if (response.isFetching) {
|
||||
if (response.isLoading && widget.panelTypes !== PANEL_TYPES.LIST) {
|
||||
return <Spinner height="100%" size="large" tip="Loading..." />;
|
||||
}
|
||||
|
||||
@ -189,6 +179,9 @@ function FullView({
|
||||
<div className="full-view-header-container">
|
||||
{fullViewOptions && (
|
||||
<TimeContainer $panelType={widget.panelTypes}>
|
||||
{response.isFetching && (
|
||||
<Spin spinning indicator={<LoadingOutlined spin />} />
|
||||
)}
|
||||
<TimePreference
|
||||
selectedTime={selectedTime}
|
||||
setSelectedTime={setSelectedTime}
|
||||
@ -214,47 +207,24 @@ function FullView({
|
||||
})}
|
||||
ref={fullViewRef}
|
||||
>
|
||||
{chartOptions && (
|
||||
<GraphContainer
|
||||
style={{
|
||||
height: isListView ? '100%' : '90%',
|
||||
}}
|
||||
isGraphLegendToggleAvailable={canModifyChart}
|
||||
>
|
||||
<GridPanelSwitch
|
||||
panelType={widget.panelTypes}
|
||||
data={chartData}
|
||||
options={chartOptions}
|
||||
onClickHandler={onClickHandler}
|
||||
name={name}
|
||||
yAxisUnit={yAxisUnit}
|
||||
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
|
||||
style={{
|
||||
height: isListView ? '100%' : '90%',
|
||||
}}
|
||||
isGraphLegendToggleAvailable={canModifyChart}
|
||||
>
|
||||
<PanelWrapper
|
||||
queryResponse={response}
|
||||
widget={widget}
|
||||
setRequestData={setRequestData}
|
||||
isFullViewMode
|
||||
onToggleModelHandler={onToggleModelHandler}
|
||||
setGraphVisibility={setGraphsVisibilityStates}
|
||||
graphVisibility={graphsVisibilityStates}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</GraphContainer>
|
||||
</div>
|
||||
|
||||
{canModifyChart && chartOptions && !isDashboardLocked && (
|
||||
<GraphManager
|
||||
data={chartData}
|
||||
name={originalName}
|
||||
options={chartOptions}
|
||||
yAxisUnit={yAxisUnit}
|
||||
onToggleModelHandler={onToggleModelHandler}
|
||||
setGraphsVisibilityStates={setGraphsVisibilityStates}
|
||||
graphsVisibilityStates={graphsVisibilityStates}
|
||||
lineChartRef={fullViewChartRef}
|
||||
parentChartRef={parentChartRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ export const NotFoundContainer = styled.div`
|
||||
export const TimeContainer = styled.div<Props>`
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
${({ $panelType }): FlattenSimpleInterpolation =>
|
||||
$panelType === PANEL_TYPES.TABLE
|
||||
? css`
|
||||
|
@ -53,10 +53,8 @@ export interface FullViewProps {
|
||||
version?: string;
|
||||
originalName: string;
|
||||
yAxisUnit?: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
isDependedDataLoaded?: boolean;
|
||||
onToggleModelHandler?: GraphManagerProps['onToggleModelHandler'];
|
||||
parentChartRef: GraphManagerProps['lineChartRef'];
|
||||
}
|
||||
|
||||
export interface GraphManagerProps extends UplotProps {
|
||||
|
@ -6,7 +6,7 @@ import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
@ -33,23 +33,20 @@ import FullView from './FullView';
|
||||
import { Modal } from './styles';
|
||||
import { WidgetGraphComponentProps } from './types';
|
||||
import { getLocalStorageGraphVisibilityState } from './utils';
|
||||
// import { getLocalStorageGraphVisibilityState } from './utils';
|
||||
|
||||
function WidgetGraphComponent({
|
||||
widget,
|
||||
queryResponse,
|
||||
errorMessage,
|
||||
name,
|
||||
version,
|
||||
threshold,
|
||||
headerMenuList,
|
||||
isWarning,
|
||||
data,
|
||||
options,
|
||||
graphVisibiltyState,
|
||||
isFetchingResponse,
|
||||
setRequestData,
|
||||
onClickHandler,
|
||||
onDragSelect,
|
||||
setGraphVisibility,
|
||||
isFetchingResponse,
|
||||
}: WidgetGraphComponentProps): JSX.Element {
|
||||
const [deleteModal, setDeleteModal] = useState(false);
|
||||
const [hovered, setHovered] = useState(false);
|
||||
@ -61,12 +58,15 @@ function WidgetGraphComponent({
|
||||
const isFullViewOpen = params.get(QueryParams.expandedWidgetId) === widget.id;
|
||||
|
||||
const lineChartRef = useRef<ToggleGraphProps>();
|
||||
const [graphVisibility, setGraphVisibility] = useState<boolean[]>(
|
||||
Array(queryResponse.data?.payload?.data.result.length || 0).fill(true),
|
||||
);
|
||||
const graphRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!lineChartRef.current) return;
|
||||
|
||||
graphVisibiltyState.forEach((state, index) => {
|
||||
graphVisibility.forEach((state, index) => {
|
||||
lineChartRef.current?.toggleGraph(index, state);
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@ -210,7 +210,7 @@ function WidgetGraphComponent({
|
||||
graphVisibilityStates: localStoredVisibilityState,
|
||||
} = getLocalStorageGraphVisibilityState({
|
||||
apiResponse: queryResponse.data.payload.data.result,
|
||||
name,
|
||||
name: widget.id,
|
||||
});
|
||||
setGraphVisibility(localStoredVisibilityState);
|
||||
}
|
||||
@ -252,7 +252,7 @@ function WidgetGraphComponent({
|
||||
onBlur={(): void => {
|
||||
setHovered(false);
|
||||
}}
|
||||
id={name}
|
||||
id={widget.id}
|
||||
>
|
||||
<Modal
|
||||
destroyOnClose
|
||||
@ -278,14 +278,12 @@ function WidgetGraphComponent({
|
||||
className="widget-full-view"
|
||||
>
|
||||
<FullView
|
||||
name={`${name}expanded`}
|
||||
name={`${widget.id}expanded`}
|
||||
version={version}
|
||||
originalName={name}
|
||||
originalName={widget.id}
|
||||
widget={widget}
|
||||
yAxisUnit={widget.yAxisUnit}
|
||||
onToggleModelHandler={onToggleModelHandler}
|
||||
parentChartRef={lineChartRef}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</Modal>
|
||||
|
||||
@ -305,26 +303,22 @@ function WidgetGraphComponent({
|
||||
isFetchingResponse={isFetchingResponse}
|
||||
/>
|
||||
</div>
|
||||
{queryResponse.isLoading && <Skeleton />}
|
||||
{queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && (
|
||||
<Skeleton />
|
||||
)}
|
||||
{(queryResponse.isSuccess || widget.panelTypes === PANEL_TYPES.LIST) && (
|
||||
<div
|
||||
className={cx('widget-graph-container', widget.panelTypes)}
|
||||
ref={graphRef}
|
||||
>
|
||||
<GridPanelSwitch
|
||||
panelType={widget.panelTypes}
|
||||
data={data}
|
||||
name={name}
|
||||
ref={lineChartRef}
|
||||
options={options}
|
||||
yAxisUnit={widget.yAxisUnit}
|
||||
<PanelWrapper
|
||||
widget={widget}
|
||||
queryResponse={queryResponse}
|
||||
setRequestData={setRequestData}
|
||||
setGraphVisibility={setGraphVisibility}
|
||||
graphVisibility={graphVisibility}
|
||||
onClickHandler={onClickHandler}
|
||||
panelData={queryResponse.data?.payload?.data.newResult.data.result || []}
|
||||
query={widget.query}
|
||||
thresholds={widget.thresholds}
|
||||
selectedLogFields={widget.selectedLogFields}
|
||||
dataSource={widget.query.builder?.queryData[0]?.dataSource}
|
||||
selectedTracesFields={widget.selectedTracesFields}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -4,80 +4,43 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { CustomTimeType } from 'container/TopNav/DateTimeSelectionV2/config';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { useStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useResizeObserver } from 'hooks/useDimensions';
|
||||
import { useIntersectionObserver } from 'hooks/useIntersectionObserver';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import getTimeString from 'lib/getTimeString';
|
||||
import history from 'lib/history';
|
||||
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import isEmpty from 'lodash-es/isEmpty';
|
||||
import _noop from 'lodash-es/noop';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { memo, useEffect, useRef, useState } from 'react';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
||||
import { getTimeRange } from 'utils/getTimeRange';
|
||||
|
||||
import EmptyWidget from '../EmptyWidget';
|
||||
import { MenuItemKeys } from '../WidgetHeader/contants';
|
||||
import { GridCardGraphProps } from './types';
|
||||
import { getLocalStorageGraphVisibilityState } from './utils';
|
||||
import WidgetGraphComponent from './WidgetGraphComponent';
|
||||
|
||||
function GridCardGraph({
|
||||
widget,
|
||||
name,
|
||||
onClickHandler = _noop,
|
||||
headerMenuList = [MenuItemKeys.View],
|
||||
isQueryEnabled,
|
||||
threshold,
|
||||
variables,
|
||||
fillSpans = false,
|
||||
version,
|
||||
onClickHandler,
|
||||
onDragSelect,
|
||||
}: GridCardGraphProps): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
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<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
|
||||
const onDragSelect = useCallback(
|
||||
(start: number, end: number): void => {
|
||||
const startTimestamp = Math.trunc(start);
|
||||
const endTimestamp = Math.trunc(end);
|
||||
|
||||
if (startTimestamp !== endTimestamp) {
|
||||
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
||||
}
|
||||
|
||||
const { maxTime, minTime } = GetMinMax('custom', [
|
||||
startTimestamp,
|
||||
endTimestamp,
|
||||
]);
|
||||
|
||||
urlQuery.set(QueryParams.startTime, minTime.toString());
|
||||
urlQuery.set(QueryParams.endTime, maxTime.toString());
|
||||
const generatedUrl = `${location.pathname}?${urlQuery.toString()}`;
|
||||
history.push(generatedUrl);
|
||||
},
|
||||
[dispatch, location.pathname, urlQuery],
|
||||
);
|
||||
|
||||
const handleBackNavigation = (): void => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const startTime = searchParams.get(QueryParams.startTime);
|
||||
@ -127,19 +90,39 @@ function GridCardGraph({
|
||||
const isEmptyWidget =
|
||||
widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget);
|
||||
|
||||
const queryEnabledCondition =
|
||||
isVisible &&
|
||||
!isEmptyWidget &&
|
||||
isQueryEnabled &&
|
||||
widget.panelTypes !== PANEL_TYPES.LIST;
|
||||
const queryEnabledCondition = isVisible && !isEmptyWidget && isQueryEnabled;
|
||||
|
||||
const [requestData, setRequestData] = useState<GetQueryResultsProps>(() => {
|
||||
if (widget.panelTypes !== PANEL_TYPES.LIST) {
|
||||
return {
|
||||
selectedTime: widget?.timePreferance,
|
||||
graphType: getGraphType(widget.panelTypes),
|
||||
query: updatedQuery,
|
||||
globalSelectedInterval,
|
||||
variables: getDashboardVariables(variables),
|
||||
};
|
||||
}
|
||||
updatedQuery.builder.queryData[0].pageSize = 10;
|
||||
return {
|
||||
query: updatedQuery,
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval,
|
||||
tableParams: {
|
||||
pagination: {
|
||||
offset: 0,
|
||||
limit: updatedQuery.builder.queryData[0].limit || 0,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const queryResponse = useGetQueryRange(
|
||||
{
|
||||
selectedTime: widget?.timePreferance,
|
||||
graphType: getGraphType(widget.panelTypes),
|
||||
query: updatedQuery,
|
||||
globalSelectedInterval,
|
||||
...requestData,
|
||||
variables: getDashboardVariables(variables),
|
||||
selectedTime: 'GLOBAL_TIME',
|
||||
globalSelectedInterval,
|
||||
},
|
||||
version || DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
@ -151,6 +134,7 @@ function GridCardGraph({
|
||||
widget?.query,
|
||||
widget?.panelTypes,
|
||||
widget.timePreferance,
|
||||
requestData,
|
||||
],
|
||||
retry(failureCount, error): boolean {
|
||||
if (
|
||||
@ -173,15 +157,6 @@ function GridCardGraph({
|
||||
|
||||
const isEmptyLayout = widget?.id === PANEL_TYPES.EMPTY_WIDGET;
|
||||
|
||||
const containerDimensions = useResizeObserver(graphRef);
|
||||
|
||||
useEffect((): void => {
|
||||
const { startTime, endTime } = getTimeRange(queryResponse);
|
||||
|
||||
setMinTimeScale(startTime);
|
||||
setMaxTimeScale(endTime);
|
||||
}, [maxTime, minTime, globalSelectedInterval, queryResponse]);
|
||||
|
||||
if (queryResponse.data && widget.panelTypes === PANEL_TYPES.BAR) {
|
||||
const sortedSeriesData = getSortedSeriesData(
|
||||
queryResponse.data?.payload.data.result,
|
||||
@ -189,89 +164,29 @@ function GridCardGraph({
|
||||
queryResponse.data.payload.data.result = sortedSeriesData;
|
||||
}
|
||||
|
||||
const chartData = getUPlotChartData(queryResponse?.data?.payload, fillSpans);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const menuList =
|
||||
widget.panelTypes === PANEL_TYPES.TABLE ||
|
||||
widget.panelTypes === PANEL_TYPES.LIST
|
||||
? headerMenuList.filter((menu) => menu !== MenuItemKeys.CreateAlerts)
|
||||
: headerMenuList;
|
||||
|
||||
const [graphVisibility, setGraphVisibility] = useState<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 (
|
||||
<div style={{ height: '100%', width: '100%' }} ref={graphRef}>
|
||||
{isEmptyLayout ? (
|
||||
<EmptyWidget />
|
||||
) : (
|
||||
<WidgetGraphComponent
|
||||
data={chartData}
|
||||
options={options}
|
||||
widget={widget}
|
||||
queryResponse={queryResponse}
|
||||
errorMessage={errorMessage}
|
||||
isWarning={false}
|
||||
name={name}
|
||||
version={version}
|
||||
onDragSelect={onDragSelect}
|
||||
threshold={threshold}
|
||||
headerMenuList={menuList}
|
||||
onClickHandler={onClickHandler}
|
||||
graphVisibiltyState={graphVisibility}
|
||||
setGraphVisibility={setGraphVisibility}
|
||||
isFetchingResponse={queryResponse.isFetching}
|
||||
setRequestData={setRequestData}
|
||||
onClickHandler={onClickHandler}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import { UplotProps } from 'components/Uplot/Uplot';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import uPlot from 'uplot';
|
||||
@ -16,35 +16,32 @@ export interface GraphVisibilityLegendEntryProps {
|
||||
legendEntry: LegendEntryProps[];
|
||||
}
|
||||
|
||||
export interface WidgetGraphComponentProps extends UplotProps {
|
||||
export interface WidgetGraphComponentProps {
|
||||
widget: Widgets;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps> | ErrorResponse
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
errorMessage: string | undefined;
|
||||
name: string;
|
||||
version?: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
onClickHandler?: OnClickPluginOpts['onClick'];
|
||||
threshold?: ReactNode;
|
||||
headerMenuList: MenuItemKeys[];
|
||||
isWarning: boolean;
|
||||
graphVisibiltyState: boolean[];
|
||||
setGraphVisibility: Dispatch<SetStateAction<boolean[]>>;
|
||||
isFetchingResponse: boolean;
|
||||
setRequestData?: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
onClickHandler?: OnClickPluginOpts['onClick'];
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
}
|
||||
|
||||
export interface GridCardGraphProps {
|
||||
widget: Widgets;
|
||||
name: string;
|
||||
onDragSelect?: (start: number, end: number) => void;
|
||||
onClickHandler?: OnClickPluginOpts['onClick'];
|
||||
threshold?: ReactNode;
|
||||
headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
|
||||
onClickHandler?: OnClickPluginOpts['onClick'];
|
||||
isQueryEnabled: boolean;
|
||||
variables?: Dashboard['data']['variables'];
|
||||
fillSpans?: boolean;
|
||||
version?: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
}
|
||||
|
||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||
|
@ -3,20 +3,25 @@ import './GridCardLayout.styles.scss';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import { Tooltip } from 'antd';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import history from 'lib/history';
|
||||
import isEqual from 'lodash-es/isEqual';
|
||||
import { FullscreenIcon } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { FullScreen, useFullScreenHandle } from 'react-full-screen';
|
||||
import { Layout } from 'react-grid-layout';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
@ -45,6 +50,8 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
} = useDashboard();
|
||||
const { data } = selectedDashboard || {};
|
||||
const handle = useFullScreenHandle();
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { widgets, variables } = data || {};
|
||||
|
||||
@ -61,6 +68,7 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
const updateDashboardMutation = useUpdateDashboard();
|
||||
|
||||
const { notifications } = useNotifications();
|
||||
const urlQuery = useUrlQuery();
|
||||
|
||||
let permissions: ComponentTypes[] = ['save_layout', 'add_panel'];
|
||||
|
||||
@ -126,6 +134,23 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
}
|
||||
};
|
||||
|
||||
const onDragSelect = useCallback(
|
||||
(start: number, end: number) => {
|
||||
const startTimestamp = Math.trunc(start);
|
||||
const endTimestamp = Math.trunc(end);
|
||||
|
||||
urlQuery.set(QueryParams.startTime, startTimestamp.toString());
|
||||
urlQuery.set(QueryParams.endTime, endTimestamp.toString());
|
||||
const generatedUrl = `${pathname}?${urlQuery.toString()}`;
|
||||
history.replace(generatedUrl);
|
||||
|
||||
if (startTimestamp !== endTimestamp) {
|
||||
dispatch(UpdateTimeInterval('custom', [startTimestamp, endTimestamp]));
|
||||
}
|
||||
},
|
||||
[dispatch, pathname, urlQuery],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
dashboardLayout &&
|
||||
@ -200,11 +225,10 @@ function GraphLayout({ onAddPanelHandler }: GraphLayoutProps): JSX.Element {
|
||||
>
|
||||
<GridCard
|
||||
widget={currentWidget || ({ id, query: {} } as Widgets)}
|
||||
name={currentWidget?.id || ''}
|
||||
headerMenuList={widgetActions}
|
||||
variables={variables}
|
||||
fillSpans={currentWidget?.fillSpans}
|
||||
version={selectedDashboard?.data?.version}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</Card>
|
||||
</CardContainer>
|
||||
|
@ -1,10 +1,8 @@
|
||||
import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { getComponentForPanelType } from 'constants/panelTypes';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GRID_TABLE_CONFIG } from 'container/GridTableComponent/config';
|
||||
import { FC, forwardRef, memo, useMemo } from 'react';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
|
||||
import { GridPanelSwitchProps, PropsTypePropsMap } from './types';
|
||||
|
||||
@ -21,10 +19,7 @@ const GridPanelSwitch = forwardRef<
|
||||
query,
|
||||
options,
|
||||
thresholds,
|
||||
selectedLogFields,
|
||||
selectedTracesFields,
|
||||
dataSource,
|
||||
selectedTime,
|
||||
},
|
||||
ref,
|
||||
): JSX.Element | null => {
|
||||
@ -46,20 +41,7 @@ const GridPanelSwitch = forwardRef<
|
||||
query,
|
||||
thresholds,
|
||||
},
|
||||
[PANEL_TYPES.LIST]:
|
||||
dataSource === DataSource.LOGS
|
||||
? {
|
||||
selectedLogsFields: selectedLogFields || [],
|
||||
query,
|
||||
version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3
|
||||
selectedTime,
|
||||
}
|
||||
: {
|
||||
selectedTracesFields: selectedTracesFields || [],
|
||||
query,
|
||||
version: DEFAULT_ENTITY_VERSION, // As we don't support for Metrics, defaulting to v3
|
||||
selectedTime,
|
||||
},
|
||||
[PANEL_TYPES.LIST]: null,
|
||||
[PANEL_TYPES.TRACE]: null,
|
||||
[PANEL_TYPES.BAR]: {
|
||||
data,
|
||||
@ -70,19 +52,7 @@ const GridPanelSwitch = forwardRef<
|
||||
};
|
||||
|
||||
return result;
|
||||
}, [
|
||||
data,
|
||||
options,
|
||||
ref,
|
||||
yAxisUnit,
|
||||
thresholds,
|
||||
panelData,
|
||||
query,
|
||||
dataSource,
|
||||
selectedLogFields,
|
||||
selectedTime,
|
||||
selectedTracesFields,
|
||||
]);
|
||||
}, [data, options, ref, yAxisUnit, thresholds, panelData, query]);
|
||||
|
||||
const Component = getComponentForPanelType(panelType, dataSource) as FC<
|
||||
PropsTypePropsMap[typeof panelType]
|
||||
|
@ -2,9 +2,7 @@ import { StaticLineProps, ToggleGraphProps } from 'components/Graph/types';
|
||||
import { UplotProps } from 'components/Uplot/Uplot';
|
||||
import { GridTableComponentProps } from 'container/GridTableComponent/types';
|
||||
import { GridValueComponentProps } from 'container/GridValueComponent/types';
|
||||
import { LogsPanelComponentProps } from 'container/LogsPanelTable/LogsPanelComponent';
|
||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { TracesTableComponentProps } from 'container/TracesTableComponent/TracesTableComponent';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { ForwardedRef } from 'react';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
@ -40,7 +38,7 @@ export type PropsTypePropsMap = {
|
||||
[PANEL_TYPES.VALUE]: GridValueComponentProps;
|
||||
[PANEL_TYPES.TABLE]: GridTableComponentProps;
|
||||
[PANEL_TYPES.TRACE]: null;
|
||||
[PANEL_TYPES.LIST]: LogsPanelComponentProps | TracesTableComponentProps;
|
||||
[PANEL_TYPES.LIST]: null;
|
||||
[PANEL_TYPES.BAR]: UplotProps & {
|
||||
ref: ForwardedRef<ToggleGraphProps | undefined>;
|
||||
};
|
||||
|
@ -4,82 +4,53 @@ import { Table } from 'antd';
|
||||
import LogDetail from 'components/LogDetail';
|
||||
import { VIEW_TYPES } from 'components/LogDetail/constants';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { OPERATORS, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import Controls from 'container/Controls';
|
||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
|
||||
import { tableStyles } from 'container/TracesExplorer/ListView/styles';
|
||||
import { useActiveLog } from 'hooks/logs/useActiveLog';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { Pagination } from 'hooks/queryPagination';
|
||||
import { useLogsData } from 'hooks/useLogsData';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { FlatLogData } from 'lib/logs/flatLogData';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import {
|
||||
Dispatch,
|
||||
HTMLAttributes,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
import { getLogPanelColumnsList } from './utils';
|
||||
import { getLogPanelColumnsList, getNextOrPreviousItems } from './utils';
|
||||
|
||||
function LogsPanelComponent({
|
||||
selectedLogsFields,
|
||||
query,
|
||||
selectedTime,
|
||||
widget,
|
||||
setRequestData,
|
||||
queryResponse,
|
||||
}: LogsPanelComponentProps): JSX.Element {
|
||||
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
|
||||
const [pagination, setPagination] = useState<Pagination>({
|
||||
offset: 0,
|
||||
limit: 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,
|
||||
},
|
||||
};
|
||||
limit: widget.query.builder.queryData[0].limit || 0,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
setRequestData({
|
||||
...requestData,
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
setRequestData((prev) => ({
|
||||
...prev,
|
||||
tableParams: {
|
||||
pagination,
|
||||
},
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [pagination]);
|
||||
}));
|
||||
}, [pagination, setRequestData]);
|
||||
|
||||
const [pageSize, setPageSize] = useState<number>(10);
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const handleChangePageSize = (value: number): void => {
|
||||
setPagination({
|
||||
@ -88,53 +59,35 @@ function LogsPanelComponent({
|
||||
offset: value,
|
||||
});
|
||||
setPageSize(value);
|
||||
const newQueryData = { ...requestData.query };
|
||||
newQueryData.builder.queryData[0].pageSize = value;
|
||||
const newRequestData = {
|
||||
...requestData,
|
||||
query: newQueryData,
|
||||
tableParams: {
|
||||
pagination,
|
||||
},
|
||||
};
|
||||
setRequestData(newRequestData);
|
||||
setRequestData((prev) => {
|
||||
const newQueryData = { ...prev.query };
|
||||
newQueryData.builder.queryData[0].pageSize = value;
|
||||
return {
|
||||
...prev,
|
||||
query: newQueryData,
|
||||
tableParams: {
|
||||
pagination: {
|
||||
limit: 0,
|
||||
offset: value,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const { data, isFetching, isError } = useGetQueryRange(
|
||||
{
|
||||
...requestData,
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
selectedTime: selectedTime?.enum || 'GLOBAL_TIME',
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
DEFAULT_ENTITY_VERSION,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
globalSelectedTime,
|
||||
maxTime,
|
||||
minTime,
|
||||
requestData,
|
||||
pagination,
|
||||
selectedDashboard?.data.variables,
|
||||
],
|
||||
enabled: !!requestData.query && !!selectedLogsFields?.length,
|
||||
},
|
||||
);
|
||||
|
||||
const columns = getLogPanelColumnsList(selectedLogsFields);
|
||||
const columns = getLogPanelColumnsList(widget.selectedLogFields);
|
||||
|
||||
const dataLength =
|
||||
data?.payload?.data?.newResult?.data?.result[0]?.list?.length;
|
||||
queryResponse.data?.payload?.data?.newResult?.data?.result[0]?.list?.length;
|
||||
const totalCount = useMemo(() => dataLength || 0, [dataLength]);
|
||||
|
||||
const [firstLog, setFirstLog] = useState<ILog>();
|
||||
const [lastLog, setLastLog] = useState<ILog>();
|
||||
|
||||
const { logs } = useLogsData({
|
||||
result: data?.payload.data.newResult.data.result,
|
||||
result: queryResponse.data?.payload.data.newResult.data.result,
|
||||
panelType: PANEL_TYPES.LIST,
|
||||
stagedQuery: query,
|
||||
stagedQuery: widget.query,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
@ -167,92 +120,86 @@ function LogsPanelComponent({
|
||||
);
|
||||
|
||||
const isOrderByTimeStamp =
|
||||
query.builder.queryData[0].orderBy.length > 0 &&
|
||||
query.builder.queryData[0].orderBy[0].columnName === 'timestamp';
|
||||
widget.query.builder.queryData[0].orderBy.length > 0 &&
|
||||
widget.query.builder.queryData[0].orderBy[0].columnName === 'timestamp';
|
||||
|
||||
const handlePreviousPagination = (): void => {
|
||||
if (isOrderByTimeStamp) {
|
||||
setRequestData({
|
||||
...requestData,
|
||||
setRequestData((prev) => ({
|
||||
...prev,
|
||||
query: {
|
||||
...requestData.query,
|
||||
...prev.query,
|
||||
builder: {
|
||||
...requestData.query.builder,
|
||||
...prev.query.builder,
|
||||
queryData: [
|
||||
{
|
||||
...requestData.query.builder.queryData[0],
|
||||
...prev.query.builder.queryData[0],
|
||||
filters: {
|
||||
...requestData.query.builder.queryData[0].filters,
|
||||
...prev.query.builder.queryData[0].filters,
|
||||
items: [
|
||||
{
|
||||
id: uuid(),
|
||||
key: {
|
||||
key: 'id',
|
||||
type: '',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['>'],
|
||||
value: firstLog?.id || '',
|
||||
},
|
||||
...getNextOrPreviousItems(
|
||||
prev.query.builder.queryData[0].filters.items,
|
||||
'PREV',
|
||||
firstLog,
|
||||
),
|
||||
],
|
||||
},
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}));
|
||||
}
|
||||
if (!isOrderByTimeStamp) {
|
||||
setPagination({
|
||||
...pagination,
|
||||
limit: 0,
|
||||
offset: pagination.offset - pageSize,
|
||||
});
|
||||
}
|
||||
setPagination({
|
||||
...pagination,
|
||||
limit: 0,
|
||||
offset: pagination.offset - pageSize,
|
||||
});
|
||||
};
|
||||
|
||||
const handleNextPagination = (): void => {
|
||||
if (isOrderByTimeStamp) {
|
||||
setRequestData({
|
||||
...requestData,
|
||||
setRequestData((prev) => ({
|
||||
...prev,
|
||||
query: {
|
||||
...requestData.query,
|
||||
...prev.query,
|
||||
builder: {
|
||||
...requestData.query.builder,
|
||||
...prev.query.builder,
|
||||
queryData: [
|
||||
{
|
||||
...requestData.query.builder.queryData[0],
|
||||
...prev.query.builder.queryData[0],
|
||||
filters: {
|
||||
...requestData.query.builder.queryData[0].filters,
|
||||
...prev.query.builder.queryData[0].filters,
|
||||
items: [
|
||||
{
|
||||
id: uuid(),
|
||||
key: {
|
||||
key: 'id',
|
||||
type: '',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['<'],
|
||||
value: lastLog?.id || '',
|
||||
},
|
||||
...getNextOrPreviousItems(
|
||||
prev.query.builder.queryData[0].filters.items,
|
||||
'NEXT',
|
||||
lastLog,
|
||||
),
|
||||
],
|
||||
},
|
||||
limit: 0,
|
||||
offset: 0,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
return;
|
||||
}));
|
||||
}
|
||||
if (!isOrderByTimeStamp) {
|
||||
setPagination({
|
||||
...pagination,
|
||||
limit: 0,
|
||||
offset: pagination.offset + pageSize,
|
||||
});
|
||||
}
|
||||
setPagination({
|
||||
...pagination,
|
||||
limit: 0,
|
||||
offset: pagination.offset + pageSize,
|
||||
});
|
||||
};
|
||||
|
||||
if (isError) {
|
||||
if (queryResponse.isError) {
|
||||
return <div>{SOMETHING_WENT_WRONG}</div>;
|
||||
}
|
||||
|
||||
@ -265,19 +212,19 @@ function LogsPanelComponent({
|
||||
tableLayout="fixed"
|
||||
scroll={{ x: `calc(50vw - 10px)` }}
|
||||
sticky
|
||||
loading={isFetching}
|
||||
loading={queryResponse.isFetching}
|
||||
style={tableStyles}
|
||||
dataSource={flattenLogData}
|
||||
columns={columns}
|
||||
onRow={handleRow}
|
||||
/>
|
||||
</div>
|
||||
{!query.builder.queryData[0].limit && (
|
||||
{!widget.query.builder.queryData[0].limit && (
|
||||
<div className="controller">
|
||||
<Controls
|
||||
totalCount={totalCount}
|
||||
perPageOptions={PER_PAGE_OPTIONS}
|
||||
isLoading={isFetching}
|
||||
isLoading={queryResponse.isFetching}
|
||||
offset={pagination.offset}
|
||||
countPerPage={pageSize}
|
||||
handleNavigatePrevious={handlePreviousPagination}
|
||||
@ -301,13 +248,12 @@ function LogsPanelComponent({
|
||||
}
|
||||
|
||||
export type LogsPanelComponentProps = {
|
||||
selectedLogsFields: Widgets['selectedLogFields'];
|
||||
query: Query;
|
||||
selectedTime?: timePreferance;
|
||||
};
|
||||
|
||||
LogsPanelComponent.defaultProps = {
|
||||
selectedTime: undefined,
|
||||
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
widget: Widgets;
|
||||
};
|
||||
|
||||
export default LogsPanelComponent;
|
||||
|
@ -1,10 +1,15 @@
|
||||
import { ColumnsType } from 'antd/es/table';
|
||||
import { Typography } from 'antd/lib';
|
||||
import { OPERATORS } from 'constants/queryBuilder';
|
||||
// import Typography from 'antd/es/typography/Typography';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { ReactNode } from 'react';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { IField } from 'types/api/logs/fields';
|
||||
import { ILog } from 'types/api/logs/log';
|
||||
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export const getLogPanelColumnsList = (
|
||||
selectedLogFields: Widgets['selectedLogFields'],
|
||||
@ -36,3 +41,49 @@ export const getLogPanelColumnsList = (
|
||||
|
||||
return [...initialColumns, ...columns];
|
||||
};
|
||||
|
||||
export const getNextOrPreviousItems = (
|
||||
items: TagFilterItem[],
|
||||
direction: 'NEXT' | 'PREV',
|
||||
log?: ILog,
|
||||
): TagFilterItem[] => {
|
||||
const nextItem = {
|
||||
id: uuid(),
|
||||
key: {
|
||||
key: 'id',
|
||||
type: '',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['<'],
|
||||
value: log?.id || '',
|
||||
};
|
||||
const prevItem = {
|
||||
id: uuid(),
|
||||
key: {
|
||||
key: 'id',
|
||||
type: '',
|
||||
dataType: DataTypes.String,
|
||||
isColumn: true,
|
||||
},
|
||||
op: OPERATORS['>'],
|
||||
value: log?.id || '',
|
||||
};
|
||||
let index = items.findIndex((item) => item.op === OPERATORS['<']);
|
||||
if (index === -1) {
|
||||
index = items.findIndex((item) => item.op === OPERATORS['>']);
|
||||
}
|
||||
if (index === -1) {
|
||||
if (direction === 'NEXT') {
|
||||
return [...items, nextItem];
|
||||
}
|
||||
return [...items, prevItem];
|
||||
}
|
||||
const newItems = [...items];
|
||||
if (direction === 'NEXT') {
|
||||
newItems[index] = nextItem;
|
||||
} else {
|
||||
newItems[index] = prevItem;
|
||||
}
|
||||
return newItems;
|
||||
};
|
||||
|
@ -8,6 +8,7 @@ export const getWidgetQueryBuilder = ({
|
||||
title = '',
|
||||
panelTypes,
|
||||
yAxisUnit = '',
|
||||
fillSpans = false,
|
||||
id,
|
||||
}: GetWidgetQueryBuilderProps): Widgets => ({
|
||||
description: '',
|
||||
@ -24,4 +25,5 @@ export const getWidgetQueryBuilder = ({
|
||||
softMin: null,
|
||||
selectedLogFields: [],
|
||||
selectedTracesFields: [],
|
||||
fillSpans,
|
||||
});
|
||||
|
@ -70,6 +70,7 @@ function DBCall(): JSX.Element {
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'reqps',
|
||||
id: SERVICE_CHART_ID.dbCallsRPS,
|
||||
fillSpans: false,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
@ -89,7 +90,8 @@ function DBCall(): JSX.Element {
|
||||
title: GraphTitle.DATABASE_CALLS_AVG_DURATION,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ms',
|
||||
id: SERVICE_CHART_ID.dbCallsAvgDuration,
|
||||
id: GraphTitle.DATABASE_CALLS_AVG_DURATION,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
@ -112,8 +114,6 @@ function DBCall(): JSX.Element {
|
||||
<Card data-testid="database_call_rps">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
fillSpans={false}
|
||||
name="database_call_rps"
|
||||
widget={databaseCallsRPSWidget}
|
||||
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
|
||||
onGraphClickHandler(setSelectedTimeStamp)(
|
||||
@ -147,8 +147,6 @@ function DBCall(): JSX.Element {
|
||||
<Card data-testid="database_call_avg_duration">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
fillSpans
|
||||
name="database_call_avg_duration"
|
||||
widget={databaseCallsAverageDurationWidget}
|
||||
headerMenuList={MENU_ITEMS}
|
||||
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
|
||||
|
@ -18,7 +18,7 @@ import { useParams } from 'react-router-dom';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { GraphTitle, legend, MENU_ITEMS, SERVICE_CHART_ID } from '../constant';
|
||||
import { GraphTitle, legend, MENU_ITEMS } from '../constant';
|
||||
import { getWidgetQueryBuilder } from '../MetricsApplication.factory';
|
||||
import { Card, GraphContainer, Row } from '../styles';
|
||||
import { Button } from './styles';
|
||||
@ -60,7 +60,7 @@ function External(): JSX.Element {
|
||||
title: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: '%',
|
||||
id: SERVICE_CHART_ID.externalCallErrorPercentage,
|
||||
id: GraphTitle.EXTERNAL_CALL_ERROR_PERCENTAGE,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
@ -86,7 +86,8 @@ function External(): JSX.Element {
|
||||
title: GraphTitle.EXTERNAL_CALL_DURATION,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ms',
|
||||
id: SERVICE_CHART_ID.externalCallDuration,
|
||||
id: GraphTitle.EXTERNAL_CALL_DURATION,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
@ -108,7 +109,8 @@ function External(): JSX.Element {
|
||||
title: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'reqps',
|
||||
id: SERVICE_CHART_ID.externalCallRPSByAddress,
|
||||
id: GraphTitle.EXTERNAL_CALL_RPS_BY_ADDRESS,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
@ -130,7 +132,8 @@ function External(): JSX.Element {
|
||||
title: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ms',
|
||||
id: SERVICE_CHART_ID.externalCallDurationByAddress,
|
||||
id: GraphTitle.EXTERNAL_CALL_DURATION_BY_ADDRESS,
|
||||
fillSpans: true,
|
||||
}),
|
||||
[servicename, tagFilterItems],
|
||||
);
|
||||
@ -155,9 +158,7 @@ function External(): JSX.Element {
|
||||
<Card data-testid="external_call_error_percentage">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
fillSpans={false}
|
||||
headerMenuList={MENU_ITEMS}
|
||||
name="external_call_error_percentage"
|
||||
widget={externalCallErrorWidget}
|
||||
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
|
||||
onGraphClickHandler(setSelectedTimeStamp)(
|
||||
@ -192,8 +193,6 @@ function External(): JSX.Element {
|
||||
<Card data-testid="external_call_duration">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
fillSpans
|
||||
name="external_call_duration"
|
||||
headerMenuList={MENU_ITEMS}
|
||||
widget={externalCallDurationWidget}
|
||||
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
|
||||
@ -230,8 +229,6 @@ function External(): JSX.Element {
|
||||
<Card data-testid="external_call_rps_by_address">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
fillSpans
|
||||
name="external_call_rps_by_address"
|
||||
widget={externalCallRPSWidget}
|
||||
headerMenuList={MENU_ITEMS}
|
||||
onClickHandler={(xValue, yValue, mouseX, mouseY): Promise<void> =>
|
||||
@ -267,10 +264,8 @@ function External(): JSX.Element {
|
||||
<Card data-testid="external_call_duration_by_address">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
name="external_call_duration_by_address"
|
||||
widget={externalCallDurationAddressWidget}
|
||||
headerMenuList={MENU_ITEMS}
|
||||
fillSpans
|
||||
onClickHandler={(xValue, yValue, mouseX, mouseY): void => {
|
||||
onGraphClickHandler(setSelectedTimeStamp)(
|
||||
xValue,
|
||||
|
@ -19,13 +19,10 @@ import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useLocation, useParams } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { Tags } from 'types/reducer/trace';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
import { GraphTitle, SERVICE_CHART_ID } from '../constant';
|
||||
@ -49,9 +46,6 @@ import {
|
||||
} from './util';
|
||||
|
||||
function Application(): JSX.Element {
|
||||
const { maxTime, minTime } = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
const { servicename: encodedServiceName } = useParams<IServiceName>();
|
||||
const servicename = decodeURIComponent(encodedServiceName);
|
||||
const [selectedTimeStamp, setSelectedTimeStamp] = useState<number>(0);
|
||||
@ -59,10 +53,6 @@ function Application(): JSX.Element {
|
||||
const { queries } = useResourceAttribute();
|
||||
const urlQuery = useUrlQuery();
|
||||
|
||||
const selectedTags = useMemo(
|
||||
() => (convertRawQueriesToTraceSelectedTags(queries) as Tags[]) || [],
|
||||
[queries],
|
||||
);
|
||||
const isSpanMetricEnabled = useFeatureFlag(FeatureKeys.USE_SPAN_METRICS)
|
||||
?.active;
|
||||
|
||||
@ -94,7 +84,7 @@ function Application(): JSX.Element {
|
||||
isLoading: topLevelOperationsIsLoading,
|
||||
isError: topLevelOperationsIsError,
|
||||
} = useQuery<ServiceDataProps>({
|
||||
queryKey: [servicename, minTime, maxTime, selectedTags],
|
||||
queryKey: [servicename],
|
||||
queryFn: getTopLevelOperations,
|
||||
});
|
||||
|
||||
@ -116,49 +106,41 @@ function Application(): JSX.Element {
|
||||
[servicename, topLevelOperations],
|
||||
);
|
||||
|
||||
const operationPerSecWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: operationPerSec({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperationsRoute,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.RATE_PER_OPS,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ops',
|
||||
id: SERVICE_CHART_ID.rps,
|
||||
const operationPerSecWidget = getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: operationPerSec({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperationsRoute,
|
||||
}),
|
||||
[servicename, tagFilterItems, topLevelOperationsRoute],
|
||||
);
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.RATE_PER_OPS,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ops',
|
||||
id: SERVICE_CHART_ID.rps,
|
||||
});
|
||||
|
||||
const errorPercentageWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: errorPercentage({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperationsRoute,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.ERROR_PERCENTAGE,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: '%',
|
||||
id: SERVICE_CHART_ID.errorPercentage,
|
||||
const errorPercentageWidget = getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: errorPercentage({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
topLevelOperations: topLevelOperationsRoute,
|
||||
}),
|
||||
[servicename, tagFilterItems, topLevelOperationsRoute],
|
||||
);
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.ERROR_PERCENTAGE,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: '%',
|
||||
id: SERVICE_CHART_ID.errorPercentage,
|
||||
});
|
||||
|
||||
const onDragSelect = useCallback(
|
||||
(start: number, end: number) => {
|
||||
|
@ -89,8 +89,6 @@ function ApDexMetrics({
|
||||
|
||||
return (
|
||||
<Graph
|
||||
name="apdex"
|
||||
fillSpans={false}
|
||||
widget={apDexMetricsWidget}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={handleGraphClick('ApDex')}
|
||||
|
@ -50,7 +50,6 @@ function ApDexTraces({
|
||||
|
||||
return (
|
||||
<Graph
|
||||
name="apdex"
|
||||
widget={apDexTracesWidget}
|
||||
onDragSelect={onDragSelect}
|
||||
onClickHandler={handleGraphClick('ApDex')}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import { Skeleton } from 'antd';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
@ -46,28 +47,24 @@ function ServiceOverview({
|
||||
[isSpanMetricEnable, queries],
|
||||
);
|
||||
|
||||
const latencyWidget = useMemo(
|
||||
() =>
|
||||
getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: latency({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
isSpanMetricEnable,
|
||||
topLevelOperationsRoute,
|
||||
}),
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.LATENCY,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ns',
|
||||
id: SERVICE_CHART_ID.latency,
|
||||
const latencyWidget = getWidgetQueryBuilder({
|
||||
query: {
|
||||
queryType: EQueryType.QUERY_BUILDER,
|
||||
promql: [],
|
||||
builder: latency({
|
||||
servicename,
|
||||
tagFilterItems,
|
||||
isSpanMetricEnable,
|
||||
topLevelOperationsRoute,
|
||||
}),
|
||||
[servicename, isSpanMetricEnable, topLevelOperationsRoute, tagFilterItems],
|
||||
);
|
||||
clickhouse_sql: [],
|
||||
id: uuid(),
|
||||
},
|
||||
title: GraphTitle.LATENCY,
|
||||
panelTypes: PANEL_TYPES.TIME_SERIES,
|
||||
yAxisUnit: 'ns',
|
||||
id: SERVICE_CHART_ID.latency,
|
||||
});
|
||||
|
||||
const isQueryEnabled =
|
||||
!topLevelOperationsIsLoading && topLevelOperationsRoute.length > 0;
|
||||
@ -88,15 +85,23 @@ function ServiceOverview({
|
||||
</Button>
|
||||
<Card data-testid="service_latency">
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
name="service_latency"
|
||||
onDragSelect={onDragSelect}
|
||||
widget={latencyWidget}
|
||||
onClickHandler={handleGraphClick('Service')}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
fillSpans={false}
|
||||
version={ENTITY_VERSION_V4}
|
||||
/>
|
||||
{topLevelOperationsIsLoading && (
|
||||
<Skeleton
|
||||
style={{
|
||||
height: '100%',
|
||||
padding: '16px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!topLevelOperationsIsLoading && (
|
||||
<Graph
|
||||
onDragSelect={onDragSelect}
|
||||
widget={latencyWidget}
|
||||
onClickHandler={handleGraphClick('Service')}
|
||||
isQueryEnabled={isQueryEnabled}
|
||||
version={ENTITY_VERSION_V4}
|
||||
/>
|
||||
)}
|
||||
</GraphContainer>
|
||||
</Card>
|
||||
</>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Typography } from 'antd';
|
||||
import { Skeleton, Typography } from 'antd';
|
||||
import axios from 'axios';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import { ENTITY_VERSION_V4 } from 'constants/app';
|
||||
@ -27,15 +27,23 @@ function TopLevelOperation({
|
||||
</Typography>
|
||||
) : (
|
||||
<GraphContainer>
|
||||
<Graph
|
||||
fillSpans={false}
|
||||
name={name}
|
||||
widget={widget}
|
||||
onClickHandler={handleGraphClick(opName)}
|
||||
onDragSelect={onDragSelect}
|
||||
isQueryEnabled={!topLevelOperationsIsLoading}
|
||||
version={ENTITY_VERSION_V4}
|
||||
/>
|
||||
{topLevelOperationsIsLoading && (
|
||||
<Skeleton
|
||||
style={{
|
||||
height: '100%',
|
||||
padding: '16px',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{!topLevelOperationsIsLoading && (
|
||||
<Graph
|
||||
widget={widget}
|
||||
onClickHandler={handleGraphClick(opName)}
|
||||
onDragSelect={onDragSelect}
|
||||
isQueryEnabled={!topLevelOperationsIsLoading}
|
||||
version={ENTITY_VERSION_V4}
|
||||
/>
|
||||
)}
|
||||
</GraphContainer>
|
||||
)}
|
||||
</Card>
|
||||
|
@ -13,7 +13,7 @@ export const Card = styled(CardComponent)`
|
||||
}
|
||||
|
||||
.ant-card-body {
|
||||
height: calc(100% - 40px);
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
@ -40,7 +40,7 @@ export const ColErrorContainer = styled(ColComponent)`
|
||||
|
||||
export const GraphContainer = styled.div`
|
||||
min-height: calc(40vh - 40px);
|
||||
height: calc(100% - 40px);
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export const GraphTitle = styled(Typography)`
|
||||
|
@ -10,6 +10,7 @@ export interface GetWidgetQueryBuilderProps {
|
||||
panelTypes: Widgets['panelTypes'];
|
||||
yAxisUnit?: Widgets['yAxisUnit'];
|
||||
id?: Widgets['id'];
|
||||
fillSpans?: Widgets['fillSpans'];
|
||||
}
|
||||
|
||||
export interface NavigateToTraceProps {
|
||||
|
@ -2,14 +2,11 @@ import './QuerySection.styles.scss';
|
||||
|
||||
import { Button, Tabs, Tooltip, Typography } from 'antd';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { QBShortcuts } from 'constants/shortcuts/QBShortcuts';
|
||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||
import { QueryBuilder } from 'container/QueryBuilder';
|
||||
import { QueryBuilderProps } from 'container/QueryBuilder/QueryBuilder.interfaces';
|
||||
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import { useShareBuilderUrl } from 'hooks/queryBuilder/useShareBuilderUrl';
|
||||
import { updateStepInterval } from 'hooks/queryBuilder/useStepInterval';
|
||||
@ -22,9 +19,12 @@ import {
|
||||
getSelectedWidgetIndex,
|
||||
} from 'providers/Dashboard/util';
|
||||
import { useCallback, useEffect, useMemo } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { EQueryType } from 'types/common/dashboard';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
@ -35,7 +35,7 @@ import PromQLQueryContainer from './QueryBuilder/promQL';
|
||||
|
||||
function QuerySection({
|
||||
selectedGraph,
|
||||
selectedTime,
|
||||
queryResponse,
|
||||
}: QueryProps): JSX.Element {
|
||||
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||
const urlQuery = useUrlQuery();
|
||||
@ -51,14 +51,6 @@ function QuerySection({
|
||||
|
||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
|
||||
const getWidgetQueryRange = useGetWidgetQueryRange(
|
||||
{
|
||||
graphType: selectedGraph,
|
||||
selectedTime: selectedTime.enum,
|
||||
},
|
||||
selectedDashboard?.data?.version || DEFAULT_ENTITY_VERSION,
|
||||
);
|
||||
|
||||
const { widgets } = selectedDashboard?.data || {};
|
||||
|
||||
const getWidget = useCallback(() => {
|
||||
@ -233,7 +225,7 @@ function QuerySection({
|
||||
<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" />
|
||||
<Button
|
||||
loading={getWidgetQueryRange.isFetching}
|
||||
loading={queryResponse.isFetching}
|
||||
type="primary"
|
||||
onClick={handleRunQuery}
|
||||
className="stage-run-query"
|
||||
@ -251,7 +243,10 @@ function QuerySection({
|
||||
|
||||
interface QueryProps {
|
||||
selectedGraph: PANEL_TYPES;
|
||||
selectedTime: WidgetGraphProps['selectedTime'];
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
}
|
||||
|
||||
export default QuerySection;
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { Card, Typography } from 'antd';
|
||||
import Spinner from 'components/Spinner';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { WidgetGraphProps } from 'container/NewWidget/types';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
import { WidgetGraphContainerProps } from 'container/NewWidget/types';
|
||||
// import useUrlQuery from 'hooks/useUrlQuery';
|
||||
// import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { getSortedSeriesData } from 'utils/getSortedSeriesData';
|
||||
|
||||
import { NotFoundContainer } from './styles';
|
||||
@ -14,58 +11,36 @@ import WidgetGraph from './WidgetGraphs';
|
||||
|
||||
function WidgetGraphContainer({
|
||||
selectedGraph,
|
||||
yAxisUnit,
|
||||
selectedTime,
|
||||
thresholds,
|
||||
fillSpans = false,
|
||||
softMax,
|
||||
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) {
|
||||
queryResponse,
|
||||
setRequestData,
|
||||
selectedWidget,
|
||||
}: WidgetGraphContainerProps): JSX.Element {
|
||||
if (queryResponse.data && selectedGraph === PANEL_TYPES.BAR) {
|
||||
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) {
|
||||
return <Card>Invalid widget</Card>;
|
||||
}
|
||||
|
||||
if (getWidgetQueryRange.error) {
|
||||
if (queryResponse?.error) {
|
||||
return (
|
||||
<NotFoundContainer>
|
||||
<Typography>{getWidgetQueryRange.error.message}</Typography>
|
||||
<Typography>{queryResponse.error.message}</Typography>
|
||||
</NotFoundContainer>
|
||||
);
|
||||
}
|
||||
if (getWidgetQueryRange.isLoading) {
|
||||
if (queryResponse.isLoading && selectedGraph !== PANEL_TYPES.LIST) {
|
||||
return <Spinner size="large" tip="Loading..." />;
|
||||
}
|
||||
|
||||
if (
|
||||
selectedGraph !== PANEL_TYPES.LIST &&
|
||||
getWidgetQueryRange.data?.payload.data.result.length === 0
|
||||
queryResponse.data?.payload.data.result.length === 0
|
||||
) {
|
||||
return (
|
||||
<NotFoundContainer>
|
||||
@ -75,7 +50,7 @@ function WidgetGraphContainer({
|
||||
}
|
||||
if (
|
||||
selectedGraph === PANEL_TYPES.LIST &&
|
||||
getWidgetQueryRange.data?.payload.data.newResult.data.result.length === 0
|
||||
queryResponse.data?.payload.data.newResult.data.result.length === 0
|
||||
) {
|
||||
return (
|
||||
<NotFoundContainer>
|
||||
@ -86,17 +61,9 @@ function WidgetGraphContainer({
|
||||
|
||||
return (
|
||||
<WidgetGraph
|
||||
yAxisUnit={yAxisUnit || ''}
|
||||
getWidgetQueryRange={getWidgetQueryRange}
|
||||
selectedWidget={selectedWidget}
|
||||
thresholds={thresholds}
|
||||
fillSpans={fillSpans}
|
||||
softMax={softMax}
|
||||
softMin={softMin}
|
||||
selectedLogFields={selectedLogFields}
|
||||
selectedTracesFields={selectedTracesFields}
|
||||
selectedTime={selectedTime}
|
||||
selectedGraph={selectedGraph}
|
||||
queryResponse={queryResponse}
|
||||
setRequestData={setRequestData}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -1,98 +1,35 @@
|
||||
import { QueryParams } from 'constants/query';
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import GridPanelSwitch from 'container/GridPanelSwitch';
|
||||
import { ThresholdProps } from 'container/NewWidget/RightContainer/Threshold/types';
|
||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import PanelWrapper from 'container/PanelWrapper/PanelWrapper';
|
||||
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 { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import GetMinMax from 'lib/getMinMax';
|
||||
import getTimeString from 'lib/getTimeString';
|
||||
import history from 'lib/history';
|
||||
import { getUPlotChartOptions } from 'lib/uPlotLib/getUplotChartOptions';
|
||||
import { getUPlotChartData } from 'lib/uPlotLib/utils/getUplotChartData';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getTimeRange } from 'utils/getTimeRange';
|
||||
|
||||
function WidgetGraph({
|
||||
getWidgetQueryRange,
|
||||
selectedWidget,
|
||||
yAxisUnit,
|
||||
thresholds,
|
||||
fillSpans,
|
||||
softMax,
|
||||
softMin,
|
||||
selectedLogFields,
|
||||
selectedTracesFields,
|
||||
selectedTime,
|
||||
selectedGraph,
|
||||
queryResponse,
|
||||
setRequestData,
|
||||
}: 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 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 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,
|
||||
]);
|
||||
|
||||
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 urlQuery = useUrlQuery();
|
||||
const location = useLocation();
|
||||
|
||||
const handleBackNavigation = (): void => {
|
||||
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(() => {
|
||||
window.addEventListener('popstate', handleBackNavigation);
|
||||
|
||||
@ -123,79 +82,25 @@ function WidgetGraph({
|
||||
// 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 (
|
||||
<div ref={graphRef} style={{ height: '100%' }}>
|
||||
<GridPanelSwitch
|
||||
data={chartData}
|
||||
options={options}
|
||||
panelType={selectedWidget.panelTypes}
|
||||
name={widgetId || 'legend_widget'}
|
||||
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}
|
||||
<PanelWrapper
|
||||
widget={selectedWidget}
|
||||
queryResponse={queryResponse}
|
||||
setRequestData={setRequestData}
|
||||
onDragSelect={onDragSelect}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
interface WidgetGraphProps {
|
||||
thresholds: ThresholdProps[];
|
||||
yAxisUnit: string;
|
||||
selectedWidget: Widgets;
|
||||
fillSpans: boolean;
|
||||
getWidgetQueryRange: UseQueryResult<
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
softMax: number | null;
|
||||
softMin: number | null;
|
||||
selectedLogFields: Widgets['selectedLogFields'];
|
||||
selectedTracesFields: Widgets['selectedTracesFields'];
|
||||
selectedTime: timePreferance;
|
||||
selectedGraph: PANEL_TYPES;
|
||||
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
}
|
||||
|
||||
export default WidgetGraph;
|
||||
|
@ -1,47 +1,20 @@
|
||||
import { InfoCircleOutlined } from '@ant-design/icons';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
import { Card } from 'container/GridCardLayout/styles';
|
||||
import { useGetWidgetQueryRange } from 'hooks/queryBuilder/useGetWidgetQueryRange';
|
||||
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||
import useUrlQuery from 'hooks/useUrlQuery';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { memo } from 'react';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
|
||||
import { WidgetGraphProps } from '../../types';
|
||||
import { WidgetGraphContainerProps } from '../../types';
|
||||
import PlotTag from './PlotTag';
|
||||
import { AlertIconContainer, Container } from './styles';
|
||||
import WidgetGraphComponent from './WidgetGraphContainer';
|
||||
|
||||
function WidgetGraph({
|
||||
selectedGraph,
|
||||
yAxisUnit,
|
||||
selectedTime,
|
||||
thresholds,
|
||||
fillSpans,
|
||||
softMax,
|
||||
softMin,
|
||||
selectedLogFields,
|
||||
selectedTracesFields,
|
||||
}: WidgetGraphProps): JSX.Element {
|
||||
queryResponse,
|
||||
setRequestData,
|
||||
selectedWidget,
|
||||
}: WidgetGraphContainerProps): JSX.Element {
|
||||
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) {
|
||||
return <Card $panelType={selectedGraph}>Invalid widget</Card>;
|
||||
@ -50,22 +23,17 @@ function WidgetGraph({
|
||||
return (
|
||||
<Container $panelType={selectedGraph}>
|
||||
<PlotTag queryType={currentQuery.queryType} panelType={selectedGraph} />
|
||||
{getWidgetQueryRange.error && (
|
||||
<AlertIconContainer color="red" title={getWidgetQueryRange.error.message}>
|
||||
{queryResponse.error && (
|
||||
<AlertIconContainer color="red" title={queryResponse.error.message}>
|
||||
<InfoCircleOutlined />
|
||||
</AlertIconContainer>
|
||||
)}
|
||||
|
||||
<WidgetGraphComponent
|
||||
thresholds={thresholds}
|
||||
selectedTime={selectedTime}
|
||||
selectedGraph={selectedGraph}
|
||||
yAxisUnit={yAxisUnit}
|
||||
fillSpans={fillSpans}
|
||||
softMax={softMax}
|
||||
softMin={softMin}
|
||||
selectedLogFields={selectedLogFields}
|
||||
selectedTracesFields={selectedTracesFields}
|
||||
queryResponse={queryResponse}
|
||||
setRequestData={setRequestData}
|
||||
selectedWidget={selectedWidget}
|
||||
/>
|
||||
</Container>
|
||||
);
|
||||
|
@ -1,5 +1,16 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { memo } from 'react';
|
||||
import { DEFAULT_ENTITY_VERSION } from 'constants/app';
|
||||
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 ExplorerColumnsRenderer from './ExplorerColumnsRenderer';
|
||||
@ -9,32 +20,81 @@ import WidgetGraph from './WidgetGraph';
|
||||
|
||||
function LeftContainer({
|
||||
selectedGraph,
|
||||
yAxisUnit,
|
||||
selectedTime,
|
||||
thresholds,
|
||||
fillSpans,
|
||||
softMax,
|
||||
softMin,
|
||||
selectedLogFields,
|
||||
setSelectedLogFields,
|
||||
selectedTracesFields,
|
||||
setSelectedTracesFields,
|
||||
selectedWidget,
|
||||
}: 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 (
|
||||
<>
|
||||
<WidgetGraph
|
||||
thresholds={thresholds}
|
||||
selectedTime={selectedTime}
|
||||
selectedGraph={selectedGraph}
|
||||
yAxisUnit={yAxisUnit}
|
||||
fillSpans={fillSpans}
|
||||
softMax={softMax}
|
||||
softMin={softMin}
|
||||
selectedLogFields={selectedLogFields}
|
||||
selectedTracesFields={selectedTracesFields}
|
||||
queryResponse={queryResponse}
|
||||
setRequestData={setRequestData}
|
||||
selectedWidget={selectedWidget}
|
||||
/>
|
||||
<QueryContainer>
|
||||
<QuerySection selectedTime={selectedTime} selectedGraph={selectedGraph} />
|
||||
<QuerySection selectedGraph={selectedGraph} queryResponse={queryResponse} />
|
||||
{selectedGraph === PANEL_TYPES.LIST && (
|
||||
<ExplorerColumnsRenderer
|
||||
selectedLogFields={selectedLogFields}
|
||||
|
@ -81,7 +81,7 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
return widgets?.find((e) => e.id === widgetId);
|
||||
}, [query, widgets]);
|
||||
|
||||
const selectedWidget = getWidget();
|
||||
const [selectedWidget, setSelectedWidget] = useState(getWidget());
|
||||
|
||||
const [title, setTitle] = useState<string>(
|
||||
selectedWidget?.title?.toString() || '',
|
||||
@ -129,6 +129,44 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
: 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 => {
|
||||
setSaveModal(false);
|
||||
setDiscardModal(false);
|
||||
@ -194,21 +232,21 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
...preWidgets,
|
||||
{
|
||||
...(selectedWidget || ({} as Widgets)),
|
||||
description,
|
||||
description: selectedWidget?.description || '',
|
||||
timePreferance: selectedTime.enum,
|
||||
isStacked: stacked,
|
||||
opacity,
|
||||
nullZeroValues: selectedNullZeroValue,
|
||||
title,
|
||||
yAxisUnit,
|
||||
isStacked: selectedWidget?.isStacked || false,
|
||||
opacity: selectedWidget?.opacity || '1',
|
||||
nullZeroValues: selectedWidget?.nullZeroValues || 'zero',
|
||||
title: selectedWidget?.title,
|
||||
yAxisUnit: selectedWidget?.yAxisUnit,
|
||||
panelTypes: graphType,
|
||||
query: currentQuery,
|
||||
thresholds,
|
||||
softMin,
|
||||
softMax,
|
||||
fillSpans: isFillSpans,
|
||||
selectedLogFields,
|
||||
selectedTracesFields,
|
||||
thresholds: selectedWidget?.thresholds,
|
||||
softMin: selectedWidget?.softMin || 0,
|
||||
softMax: selectedWidget?.softMax || 0,
|
||||
fillSpans: selectedWidget?.fillSpans,
|
||||
selectedLogFields: selectedWidget?.selectedLogFields || [],
|
||||
selectedTracesFields: selectedWidget?.selectedTracesFields || [],
|
||||
},
|
||||
...afterWidgets,
|
||||
],
|
||||
@ -234,21 +272,9 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
selectedDashboard,
|
||||
preWidgets,
|
||||
selectedWidget,
|
||||
description,
|
||||
selectedTime.enum,
|
||||
stacked,
|
||||
opacity,
|
||||
selectedNullZeroValue,
|
||||
title,
|
||||
yAxisUnit,
|
||||
graphType,
|
||||
currentQuery,
|
||||
thresholds,
|
||||
softMin,
|
||||
softMax,
|
||||
isFillSpans,
|
||||
selectedLogFields,
|
||||
selectedTracesFields,
|
||||
afterWidgets,
|
||||
updateDashboardMutation,
|
||||
setSelectedDashboard,
|
||||
@ -358,19 +384,16 @@ function NewWidget({ selectedGraph }: NewWidgetProps): JSX.Element {
|
||||
|
||||
<PanelContainer>
|
||||
<LeftContainerWrapper flex={5}>
|
||||
<LeftContainer
|
||||
selectedTime={selectedTime}
|
||||
selectedGraph={graphType}
|
||||
yAxisUnit={yAxisUnit}
|
||||
thresholds={thresholds}
|
||||
fillSpans={isFillSpans}
|
||||
softMax={softMax}
|
||||
softMin={softMin}
|
||||
selectedLogFields={selectedLogFields}
|
||||
setSelectedLogFields={setSelectedLogFields}
|
||||
selectedTracesFields={selectedTracesFields}
|
||||
setSelectedTracesFields={setSelectedTracesFields}
|
||||
/>
|
||||
{selectedWidget && (
|
||||
<LeftContainer
|
||||
selectedGraph={graphType}
|
||||
selectedLogFields={selectedLogFields}
|
||||
setSelectedLogFields={setSelectedLogFields}
|
||||
selectedTracesFields={selectedTracesFields}
|
||||
setSelectedTracesFields={setSelectedTracesFields}
|
||||
selectedWidget={selectedWidget}
|
||||
/>
|
||||
)}
|
||||
</LeftContainerWrapper>
|
||||
|
||||
<RightContainerWrapper flex={1}>
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
import { UseQueryResult } from 'react-query';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { ThresholdProps } from './RightContainer/Threshold/types';
|
||||
import { timePreferance } from './RightContainer/timeItems';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
export interface NewWidgetProps {
|
||||
selectedGraph: PANEL_TYPES;
|
||||
@ -11,15 +12,23 @@ export interface NewWidgetProps {
|
||||
fillSpans: Widgets['fillSpans'];
|
||||
}
|
||||
|
||||
export interface WidgetGraphProps extends NewWidgetProps {
|
||||
selectedTime: timePreferance;
|
||||
thresholds: ThresholdProps[];
|
||||
softMin: number | null;
|
||||
softMax: number | null;
|
||||
export interface WidgetGraphProps {
|
||||
selectedLogFields: Widgets['selectedLogFields'];
|
||||
setSelectedLogFields?: Dispatch<SetStateAction<Widgets['selectedLogFields']>>;
|
||||
selectedTracesFields: Widgets['selectedTracesFields'];
|
||||
setSelectedTracesFields?: Dispatch<
|
||||
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;
|
||||
};
|
||||
|
37
frontend/src/container/PanelWrapper/ListPanelWrapper.tsx
Normal file
37
frontend/src/container/PanelWrapper/ListPanelWrapper.tsx
Normal 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;
|
40
frontend/src/container/PanelWrapper/PanelWrapper.tsx
Normal file
40
frontend/src/container/PanelWrapper/PanelWrapper.tsx
Normal 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;
|
24
frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
Normal file
24
frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
Normal 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;
|
141
frontend/src/container/PanelWrapper/UplotPanelWrapper.tsx
Normal file
141
frontend/src/container/PanelWrapper/UplotPanelWrapper.tsx
Normal 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;
|
21
frontend/src/container/PanelWrapper/ValuePanelWrapper.tsx
Normal file
21
frontend/src/container/PanelWrapper/ValuePanelWrapper.tsx
Normal 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;
|
16
frontend/src/container/PanelWrapper/constants.ts
Normal file
16
frontend/src/container/PanelWrapper/constants.ts
Normal 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,
|
||||
};
|
22
frontend/src/container/PanelWrapper/panelWrapper.types.ts
Normal file
22
frontend/src/container/PanelWrapper/panelWrapper.types.ts
Normal 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;
|
||||
};
|
@ -1,12 +1,8 @@
|
||||
import './TracesTableComponent.styles.scss';
|
||||
|
||||
import { Table } from 'antd';
|
||||
// import { ResizeTable } from 'components/ResizeTable';
|
||||
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 { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { PER_PAGE_OPTIONS } from 'container/TracesExplorer/ListView/configs';
|
||||
import { tableStyles } from 'container/TracesExplorer/ListView/styles';
|
||||
import {
|
||||
@ -14,76 +10,51 @@ import {
|
||||
getTraceLink,
|
||||
transformDataWithDate,
|
||||
} from 'container/TracesExplorer/ListView/utils';
|
||||
import { useGetQueryRange } from 'hooks/queryBuilder/useGetQueryRange';
|
||||
import { Pagination } from 'hooks/queryPagination';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import history from 'lib/history';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { HTMLAttributes, useCallback, useMemo, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import {
|
||||
Dispatch,
|
||||
HTMLAttributes,
|
||||
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 { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
|
||||
function TracesTableComponent({
|
||||
selectedTracesFields,
|
||||
query,
|
||||
version,
|
||||
selectedTime,
|
||||
widget,
|
||||
queryResponse,
|
||||
setRequestData,
|
||||
}: TracesTableComponentProps): JSX.Element {
|
||||
const { selectedTime: globalSelectedTime, maxTime, minTime } = useSelector<
|
||||
AppState,
|
||||
GlobalReducer
|
||||
>((state) => state.globalTime);
|
||||
|
||||
const [pagination, setPagination] = useState<Pagination>({
|
||||
offset: 0,
|
||||
limit: 10,
|
||||
});
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const { data, isFetching, isError } = useGetQueryRange(
|
||||
{
|
||||
query,
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
selectedTime: selectedTime?.enum || 'GLOBAL_TIME',
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
params: {
|
||||
dataSource: 'traces',
|
||||
},
|
||||
useEffect(() => {
|
||||
setRequestData((prev) => ({
|
||||
...prev,
|
||||
tableParams: {
|
||||
pagination,
|
||||
selectColumns: selectedTracesFields,
|
||||
},
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
},
|
||||
version,
|
||||
{
|
||||
queryKey: [
|
||||
REACT_QUERY_KEY.GET_QUERY_RANGE,
|
||||
globalSelectedTime,
|
||||
maxTime,
|
||||
minTime,
|
||||
query,
|
||||
pagination,
|
||||
selectedTracesFields?.length,
|
||||
selectedTime?.enum,
|
||||
selectedDashboard?.data.variables,
|
||||
],
|
||||
enabled: !!query && !!selectedTracesFields?.length,
|
||||
},
|
||||
);
|
||||
}));
|
||||
}, [pagination, setRequestData]);
|
||||
|
||||
const columns = getListColumns(selectedTracesFields || []);
|
||||
const columns = getListColumns(widget.selectedTracesFields || []);
|
||||
|
||||
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 queryTableDataResult = data?.payload.data.newResult.data.result;
|
||||
const queryTableDataResult =
|
||||
queryResponse.data?.payload.data.newResult.data.result;
|
||||
const queryTableData = useMemo(() => queryTableDataResult || [], [
|
||||
queryTableDataResult,
|
||||
]);
|
||||
@ -108,7 +79,7 @@ function TracesTableComponent({
|
||||
[],
|
||||
);
|
||||
|
||||
if (isError) {
|
||||
if (queryResponse.isError) {
|
||||
return <div>{SOMETHING_WENT_WRONG}</div>;
|
||||
}
|
||||
|
||||
@ -119,7 +90,7 @@ function TracesTableComponent({
|
||||
pagination={false}
|
||||
tableLayout="fixed"
|
||||
scroll={{ x: true }}
|
||||
loading={isFetching}
|
||||
loading={queryResponse.isFetching}
|
||||
style={tableStyles}
|
||||
dataSource={transformedQueryTableData}
|
||||
columns={columns}
|
||||
@ -131,7 +102,7 @@ function TracesTableComponent({
|
||||
<Controls
|
||||
totalCount={totalCount}
|
||||
perPageOptions={PER_PAGE_OPTIONS}
|
||||
isLoading={isFetching}
|
||||
isLoading={queryResponse.isFetching}
|
||||
offset={pagination.offset}
|
||||
countPerPage={pagination.limit}
|
||||
handleNavigatePrevious={(): void => {
|
||||
@ -160,14 +131,12 @@ function TracesTableComponent({
|
||||
}
|
||||
|
||||
export type TracesTableComponentProps = {
|
||||
selectedTracesFields: Widgets['selectedTracesFields'];
|
||||
query: Query;
|
||||
version: string;
|
||||
selectedTime?: timePreferance;
|
||||
};
|
||||
|
||||
TracesTableComponent.defaultProps = {
|
||||
selectedTime: undefined,
|
||||
queryResponse: UseQueryResult<
|
||||
SuccessResponse<MetricRangePayloadProps, unknown>,
|
||||
Error
|
||||
>;
|
||||
widget: Widgets;
|
||||
setRequestData: Dispatch<SetStateAction<GetQueryResultsProps>>;
|
||||
};
|
||||
|
||||
export default TracesTableComponent;
|
||||
|
Loading…
x
Reference in New Issue
Block a user