diff --git a/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx b/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx
index 05702a06d37..75f6fae6ea2 100644
--- a/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx
+++ b/frontend/src/components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail.tsx
@@ -3,9 +3,12 @@ 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 ROUTES from 'constants/routes';
import dayjs from 'dayjs';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useUrlQuery from 'hooks/useUrlQuery';
+import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { X } from 'lucide-react';
import { useEffect, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
@@ -24,16 +27,18 @@ export type CeleryTaskData = {
timeRange: [number, number];
};
+export interface CaptureDataProps extends CeleryTaskData {
+ widgetData: Widgets;
+}
+
export type CeleryTaskDetailProps = {
onClose: () => void;
- mainTitle: string;
widgetData: Widgets;
taskData: CeleryTaskData;
drawerOpen: boolean;
};
export default function CeleryTaskDetail({
- mainTitle,
widgetData,
taskData,
onClose,
@@ -50,7 +55,6 @@ export default function CeleryTaskDetail({
const [totalTask, setTotalTask] = useState(0);
const getGraphData = (graphData?: MetricRangePayloadProps['data']): void => {
- console.log(graphData);
setTotalTask((graphData?.result?.[0] as any)?.table?.rows.length);
};
@@ -100,12 +104,23 @@ export default function CeleryTaskDetail({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
+ const navigateToTrace = (data: RowData): void => {
+ console.log('navigateToTrace', data);
+ const urlParams = new URLSearchParams();
+ urlParams.set(QueryParams.startTime, (minTime / 1000000).toString());
+ urlParams.set(QueryParams.endTime, (maxTime / 1000000).toString());
+
+ const newTraceExplorerPath = `${ROUTES.TRACES_EXPLORER}`; // todo-sagar - add filters
+
+ history.push(newTraceExplorerPath);
+ };
+
return (
- {mainTitle}
+ {`Details - ${taskData.entity}`}
{`${formatTimestamp(taskData.timeRange[0])} ${
@@ -115,7 +130,7 @@ export default function CeleryTaskDetail({
}`}
-
task-details
+
{taskData.value}
}
@@ -135,9 +150,11 @@ export default function CeleryTaskDetail({
>
{}}
getGraphData={getGraphData}
+ panelType={PANEL_TYPES.TABLE}
queryEnabled
+ openTracesButton
+ onOpenTraceBtnClick={navigateToTrace}
/>
);
diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraph.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraph.tsx
index 09a84a9baf6..9aaf6208d23 100644
--- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraph.tsx
+++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraph.tsx
@@ -5,6 +5,8 @@ import GridCard from 'container/GridCardLayout/GridCard';
import { Card } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useUrlQuery from 'hooks/useUrlQuery';
+import { RowData } from 'lib/query/createTableColumnsFromQuery';
+import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
import { useCallback } from 'react';
import { useDispatch } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
@@ -12,18 +14,27 @@ import { UpdateTimeInterval } from 'store/actions';
import { Widgets } from 'types/api/dashboard/getAll';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
-import { CeleryTaskData } from '../CeleryTaskDetail/CeleryTaskDetail';
+import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
+import { celeryTimeSeriesTablesWidgetData } from './CeleryTaskGraphUtils';
function CeleryTaskGraph({
widgetData,
onClick,
getGraphData,
queryEnabled,
+ rightPanelTitle,
+ panelType,
+ openTracesButton,
+ onOpenTraceBtnClick,
}: {
widgetData: Widgets;
- onClick: (task: CeleryTaskData) => void;
+ onClick?: (task: CaptureDataProps) => void;
getGraphData?: (graphData?: MetricRangePayloadProps['data']) => void;
queryEnabled: boolean;
+ rightPanelTitle?: string;
+ panelType?: PANEL_TYPES;
+ openTracesButton?: boolean;
+ onOpenTraceBtnClick?: (record: RowData) => void;
}): JSX.Element {
const history = useHistory();
const { pathname } = useLocation();
@@ -51,19 +62,37 @@ function CeleryTaskGraph({
return (
{
- console.log('clicked', arg); // todo-sagar: add logic to handle click
- onClick(arg as any);
+ onClickHandler={(xValue, _yValue, _mouseX, _mouseY, data): void => {
+ const { start, end } = getStartAndEndTimesInMilliseconds(xValue);
+
+ // Extract entity and value from data
+ const [firstDataPoint] = Object.entries(data || {});
+ const [entity, value] = firstDataPoint || [];
+
+ const widgetData = celeryTimeSeriesTablesWidgetData(
+ entity,
+ value,
+ rightPanelTitle || '',
+ );
+
+ onClick?.({
+ entity,
+ value,
+ timeRange: [start, end],
+ widgetData,
+ });
}}
getGraphData={getGraphData}
isQueryEnabled={queryEnabled}
+ openTracesButton={openTracesButton}
+ onOpenTraceBtnClick={onOpenTraceBtnClick}
/>
);
@@ -71,6 +100,11 @@ function CeleryTaskGraph({
CeleryTaskGraph.defaultProps = {
getGraphData: undefined,
+ onClick: undefined,
+ rightPanelTitle: undefined,
+ panelType: PANEL_TYPES.TIME_SERIES,
+ openTracesButton: false,
+ onOpenTraceBtnClick: undefined,
};
export default CeleryTaskGraph;
diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx
index ef022b20c87..d6dcfda13b1 100644
--- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx
+++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid.tsx
@@ -1,6 +1,6 @@
import './CeleryTaskGraph.style.scss';
-import { CeleryTaskData } from '../CeleryTaskDetail/CeleryTaskDetail';
+import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
import CeleryTaskGraph from './CeleryTaskGraph';
import {
celeryActiveTasksWidgetData,
@@ -16,7 +16,7 @@ export default function CeleryTaskGraphGrid({
onClick,
queryEnabled,
}: {
- onClick: (task: CeleryTaskData) => void;
+ onClick: (task: CaptureDataProps) => void;
queryEnabled: boolean;
}): JSX.Element {
const bottomWidgetData = [
@@ -25,14 +25,19 @@ export default function CeleryTaskGraphGrid({
celeryLatencyByWorkerWidgetData,
];
+ const rightPanelTitle = [
+ 'Tasks/s by worker',
+ 'Error% by worker',
+ 'Latency by worker',
+ ];
+
return (
-
+
@@ -41,17 +46,17 @@ export default function CeleryTaskGraphGrid({
- {bottomWidgetData.map((widgetData) => (
+ {bottomWidgetData.map((widgetData, index) => (
))}
diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphUtils.ts b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphUtils.ts
index 131cb7dc6c8..bd9dafc4b4e 100644
--- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphUtils.ts
+++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphUtils.ts
@@ -5,6 +5,7 @@ import { getWidgetQuery } from 'pages/MessagingQueues/MQDetails/MetricPage/Metri
import { Widgets } from 'types/api/dashboard/getAll';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
+import { v4 as uuidv4 } from 'uuid';
// State Graphs
export const celeryAllStateWidgetData = getWidgetQueryBuilder(
@@ -491,6 +492,61 @@ export const celeryWorkerOnlineWidgetData = getWidgetQueryBuilder(
}),
);
+// Task Latency
+export const celeryTaskLatencyWidgetData = (type: string): Widgets =>
+ getWidgetQueryBuilder(
+ getWidgetQuery({
+ title: 'Task Latency',
+ description: 'Represents the latency of task execution.',
+ queryData: [
+ {
+ aggregateAttribute: {
+ dataType: DataTypes.Float64,
+ id: 'duration_nano--float64----true',
+ isColumn: true,
+ isJSON: false,
+ key: 'duration_nano',
+ type: '',
+ },
+ aggregateOperator: type || 'p99',
+ dataSource: DataSource.TRACES,
+ disabled: false,
+ expression: 'A',
+ filters: {
+ items: [],
+ op: 'AND',
+ },
+ functions: [],
+ groupBy: [
+ {
+ dataType: DataTypes.String,
+ id: 'celery.task_name--string--tag--false',
+ isColumn: false,
+ isJSON: false,
+ key: 'celery.task_name',
+ type: 'tag',
+ },
+ ],
+ having: [],
+ legend: '',
+ limit: null,
+ orderBy: [
+ {
+ columnName: '#SIGNOZ_VALUE',
+ order: 'asc',
+ },
+ ],
+ queryName: 'A',
+ reduceTo: 'avg',
+ spaceAggregation: 'sum',
+ stepInterval: 60,
+ timeAggregation: 'p99',
+ },
+ ],
+ yAxisUnit: 'ns',
+ }),
+ );
+
// Tables
export const celerySlowestTasksTableWidgetData = getWidgetQueryBuilder(
getWidgetQuery({
@@ -542,6 +598,7 @@ export const celerySlowestTasksTableWidgetData = getWidgetQueryBuilder(
timeAggregation: 'avg',
},
],
+ columnUnits: { A: 'ns' },
}),
);
@@ -609,6 +666,7 @@ export const celeryRetryTasksTableWidgetData = getWidgetQueryBuilder(
timeAggregation: 'avg',
},
],
+ columnUnits: { A: 'ns' },
}),
);
@@ -617,6 +675,7 @@ export const celeryFailedTasksTableWidgetData = getWidgetQueryBuilder(
title: 'Top 10 tasks in FAILED state',
description: 'Represents the top 10 tasks in failed state.',
panelTypes: PANEL_TYPES.TABLE,
+ columnUnits: { A: 'ns' },
queryData: [
{
aggregateAttribute: {
@@ -743,15 +802,20 @@ export const celerySuccessTasksTableWidgetData = getWidgetQueryBuilder(
timeAggregation: 'avg',
},
],
+ columnUnits: { A: 'ns' },
}),
);
-// Task Latency
-export const celeryTaskLatencyWidgetData = (type: string): Widgets =>
+export const celeryTimeSeriesTablesWidgetData = (
+ entity: string,
+ value: string | number,
+ rightPanelTitle: string,
+): Widgets =>
getWidgetQueryBuilder(
getWidgetQuery({
- title: 'Task Latency',
- description: 'Represents the latency of task execution.',
+ title: rightPanelTitle,
+ description: '',
+ panelTypes: PANEL_TYPES.TABLE,
queryData: [
{
aggregateAttribute: {
@@ -762,12 +826,26 @@ export const celeryTaskLatencyWidgetData = (type: string): Widgets =>
key: 'duration_nano',
type: '',
},
- aggregateOperator: type || 'p99',
+ aggregateOperator: 'avg',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
filters: {
- items: [],
+ items: [
+ {
+ id: uuidv4(),
+ key: {
+ dataType: DataTypes.String,
+ id: `${entity}--string--tag--false`,
+ isColumn: false,
+ isJSON: false,
+ key: `${entity}`,
+ type: 'tag',
+ },
+ op: '=',
+ value,
+ },
+ ],
op: 'AND',
},
functions: [],
@@ -784,19 +862,14 @@ export const celeryTaskLatencyWidgetData = (type: string): Widgets =>
having: [],
legend: '',
limit: null,
- orderBy: [
- {
- columnName: '#SIGNOZ_VALUE',
- order: 'asc',
- },
- ],
+ orderBy: [],
queryName: 'A',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: 60,
- timeAggregation: 'p99',
+ timeAggregation: 'avg',
},
],
- yAxisUnit: 'ns',
+ columnUnits: { A: 'ns' },
}),
);
diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskHistogram.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskHistogram.tsx
index 0651bba95ec..a9efa42f2b9 100644
--- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskHistogram.tsx
+++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskHistogram.tsx
@@ -12,7 +12,7 @@ import { useDispatch } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions';
-import { CeleryTaskData } from '../CeleryTaskDetail/CeleryTaskDetail';
+// import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
import {
celeryAllStateWidgetData,
celeryFailedStateWidgetData,
@@ -25,10 +25,10 @@ import {
} from './CeleryTaskStateGraphConfig';
function CeleryTaskHistogram({
- onClick,
+ // onClick,
queryEnabled,
}: {
- onClick: (task: CeleryTaskData) => void;
+ // onClick?: (task: CaptureDataProps) => void;
queryEnabled: boolean;
}): JSX.Element {
@@ -59,10 +59,6 @@ function CeleryTaskHistogram({
CeleryTaskState.All,
);
- const getGraphData = (graphData: any): void => {
- console.log('graphData', graphData);
- };
-
return (
{
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
isQueryEnabled={queryEnabled}
/>
)}
@@ -92,11 +83,6 @@ function CeleryTaskHistogram({
widget={celeryFailedStateWidgetData}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
- onClickHandler={(arg): void => {
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
isQueryEnabled={queryEnabled}
/>
)}
@@ -105,11 +91,6 @@ function CeleryTaskHistogram({
widget={celeryRetryStateWidgetData}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
- onClickHandler={(arg): void => {
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
isQueryEnabled={queryEnabled}
/>
)}
@@ -118,11 +99,6 @@ function CeleryTaskHistogram({
widget={celerySuccessStateWidgetData}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
- onClickHandler={(arg): void => {
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
isQueryEnabled={queryEnabled}
/>
)}
diff --git a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskLatencyGraph.tsx b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskLatencyGraph.tsx
index 45215adf7cd..e135a1cfffe 100644
--- a/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskLatencyGraph.tsx
+++ b/frontend/src/components/CeleryTask/CeleryTaskGraph/CeleryTaskLatencyGraph.tsx
@@ -8,13 +8,17 @@ import GridCard from 'container/GridCardLayout/GridCard';
import { Card } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useUrlQuery from 'hooks/useUrlQuery';
+import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
import { useCallback, useState } from 'react';
import { useDispatch } from 'react-redux';
import { useHistory, useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions';
-import { CeleryTaskData } from '../CeleryTaskDetail/CeleryTaskDetail';
-import { celeryTaskLatencyWidgetData } from './CeleryTaskGraphUtils';
+import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
+import {
+ celeryTaskLatencyWidgetData,
+ celeryTimeSeriesTablesWidgetData,
+} from './CeleryTaskGraphUtils';
interface TabData {
label: string;
@@ -31,8 +35,7 @@ function CeleryTaskLatencyGraph({
onClick,
queryEnabled,
}: {
- onClick: (task: CeleryTaskData) => void;
-
+ onClick: (task: CaptureDataProps) => void;
queryEnabled: boolean;
}): JSX.Element {
const history = useHistory();
@@ -72,8 +75,30 @@ function CeleryTaskLatencyGraph({
[dispatch, history, pathname, urlQuery],
);
- const getGraphData = (graphData: any): void => {
- console.log('graphData', graphData);
+ const onGraphClick = (
+ xValue: number,
+ _yValue: number,
+ _mouseX: number,
+ _mouseY: number,
+ data?: {
+ [key: string]: string;
+ },
+ ): void => {
+ const { start, end } = getStartAndEndTimesInMilliseconds(xValue);
+
+ // Extract entity and value from data
+ const [firstDataPoint] = Object.entries(data || {});
+ const [entity, value] = (firstDataPoint || ([] as unknown)) as [
+ string,
+ string,
+ ];
+
+ onClick?.({
+ entity,
+ value,
+ timeRange: [start, end],
+ widgetData: celeryTimeSeriesTablesWidgetData(entity, value, 'Task Latency'),
+ });
};
return (
@@ -109,11 +134,7 @@ function CeleryTaskLatencyGraph({
widget={celeryTaskLatencyWidgetData(graphState)}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
- onClickHandler={(arg): void => {
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
+ onClickHandler={onGraphClick}
isQueryEnabled={queryEnabled}
/>
)}
@@ -123,11 +144,7 @@ function CeleryTaskLatencyGraph({
widget={celeryTaskLatencyWidgetData(graphState)}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
- onClickHandler={(arg): void => {
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
+ onClickHandler={onGraphClick}
isQueryEnabled={queryEnabled}
/>
)}
@@ -136,11 +153,7 @@ function CeleryTaskLatencyGraph({
widget={celeryTaskLatencyWidgetData(graphState)}
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
- onClickHandler={(arg): void => {
- console.log('clicked', arg);
- onClick(arg as any);
- }}
- getGraphData={getGraphData}
+ onClickHandler={onGraphClick}
isQueryEnabled={queryEnabled}
/>
)}
diff --git a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
index 6cb35817491..19fc789b25e 100644
--- a/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
+++ b/frontend/src/container/GridCardLayout/GridCard/WidgetGraphComponent.tsx
@@ -45,6 +45,8 @@ function WidgetGraphComponent({
onClickHandler,
onDragSelect,
customTooltipElement,
+ openTracesButton,
+ onOpenTraceBtnClick,
}: WidgetGraphComponentProps): JSX.Element {
const [deleteModal, setDeleteModal] = useState(false);
const [hovered, setHovered] = useState(false);
@@ -333,6 +335,8 @@ function WidgetGraphComponent({
tableProcessedDataRef={tableProcessedDataRef}
customTooltipElement={customTooltipElement}
searchTerm={searchTerm}
+ openTracesButton={openTracesButton}
+ onOpenTraceBtnClick={onOpenTraceBtnClick}
/>
)}
diff --git a/frontend/src/container/GridCardLayout/GridCard/index.tsx b/frontend/src/container/GridCardLayout/GridCard/index.tsx
index 82fc498c430..4bb0694ea04 100644
--- a/frontend/src/container/GridCardLayout/GridCard/index.tsx
+++ b/frontend/src/container/GridCardLayout/GridCard/index.tsx
@@ -38,6 +38,8 @@ function GridCardGraph({
customTooltipElement,
dataAvailable,
getGraphData,
+ openTracesButton,
+ onOpenTraceBtnClick,
}: GridCardGraphProps): JSX.Element {
const dispatch = useDispatch();
const [errorMessage, setErrorMessage] = useState();
@@ -250,6 +252,8 @@ function GridCardGraph({
onClickHandler={onClickHandler}
onDragSelect={onDragSelect}
customTooltipElement={customTooltipElement}
+ openTracesButton={openTracesButton}
+ onOpenTraceBtnClick={onOpenTraceBtnClick}
/>
)}
diff --git a/frontend/src/container/GridCardLayout/GridCard/types.ts b/frontend/src/container/GridCardLayout/GridCard/types.ts
index 2c2071900f0..63ff2b566bc 100644
--- a/frontend/src/container/GridCardLayout/GridCard/types.ts
+++ b/frontend/src/container/GridCardLayout/GridCard/types.ts
@@ -1,5 +1,6 @@
import { ToggleGraphProps } from 'components/Graph/types';
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
+import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
import { Dispatch, MutableRefObject, ReactNode, SetStateAction } from 'react';
import { UseQueryResult } from 'react-query';
@@ -32,6 +33,8 @@ export interface WidgetGraphComponentProps {
onClickHandler?: OnClickPluginOpts['onClick'];
onDragSelect: (start: number, end: number) => void;
customTooltipElement?: HTMLDivElement;
+ openTracesButton?: boolean;
+ onOpenTraceBtnClick?: (record: RowData) => void;
}
export interface GridCardGraphProps {
@@ -46,6 +49,8 @@ export interface GridCardGraphProps {
customTooltipElement?: HTMLDivElement;
dataAvailable?: (isDataAvailable: boolean) => void;
getGraphData?: (graphData?: MetricRangePayloadProps['data']) => void;
+ openTracesButton?: boolean;
+ onOpenTraceBtnClick?: (record: RowData) => void;
}
export interface GetGraphVisibilityStateOnLegendClickProps {
diff --git a/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss b/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss
index 80491e991a2..bdc21e164d7 100644
--- a/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss
+++ b/frontend/src/container/GridTableComponent/GridTableComponent.styles.scss
@@ -3,3 +3,14 @@
max-height: 500px;
overflow-y: auto;
}
+
+.open-traces-button {
+ font-size: 11px;
+ border-radius: 4px;
+ border: none;
+ padding: 6px 8px;
+ cursor: pointer;
+ display: flex;
+ gap: 6px;
+ align-items: center;
+}
diff --git a/frontend/src/container/GridTableComponent/index.tsx b/frontend/src/container/GridTableComponent/index.tsx
index 63084be5f32..db3d72a9749 100644
--- a/frontend/src/container/GridTableComponent/index.tsx
+++ b/frontend/src/container/GridTableComponent/index.tsx
@@ -1,3 +1,4 @@
+/* eslint-disable sonarjs/no-duplicate-string */
import './GridTableComponent.styles.scss';
import { ExclamationCircleFilled } from '@ant-design/icons';
@@ -7,9 +8,11 @@ import { Events } from 'constants/events';
import { QueryTable } from 'container/QueryTable';
import { RowData } from 'lib/query/createTableColumnsFromQuery';
import { cloneDeep, get, isEmpty } from 'lodash-es';
+import { Compass } from 'lucide-react';
import LineClampedText from 'periscope/components/LineClampedText/LineClampedText';
import { memo, ReactNode, useCallback, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
+import styled from 'styled-components';
import { eventEmitter } from 'utils/getEventEmitter';
import { WrapperStyled } from './styles';
@@ -20,6 +23,23 @@ import {
TableData,
} from './utils';
+export const HoverButtonWrapper = styled.div`
+ position: absolute;
+ right: 0;
+ top: 50%;
+ transform: translateY(-50%);
+ opacity: 0;
+ transition: opacity 0.2s;
+`;
+
+const RelativeWrapper = styled.div`
+ position: relative;
+
+ &:hover ${HoverButtonWrapper} {
+ opacity: 1;
+ }
+`;
+
function GridTableComponent({
data,
query,
@@ -27,6 +47,8 @@ function GridTableComponent({
columnUnits,
tableProcessedDataRef,
sticky,
+ openTracesButton,
+ onOpenTraceBtnClick,
...props
}: GridTableComponentProps): JSX.Element {
const { t } = useTranslation(['valueGraph']);
@@ -161,6 +183,42 @@ function GridTableComponent({
},
}));
+ const columnDataWithOpenTracesButton = useMemo(
+ () =>
+ newColumnData.map((column, index) => ({
+ ...column,
+ render: (text: string): JSX.Element => {
+ const LineClampedTextComponent = (
+
+ );
+ if (index !== 0) {
+ return {LineClampedTextComponent}
;
+ }
+
+ return (
+
+ {LineClampedTextComponent}
+
+
+
+
+ );
+ },
+ })),
+ [newColumnData],
+ );
+
useEffect(() => {
eventEmitter.emit(Events.TABLE_COLUMNS_DATA, {
columns: newColumnData,
@@ -174,9 +232,18 @@ function GridTableComponent({
query={query}
queryTableData={data}
loading={false}
- columns={newColumnData}
+ columns={openTracesButton ? columnDataWithOpenTracesButton : newColumnData}
dataSource={dataSource}
sticky={sticky}
+ onRow={
+ openTracesButton
+ ? (record): React.HTMLAttributes => ({
+ onClick: (): void => {
+ onOpenTraceBtnClick?.(record);
+ },
+ })
+ : undefined
+ }
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
/>
diff --git a/frontend/src/container/GridTableComponent/types.ts b/frontend/src/container/GridTableComponent/types.ts
index 883e280b38f..7e930d51c38 100644
--- a/frontend/src/container/GridTableComponent/types.ts
+++ b/frontend/src/container/GridTableComponent/types.ts
@@ -15,6 +15,8 @@ export type GridTableComponentProps = {
tableProcessedDataRef?: React.MutableRefObject;
sticky?: TableProps['sticky'];
searchTerm?: string;
+ openTracesButton?: boolean;
+ onOpenTraceBtnClick?: (record: RowData) => void;
} & Pick &
Omit, 'columns' | 'dataSource'>;
diff --git a/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts b/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts
index 672bc118124..15eff76f8b7 100644
--- a/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts
+++ b/frontend/src/container/MetricsApplication/MetricsApplication.factory.ts
@@ -10,6 +10,7 @@ export const getWidgetQueryBuilder = ({
yAxisUnit = '',
fillSpans = false,
id,
+ columnUnits,
}: GetWidgetQueryBuilderProps): Widgets => ({
description: '',
id: id || v4(),
@@ -26,4 +27,5 @@ export const getWidgetQueryBuilder = ({
selectedLogFields: [],
selectedTracesFields: [],
fillSpans,
+ columnUnits,
});
diff --git a/frontend/src/container/MetricsApplication/types.ts b/frontend/src/container/MetricsApplication/types.ts
index 4b4cc2f4f13..60e7f269341 100644
--- a/frontend/src/container/MetricsApplication/types.ts
+++ b/frontend/src/container/MetricsApplication/types.ts
@@ -11,6 +11,7 @@ export interface GetWidgetQueryBuilderProps {
yAxisUnit?: Widgets['yAxisUnit'];
id?: Widgets['id'];
fillSpans?: Widgets['fillSpans'];
+ columnUnits?: Widgets['columnUnits'];
}
export interface NavigateToTraceProps {
diff --git a/frontend/src/container/PanelWrapper/HistogramPanelWrapper.tsx b/frontend/src/container/PanelWrapper/HistogramPanelWrapper.tsx
index a6c80b01bde..9f69a7eef95 100644
--- a/frontend/src/container/PanelWrapper/HistogramPanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/HistogramPanelWrapper.tsx
@@ -5,6 +5,7 @@ import { getLocalStorageGraphVisibilityState } from 'container/GridCardLayout/Gr
import { useIsDarkMode } from 'hooks/useDarkMode';
import { useResizeObserver } from 'hooks/useDimensions';
import { getUplotHistogramChartOptions } from 'lib/uPlotLib/getUplotHistogramChartOptions';
+import _noop from 'lodash-es/noop';
import { useDashboard } from 'providers/Dashboard/Dashboard';
import { useEffect, useMemo, useRef } from 'react';
@@ -18,6 +19,7 @@ function HistogramPanelWrapper({
graphVisibility,
isFullViewMode,
onToggleModelHandler,
+ onClickHandler,
}: PanelWrapperProps): JSX.Element {
const graphRef = useRef(null);
const { toScrollWidgetId, setToScrollWidgetId } = useDashboard();
@@ -67,6 +69,7 @@ function HistogramPanelWrapper({
setGraphsVisibilityStates: setGraphVisibility,
graphsVisibilityStates: graphVisibility,
mergeAllQueries: widget.mergeAllActiveQueries,
+ onClickHandler: onClickHandler || _noop,
}),
[
containerDimensions,
@@ -78,6 +81,7 @@ function HistogramPanelWrapper({
widget.id,
widget.mergeAllActiveQueries,
widget.panelTypes,
+ onClickHandler,
],
);
diff --git a/frontend/src/container/PanelWrapper/PanelWrapper.tsx b/frontend/src/container/PanelWrapper/PanelWrapper.tsx
index 2f5b35485ee..646cff6f439 100644
--- a/frontend/src/container/PanelWrapper/PanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/PanelWrapper.tsx
@@ -17,6 +17,8 @@ function PanelWrapper({
tableProcessedDataRef,
customTooltipElement,
searchTerm,
+ openTracesButton,
+ onOpenTraceBtnClick,
}: PanelWrapperProps): JSX.Element {
const Component = PanelTypeVsPanelWrapper[
selectedGraph || widget.panelTypes
@@ -41,6 +43,8 @@ function PanelWrapper({
tableProcessedDataRef={tableProcessedDataRef}
customTooltipElement={customTooltipElement}
searchTerm={searchTerm}
+ openTracesButton={openTracesButton}
+ onOpenTraceBtnClick={onOpenTraceBtnClick}
/>
);
}
diff --git a/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx b/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
index c5222e8d538..58ddaef5a8c 100644
--- a/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
+++ b/frontend/src/container/PanelWrapper/TablePanelWrapper.tsx
@@ -9,6 +9,8 @@ function TablePanelWrapper({
queryResponse,
tableProcessedDataRef,
searchTerm,
+ openTracesButton,
+ onOpenTraceBtnClick,
}: PanelWrapperProps): JSX.Element {
const panelData =
(queryResponse.data?.payload?.data?.result?.[0] as any)?.table || [];
@@ -22,6 +24,8 @@ function TablePanelWrapper({
tableProcessedDataRef={tableProcessedDataRef}
sticky={widget.panelTypes === PANEL_TYPES.TABLE}
searchTerm={searchTerm}
+ openTracesButton={openTracesButton}
+ onOpenTraceBtnClick={onOpenTraceBtnClick}
// eslint-disable-next-line react/jsx-props-no-spreading
{...GRID_TABLE_CONFIG}
/>
diff --git a/frontend/src/container/PanelWrapper/panelWrapper.types.ts b/frontend/src/container/PanelWrapper/panelWrapper.types.ts
index 4778ffdb976..9c2ca0d7da5 100644
--- a/frontend/src/container/PanelWrapper/panelWrapper.types.ts
+++ b/frontend/src/container/PanelWrapper/panelWrapper.types.ts
@@ -25,6 +25,8 @@ export type PanelWrapperProps = {
tableProcessedDataRef?: React.MutableRefObject;
searchTerm?: string;
customTooltipElement?: HTMLDivElement;
+ openTracesButton?: boolean;
+ onOpenTraceBtnClick?: (record: RowData) => void;
};
export type TooltipData = {
diff --git a/frontend/src/lib/uPlotLib/getUplotHistogramChartOptions.ts b/frontend/src/lib/uPlotLib/getUplotHistogramChartOptions.ts
index 2ff0f3051e9..81750ff857f 100644
--- a/frontend/src/lib/uPlotLib/getUplotHistogramChartOptions.ts
+++ b/frontend/src/lib/uPlotLib/getUplotHistogramChartOptions.ts
@@ -4,12 +4,14 @@ import { themeColors } from 'constants/theme';
import { saveLegendEntriesToLocalStorage } from 'container/GridCardLayout/GridCard/FullView/utils';
import { Dimensions } from 'hooks/useDimensions';
import getLabelName from 'lib/getLabelName';
+import _noop from 'lodash-es/noop';
import { Dispatch, SetStateAction } from 'react';
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
import { Query } from 'types/api/queryBuilder/queryBuilderData';
import { QueryData } from 'types/api/widgets/getQuery';
import uPlot from 'uplot';
+import onClickPlugin, { OnClickPluginOpts } from './plugins/onClickPlugin';
import tooltipPlugin from './plugins/tooltipPlugin';
import { drawStyles } from './utils/constants';
import { generateColor } from './utils/generateColor';
@@ -27,6 +29,7 @@ type GetUplotHistogramChartOptionsProps = {
graphsVisibilityStates?: boolean[];
setGraphsVisibilityStates?: Dispatch>;
mergeAllQueries?: boolean;
+ onClickHandler?: OnClickPluginOpts['onClick'];
};
type GetHistogramSeriesProps = {
@@ -119,6 +122,7 @@ export const getUplotHistogramChartOptions = ({
graphsVisibilityStates,
setGraphsVisibilityStates,
mergeAllQueries,
+ onClickHandler = _noop,
}: GetUplotHistogramChartOptionsProps): uPlot.Options =>
({
id,
@@ -140,6 +144,10 @@ export const getUplotHistogramChartOptions = ({
isMergedSeries: mergeAllQueries,
isDarkMode,
}),
+ onClickPlugin({
+ onClick: onClickHandler,
+ apiResponse,
+ }),
],
scales: {
x: {
diff --git a/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx b/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx
index 3c3008fb017..b33040ced18 100644
--- a/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx
+++ b/frontend/src/pages/Celery/CeleryTask/CeleryTask.tsx
@@ -2,19 +2,19 @@ import './CeleryTask.styles.scss';
import CeleryTaskConfigOptions from 'components/CeleryTask/CeleryTaskConfigOptions/CeleryTaskConfigOptions';
import CeleryTaskDetail, {
- CeleryTaskData,
+ CaptureDataProps,
} from 'components/CeleryTask/CeleryTaskDetail/CeleryTaskDetail';
import CeleryTaskGraphGrid from 'components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphGrid';
-import { celerySlowestTasksTableWidgetData } from 'components/CeleryTask/CeleryTaskGraph/CeleryTaskGraphUtils';
import DateTimeSelectionV2 from 'container/TopNav/DateTimeSelectionV2';
import { ListMinus } from 'lucide-react';
import { useState } from 'react';
export default function CeleryTask(): JSX.Element {
- const [task, setTask] = useState(null);
+ const [task, setTask] = useState(null);
- const onTaskClick = (task: CeleryTaskData): void => {
- setTask(task);
+ const onTaskClick = (captureData: CaptureDataProps): void => {
+ setTask(captureData);
+ console.log(captureData);
};
return (
@@ -36,13 +36,8 @@ export default function CeleryTask(): JSX.Element {
onClose={(): void => {
setTask(null);
}}
- mainTitle="Celery Task"
- widgetData={celerySlowestTasksTableWidgetData}
- taskData={{
- entity: 'task',
- value: 'task',
- timeRange: [1737569089000, 1737570889000],
- }}
+ widgetData={task.widgetData}
+ taskData={task}
drawerOpen={!!task}
/>
)}
diff --git a/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts b/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts
index 89bfaaa3eb2..f2f84c0f882 100644
--- a/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts
+++ b/frontend/src/pages/MessagingQueues/MQDetails/MetricPage/MetricPageUtil.ts
@@ -14,11 +14,13 @@ interface GetWidgetQueryProps {
queryData: IBuilderQuery[];
panelTypes?: PANEL_TYPES;
yAxisUnit?: string;
+ columnUnits?: Record;
}
interface GetWidgetQueryPropsReturn extends GetWidgetQueryBuilderProps {
description?: string;
nullZeroValues: string;
+ columnUnits?: Record;
}
export const getWidgetQueryBuilder = ({
@@ -51,7 +53,7 @@ export const getWidgetQueryBuilder = ({
export function getWidgetQuery(
props: GetWidgetQueryProps,
): GetWidgetQueryPropsReturn {
- const { title, description, panelTypes, yAxisUnit } = props;
+ const { title, description, panelTypes, yAxisUnit, columnUnits } = props;
return {
title,
yAxisUnit: yAxisUnit || 'none',
@@ -59,6 +61,7 @@ export function getWidgetQuery(
fillSpans: false,
description,
nullZeroValues: 'zero',
+ columnUnits,
query: {
queryType: EQueryType.QUERY_BUILDER,
promql: [],