Fixed some issues with dashboard (#570)
-Added padding to the bottom of dashboard -Added calculations for linear chart tick interval -Added default min width configurations to panels. - Fixed crash on clean tenant Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -14,38 +14,40 @@
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import React, { useEffect, useMemo, useState, useCallback } from "react";
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import ReactGridLayout from "react-grid-layout";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { Button } from "@material-ui/core";
|
||||
import {
|
||||
actionsTray,
|
||||
containerForHeader,
|
||||
} from "../../Common/FormComponents/common/styleLibrary";
|
||||
import SingleValueWidget from "./Widgets/SingleValueWidget";
|
||||
|
||||
import { AutoSizer } from "react-virtualized";
|
||||
import LinearGraphWidget from "./Widgets/LinearGraphWidget";
|
||||
import {
|
||||
IBarChartConfiguration,
|
||||
IDataSRep,
|
||||
ILinearGraphConfiguration,
|
||||
IPieChartConfiguration,
|
||||
} from "./Widgets/types";
|
||||
import BarChartWidget from "./Widgets/BarChartWidget";
|
||||
import PieChartWidget from "./Widgets/PieChartWidget";
|
||||
import SingleRepWidget from "./Widgets/SingleRepWidget";
|
||||
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
|
||||
import { IDashboardPanel, widgetType } from "./types";
|
||||
import api from "../../../../common/api";
|
||||
import {
|
||||
getDashboardDistribution,
|
||||
getWidgetsWithValue,
|
||||
panelsConfiguration,
|
||||
saveDashboardDistribution,
|
||||
} from "./utils";
|
||||
import { Button } from "@material-ui/core";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import { setErrorSnackMessage } from "../../../../actions";
|
||||
import SingleValueWidget from "./Widgets/SingleValueWidget";
|
||||
import LinearGraphWidget from "./Widgets/LinearGraphWidget";
|
||||
import BarChartWidget from "./Widgets/BarChartWidget";
|
||||
import PieChartWidget from "./Widgets/PieChartWidget";
|
||||
import SingleRepWidget from "./Widgets/SingleRepWidget";
|
||||
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
|
||||
import api from "../../../../common/api";
|
||||
|
||||
interface IPrDashboard {
|
||||
classes: any;
|
||||
@@ -56,6 +58,7 @@ const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
widgetsContainer: {
|
||||
height: "calc(100vh - 250px)",
|
||||
paddingBottom: 235,
|
||||
},
|
||||
...actionsTray,
|
||||
...containerForHeader(theme.spacing(4)),
|
||||
@@ -70,72 +73,91 @@ const PrDashboard = ({ classes, displayErrorMessage }: IPrDashboard) => {
|
||||
);
|
||||
|
||||
const minHeight = 600;
|
||||
const colsInGrid = 8;
|
||||
const xSpacing = 10;
|
||||
const ySpacing = 10;
|
||||
|
||||
const panels = useMemo(() => {
|
||||
const componentToUse = (value: IDashboardPanel) => {
|
||||
switch (value.type) {
|
||||
case widgetType.singleValue:
|
||||
return (
|
||||
<SingleValueWidget
|
||||
title={value.title}
|
||||
data={value.data as string}
|
||||
/>
|
||||
);
|
||||
case widgetType.pieChart:
|
||||
return (
|
||||
<PieChartWidget
|
||||
title={value.title}
|
||||
dataInner={value.data as object[]}
|
||||
dataOuter={(value.dataOuter as object[]) || null}
|
||||
pieChartConfiguration={
|
||||
value.widgetConfiguration as IPieChartConfiguration
|
||||
}
|
||||
middleLabel={value.innerLabel}
|
||||
/>
|
||||
);
|
||||
case widgetType.linearGraph:
|
||||
return (
|
||||
<LinearGraphWidget
|
||||
title={value.title}
|
||||
data={value.data as object[]}
|
||||
linearConfiguration={
|
||||
value.widgetConfiguration as ILinearGraphConfiguration[]
|
||||
}
|
||||
hideYAxis={value.disableYAxis}
|
||||
xAxisFormatter={value.xAxisFormatter}
|
||||
yAxisFormatter={value.yAxisFormatter}
|
||||
/>
|
||||
);
|
||||
case widgetType.barChart:
|
||||
return (
|
||||
<BarChartWidget
|
||||
title={value.title}
|
||||
data={value.data as object[]}
|
||||
barChartConfiguration={
|
||||
value.widgetConfiguration as IBarChartConfiguration[]
|
||||
}
|
||||
/>
|
||||
);
|
||||
case widgetType.singleRep:
|
||||
const fillColor = value.fillColor ? value.fillColor : value.color;
|
||||
return (
|
||||
<SingleRepWidget
|
||||
title={value.title}
|
||||
data={value.data as IDataSRep[]}
|
||||
label={value.innerLabel as string}
|
||||
color={value.color as string}
|
||||
fillColor={fillColor as string}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
const dashboardDistr = getDashboardDistribution();
|
||||
|
||||
return panelInformation.map((val) => {
|
||||
return <div key={val.layoutIdentifier}>{componentToUse(val)}</div>;
|
||||
});
|
||||
}, [panelInformation]);
|
||||
const autoSizerStyleProp = {
|
||||
width: "100%",
|
||||
height: "auto",
|
||||
paddingBottom: 45,
|
||||
};
|
||||
|
||||
const panels = useCallback(
|
||||
(width: number) => {
|
||||
const singlePanelWidth = width / colsInGrid + xSpacing / 2;
|
||||
|
||||
const componentToUse = (value: IDashboardPanel, index: number) => {
|
||||
switch (value.type) {
|
||||
case widgetType.singleValue:
|
||||
return (
|
||||
<SingleValueWidget
|
||||
title={value.title}
|
||||
data={value.data as string}
|
||||
/>
|
||||
);
|
||||
case widgetType.pieChart:
|
||||
return (
|
||||
<PieChartWidget
|
||||
title={value.title}
|
||||
dataInner={value.data as object[]}
|
||||
dataOuter={(value.dataOuter as object[]) || null}
|
||||
pieChartConfiguration={
|
||||
value.widgetConfiguration as IPieChartConfiguration
|
||||
}
|
||||
middleLabel={value.innerLabel}
|
||||
/>
|
||||
);
|
||||
case widgetType.linearGraph:
|
||||
return (
|
||||
<LinearGraphWidget
|
||||
title={value.title}
|
||||
data={value.data as object[]}
|
||||
linearConfiguration={
|
||||
value.widgetConfiguration as ILinearGraphConfiguration[]
|
||||
}
|
||||
hideYAxis={value.disableYAxis}
|
||||
xAxisFormatter={value.xAxisFormatter}
|
||||
yAxisFormatter={value.yAxisFormatter}
|
||||
panelWidth={singlePanelWidth * dashboardDistr[index].w}
|
||||
/>
|
||||
);
|
||||
case widgetType.barChart:
|
||||
return (
|
||||
<BarChartWidget
|
||||
title={value.title}
|
||||
data={value.data as object[]}
|
||||
barChartConfiguration={
|
||||
value.widgetConfiguration as IBarChartConfiguration[]
|
||||
}
|
||||
/>
|
||||
);
|
||||
case widgetType.singleRep:
|
||||
const fillColor = value.fillColor ? value.fillColor : value.color;
|
||||
return (
|
||||
<SingleRepWidget
|
||||
title={value.title}
|
||||
data={value.data as IDataSRep[]}
|
||||
label={value.innerLabel as string}
|
||||
color={value.color as string}
|
||||
fillColor={fillColor as string}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return panelInformation.map((val, index) => {
|
||||
return (
|
||||
<div key={val.layoutIdentifier}>{componentToUse(val, index)}</div>
|
||||
);
|
||||
});
|
||||
},
|
||||
[panelInformation, dashboardDistr]
|
||||
);
|
||||
|
||||
const fetchUsage = useCallback(() => {
|
||||
let stepCalc = 15;
|
||||
@@ -184,8 +206,6 @@ const PrDashboard = ({ classes, displayErrorMessage }: IPrDashboard) => {
|
||||
}
|
||||
}, [loading, fetchUsage]);
|
||||
|
||||
const dashboardDistr = getDashboardDistribution();
|
||||
|
||||
return (
|
||||
<Grid container className={classes.container}>
|
||||
<Grid
|
||||
@@ -207,19 +227,19 @@ const PrDashboard = ({ classes, displayErrorMessage }: IPrDashboard) => {
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.widgetsContainer}>
|
||||
<AutoSizer>
|
||||
<AutoSizer style={autoSizerStyleProp}>
|
||||
{({ width, height }: any) => {
|
||||
const hpanel = height < minHeight ? minHeight : height;
|
||||
return (
|
||||
<ReactGridLayout
|
||||
width={width}
|
||||
cols={8}
|
||||
containerPadding={[10, 10]}
|
||||
cols={colsInGrid}
|
||||
containerPadding={[xSpacing, ySpacing]}
|
||||
onLayoutChange={saveDashboardDistribution}
|
||||
layout={dashboardDistr}
|
||||
rowHeight={hpanel / 6}
|
||||
>
|
||||
{panels}
|
||||
{panels(width)}
|
||||
</ReactGridLayout>
|
||||
);
|
||||
}}
|
||||
|
||||
@@ -37,6 +37,7 @@ interface ILinearGraphWidget {
|
||||
hideYAxis?: boolean;
|
||||
yAxisFormatter?: (item: string) => string;
|
||||
xAxisFormatter?: (item: string) => string;
|
||||
panelWidth?: number;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
@@ -73,7 +74,23 @@ const LinearGraphWidget = ({
|
||||
hideYAxis = false,
|
||||
yAxisFormatter = (item: string) => item,
|
||||
xAxisFormatter = (item: string) => item,
|
||||
panelWidth = 0,
|
||||
}: ILinearGraphWidget) => {
|
||||
let intervalCount = 5;
|
||||
|
||||
if (panelWidth !== 0) {
|
||||
if (panelWidth > 400) {
|
||||
intervalCount = 5;
|
||||
} else if (panelWidth > 350) {
|
||||
intervalCount = 10;
|
||||
} else if (panelWidth > 300) {
|
||||
intervalCount = 15;
|
||||
} else if (panelWidth > 250) {
|
||||
intervalCount = 20;
|
||||
} else {
|
||||
intervalCount = 30;
|
||||
}
|
||||
}
|
||||
return (
|
||||
<div className={classes.singleValueContainer}>
|
||||
<div className={classes.titleContainer}>{title}</div>
|
||||
@@ -97,7 +114,7 @@ const LinearGraphWidget = ({
|
||||
<XAxis
|
||||
dataKey="name"
|
||||
tickFormatter={(value: any) => xAxisFormatter(value)}
|
||||
interval={5}
|
||||
interval={intervalCount}
|
||||
tick={{ fontSize: "70%" }}
|
||||
tickCount={10}
|
||||
/>
|
||||
|
||||
@@ -27,27 +27,216 @@ import {
|
||||
const dLocalStorageV = "dashboardConfig";
|
||||
|
||||
export const defaultWidgetsLayout: Layout[] = [
|
||||
{ w: 1, h: 2, x: 0, y: 0, i: "panel-0", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 0, i: "panel-1", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 1, i: "panel-2", moved: false, static: false },
|
||||
{ w: 1, h: 2, x: 2, y: 0, i: "panel-3", moved: false, static: false },
|
||||
{ w: 2, h: 2, x: 3, y: 0, i: "panel-4", moved: false, static: false },
|
||||
{ w: 3, h: 2, x: 5, y: 0, i: "panel-5", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 0, y: 2, i: "panel-6", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 0, y: 3, i: "panel-7", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 2, i: "panel-8", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 1, y: 3, i: "panel-9", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 2, y: 2, i: "panel-10", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 2, y: 3, i: "panel-11", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 3, y: 2, i: "panel-12", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 7, y: 2, i: "panel-13", moved: false, static: false },
|
||||
{ w: 1, h: 1, x: 7, y: 3, i: "panel-14", moved: false, static: false },
|
||||
{ w: 8, h: 2, x: 0, y: 4, i: "panel-15", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 0, y: 5, i: "panel-16", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 5, y: 5, i: "panel-17", moved: false, static: false },
|
||||
{ w: 8, h: 2, x: 0, y: 7, i: "panel-18", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 0, y: 9, i: "panel-19", moved: false, static: false },
|
||||
{ w: 4, h: 2, x: 5, y: 9, i: "panel-20", moved: false, static: false },
|
||||
{
|
||||
w: 1,
|
||||
h: 2,
|
||||
x: 0,
|
||||
y: 0,
|
||||
minW: 1,
|
||||
i: "panel-0",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 1,
|
||||
y: 0,
|
||||
minW: 1,
|
||||
i: "panel-1",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 1,
|
||||
y: 1,
|
||||
minW: 1,
|
||||
i: "panel-2",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 2,
|
||||
x: 2,
|
||||
y: 0,
|
||||
minW: 1,
|
||||
i: "panel-3",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 2,
|
||||
h: 2,
|
||||
x: 3,
|
||||
y: 0,
|
||||
minW: 2,
|
||||
i: "panel-4",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 3,
|
||||
h: 2,
|
||||
x: 5,
|
||||
y: 0,
|
||||
minW: 2,
|
||||
i: "panel-5",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 0,
|
||||
y: 2,
|
||||
minW: 1,
|
||||
i: "panel-6",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 0,
|
||||
y: 3,
|
||||
minW: 1,
|
||||
i: "panel-7",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 1,
|
||||
y: 2,
|
||||
minW: 1,
|
||||
i: "panel-8",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 1,
|
||||
y: 3,
|
||||
minW: 1,
|
||||
i: "panel-9",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 2,
|
||||
y: 2,
|
||||
minW: 1,
|
||||
i: "panel-10",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 2,
|
||||
y: 3,
|
||||
minW: 1,
|
||||
i: "panel-11",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 4,
|
||||
h: 2,
|
||||
x: 3,
|
||||
y: 2,
|
||||
minW: 2,
|
||||
i: "panel-12",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 7,
|
||||
y: 2,
|
||||
minW: 1,
|
||||
i: "panel-13",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 1,
|
||||
h: 1,
|
||||
x: 7,
|
||||
y: 3,
|
||||
minW: 1,
|
||||
i: "panel-14",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 8,
|
||||
h: 2,
|
||||
x: 0,
|
||||
y: 4,
|
||||
minW: 2,
|
||||
i: "panel-15",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 4,
|
||||
h: 2,
|
||||
x: 0,
|
||||
y: 5,
|
||||
minW: 2,
|
||||
i: "panel-16",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 4,
|
||||
h: 2,
|
||||
x: 5,
|
||||
y: 5,
|
||||
minW: 2,
|
||||
i: "panel-17",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 8,
|
||||
h: 2,
|
||||
x: 0,
|
||||
y: 7,
|
||||
minW: 2,
|
||||
i: "panel-18",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 4,
|
||||
h: 2,
|
||||
x: 0,
|
||||
y: 9,
|
||||
minW: 2,
|
||||
i: "panel-19",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
{
|
||||
w: 4,
|
||||
h: 2,
|
||||
x: 5,
|
||||
y: 9,
|
||||
minW: 2,
|
||||
i: "panel-20",
|
||||
moved: false,
|
||||
static: false,
|
||||
},
|
||||
];
|
||||
|
||||
const colorsMain = [
|
||||
@@ -347,6 +536,10 @@ export const panelsConfiguration: IDashboardPanel[] = [
|
||||
];
|
||||
|
||||
const calculateMainValue = (elements: any[], metricCalc: string) => {
|
||||
if (elements.length === 0) {
|
||||
return ["", "0"];
|
||||
}
|
||||
|
||||
switch (metricCalc) {
|
||||
case "mean":
|
||||
const sumValues = elements.reduce((accumulator, currValue) => {
|
||||
@@ -394,7 +587,12 @@ export const getWidgetsWithValue = (payload: any[]) => {
|
||||
case widgetType.singleValue:
|
||||
if (typeOfPayload === "stat" || typeOfPayload === "singlestat") {
|
||||
// We sort values & get the last value
|
||||
const elements = get(payloadData, "targets[0].result[0].values", []);
|
||||
let elements = get(payloadData, "targets[0].result[0].values", []);
|
||||
|
||||
if (elements === null) {
|
||||
elements = [];
|
||||
}
|
||||
|
||||
const metricCalc = get(
|
||||
payloadData,
|
||||
"options.reduceOptions.calcs[0]",
|
||||
@@ -415,17 +613,22 @@ export const getWidgetsWithValue = (payload: any[]) => {
|
||||
break;
|
||||
case widgetType.pieChart:
|
||||
if (typeOfPayload === "gauge") {
|
||||
const chartSeries = get(payloadData, "targets[0].result", []);
|
||||
let chartSeries = get(payloadData, "targets[0].result", []);
|
||||
|
||||
if (chartSeries === null) {
|
||||
chartSeries = [];
|
||||
}
|
||||
|
||||
const metricCalc = get(
|
||||
payloadData,
|
||||
"options.reduceOptions.calcs[0]",
|
||||
"lastNotNull"
|
||||
);
|
||||
|
||||
const totalValues = calculateMainValue(
|
||||
chartSeries[0].values,
|
||||
metricCalc
|
||||
);
|
||||
const valuesArray =
|
||||
chartSeries.length > 0 ? chartSeries[0].values : [];
|
||||
|
||||
const totalValues = calculateMainValue(valuesArray, metricCalc);
|
||||
|
||||
const values = chartSeries.map((elementValue: any) => {
|
||||
const values = get(elementValue, "values", []);
|
||||
@@ -549,7 +752,12 @@ export const getWidgetsWithValue = (payload: any[]) => {
|
||||
break;
|
||||
case widgetType.barChart:
|
||||
if (typeOfPayload === "bargauge") {
|
||||
const chartBars = get(payloadData, "targets[0].result", []);
|
||||
let chartBars = get(payloadData, "targets[0].result", []);
|
||||
|
||||
if (chartBars === null) {
|
||||
chartBars = [];
|
||||
}
|
||||
|
||||
const sortFunction = (value1: any[], value2: any[]) =>
|
||||
value1[0] - value2[0];
|
||||
|
||||
@@ -567,7 +775,7 @@ export const getWidgetsWithValue = (payload: any[]) => {
|
||||
const elements = get(metricTake, "values", []);
|
||||
|
||||
const sortResult = elements.sort(sortFunction);
|
||||
const lastValue = sortResult[sortResult.length - 1];
|
||||
const lastValue = sortResult[sortResult.length - 1] || ["", "0"];
|
||||
|
||||
return {
|
||||
name: structureItem.displayTag,
|
||||
@@ -584,7 +792,7 @@ export const getWidgetsWithValue = (payload: any[]) => {
|
||||
const elements = get(elementValue, "values", []);
|
||||
|
||||
const sortResult = elements.sort(sortFunction);
|
||||
const lastValue = sortResult[sortResult.length - 1];
|
||||
const lastValue = sortResult[sortResult.length - 1] || ["", "0"];
|
||||
return { name: metricName, a: parseInt(lastValue[1]) };
|
||||
});
|
||||
}
|
||||
@@ -652,5 +860,14 @@ export const getDashboardDistribution = () => {
|
||||
return defaultWidgetsLayout;
|
||||
}
|
||||
|
||||
return JSON.parse(atob(storedConfiguration));
|
||||
const parsedConfig = JSON.parse(atob(storedConfiguration));
|
||||
|
||||
if (
|
||||
parsedConfig.length === 0 ||
|
||||
(parsedConfig.length > 0 && !parsedConfig[0].minW)
|
||||
) {
|
||||
return defaultWidgetsLayout;
|
||||
}
|
||||
|
||||
return parsedConfig;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user