mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-01 00:11:59 +08:00
Merge pull request #935 from pranshuchittora/pranshuchittora/feat/graph-memory-issue
feat: FE memory fixes and UX enhancements
This commit is contained in:
commit
a69bc321a9
17
frontend/src/components/Graph/Plugin/EmptyGraph.ts
Normal file
17
frontend/src/components/Graph/Plugin/EmptyGraph.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { grey } from '@ant-design/colors';
|
||||||
|
import { Chart } from 'chart.js';
|
||||||
|
|
||||||
|
export const emptyGraph = {
|
||||||
|
id: 'emptyChart',
|
||||||
|
afterDraw(chart: Chart): void {
|
||||||
|
const { height, width, ctx } = chart;
|
||||||
|
chart.clear();
|
||||||
|
ctx.save();
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'middle';
|
||||||
|
ctx.font = '1.5rem sans-serif';
|
||||||
|
ctx.fillStyle = `${grey.primary}`;
|
||||||
|
ctx.fillText('No data to display', width / 2, height / 2);
|
||||||
|
ctx.restore();
|
||||||
|
},
|
||||||
|
};
|
19
frontend/src/components/Graph/hasData.ts
Normal file
19
frontend/src/components/Graph/hasData.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* eslint-disable no-restricted-syntax */
|
||||||
|
import { ChartData } from 'chart.js';
|
||||||
|
|
||||||
|
export const hasData = (data: ChartData): boolean => {
|
||||||
|
const { datasets = [] } = data;
|
||||||
|
let hasData = false;
|
||||||
|
try {
|
||||||
|
for (const dataset of datasets) {
|
||||||
|
if (dataset.data.length > 0 && dataset.data.some((item) => item !== 0)) {
|
||||||
|
hasData = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return hasData;
|
||||||
|
};
|
@ -27,7 +27,9 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
|
import { hasData } from './hasData';
|
||||||
import { legend } from './Plugin';
|
import { legend } from './Plugin';
|
||||||
|
import { emptyGraph } from './Plugin/EmptyGraph';
|
||||||
import { LegendsContainer } from './styles';
|
import { LegendsContainer } from './styles';
|
||||||
import { useXAxisTimeUnit } from './xAxisConfig';
|
import { useXAxisTimeUnit } from './xAxisConfig';
|
||||||
import { getYAxisFormattedValue } from './yAxisConfig';
|
import { getYAxisFormattedValue } from './yAxisConfig';
|
||||||
@ -128,6 +130,7 @@ function Graph({
|
|||||||
grid: {
|
grid: {
|
||||||
display: true,
|
display: true,
|
||||||
color: getGridColor(),
|
color: getGridColor(),
|
||||||
|
drawTicks: true,
|
||||||
},
|
},
|
||||||
adapters: {
|
adapters: {
|
||||||
date: chartjsAdapter,
|
date: chartjsAdapter,
|
||||||
@ -180,12 +183,18 @@ function Graph({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
const chartHasData = hasData(data);
|
||||||
|
const chartPlugins = [];
|
||||||
|
if (chartHasData) {
|
||||||
|
chartPlugins.push(legend(name, data.datasets.length > 3));
|
||||||
|
} else {
|
||||||
|
chartPlugins.push(emptyGraph);
|
||||||
|
}
|
||||||
lineChartRef.current = new Chart(chartRef.current, {
|
lineChartRef.current = new Chart(chartRef.current, {
|
||||||
type,
|
type,
|
||||||
data,
|
data,
|
||||||
options,
|
options,
|
||||||
plugins: [legend(name, data.datasets.length > 3)],
|
plugins: chartPlugins,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
import Graph, { GraphOnClickHandler } from 'components/Graph';
|
|
||||||
import { timePreferance } from 'container/NewWidget/RightContainer/timeItems';
|
|
||||||
import GetMaxMinTime from 'lib/getMaxMinTime';
|
|
||||||
import { colors } from 'lib/getRandomColor';
|
|
||||||
import getStartAndEndTime from 'lib/getStartAndEndTime';
|
|
||||||
import getTimeString from 'lib/getTimeString';
|
|
||||||
import React, { useCallback } from 'react';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
|
||||||
|
|
||||||
function EmptyGraph({
|
|
||||||
selectedTime,
|
|
||||||
widget,
|
|
||||||
onClickHandler,
|
|
||||||
}: EmptyGraphProps): JSX.Element {
|
|
||||||
const { minTime, maxTime, loading } = useSelector<AppState, GlobalReducer>(
|
|
||||||
(state) => state.globalTime,
|
|
||||||
);
|
|
||||||
|
|
||||||
const maxMinTime = GetMaxMinTime({
|
|
||||||
graphType: widget.panelTypes,
|
|
||||||
maxTime,
|
|
||||||
minTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
const { end, start } = getStartAndEndTime({
|
|
||||||
type: selectedTime.enum,
|
|
||||||
maxTime: maxMinTime.maxTime,
|
|
||||||
minTime: maxMinTime.minTime,
|
|
||||||
});
|
|
||||||
|
|
||||||
const dateFunction = useCallback(() => {
|
|
||||||
if (!loading) {
|
|
||||||
const dates: Date[] = [];
|
|
||||||
|
|
||||||
const startString = getTimeString(start);
|
|
||||||
const endString = getTimeString(end);
|
|
||||||
|
|
||||||
const parsedStart = parseInt(startString, 10);
|
|
||||||
const parsedEnd = parseInt(endString, 10);
|
|
||||||
|
|
||||||
let startDate = parsedStart;
|
|
||||||
const endDate = parsedEnd;
|
|
||||||
|
|
||||||
while (endDate >= startDate) {
|
|
||||||
const newDate = new Date(startDate);
|
|
||||||
|
|
||||||
startDate += 20000;
|
|
||||||
|
|
||||||
dates.push(newDate);
|
|
||||||
}
|
|
||||||
return dates;
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [start, end, loading]);
|
|
||||||
|
|
||||||
const date = dateFunction();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Graph
|
|
||||||
name=""
|
|
||||||
{...{
|
|
||||||
type: 'line',
|
|
||||||
onClickHandler,
|
|
||||||
data: {
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
data: new Array(date?.length).fill(0),
|
|
||||||
borderColor: colors[0],
|
|
||||||
showLine: true,
|
|
||||||
borderWidth: 1.5,
|
|
||||||
spanGaps: true,
|
|
||||||
pointRadius: 0,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
labels: date,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EmptyGraphProps {
|
|
||||||
selectedTime: timePreferance;
|
|
||||||
widget: Widgets;
|
|
||||||
onClickHandler: GraphOnClickHandler | undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default EmptyGraph;
|
|
@ -23,14 +23,12 @@ import { AppState } from 'store/reducers';
|
|||||||
import { Widgets } from 'types/api/dashboard/getAll';
|
import { Widgets } from 'types/api/dashboard/getAll';
|
||||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||||
|
|
||||||
import EmptyGraph from './EmptyGraph';
|
|
||||||
import { NotFoundContainer, TimeContainer } from './styles';
|
import { NotFoundContainer, TimeContainer } from './styles';
|
||||||
|
|
||||||
function FullView({
|
function FullView({
|
||||||
widget,
|
widget,
|
||||||
fullViewOptions = true,
|
fullViewOptions = true,
|
||||||
onClickHandler,
|
onClickHandler,
|
||||||
noDataGraph = false,
|
|
||||||
name,
|
name,
|
||||||
yAxisUnit,
|
yAxisUnit,
|
||||||
}: FullViewProps): JSX.Element {
|
}: FullViewProps): JSX.Element {
|
||||||
@ -166,38 +164,6 @@ function FullView({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.loading === false && state.payload.datasets.length === 0) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{fullViewOptions && (
|
|
||||||
<TimeContainer>
|
|
||||||
<TimePreference
|
|
||||||
{...{
|
|
||||||
selectedTime,
|
|
||||||
setSelectedTime,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button onClick={onFetchDataHandler} type="primary">
|
|
||||||
Refresh
|
|
||||||
</Button>
|
|
||||||
</TimeContainer>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{noDataGraph ? (
|
|
||||||
<EmptyGraph
|
|
||||||
onClickHandler={onClickHandler}
|
|
||||||
widget={widget}
|
|
||||||
selectedTime={selectedTime}
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<NotFoundContainer>
|
|
||||||
<Typography>No Data</Typography>
|
|
||||||
</NotFoundContainer>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{fullViewOptions && (
|
{fullViewOptions && (
|
||||||
@ -243,7 +209,6 @@ interface FullViewProps {
|
|||||||
widget: Widgets;
|
widget: Widgets;
|
||||||
fullViewOptions?: boolean;
|
fullViewOptions?: boolean;
|
||||||
onClickHandler?: GraphOnClickHandler;
|
onClickHandler?: GraphOnClickHandler;
|
||||||
noDataGraph?: boolean;
|
|
||||||
name: string;
|
name: string;
|
||||||
yAxisUnit?: string;
|
yAxisUnit?: string;
|
||||||
}
|
}
|
||||||
@ -251,7 +216,6 @@ interface FullViewProps {
|
|||||||
FullView.defaultProps = {
|
FullView.defaultProps = {
|
||||||
fullViewOptions: undefined,
|
fullViewOptions: undefined,
|
||||||
onClickHandler: undefined,
|
onClickHandler: undefined,
|
||||||
noDataGraph: undefined,
|
|
||||||
yAxisUnit: undefined,
|
yAxisUnit: undefined,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -179,7 +179,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="request_per_sec"
|
name="request_per_sec"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
onClickHandler={(event, element, chart, data): void => {
|
onClickHandler={(event, element, chart, data): void => {
|
||||||
onClickhandler(event, element, chart, data, 'Request');
|
onClickhandler(event, element, chart, data, 'Request');
|
||||||
@ -214,7 +213,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="error_percentage_%"
|
name="error_percentage_%"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||||
onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
|
onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
|
||||||
|
@ -17,7 +17,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="database_call_rps"
|
name="database_call_rps"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -37,7 +36,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="database_call_avg_duration"
|
name="database_call_avg_duration"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<FullView
|
<FullView
|
||||||
name="external_call_error_percentage"
|
name="external_call_error_percentage"
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
noDataGraph
|
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
query: `max((sum(rate(signoz_external_call_latency_count{service_name="${servicename}", status_code="STATUS_CODE_ERROR"}[1m]) OR rate(signoz_external_call_latency_count{service_name="${servicename}", http_status_code=~"5.."}[1m]) OR vector(0)) by (http_url))*100/sum(rate(signoz_external_call_latency_count{service_name="${servicename}"}[1m])) by (http_url)) < 1000 OR vector(0)`,
|
||||||
@ -40,7 +39,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="external_call_duration"
|
name="external_call_duration"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -62,7 +60,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
name="external_call_rps_by_address"
|
name="external_call_rps_by_address"
|
||||||
noDataGraph
|
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
{
|
{
|
||||||
@ -81,7 +78,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
|||||||
<GraphTitle>External Call duration(by Address)</GraphTitle>
|
<GraphTitle>External Call duration(by Address)</GraphTitle>
|
||||||
<GraphContainer>
|
<GraphContainer>
|
||||||
<FullView
|
<FullView
|
||||||
noDataGraph
|
|
||||||
name="external_call_duration_by_address"
|
name="external_call_duration_by_address"
|
||||||
fullViewOptions={false}
|
fullViewOptions={false}
|
||||||
widget={getWidget([
|
widget={getWidget([
|
||||||
|
Loading…
x
Reference in New Issue
Block a user