From cf95b15ba118bbc8993973085ad7c525efff4d84 Mon Sep 17 00:00:00 2001 From: SagarRajput-7 <162284829+SagarRajput-7@users.noreply.github.com> Date: Tue, 4 Feb 2025 09:41:27 +0530 Subject: [PATCH] feat: celery - misc feedback fixes (#7019) * feat: celery - misc feedback fixes * feat: enabled better sharing and auto-refresh * feat: made task name filter more visible --- .../CeleryOverviewConfigOptions.tsx | 31 +--- .../CeleryOverviewTable.styles.scss | 1 - .../CeleryOverviewTable.tsx | 23 ++- .../CeleryTaskDetail/CeleryTaskDetail.tsx | 101 +----------- .../CeleryTaskGraph/CeleryTaskBar.tsx | 46 +++++- .../CeleryTaskGraph.style.scss | 23 ++- .../CeleryTaskGraph/CeleryTaskGraph.tsx | 12 ++ .../CeleryTaskGraph/CeleryTaskGraphGrid.tsx | 5 +- .../CeleryTaskGraph/CeleryTaskGraphUtils.ts | 102 +++++------- .../CeleryTaskLatencyGraph.tsx | 147 ++++++++++++------ .../CeleryTaskStateGraphConfig.tsx | 45 ++++-- .../src/components/CeleryTask/CeleryUtils.ts | 37 +++++ .../CeleryTask/useNavigateToTraces.ts | 10 +- .../GridCard/WidgetGraphComponent.tsx | 8 + .../GridCardLayout/GridCard/index.tsx | 14 ++ .../GridCardLayout/GridCard/types.ts | 4 + .../GridCardLayout/GridCardLayout.styles.scss | 10 ++ .../Celery/CeleryOverview/CeleryOverview.tsx | 2 +- .../pages/Celery/CeleryTask/CeleryTask.tsx | 29 +--- 19 files changed, 352 insertions(+), 298 deletions(-) diff --git a/frontend/src/components/CeleryOverview/CeleryOverviewConfigOptions/CeleryOverviewConfigOptions.tsx b/frontend/src/components/CeleryOverview/CeleryOverviewConfigOptions/CeleryOverviewConfigOptions.tsx index c50f379fda..ae2f1f6c1a 100644 --- a/frontend/src/components/CeleryOverview/CeleryOverviewConfigOptions/CeleryOverviewConfigOptions.tsx +++ b/frontend/src/components/CeleryOverview/CeleryOverviewConfigOptions/CeleryOverviewConfigOptions.tsx @@ -1,7 +1,6 @@ import './CeleryOverviewConfigOptions.styles.scss'; -import { Color } from '@signozhq/design-tokens'; -import { Button, Row, Select, Spin, Tooltip } from 'antd'; +import { Row, Select, Spin } from 'antd'; import { getValuesFromQueryParams, setQueryParamsFromOptions, @@ -10,10 +9,7 @@ import { useCeleryFilterOptions } from 'components/CeleryTask/useCeleryFilterOpt import { SelectMaxTagPlaceholder } from 'components/MessagingQueues/MQCommon/MQCommon'; import { QueryParams } from 'constants/query'; import useUrlQuery from 'hooks/useUrlQuery'; -import { Check, Share2 } from 'lucide-react'; -import { useState } from 'react'; import { useHistory, useLocation } from 'react-router-dom'; -import { useCopyToClipboard } from 'react-use'; interface SelectOptionConfig { placeholder: string; @@ -66,10 +62,6 @@ function FilterSelect({ } function CeleryOverviewConfigOptions(): JSX.Element { - const [isURLCopied, setIsURLCopied] = useState(false); - - const [, handleCopyToClipboard] = useCopyToClipboard(); - const selectConfigs: SelectOptionConfig[] = [ { placeholder: 'Service Name', @@ -98,14 +90,6 @@ function CeleryOverviewConfigOptions(): JSX.Element { }, ]; - const handleShareURL = (): void => { - handleCopyToClipboard(window.location.href); - setIsURLCopied(true); - setTimeout(() => { - setIsURLCopied(false); - }, 1000); - }; - return (
@@ -118,19 +102,6 @@ function CeleryOverviewConfigOptions(): JSX.Element { /> ))} - -
); } diff --git a/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.styles.scss b/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.styles.scss index 5273c4f893..676204585d 100644 --- a/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.styles.scss +++ b/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.styles.scss @@ -37,7 +37,6 @@ font-weight: 600; line-height: 18px; /* 163.636% */ letter-spacing: 0.44px; - text-transform: uppercase; &::before { background-color: transparent; diff --git a/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx b/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx index 685ee44f52..0b442ffeb5 100644 --- a/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx +++ b/frontend/src/components/CeleryOverview/CeleryOverviewTable/CeleryOverviewTable.tsx @@ -218,20 +218,26 @@ function getColumns(data: RowData[]): TableColumnsType { showTitle: false, }, width: 200, - sorter: (a: RowData, b: RowData): number => - String(a.error_percentage).localeCompare(String(b.error_percentage)), + sorter: (a: RowData, b: RowData): number => { + const aValue = Number(a.error_percentage); + const bValue = Number(b.error_percentage); + return aValue - bValue; + }, render: ProgressRender, }, { - title: 'LATENCY (P95)', + title: 'LATENCY (P95) in ms', dataIndex: 'p95_latency', key: 'p95_latency', ellipsis: { showTitle: false, }, width: 100, - sorter: (a: RowData, b: RowData): number => - String(a.p95_latency).localeCompare(String(b.p95_latency)), + sorter: (a: RowData, b: RowData): number => { + const aValue = Number(a.p95_latency); + const bValue = Number(b.p95_latency); + return aValue - bValue; + }, render: (value: number | string): string => { if (!isNumber(value)) return value.toString(); return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3); @@ -245,8 +251,11 @@ function getColumns(data: RowData[]): TableColumnsType { showTitle: false, }, width: 100, - sorter: (a: RowData, b: RowData): number => - String(a.throughput).localeCompare(String(b.throughput)), + sorter: (a: RowData, b: RowData): number => { + const aValue = Number(a.throughput); + const bValue = Number(b.throughput); + return aValue - bValue; + }, render: (value: number | string): string => { if (!isNumber(value)) return value.toString(); return (typeof value === 'string' ? parseFloat(value) : value).toFixed(3); diff --git a/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx b/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx index ae5ba5ffb6..4e28c6dc0d 100644 --- a/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx +++ b/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx @@ -2,24 +2,16 @@ import './CeleryTaskDetail.style.scss'; import { Color, Spacing } from '@signozhq/design-tokens'; import { Divider, Drawer, Typography } from 'antd'; -import { QueryParams } from 'constants/query'; import { PANEL_TYPES } from 'constants/queryBuilder'; import dayjs from 'dayjs'; import { useIsDarkMode } from 'hooks/useDarkMode'; -import useUrlQuery from 'hooks/useUrlQuery'; import { X } from 'lucide-react'; -import { useEffect, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { useHistory, useLocation } from 'react-router-dom'; -import { UpdateTimeInterval } from 'store/actions'; -import { AppState } from 'store/reducers'; +import { useState } from 'react'; import { Widgets } from 'types/api/dashboard/getAll'; import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange'; -import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse'; -import { GlobalReducer } from 'types/reducer/globalTime'; -import { v4 as uuidv4 } from 'uuid'; import CeleryTaskGraph from '../CeleryTaskGraph/CeleryTaskGraph'; +import { createFiltersFromData } from '../CeleryUtils'; import { useNavigateToTraces } from '../useNavigateToTraces'; export type CeleryTaskData = { @@ -39,40 +31,6 @@ export type CeleryTaskDetailProps = { drawerOpen: boolean; }; -const createFiltersFromData = ( - data: Record, -): Array<{ - id: string; - key: { - key: string; - dataType: DataTypes; - type: string; - isColumn: boolean; - isJSON: boolean; - id: string; - }; - op: string; - value: string; -}> => { - const excludeKeys = ['A', 'A_without_unit']; - - return Object.entries(data) - .filter(([key]) => !excludeKeys.includes(key)) - .map(([key, value]) => ({ - id: uuidv4(), - key: { - key, - dataType: DataTypes.String, - type: 'tag', - isColumn: false, - isJSON: false, - id: `${key}--string--tag--false`, - }, - op: '=', - value: value.toString(), - })); -}; - export default function CeleryTaskDetail({ widgetData, taskData, @@ -85,7 +43,7 @@ export default function CeleryTaskDetail({ !!taskData.entity && !!taskData.timeRange[0] && drawerOpen; const formatTimestamp = (timestamp: number): string => - dayjs(timestamp * 1000).format('MM-DD-YYYY hh:mm A'); + dayjs(timestamp).format('DD-MM-YYYY hh:mm A'); const [totalTask, setTotalTask] = useState(0); @@ -93,52 +51,9 @@ export default function CeleryTaskDetail({ setTotalTask((graphData?.result?.[0] as any)?.table?.rows.length); }; - // set time range - const { minTime, maxTime, selectedTime } = useSelector< - AppState, - GlobalReducer - >((state) => state.globalTime); - const startTime = taskData.timeRange[0]; const endTime = taskData.timeRange[1]; - const urlQuery = useUrlQuery(); - const location = useLocation(); - const history = useHistory(); - const dispatch = useDispatch(); - - useEffect(() => { - urlQuery.delete(QueryParams.relativeTime); - urlQuery.set(QueryParams.startTime, startTime.toString()); - urlQuery.set(QueryParams.endTime, endTime.toString()); - - const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; - history.replace(generatedUrl); - - if (startTime !== endTime) { - dispatch(UpdateTimeInterval('custom', [startTime, endTime])); - } - - return (): void => { - urlQuery.delete(QueryParams.relativeTime); - urlQuery.delete(QueryParams.startTime); - urlQuery.delete(QueryParams.endTime); - - if (selectedTime !== 'custom') { - dispatch(UpdateTimeInterval(selectedTime)); - urlQuery.set(QueryParams.relativeTime, selectedTime); - } else { - dispatch(UpdateTimeInterval('custom', [minTime / 1e6, maxTime / 1e6])); - urlQuery.set(QueryParams.startTime, Math.floor(minTime / 1e6).toString()); - urlQuery.set(QueryParams.endTime, Math.floor(maxTime / 1e6).toString()); - } - - const generatedUrl = `${location.pathname}?${urlQuery.toString()}`; - history.replace(generatedUrl); - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const navigateToTrace = useNavigateToTraces(); return ( @@ -149,10 +64,8 @@ export default function CeleryTaskDetail({ {`Details - ${taskData.entity}`}
- {`${formatTimestamp(taskData.timeRange[0])} ${ - taskData.timeRange[1] - ? `- ${formatTimestamp(taskData.timeRange[1])}` - : '' + {`${formatTimestamp(startTime)} ${ + endTime ? `- ${formatTimestamp(endTime)}` : '' }`} @@ -185,8 +98,10 @@ export default function CeleryTaskDetail({ ...rowData, [taskData.entity]: taskData.value, }); - navigateToTrace(filters); + navigateToTrace(filters, startTime, endTime); }} + start={startTime} + end={endTime} /> ); diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskBar.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskBar.tsx index 9e5f50c6b2..77d8ae2d5a 100644 --- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskBar.tsx +++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskBar.tsx @@ -19,6 +19,10 @@ import { Widgets } from 'types/api/dashboard/getAll'; import { GlobalReducer } from 'types/reducer/globalTime'; import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail'; +import { + applyCeleryFilterOnWidgetData, + getFiltersFromQueryParams, +} from '../CeleryUtils'; import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries'; import { celeryAllStateWidgetData, @@ -72,26 +76,60 @@ function CeleryTaskBar({ const [barState, setBarState] = useState(CeleryTaskState.All); + const selectedFilters = useMemo( + () => + getFiltersFromQueryParams( + QueryParams.taskName, + urlQuery, + 'celery.task_name', + ), + [urlQuery], + ); + const celeryAllStateData = useMemo( () => celeryAllStateWidgetData(minTime, maxTime), [minTime, maxTime], ); + const celeryAllStateFilteredData = useMemo( + () => + applyCeleryFilterOnWidgetData(selectedFilters || [], celeryAllStateData), + [selectedFilters, celeryAllStateData], + ); + const celeryFailedStateData = useMemo( () => celeryFailedStateWidgetData(minTime, maxTime), [minTime, maxTime], ); + const celeryFailedStateFilteredData = useMemo( + () => + applyCeleryFilterOnWidgetData(selectedFilters || [], celeryFailedStateData), + [selectedFilters, celeryFailedStateData], + ); + const celeryRetryStateData = useMemo( () => celeryRetryStateWidgetData(minTime, maxTime), [minTime, maxTime], ); + const celeryRetryStateFilteredData = useMemo( + () => + applyCeleryFilterOnWidgetData(selectedFilters || [], celeryRetryStateData), + [selectedFilters, celeryRetryStateData], + ); + const celerySuccessStateData = useMemo( () => celerySuccessStateWidgetData(minTime, maxTime), [minTime, maxTime], ); + const celerySuccessStateFilteredData = useMemo( + () => + applyCeleryFilterOnWidgetData(selectedFilters || [], celerySuccessStateData), + [selectedFilters, celerySuccessStateData], + ); + const onGraphClick = ( widgetData: Widgets, xValue: number, @@ -141,7 +179,7 @@ function CeleryTaskBar({
{barState === CeleryTaskState.All && ( void; @@ -42,6 +45,9 @@ function CeleryTaskGraph({ openTracesButton?: boolean; onOpenTraceBtnClick?: (record: RowData) => void; applyCeleryTaskFilter?: boolean; + customErrorMessage?: string; + start?: number; + end?: number; }): JSX.Element { const history = useHistory(); const { pathname } = useLocation(); @@ -116,6 +122,9 @@ function CeleryTaskGraph({ openTracesButton={openTracesButton} onOpenTraceBtnClick={onOpenTraceBtnClick} version={ENTITY_VERSION_V4} + customErrorMessage={customErrorMessage} + start={start} + end={end} /> ); @@ -129,6 +138,9 @@ CeleryTaskGraph.defaultProps = { openTracesButton: false, onOpenTraceBtnClick: undefined, applyCeleryTaskFilter: false, + customErrorMessage: undefined, + start: undefined, + end: undefined, }; export default CeleryTaskGraph; diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx index 69127b45e2..050e4e42cc 100644 --- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx +++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx @@ -123,11 +123,12 @@ export default function CeleryTaskGraphGrid({ key={celeryActiveTasksData.id} widgetData={celeryActiveTasksData} queryEnabled={queryEnabled} + customErrorMessage="Enable Flower metrics to view this graph" />
- Worker Count + Worker Online
@@ -173,7 +174,7 @@ export default function CeleryTaskGraphGrid({ {!collapsedSections.traceBasedGraphs && ( <> - +
{bottomWidgetData.map((widgetData, index) => ( void; queryEnabled: boolean; }): JSX.Element { const history = useHistory(); @@ -106,31 +105,51 @@ function CeleryTaskLatencyGraph({ [celeryTaskLatencyData, selectedFilters], ); - const onGraphClick = ( - xValue: number, - _yValue: number, - _mouseX: number, - _mouseY: number, - data?: { - [key: string]: string; + const [selectedTimeStamp, setSelectedTimeStamp] = useState(0); + const [entityData, setEntityData] = useState<{ + entity: string; + value: string; + }>(); + + const handleSetTimeStamp = useCallback((selectTime: number) => { + setSelectedTimeStamp(selectTime); + }, []); + + const onGraphClick = useCallback( + (type: string): OnClickPluginOpts['onClick'] => ( + xValue, + yValue, + mouseX, + mouseY, + data, + ): Promise => { + const [firstDataPoint] = Object.entries(data || {}); + const [entity, value] = firstDataPoint; + setEntityData({ + entity, + value, + }); + + return onGraphClickHandler(handleSetTimeStamp)( + xValue, + yValue, + mouseX, + mouseY, + type, + ); }, - ): void => { - const { start, end } = getStartAndEndTimesInMilliseconds(xValue); + [handleSetTimeStamp], + ); - // Extract entity and value from data - const [firstDataPoint] = Object.entries(data || {}); - const [entity, value] = (firstDataPoint || ([] as unknown)) as [ - string, - string, - ]; + const navigateToTraces = useNavigateToTraces(); - onClick?.({ - entity, - value, - timeRange: [start, end], - widgetData: celeryTimeSeriesTablesWidgetData(entity, value, 'Task Latency'), + const goToTraces = useCallback(() => { + const { start, end } = getStartAndEndTimesInMilliseconds(selectedTimeStamp); + const filters = createFiltersFromData({ + [entityData?.entity as string]: entityData?.value, }); - }; + navigateToTraces(filters, start, end, true); + }, [entityData, navigateToTraces, selectedTimeStamp]); return (
{graphState === CeleryTaskGraphState.P99 && ( - + <> + + + )} {graphState === CeleryTaskGraphState.P95 && ( - + <> + + + )} {graphState === CeleryTaskGraphState.P90 && ( - + <> + + + )}
diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskStateGraphConfig.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskStateGraphConfig.tsx index 21d8180d16..7768df0530 100644 --- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskStateGraphConfig.tsx +++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskStateGraphConfig.tsx @@ -2,8 +2,14 @@ import './CeleryTaskGraph.style.scss'; import { Col, Row } from 'antd'; -import { Dispatch, SetStateAction } from 'react'; +import { QueryParams } from 'constants/query'; +import useUrlQuery from 'hooks/useUrlQuery'; +import { Dispatch, SetStateAction, useMemo } from 'react'; +import { + applyCeleryFilterOnWidgetData, + getFiltersFromQueryParams, +} from '../CeleryUtils'; import { celeryAllStateCountWidgetData, celeryFailedStateCountWidgetData, @@ -42,16 +48,29 @@ function CeleryTaskStateGraphConfig({ setBarState(key as CeleryTaskState); }; - const { values, isLoading, isError } = useGetValueFromWidget( - [ - celeryAllStateCountWidgetData, - celeryFailedStateCountWidgetData, - celeryRetryStateCountWidgetData, - celerySuccessStateCountWidgetData, - ], - ['celery-task-states'], + const urlQuery = useUrlQuery(); + + const selectedFilters = useMemo( + () => + getFiltersFromQueryParams( + QueryParams.taskName, + urlQuery, + 'celery.task_name', + ), + [urlQuery], ); + const widgetData = [ + celeryAllStateCountWidgetData, + celeryFailedStateCountWidgetData, + celeryRetryStateCountWidgetData, + celerySuccessStateCountWidgetData, + ].map((data) => applyCeleryFilterOnWidgetData(selectedFilters || [], data)); + + const { values, isLoading, isError } = useGetValueFromWidget(widgetData, [ + 'celery-task-states', + ]); + return ( {tabs.map((tab, index) => ( @@ -66,7 +85,13 @@ function CeleryTaskStateGraphConfig({
{tab.label}
- {isLoading ? '-' : isError ? '-' : values[index]} + {isLoading + ? '-' + : isError + ? '-' + : Number.isNaN(values[index]) + ? '-' + : Math.round(Number(values[index]))}
{tab.key === barState &&
} diff --git a/frontend/src/components/CeleryTask/CeleryUtils.ts b/frontend/src/components/CeleryTask/CeleryUtils.ts index 93f1ef7629..1a2a4bfb48 100644 --- a/frontend/src/components/CeleryTask/CeleryUtils.ts +++ b/frontend/src/components/CeleryTask/CeleryUtils.ts @@ -90,3 +90,40 @@ export const paths = ( return renderer(u, seriesIdx, idx0, idx1, extendGap, buildClip); }; + +export const createFiltersFromData = ( + data: Record, +): Array<{ + id: string; + key: { + key: string; + dataType: DataTypes; + type: string; + isColumn: boolean; + isJSON: boolean; + id: string; + }; + op: string; + value: string; +}> => { + const excludeKeys = ['A', 'A_without_unit']; + + return ( + Object.entries(data) + .filter(([key]) => !excludeKeys.includes(key)) + // eslint-disable-next-line sonarjs/no-identical-functions + .map(([key, value]) => ({ + id: uuidv4(), + key: { + key, + dataType: DataTypes.String, + type: 'tag', + isColumn: false, + isJSON: false, + id: `${key}--string--tag--false`, + }, + op: '=', + value: value.toString(), + })) + ); +}; diff --git a/frontend/src/components/CeleryTask/useNavigateToTraces.ts b/frontend/src/components/CeleryTask/useNavigateToTraces.ts index 9bb8db9cf5..4d5885af89 100644 --- a/frontend/src/components/CeleryTask/useNavigateToTraces.ts +++ b/frontend/src/components/CeleryTask/useNavigateToTraces.ts @@ -12,6 +12,7 @@ export function useNavigateToTraces(): ( filters: TagFilterItem[], startTime?: number, endTime?: number, + sameTab?: boolean, ) => void { const { currentQuery } = useQueryBuilder(); const { minTime, maxTime } = useSelector( @@ -38,7 +39,12 @@ export function useNavigateToTraces(): ( ); return useCallback( - (filters: TagFilterItem[], startTime?: number, endTime?: number): void => { + ( + filters: TagFilterItem[], + startTime?: number, + endTime?: number, + sameTab?: boolean, + ): void => { const urlParams = new URLSearchParams(); if (startTime && endTime) { urlParams.set(QueryParams.startTime, startTime.toString()); @@ -58,7 +64,7 @@ export function useNavigateToTraces(): ( QueryParams.compositeQuery }=${JSONCompositeQuery}`; - window.open(newTraceExplorerPath, '_blank'); + window.open(newTraceExplorerPath, sameTab ? '_self' : '_blank'); }, [minTime, maxTime, prepareQuery], ); diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx index 073c331b17..030c4f707e 100644 --- a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx @@ -48,6 +48,7 @@ function WidgetGraphComponent({ openTracesButton, onOpenTraceBtnClick, customSeries, + customErrorMessage, }: WidgetGraphComponentProps): JSX.Element { const [deleteModal, setDeleteModal] = useState(false); const [hovered, setHovered] = useState(false); @@ -317,6 +318,13 @@ function WidgetGraphComponent({ setSearchTerm={setSearchTerm} />
+ + {queryResponse.error && customErrorMessage && ( +
+ {customErrorMessage} +
+ )} + {queryResponse.isLoading && widget.panelTypes !== PANEL_TYPES.LIST && ( )} diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx index 86a4d81fe8..388304bf99 100644 --- a/frontend/src/container/GridCardLayout/GridCard/index.tsx +++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx @@ -41,9 +41,15 @@ function GridCardGraph({ openTracesButton, onOpenTraceBtnClick, customSeries, + customErrorMessage, + start, + end, }: GridCardGraphProps): JSX.Element { const dispatch = useDispatch(); const [errorMessage, setErrorMessage] = useState(); + const [isInternalServerError, setIsInternalServerError] = useState( + false, + ); const { toScrollWidgetId, setToScrollWidgetId, @@ -178,6 +184,8 @@ function GridCardGraph({ variables: getDashboardVariables(variables), selectedTime: widget.timePreferance || 'GLOBAL_TIME', globalSelectedInterval, + start, + end, }, version || DEFAULT_ENTITY_VERSION, { @@ -207,6 +215,11 @@ function GridCardGraph({ refetchOnMount: false, onError: (error) => { setErrorMessage(error.message); + if (customErrorMessage) { + setIsInternalServerError( + String(error.message).includes('API responded with 500'), + ); + } setDashboardQueryRangeCalled(true); }, onSettled: (data) => { @@ -256,6 +269,7 @@ function GridCardGraph({ openTracesButton={openTracesButton} onOpenTraceBtnClick={onOpenTraceBtnClick} customSeries={customSeries} + customErrorMessage={isInternalServerError ? customErrorMessage : undefined} /> )}
diff --git a/frontend/src/container/GridCardLayout/GridCard/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts index 54c9d44c1c..9067f8cf72 100644 --- a/frontend/src/container/GridCardLayout/GridCard/types.ts +++ b/frontend/src/container/GridCardLayout/GridCard/types.ts @@ -37,6 +37,7 @@ export interface WidgetGraphComponentProps { openTracesButton?: boolean; onOpenTraceBtnClick?: (record: RowData) => void; customSeries?: (data: QueryData[]) => uPlot.Series[]; + customErrorMessage?: string; } export interface GridCardGraphProps { @@ -54,6 +55,9 @@ export interface GridCardGraphProps { openTracesButton?: boolean; onOpenTraceBtnClick?: (record: RowData) => void; customSeries?: (data: QueryData[]) => uPlot.Series[]; + customErrorMessage?: string; + start?: number; + end?: number; } export interface GetGraphVisibilityStateOnLegendClickProps { diff --git a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss index 762fcdbca8..567bd10470 100644 --- a/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss +++ b/frontend/src/container/GridCardLayout/GridCardLayout.styles.scss @@ -106,6 +106,16 @@ } } +.error-message-container { + display: flex; + width: 100%; + height: 100%; + justify-content: center; + align-items: center; + padding-top: 0; + padding-bottom: 32px; +} + .row-settings { .ant-popover-inner { width: 191px; diff --git a/frontend/src/pages/Celery/CeleryOverview/CeleryOverview.tsx b/frontend/src/pages/Celery/CeleryOverview/CeleryOverview.tsx index 9d3728516e..5fe85d6922 100644 --- a/frontend/src/pages/Celery/CeleryOverview/CeleryOverview.tsx +++ b/frontend/src/pages/Celery/CeleryOverview/CeleryOverview.tsx @@ -23,7 +23,7 @@ export default function CeleryOverview(): JSX.Element {

Messaging Queue Overview

- +
diff --git a/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx b/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx index 42252fe0b4..6602b11eb7 100644 --- a/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx +++ b/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx @@ -1,16 +1,12 @@ import './CeleryTask.styles.scss'; -import { Color } from '@signozhq/design-tokens'; -import { Button, Tooltip } from 'antd'; import CeleryTaskConfigOptions from 'components/CeleryTask/CeleryTaskConfigOptions/CeleryTaskConfigOptions'; import CeleryTaskDetail, { CaptureDataProps, } from 'components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail'; import CeleryTaskGraphGrid from 'components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid'; import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2'; -import { Check, Share2 } from 'lucide-react'; import { useState } from 'react'; -import { useCopyToClipboard } from 'react-use'; export default function CeleryTask(): JSX.Element { const [task, setTask] = useState(null); @@ -19,36 +15,13 @@ export default function CeleryTask(): JSX.Element { setTask(captureData); }; - const [isURLCopied, setIsURLCopied] = useState(false); - - const [, handleCopyToClipboard] = useCopyToClipboard(); - return (

Celery

- - -