mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-31 03: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 AppReducer from 'types/reducer/app';
|
||||
|
||||
import { hasData } from './hasData';
|
||||
import { legend } from './Plugin';
|
||||
import { emptyGraph } from './Plugin/EmptyGraph';
|
||||
import { LegendsContainer } from './styles';
|
||||
import { useXAxisTimeUnit } from './xAxisConfig';
|
||||
import { getYAxisFormattedValue } from './yAxisConfig';
|
||||
@ -128,6 +130,7 @@ function Graph({
|
||||
grid: {
|
||||
display: true,
|
||||
color: getGridColor(),
|
||||
drawTicks: true,
|
||||
},
|
||||
adapters: {
|
||||
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, {
|
||||
type,
|
||||
data,
|
||||
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 { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
import EmptyGraph from './EmptyGraph';
|
||||
import { NotFoundContainer, TimeContainer } from './styles';
|
||||
|
||||
function FullView({
|
||||
widget,
|
||||
fullViewOptions = true,
|
||||
onClickHandler,
|
||||
noDataGraph = false,
|
||||
name,
|
||||
yAxisUnit,
|
||||
}: 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 (
|
||||
<>
|
||||
{fullViewOptions && (
|
||||
@ -243,7 +209,6 @@ interface FullViewProps {
|
||||
widget: Widgets;
|
||||
fullViewOptions?: boolean;
|
||||
onClickHandler?: GraphOnClickHandler;
|
||||
noDataGraph?: boolean;
|
||||
name: string;
|
||||
yAxisUnit?: string;
|
||||
}
|
||||
@ -251,7 +216,6 @@ interface FullViewProps {
|
||||
FullView.defaultProps = {
|
||||
fullViewOptions: undefined,
|
||||
onClickHandler: undefined,
|
||||
noDataGraph: undefined,
|
||||
yAxisUnit: undefined,
|
||||
};
|
||||
|
||||
|
@ -179,7 +179,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="request_per_sec"
|
||||
noDataGraph
|
||||
fullViewOptions={false}
|
||||
onClickHandler={(event, element, chart, data): void => {
|
||||
onClickhandler(event, element, chart, data, 'Request');
|
||||
@ -214,7 +213,6 @@ function Application({ getWidget }: DashboardProps): JSX.Element {
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="error_percentage_%"
|
||||
noDataGraph
|
||||
fullViewOptions={false}
|
||||
onClickHandler={(ChartEvent, activeElements, chart, data): void => {
|
||||
onClickhandler(ChartEvent, activeElements, chart, data, 'Error');
|
||||
|
@ -17,7 +17,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="database_call_rps"
|
||||
noDataGraph
|
||||
fullViewOptions={false}
|
||||
widget={getWidget([
|
||||
{
|
||||
@ -37,7 +36,6 @@ function DBCall({ getWidget }: DBCallProps): JSX.Element {
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="database_call_avg_duration"
|
||||
noDataGraph
|
||||
fullViewOptions={false}
|
||||
widget={getWidget([
|
||||
{
|
||||
|
@ -21,7 +21,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
||||
<FullView
|
||||
name="external_call_error_percentage"
|
||||
fullViewOptions={false}
|
||||
noDataGraph
|
||||
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)`,
|
||||
@ -40,7 +39,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="external_call_duration"
|
||||
noDataGraph
|
||||
fullViewOptions={false}
|
||||
widget={getWidget([
|
||||
{
|
||||
@ -62,7 +60,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
name="external_call_rps_by_address"
|
||||
noDataGraph
|
||||
fullViewOptions={false}
|
||||
widget={getWidget([
|
||||
{
|
||||
@ -81,7 +78,6 @@ function External({ getWidget }: ExternalProps): JSX.Element {
|
||||
<GraphTitle>External Call duration(by Address)</GraphTitle>
|
||||
<GraphContainer>
|
||||
<FullView
|
||||
noDataGraph
|
||||
name="external_call_duration_by_address"
|
||||
fullViewOptions={false}
|
||||
widget={getWidget([
|
||||
|
Loading…
x
Reference in New Issue
Block a user