Rajat Dabade ecd5ce92c2
List View for Dashboard (#4517)
* refactor: initial setup for list view logs

* feat: done with basic functionality panel view logs

* feat: added panel view

* fix: discard and edit issue

* refactor: removed not required params from uselogdata

* feat: trace list view

* fix: loader

* refactor: traces table component css update

* refactor: added open san font and udpated css

* fix: full view traces issue and search column css update

* refactor: remove consoles

* refactor: removed commented code and updated logic

* chore: build failure

* refactor: icons change for apdd panels

* refactor: rebased to develop

* refactor: added support for light mode

* refactor: fix tsc

* fix: query select issue

* chore: table column to lower case

* refactor: updated styling for both log and traces tables

* chore: removed comment code

* chore: remove the resizable block from table header traces

* refactor: log table header and body stayling updated

* fix: query range on every column add

* refactor: styling updates

* fix: query range log respect global time

* refactor: css update log table header

* refactor: removed unnecessary code

* refactor: log query range respect globaltime

* refactor: dropdown support to qb

* refactor: remove creating alert for list view

* refactor: fix the height of column select dropdown

* fix: dropdown suggestion for orderby

* refactor: remove the commented code

* refactor: full view respect global time

* refactor: css updates

* refactor: should fire query range on variable change

* refactor: css updates for log list view

* refactor: removed the unused changes

* refactor: handle error state for exploere columns

* refactor: handle error state for explorer columns

* chore: generate yarn lock file

* refactor: pagination for order by timestamp

* fix: full text body contain issue

* refactor: inverted the operator for next and previous button config

* refactor: rename variable handle light mode

* fix: no log issue

* chore: renamed variables

---------

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
2024-02-20 16:21:07 +05:30

248 lines
7.1 KiB
TypeScript

import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
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 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 { 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 { getTimeRange } from 'utils/getTimeRange';
import EmptyWidget from '../EmptyWidget';
import { MenuItemKeys } from '../WidgetHeader/contants';
import { GridCardGraphProps } from './types';
import WidgetGraphComponent from './WidgetGraphComponent';
function GridCardGraph({
widget,
name,
onClickHandler = _noop,
headerMenuList = [MenuItemKeys.View],
isQueryEnabled,
threshold,
variables,
fillSpans = false,
}: 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);
const endTime = searchParams.get(QueryParams.endTime);
if (startTime && endTime && startTime !== endTime) {
dispatch(
UpdateTimeInterval('custom', [
parseInt(getTimeString(startTime), 10),
parseInt(getTimeString(endTime), 10),
]),
);
}
};
useEffect(() => {
window.addEventListener('popstate', handleBackNavigation);
return (): void => {
window.removeEventListener('popstate', handleBackNavigation);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const graphRef = useRef<HTMLDivElement>(null);
const isVisible = useIntersectionObserver(graphRef, undefined, true);
useEffect(() => {
if (toScrollWidgetId === widget.id) {
graphRef.current?.scrollIntoView({
behavior: 'smooth',
block: 'center',
});
graphRef.current?.focus();
setToScrollWidgetId('');
}
}, [toScrollWidgetId, setToScrollWidgetId, widget.id]);
const updatedQuery = useStepInterval(widget?.query);
const isEmptyWidget =
widget?.id === PANEL_TYPES.EMPTY_WIDGET || isEmpty(widget);
const queryEnabledCondition =
isVisible &&
!isEmptyWidget &&
isQueryEnabled &&
widget.panelTypes !== PANEL_TYPES.LIST;
const queryResponse = useGetQueryRange(
{
selectedTime: widget?.timePreferance,
graphType: widget?.panelTypes,
query: updatedQuery,
globalSelectedInterval,
variables: getDashboardVariables(variables),
},
{
queryKey: [
maxTime,
minTime,
globalSelectedInterval,
variables,
widget?.query,
widget?.panelTypes,
widget.timePreferance,
],
keepPreviousData: true,
enabled: queryEnabledCondition,
refetchOnMount: false,
onError: (error) => {
setErrorMessage(error.message);
},
},
);
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]);
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),
);
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,
}),
[
widget?.id,
widget?.yAxisUnit,
widget.thresholds,
widget.softMax,
widget.softMin,
queryResponse.data?.payload,
containerDimensions,
isDarkMode,
onDragSelect,
onClickHandler,
minTimeScale,
maxTimeScale,
graphVisibility,
setGraphVisibility,
],
);
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}
onDragSelect={onDragSelect}
threshold={threshold}
headerMenuList={menuList}
onClickHandler={onClickHandler}
graphVisibiltyState={graphVisibility}
setGraphVisibility={setGraphVisibility}
isFetchingResponse={queryResponse.isFetching}
/>
)}
</div>
);
}
GridCardGraph.defaultProps = {
onDragSelect: undefined,
onClickHandler: undefined,
isQueryEnabled: true,
threshold: undefined,
headerMenuList: [MenuItemKeys.View],
};
export default memo(GridCardGraph);