feat: celery task page restructuring and other misc fixes (#6985)

* feat: celery task page restructuring and other misc fixes

* feat: added custom series hook

* feat: code fix and typo

* feat: added feedback fixes

* feat: configured error % graphs

* feat: resolved comments
This commit is contained in:
SagarRajput-7 2025-01-30 21:23:37 +05:30 committed by GitHub
parent d1e7cc128f
commit 3849ca1ecc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 575 additions and 238 deletions

View File

@ -1,14 +1,10 @@
import './CeleryTaskConfigOptions.styles.scss';
import { Color } from '@signozhq/design-tokens';
import { Button, Select, Spin, Tooltip, Typography } from 'antd';
import { Select, Spin, Typography } from 'antd';
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';
import {
getValuesFromQueryParams,
@ -23,11 +19,8 @@ function CeleryTaskConfigOptions(): JSX.Element {
const history = useHistory();
const location = useLocation();
const [isURLCopied, setIsURLCopied] = useState(false);
const urlQuery = useUrlQuery();
const [, handleCopyToClipboard] = useCopyToClipboard();
return (
<div className="celery-task-filters">
<div className="celery-filters">
@ -66,25 +59,6 @@ function CeleryTaskConfigOptions(): JSX.Element {
}}
/>
</div>
<Tooltip title="Share this" arrow={false}>
<Button
className="periscope-btn copy-url-btn"
onClick={(): void => {
handleCopyToClipboard(window.location.href);
setIsURLCopied(true);
setTimeout(() => {
setIsURLCopied(false);
}, 1000);
}}
icon={
isURLCopied ? (
<Check size={14} color={Color.BG_FOREST_500} />
) : (
<Share2 size={14} />
)
}
/>
</Tooltip>
</div>
);
}

View File

@ -1,9 +1,13 @@
/* eslint-disable react-hooks/exhaustive-deps */
import { DefaultOptionType } from 'antd/es/select';
import { getAttributesValues } from 'api/queryBuilder/getAttributesValues';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import { useQuery } from 'react-query';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder';
import { GlobalReducer } from 'types/reducer/globalTime';
export interface Filters {
searchText: string;
@ -31,8 +35,18 @@ export function useGetAllFilters(props: Filters): GetAllFiltersResponse {
tagType,
} = props;
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
);
const { data, isLoading } = useQuery(
['attributesValues', attributeKey, searchText],
[
REACT_QUERY_KEY.GET_ATTRIBUTE_VALUES,
attributeKey,
searchText,
minTime,
maxTime,
],
async () => {
const keys = Array.isArray(attributeKey) ? attributeKey : [attributeKey];

View File

@ -3,14 +3,12 @@ import './CeleryTaskGraph.style.scss';
import { Color } from '@signozhq/design-tokens';
import { QueryParams } from 'constants/query';
import { PANEL_TYPES } from 'constants/queryBuilder';
import { themeColors } from 'constants/theme';
import { ViewMenuAction } from 'container/GridCardLayout/config';
import GridCard from 'container/GridCardLayout/GridCard';
import { Card } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import useUrlQuery from 'hooks/useUrlQuery';
import getLabelName from 'lib/getLabelName';
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
import { isEmpty } from 'lodash-es';
import { getStartAndEndTimesInMilliseconds } from 'pages/MessagingQueues/MessagingQueuesUtils';
import { useCallback, useMemo, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
@ -18,11 +16,10 @@ import { useHistory, useLocation } from 'react-router-dom';
import { UpdateTimeInterval } from 'store/actions';
import { AppState } from 'store/reducers';
import { Widgets } from 'types/api/dashboard/getAll';
import { QueryData } from 'types/api/widgets/getQuery';
import { GlobalReducer } from 'types/reducer/globalTime';
import { CaptureDataProps } from '../CeleryTaskDetail/CeleryTaskDetail';
import { paths } from '../CeleryUtils';
import { useGetGraphCustomSeries } from '../useGetGraphCustomSeries';
import {
celeryAllStateWidgetData,
celeryFailedStateWidgetData,
@ -114,58 +111,26 @@ function CeleryTaskBar({
string,
];
onClick?.({
entity,
value,
timeRange: [start, end],
widgetData,
});
if (!isEmpty(entity) || !isEmpty(value)) {
onClick?.({
entity,
value,
timeRange: [start, end],
widgetData,
});
}
};
const getGraphSeries = (color: string, label: string): any => ({
const { getCustomSeries } = useGetGraphCustomSeries({
isDarkMode,
drawStyle: 'bars',
paths,
lineInterpolation: 'spline',
show: true,
label,
fill: `${color}90`,
stroke: color,
width: 2,
spanGaps: true,
points: {
size: 5,
show: false,
stroke: color,
colorMapping: {
SUCCESS: Color.BG_FOREST_500,
FAILURE: Color.BG_CHERRY_500,
RETRY: Color.BG_AMBER_400,
},
});
const customSeries = (data: QueryData[]): uPlot.Series[] => {
const configurations: uPlot.Series[] = [
{ label: 'Timestamp', stroke: 'purple' },
];
for (let i = 0; i < data.length; i += 1) {
const { metric = {}, queryName = '', legend = '' } = data[i] || {};
const label = getLabelName(metric, queryName || '', legend || '');
let color = generateColor(
label,
isDarkMode ? themeColors.chartcolors : themeColors.lightModeColor,
);
if (label === 'SUCCESS') {
color = Color.BG_FOREST_500;
}
if (label === 'FAILURE') {
color = Color.BG_CHERRY_500;
}
if (label === 'RETRY') {
color = Color.BG_AMBER_400;
}
const series = getGraphSeries(color, label);
configurations.push(series);
}
return configurations;
};
return (
<Card
isDarkMode={isDarkMode}
@ -183,7 +148,7 @@ function CeleryTaskBar({
onClickHandler={(...args): void =>
onGraphClick(celerySlowestTasksTableWidgetData, ...args)
}
customSeries={customSeries}
customSeries={getCustomSeries}
/>
)}
{barState === CeleryTaskState.Failed && (
@ -195,7 +160,7 @@ function CeleryTaskBar({
onClickHandler={(...args): void =>
onGraphClick(celeryFailedTasksTableWidgetData, ...args)
}
customSeries={customSeries}
customSeries={getCustomSeries}
/>
)}
{barState === CeleryTaskState.Retry && (
@ -207,7 +172,7 @@ function CeleryTaskBar({
onClickHandler={(...args): void =>
onGraphClick(celeryRetryTasksTableWidgetData, ...args)
}
customSeries={customSeries}
customSeries={getCustomSeries}
/>
)}
{barState === CeleryTaskState.Successful && (
@ -219,7 +184,7 @@ function CeleryTaskBar({
onClickHandler={(...args): void =>
onGraphClick(celerySuccessTasksTableWidgetData, ...args)
}
customSeries={customSeries}
customSeries={getCustomSeries}
/>
)}
</div>

View File

@ -6,93 +6,85 @@
padding-bottom: 16px;
margin-bottom: 16px;
.celery-task-graph-grid {
display: grid;
grid-template-columns: 60% 40%;
align-items: flex-start;
gap: 10px;
.celery-task-graph-bar,
.celery-task-graph-task-latency {
height: 380px !important;
width: 100%;
box-sizing: border-box;
.celery-task-graph {
height: 380px !important;
.celery-task-graph-grid-content {
padding: 6px;
width: 100%;
box-sizing: border-box;
height: 100%;
}
.ant-card-body {
height: calc(100% - 18px);
.ant-card-body {
height: calc(100% - 18px);
.widget-graph-container {
&.bar {
height: calc(100% - 110px);
}
.widget-graph-container {
&.bar {
height: calc(100% - 110px);
}
&.graph {
height: calc(100% - 80px);
}
}
}
}
.celery-task-graph-worker-count {
.celery-task-graph-worker-count {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--bg-slate-500);
background: linear-gradient(
0deg,
rgba(171, 189, 255, 0) 0%,
rgba(171, 189, 255, 0) 100%
),
#0b0c0e;
.ant-card-body {
height: 100%;
width: 100%;
}
.worker-count-text-container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--bg-slate-500);
background: linear-gradient(
0deg,
rgba(171, 189, 255, 0) 0%,
rgba(171, 189, 255, 0) 100%
),
#0b0c0e;
.ant-card-body {
height: 100%;
width: 100%;
}
.worker-count-text-container {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
.celery-task-graph-worker-count-text {
font-size: 2.5vw;
text-align: center;
}
}
.worker-count-header {
display: flex;
justify-content: start;
align-items: start;
position: absolute;
height: 100%;
width: 100%;
.celery-task-graph-worker-count-text {
font-size: 2.5vw;
text-align: center;
}
}
.celery-task-graph-bar,
.celery-task-graph-task-latency {
height: 380px !important;
.worker-count-header {
display: flex;
justify-content: start;
align-items: start;
position: absolute;
height: 100%;
width: 100%;
box-sizing: border-box;
}
}
.celery-task-graph-grid-content {
padding: 6px;
height: 100%;
}
.celery-task-graph {
height: 380px !important;
padding: 6px;
width: 100%;
box-sizing: border-box;
.ant-card-body {
height: calc(100% - 18px);
.ant-card-body {
height: calc(100% - 18px);
.widget-graph-container {
&.bar {
height: calc(100% - 110px);
}
&.graph {
height: calc(100% - 80px);
}
.widget-graph-container {
&.bar {
height: calc(100% - 110px);
}
}
}
@ -116,6 +108,85 @@
}
}
}
.metric-based-graphs,
.trace-based-graphs {
display: flex;
flex-direction: column;
gap: 10px;
.metric-page-grid {
display: grid;
grid-template-columns: 50% 50%;
align-items: flex-start;
gap: 10px;
width: 100%;
.celery-task-graph {
height: 280px !important;
}
}
}
.trace-based-graphs {
.trace-based-graphs-header {
display: grid;
grid-template-columns: 50% 50%;
align-items: center;
gap: 24px;
width: 100%;
}
}
.configure-option-Info {
display: grid;
grid-template-columns: max-content 1fr;
gap: 16px;
align-items: center;
.configure-option-Info-text {
color: var(--bg-vanilla-400);
font-size: 12px;
font-weight: 400;
}
}
.row-panel {
border-radius: 4px;
background: rgba(18, 19, 23, 0.4);
padding: 8px;
display: flex;
gap: 6px;
align-items: center;
height: 48px !important;
width: 100%;
.ant-typography {
font-size: 14px;
font-weight: 500;
}
.row-panel-section {
display: flex;
gap: 6px;
align-items: center;
.row-icon {
color: var(--bg-vanilla-400);
cursor: pointer;
}
.section-title {
color: var(--bg-vanilla-400);
font-family: Inter;
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 20px;
letter-spacing: -0.07px;
}
}
}
}
.celery-task-states {

View File

@ -1,7 +1,10 @@
import './CeleryTaskGraph.style.scss';
import { Card, Typography } from 'antd';
import { useMemo } from 'react';
import { CardContainer } from 'container/GridCardLayout/styles';
import { useIsDarkMode } from 'hooks/useDarkMode';
import { ChevronDown, ChevronUp } from 'lucide-react';
import { useMemo, useState } from 'react';
import { useSelector } from 'react-redux';
import { AppState } from 'store/reducers';
import { DataTypes } from 'types/api/queryBuilder/queryAutocompleteResponse';
@ -23,9 +26,11 @@ import CeleryTaskLatencyGraph from './CeleryTaskLatencyGraph';
export default function CeleryTaskGraphGrid({
onClick,
queryEnabled,
configureOptionComponent,
}: {
onClick: (task: CaptureDataProps) => void;
queryEnabled: boolean;
configureOptionComponent?: React.ReactNode;
}): JSX.Element {
const { minTime, maxTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
@ -71,44 +76,123 @@ export default function CeleryTaskGraphGrid({
DataTypes.String,
'tag',
);
const isDarkMode = useIsDarkMode();
const [collapsedSections, setCollapsedSections] = useState<{
[key: string]: boolean;
}>({
metricBasedGraphs: false,
traceBasedGraphs: false,
});
const toggleCollapse = (key: string): void => {
setCollapsedSections((prev) => ({
...prev,
[key]: !prev[key],
}));
};
return (
<div className="celery-task-graph-grid-container">
<div className="celery-task-graph-grid">
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
<Card className="celery-task-graph-worker-count">
<div className="worker-count-header">
<Typography.Text className="worker-count-header-text">
Worker Count
<div className="metric-based-graphs">
<CardContainer className="row-card" isDarkMode={isDarkMode}>
<div className="row-panel">
<div className="row-panel-section">
<Typography.Text className="section-title">
Flower Metrics
</Typography.Text>
{collapsedSections.metricBasedGraphs ? (
<ChevronDown
size={14}
onClick={(): void => toggleCollapse('metricBasedGraphs')}
className="row-icon"
/>
) : (
<ChevronUp
size={14}
onClick={(): void => toggleCollapse('metricBasedGraphs')}
className="row-icon"
/>
)}
</div>
</div>
</CardContainer>
{!collapsedSections.metricBasedGraphs && (
<div className="metric-page-grid">
<CeleryTaskGraph
key={celeryActiveTasksData.id}
widgetData={celeryActiveTasksData}
queryEnabled={queryEnabled}
/>
<Card className="celery-task-graph-worker-count">
<div className="worker-count-header">
<Typography.Text className="worker-count-header-text">
Worker Count
</Typography.Text>
</div>
<div className="worker-count-text-container">
<Typography.Text className="celery-task-graph-worker-count-text">
{options.filter((option) => option.value).length}
</Typography.Text>
</div>
</Card>
</div>
)}
</div>
<div className="trace-based-graphs">
<div className="trace-based-graphs-header">
<CardContainer className="row-card" isDarkMode={isDarkMode}>
<div className="row-panel">
<div className="row-panel-section">
<Typography.Text className="section-title">
Span Based Stats
</Typography.Text>
{collapsedSections.traceBasedGraphs ? (
<ChevronDown
size={14}
onClick={(): void => toggleCollapse('traceBasedGraphs')}
className="row-icon"
/>
) : (
<ChevronUp
size={14}
onClick={(): void => toggleCollapse('traceBasedGraphs')}
className="row-icon"
/>
)}
</div>
</div>
</CardContainer>
<div className="configure-option-Info">
{configureOptionComponent}
<Typography.Text className="configure-option-Info-text">
Click on a graph co-ordinate to see more details
</Typography.Text>
</div>
<div className="worker-count-text-container">
<Typography.Text className="celery-task-graph-worker-count-text">
{options.filter((option) => option.value).length}
</Typography.Text>
</div>
</Card>
</div>
<div className="celery-task-graph-grid">
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
<CeleryTaskGraph
key={celeryActiveTasksData.id}
widgetData={celeryActiveTasksData}
queryEnabled={queryEnabled}
/>
</div>
<div className="celery-task-graph-grid-bottom">
{bottomWidgetData.map((widgetData, index) => (
<CeleryTaskGraph
key={widgetData.id}
widgetData={widgetData}
onClick={onClick}
queryEnabled={queryEnabled}
rightPanelTitle={rightPanelTitle[index]}
applyCeleryTaskFilter
/>
))}
</div>
{!collapsedSections.traceBasedGraphs && (
<>
<CeleryTaskBar queryEnabled={queryEnabled} onClick={onClick} />
<CeleryTaskLatencyGraph onClick={onClick} queryEnabled={queryEnabled} />
<div className="celery-task-graph-grid-bottom">
{bottomWidgetData.map((widgetData, index) => (
<CeleryTaskGraph
key={widgetData.id}
widgetData={widgetData}
onClick={onClick}
queryEnabled={queryEnabled}
rightPanelTitle={rightPanelTitle[index]}
applyCeleryTaskFilter
/>
))}
</div>
</>
)}
</div>
</div>
);
}
CeleryTaskGraphGrid.defaultProps = {
configureOptionComponent: null,
};

View File

@ -332,6 +332,7 @@ export const celeryTasksByWorkerWidgetData = (
timeAggregation: 'rate',
},
],
yAxisUnit: 'cps',
}),
);
@ -345,44 +346,103 @@ export const celeryErrorByWorkerWidgetData = (
description: 'Represents the number of errors by each worker.',
queryData: [
{
dataSource: DataSource.TRACES,
queryName: 'A',
aggregateOperator: 'count_distinct',
aggregateAttribute: {
dataType: DataTypes.String,
id: '------false',
isColumn: false,
dataType: 'string',
id: 'span_id--string----true',
isColumn: true,
isJSON: false,
key: '',
key: 'span_id',
type: '',
},
aggregateOperator: 'rate',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
timeAggregation: 'count_distinct',
spaceAggregation: 'sum',
functions: [],
filters: {
items: [],
items: [
{
id: uuidv4(),
key: {
dataType: DataTypes.bool,
id: 'has_error--bool----true',
isColumn: true,
isJSON: false,
key: 'has_error',
type: '',
},
op: '=',
value: 'true',
},
],
op: 'AND',
},
functions: [],
expression: 'A',
disabled: true,
stepInterval: getStepInterval(startTime, endTime),
having: [],
limit: null,
orderBy: [],
groupBy: [
{
dataType: DataTypes.String,
id: 'celery.hostname--string--tag--false',
isColumn: false,
isJSON: false,
key: 'celery.hostname',
type: 'tag',
id: 'celery.hostname--string--tag--false',
},
],
having: [],
legend: '{{celery.hostname}}',
limit: 10,
orderBy: [],
queryName: 'A',
legend: '',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: getStepInterval(startTime, endTime),
timeAggregation: 'rate',
},
{
dataSource: 'traces',
queryName: 'B',
aggregateOperator: 'count_distinct',
aggregateAttribute: {
dataType: 'string',
id: 'span_id--string----true',
isColumn: true,
isJSON: false,
key: 'span_id',
type: '',
},
timeAggregation: 'count_distinct',
spaceAggregation: 'sum',
functions: [],
filters: {
items: [],
op: 'AND',
},
expression: 'B',
disabled: true,
stepInterval: getStepInterval(startTime, endTime),
having: [],
limit: null,
orderBy: [],
groupBy: [
{
dataType: DataTypes.String,
isColumn: false,
isJSON: false,
key: 'celery.hostname',
type: 'tag',
id: 'celery.hostname--string--tag--false',
},
],
legend: '',
reduceTo: 'avg',
},
{
queryName: 'F1',
expression: '(A/B)*100',
disabled: false,
legend: '{{celery.hostname}}',
} as any,
],
yAxisUnit: 'percent',
}),
);
@ -392,7 +452,7 @@ export const celeryLatencyByWorkerWidgetData = (
): Widgets =>
getWidgetQueryBuilder(
getWidgetQuery({
title: 'Latency by Worker',
title: 'Latency by worker',
description: 'Represents the latency of tasks by each worker.',
queryData: [
{
@ -444,7 +504,7 @@ export const celeryActiveTasksWidgetData = (
): Widgets =>
getWidgetQueryBuilder(
getWidgetQuery({
title: 'Tasks/ worker (Active tasks)',
title: 'Active Tasks by worker',
description: 'Represents the number of active tasks.',
queryData: [
{
@ -487,6 +547,7 @@ export const celeryActiveTasksWidgetData = (
timeAggregation: 'latest',
},
],
yAxisUnit: 'cps',
}),
);

View File

@ -0,0 +1,70 @@
import { Color } from '@signozhq/design-tokens';
import { themeColors } from 'constants/theme';
import getLabelName from 'lib/getLabelName';
import { drawStyles } from 'lib/uPlotLib/utils/constants';
import { generateColor } from 'lib/uPlotLib/utils/generateColor';
import { QueryData } from 'types/api/widgets/getQuery';
import { paths } from './CeleryUtils';
interface UseGetGraphCustomSeriesProps {
isDarkMode: boolean;
drawStyle?: typeof drawStyles[keyof typeof drawStyles];
colorMapping?: Record<string, string>;
}
const defaultColorMapping = {
SUCCESS: Color.BG_FOREST_500,
FAILURE: Color.BG_CHERRY_500,
RETRY: Color.BG_AMBER_400,
};
export const useGetGraphCustomSeries = ({
isDarkMode,
drawStyle = 'bars',
colorMapping = defaultColorMapping,
}: UseGetGraphCustomSeriesProps): {
getCustomSeries: (data: QueryData[]) => uPlot.Series[];
} => {
const getGraphSeries = (color: string, label: string): any => ({
drawStyle,
paths,
lineInterpolation: 'spline',
show: true,
label,
fill: `${color}90`,
stroke: color,
width: 2,
spanGaps: true,
points: {
size: 5,
show: false,
stroke: color,
},
});
const getCustomSeries = (data: QueryData[]): uPlot.Series[] => {
const configurations: uPlot.Series[] = [
{ label: 'Timestamp', stroke: 'purple' },
];
for (let i = 0; i < data.length; i += 1) {
const { metric = {}, queryName = '', legend = '' } = data[i] || {};
const label = getLabelName(metric, queryName || '', legend || '');
// Check if label exists in colorMapping
const color =
colorMapping[label] ||
generateColor(
label,
isDarkMode ? themeColors.chartcolors : themeColors.lightModeColor,
);
const series = getGraphSeries(color, label);
configurations.push(series);
}
return configurations;
};
return { getCustomSeries };
};

View File

@ -32,4 +32,5 @@ export const REACT_QUERY_KEY = {
GET_JOB_LIST: 'GET_JOB_LIST',
GET_DAEMONSET_LIST: 'GET_DAEMONSET_LIST,',
GET_VOLUME_LIST: 'GET_VOLUME_LIST',
GET_ATTRIBUTE_VALUES: 'GET_ATTRIBUTE_VALUES',
};

View File

@ -1,4 +1,6 @@
import { Color } from '@signozhq/design-tokens';
import { Card } from 'antd';
import { useGetGraphCustomSeries } from 'components/CeleryTask/useGetGraphCustomSeries';
import { useNavigateToTraces } from 'components/CeleryTask/useNavigateToTraces';
import { QueryParams } from 'constants/query';
import { ViewMenuAction } from 'container/GridCardLayout/config';
@ -109,6 +111,18 @@ export default function OverviewRightPanelGraph({
},
[navigateToTraces, filters, selectedTimeStamp],
);
const { getCustomSeries } = useGetGraphCustomSeries({
isDarkMode: false,
drawStyle: 'bars',
colorMapping: {
False: Color.BG_CHERRY_500,
True: Color.BG_FOREST_400,
None: Color.BG_SLATE_200,
'Request Rate': Color.BG_ROBIN_500,
},
});
return (
<Card className="overview-right-panel-graph-card">
<div className="request-rate-card">
@ -125,6 +139,7 @@ export default function OverviewRightPanelGraph({
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('Celery_request_rate')}
customSeries={getCustomSeries}
/>
</div>
<div className="error-rate-card">
@ -141,6 +156,7 @@ export default function OverviewRightPanelGraph({
headerMenuList={[...ViewMenuAction]}
onDragSelect={onDragSelect}
onClickHandler={handleGraphClick('Celery_error_rate')}
customSeries={getCustomSeries}
/>
</div>
<div className="avg-latency-card">

View File

@ -195,6 +195,7 @@ export const celeryOverviewRequestRateGraphData = (
},
],
panelTypes: PANEL_TYPES.BAR,
yAxisUnit: 'reqps',
}),
);
@ -206,22 +207,24 @@ export const celeryOverviewErrorRateGraphData = (
): Widgets =>
getWidgetQueryBuilder(
getWidgetQuery({
title: 'Error',
title: 'Error %',
description: 'Represents Error in the service',
queryData: [
{
dataSource: DataSource.TRACES,
queryName: 'A',
aggregateOperator: 'count_distinct',
aggregateAttribute: {
dataType: DataTypes.EMPTY,
id: '------false',
isColumn: false,
dataType: DataTypes.String,
id: 'span_id--string----true',
isColumn: true,
isJSON: false,
key: '',
key: 'span_id',
type: '',
},
aggregateOperator: 'rate',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'A',
timeAggregation: 'count_distinct',
spaceAggregation: 'sum',
functions: [],
filters: {
items: [
...(filters ?? []),
@ -241,31 +244,31 @@ export const celeryOverviewErrorRateGraphData = (
],
op: 'AND',
},
functions: [],
groupBy: groupByFilter ? [groupByFilter] : [],
expression: 'A',
disabled: true,
stepInterval: getStepInterval(startTime, endTime),
having: [],
legend: 'True',
limit: null,
orderBy: [],
queryName: 'A',
groupBy: groupByFilter ? [groupByFilter] : [],
legend: '',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: getStepInterval(startTime, endTime),
timeAggregation: 'rate',
},
{
dataSource: DataSource.TRACES,
queryName: 'B',
aggregateOperator: 'count_distinct',
aggregateAttribute: {
dataType: DataTypes.EMPTY,
id: '------false',
isColumn: false,
dataType: DataTypes.String,
id: 'span_id--string----true',
isColumn: true,
isJSON: false,
key: '',
key: 'span_id',
type: '',
},
aggregateOperator: 'rate',
dataSource: DataSource.TRACES,
disabled: false,
expression: 'B',
timeAggregation: 'count_distinct',
spaceAggregation: 'sum',
functions: [],
filters: {
items: [
...(filters ?? []),
@ -285,20 +288,60 @@ export const celeryOverviewErrorRateGraphData = (
],
op: 'AND',
},
functions: [],
groupBy: groupByFilter ? [groupByFilter] : [],
expression: 'B',
disabled: true,
stepInterval: getStepInterval(startTime, endTime),
having: [],
legend: 'False',
limit: null,
orderBy: [],
queryName: 'B',
groupBy: groupByFilter ? [groupByFilter] : [],
legend: '',
reduceTo: 'avg',
spaceAggregation: 'sum',
stepInterval: getStepInterval(startTime, endTime),
timeAggregation: 'rate',
},
{
dataSource: DataSource.TRACES,
queryName: 'C',
aggregateOperator: 'count_distinct',
aggregateAttribute: {
dataType: DataTypes.String,
id: 'span_id--string----true',
isColumn: true,
isJSON: false,
key: 'span_id',
type: '',
},
timeAggregation: 'count_distinct',
spaceAggregation: 'sum',
functions: [],
filters: {
items: filters ?? [],
op: 'AND',
},
expression: 'C',
disabled: true,
stepInterval: getStepInterval(startTime, endTime),
having: [],
limit: null,
orderBy: [],
groupBy: groupByFilter ? [groupByFilter] : [],
legend: '',
reduceTo: 'avg',
},
{
queryName: 'F1',
expression: '(A/C)*100',
disabled: false,
legend: 'True',
} as any,
{
queryName: 'F2',
expression: '(B/C)*100',
disabled: false,
legend: 'False',
} as any,
],
panelTypes: PANEL_TYPES.BAR,
yAxisUnit: 'percent',
}),
);

View File

@ -21,6 +21,12 @@
line-height: 32px;
margin: 0;
}
.celery-content-header-right {
display: flex;
align-items: center;
gap: 8px;
}
}
}
}

View File

@ -1,12 +1,16 @@
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<CaptureDataProps | null>(null);
@ -15,15 +19,43 @@ export default function CeleryTask(): JSX.Element {
setTask(captureData);
};
const [isURLCopied, setIsURLCopied] = useState(false);
const [, handleCopyToClipboard] = useCopyToClipboard();
return (
<div className="celery-task-container">
<div className="celery-content">
<div className="celery-content-header">
<p className="celery-content-header-title">Celery Task</p>
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
<p className="celery-content-header-title">Celery</p>
<div className="celery-content-header-right">
<DateTimeSelectionV2 showAutoRefresh={false} hideShareModal />
<Tooltip title="Share this" arrow={false}>
<Button
className="periscope-btn copy-url-btn"
onClick={(): void => {
handleCopyToClipboard(window.location.href);
setIsURLCopied(true);
setTimeout(() => {
setIsURLCopied(false);
}, 1000);
}}
icon={
isURLCopied ? (
<Check size={14} color={Color.BG_FOREST_500} />
) : (
<Share2 size={14} />
)
}
/>
</Tooltip>
</div>
</div>
<CeleryTaskConfigOptions />
<CeleryTaskGraphGrid onClick={onTaskClick} queryEnabled={!task} />
<CeleryTaskGraphGrid
onClick={onTaskClick}
queryEnabled={!task}
configureOptionComponent={<CeleryTaskConfigOptions />}
/>
</div>
{!!task && (
<CeleryTaskDetail

View File

@ -38,7 +38,7 @@ export const Celery: TabRoutes = {
Component: CeleryTask,
name: (
<div className="tab-item">
<Rows3 size={16} /> Celery Task
<Rows3 size={16} /> Celery
</div>
),
route: ROUTES.MESSAGING_QUEUES_CELERY_TASK,