mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-26 10:14:30 +08:00

* 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>
248 lines
7.1 KiB
TypeScript
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);
|