Updated styles in Prometheus dashboard (#1078)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2021-09-23 12:01:56 -05:00
committed by GitHub
parent 951a041bc5
commit cb886f0130
18 changed files with 703 additions and 768 deletions

View File

@@ -22,7 +22,6 @@
"@types/react-router": "^5.1.3",
"@types/react-router-dom": "^5.1.2",
"@types/react-virtualized": "^9.21.10",
"@types/recharts": "^1.8.19",
"@types/superagent": "^4.1.12",
"@types/webpack-env": "^1.14.1",
"@types/websocket": "^1.0.0",
@@ -49,7 +48,7 @@
"react-scripts": "4.0.3",
"react-virtualized": "^9.22.2",
"react-window-infinite-loader": "^1.0.5",
"recharts": "^2.0.3",
"recharts": "^2.1.1",
"redux": "^4.0.5",
"redux-thunk": "^2.3.0",
"superagent": "^6.1.0",

View File

@@ -34,6 +34,7 @@ interface IDateTimePicker {
tooltip?: string;
id: string;
disabled?: boolean;
noInputIcon?: boolean;
}
const styles = (theme: Theme) =>
@@ -93,18 +94,27 @@ const DateTimePickerWrapper = ({
required,
id,
disabled = false,
noInputIcon = false,
}: IDateTimePicker) => {
let adornment = {};
if (!noInputIcon) {
adornment = {
startAdornment: (
<InputAdornment position="start">
<ScheduleIcon />
</InputAdornment>
),
};
}
const inputItem = (
<MuiPickersUtilsProvider utils={MomentUtils}>
<DateTimePicker
value={value}
onChange={onChange}
InputProps={{
startAdornment: (
<InputAdornment position="start">
<ScheduleIcon />
</InputAdornment>
),
...adornment,
className: forSearchBlock ? classes.dateSelectorOverride : "",
}}
label=""

View File

@@ -148,9 +148,17 @@ export const containerForHeader = (bottomSpacing: any) => ({
});
export const actionsTray = {
filterTitle: {
color: "#848484",
fontSize: 13,
alignSelf: "center" as const,
whiteSpace: "nowrap" as const,
"&:not(:first-of-type)": {
marginLeft: 10,
},
},
label: {
color: "#393939",
fontWeight: 600,
color: "#07193E",
fontSize: 13,
alignSelf: "center" as const,
whiteSpace: "nowrap" as const,
@@ -160,6 +168,7 @@ export const actionsTray = {
},
timeContainers: {
height: 40,
maxWidth: 1185,
},
actionsTray: {
display: "flex" as const,
@@ -399,29 +408,29 @@ export const logsCommon = {
export const widgetCommon = {
singleValueContainer: {
position: "relative" as const,
flexGrow: 1,
height: 200,
minWidth: 280,
maxWidth: 1185,
border: "#eef1f4 2px solid",
borderRadius: 10,
width: "100%",
height: "100%",
border: "#EAEAEA 1px solid",
borderRadius: 5,
backgroundColor: "#fff",
padding: 16,
},
titleContainer: {
color: "#393939",
fontWeight: 600,
height: 15,
textAlign: "center" as const,
fontSize: 10,
color: "#404143",
fontSize: 14,
textTransform: "uppercase" as const,
fontWeight: 800,
borderBottom: "#eef1f4 1px solid",
paddingBottom: 14,
marginBottom: 5,
},
contentContainer: {
flexGrow: 2,
justifyContent: "center" as const,
alignItems: "center" as const,
display: "flex" as const,
position: "absolute" as const,
width: "100%",
height: "calc(100% - 15px)",
height: 140,
},
contentContainerWithLabel: {
height: "calc(100% - 25px)",
@@ -445,7 +454,6 @@ export const widgetCommon = {
width: 8,
height: 8,
minWidth: 8,
borderRadius: "100%",
marginRight: 5,
},
legendLabel: {

View File

@@ -18,7 +18,6 @@ import React, { Fragment, useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button, LinearProgress } from "@material-ui/core";
import CssBaseline from "@material-ui/core/CssBaseline";
import Container from "@material-ui/core/Container";
import Snackbar from "@material-ui/core/Snackbar";
import history from "../../history";
import { Redirect, Route, Router, Switch, useLocation } from "react-router-dom";

View File

@@ -20,10 +20,8 @@ import Grid from "@material-ui/core/Grid";
import { IDriveInfo, Usage } from "../types";
import { calculateBytes } from "../../../../common/utils";
import { TabPanel } from "../../../shared/tabs";
import ReportedUsageIcon from "../../../../icons/ReportedUsageIcon";
import ServerInfoCard from "./ServerInfoCard";
import DriveInfoCard from "./DriveInfoCard";
import { BucketsIcon, TotalObjectsIcon } from "../../../../icons";
import CommonCard from "../CommonCard";
import TabSelector from "../../Common/TabSelector/TabSelector";
import GeneralUsePaginator from "../../Common/GeneralUsePaginator/GeneralUsePaginator";
@@ -133,23 +131,19 @@ const BasicDashboard = ({ classes, usage }: IDashboardProps) => {
</Grid>
<Grid item xs={12} className={classes.generalStatusCards}>
<CommonCard
avatar={<BucketsIcon />}
title={"All Buckets"}
metricValue={usage ? prettyNumber(usage.buckets) : 0}
/>
<CommonCard
avatar={<ReportedUsageIcon />}
title={"Usage"}
metricValue={usageToRepresent.total}
metricUnit={usageToRepresent.unit}
/>
<CommonCard
avatar={<TotalObjectsIcon />}
title={"Total Objects"}
metricValue={usage ? prettyNumber(usage.objects) : 0}
/>
<CommonCard
avatar={<TotalObjectsIcon />}
title={"Servers"}
metricValue={usage ? prettyNumber(serverArray.length) : 0}
subMessage={{ message: "Total" }}

View File

@@ -23,6 +23,7 @@ import {
makeStyles,
} from "@material-ui/core/styles";
import React, { Fragment } from "react";
import { widgetCommon } from "../Common/FormComponents/common/styleLibrary";
export interface ISubInterface {
message: string;
@@ -30,7 +31,6 @@ export interface ISubInterface {
}
interface ICommonCard {
avatar: any;
title: string;
metricValue: any;
metricUnit?: string;
@@ -42,12 +42,10 @@ interface ICommonCard {
const styles = (theme: Theme) =>
createStyles({
...widgetCommon,
cardRoot: {
border: "#eef1f4 2px solid",
borderRadius: 10,
...widgetCommon.singleValueContainer,
maxWidth: 280,
width: "100%",
margin: 10,
},
cardsContainer: {
maxHeight: 440,
@@ -94,15 +92,9 @@ const styles = (theme: Theme) =>
});
const cardSubStyles = makeStyles({
root: { backgroundColor: "#fff" },
root: { backgroundColor: "#fff", padding: 0 },
title: {
color: "#404144",
fontSize: 14,
textTransform: "uppercase",
fontWeight: "bold",
borderBottom: "#eef1f4 1px solid",
paddingBottom: 14,
marginBottom: 5,
...widgetCommon.titleContainer
},
content: {
maxWidth: "100%",
@@ -110,7 +102,6 @@ const cardSubStyles = makeStyles({
});
const CommonCard = ({
avatar,
title,
metricValue,
metricUnit,

View File

@@ -0,0 +1,42 @@
// This file is part of MinIO Console Server
// Copyright (c) 2021 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// 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, { Fragment } from "react";
import CommonCard from "../CommonCard";
interface IMergedWidgets {
title: string;
leftComponent: any;
rightComponent: any;
}
const MergedWidgets = ({
title,
leftComponent,
rightComponent,
}: IMergedWidgets) => {
return (
<Fragment>
<CommonCard
title={title}
metricValue={leftComponent}
rightComponent={rightComponent}
/>
</Fragment>
);
};
export default MergedWidgets;

View File

@@ -14,21 +14,16 @@
// 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, { useCallback, useEffect, useState } from "react";
import React, { Fragment, useCallback, useEffect, useState } from "react";
import { connect } from "react-redux";
import ReactGridLayout from "react-grid-layout";
import Grid from "@material-ui/core/Grid";
import ScheduleIcon from "@material-ui/icons/Schedule";
import WatchLaterIcon from "@material-ui/icons/WatchLater";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { Button } from "@material-ui/core";
import { actionsTray } from "../../Common/FormComponents/common/styleLibrary";
import { AutoSizer } from "react-virtualized";
import { IDashboardPanel, widgetType } from "./types";
import {
getDashboardDistribution,
getWidgetsWithValue,
panelsConfiguration,
saveDashboardDistribution,
} from "./utils";
import { getWidgetsWithValue, panelsConfiguration } from "./utils";
import { TabPanel } from "../../../shared/tabs";
import { ErrorResponseHandler } from "../../../../common/types";
import { setErrorSnackMessage } from "../../../../actions";
@@ -40,8 +35,9 @@ import SingleRepWidget from "./Widgets/SingleRepWidget";
import DateTimePickerWrapper from "../../Common/FormComponents/DateTimePickerWrapper/DateTimePickerWrapper";
import api from "../../../../common/api";
import SyncIcon from "../../../../icons/SyncIcon";
import Tabs from "@material-ui/core/Tabs";
import Tab from "@material-ui/core/Tab";
import TabSelector from "../../Common/TabSelector/TabSelector";
import SimpleWidget from "./Widgets/SimpleWidget";
import MergedWidgets from "./MergedWidgets";
interface IPrDashboard {
classes: any;
@@ -53,8 +49,11 @@ const styles = (theme: Theme) =>
createStyles({
...actionsTray,
widgetsContainer: {
height: "calc(100vh - 250px)",
paddingBottom: 235,
position: "relative",
display: "flex",
flexGrow: 1,
width: "100%",
height: "100%",
},
syncButton: {
"&.MuiButton-root .MuiButton-iconSizeMedium > *:first-child": {
@@ -65,6 +64,24 @@ const styles = (theme: Theme) =>
...actionsTray.actionsTray,
padding: "0 10px",
},
dashboardRow: {
display: "flex",
flexDirection: "row",
justifyContent: "flex-start",
flexWrap: "wrap",
maxWidth: 1180,
},
widgetPanelDelimiter: {
margin: 10,
},
schedulerIcon: {
opacity: 0.4,
fontSize: 10,
"& svg": {
width: 18,
height: 18,
},
},
});
const PrDashboard = ({
@@ -79,23 +96,8 @@ const PrDashboard = ({
useState<IDashboardPanel[]>(panelsConfiguration);
const [curTab, setCurTab] = useState<number>(0);
const minHeight = 600;
const colsInGrid = 8;
const xSpacing = 10;
const ySpacing = 10;
const dashboardDistr = getDashboardDistribution(panelInformation.length);
const autoSizerStyleProp = {
width: "100%",
height: "auto",
paddingBottom: 45,
};
const panels = useCallback(
(width: number, filterPanels?: number[] | null) => {
const singlePanelWidth = width / colsInGrid + xSpacing / 2;
(tabName: string, filterPanels?: number[][] | null) => {
const componentToUse = (value: IDashboardPanel, index: number) => {
switch (value.type) {
case widgetType.singleValue:
@@ -109,6 +111,18 @@ const PrDashboard = ({
apiPrefix={apiPrefix}
/>
);
case widgetType.simpleWidget:
return (
<SimpleWidget
title={value.title}
panelItem={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
iconWidget={value.widgetIcon}
/>
);
case widgetType.pieChart:
return (
<PieChartWidget
@@ -121,6 +135,7 @@ const PrDashboard = ({
/>
);
case widgetType.linearGraph:
case widgetType.areaGraph:
return (
<LinearGraphWidget
title={value.title}
@@ -131,12 +146,8 @@ const PrDashboard = ({
hideYAxis={value.disableYAxis}
xAxisFormatter={value.xAxisFormatter}
yAxisFormatter={value.yAxisFormatter}
panelWidth={
dashboardDistr[index]
? singlePanelWidth * dashboardDistr[index].w
: singlePanelWidth
}
apiPrefix={apiPrefix}
areaWidget={value.type === widgetType.areaGraph}
/>
);
case widgetType.barChart:
@@ -169,21 +180,66 @@ const PrDashboard = ({
}
};
return panelInformation
.filter((val) => {
if (filterPanels) {
return filterPanels.indexOf(val.id) > -1;
} else {
return true;
}
})
.map((val, index) => {
return (
<div key={val.layoutIdentifier}>{componentToUse(val, index)}</div>
);
});
return filterPanels?.map((panelLine, indexLine) => {
const totalPanelsContained = panelLine.length;
const perc = 100 / totalPanelsContained;
return (
<Grid
item
xs={12}
key={`line-${tabName}-${indexLine}`}
className={classes.dashboardRow}
>
{panelLine.map((panelInline, indexPanel) => {
const panelInfo = panelInformation.find(
(panel) => panel.id === panelInline
);
return (
<div
key={`widget-${panelInline}-${indexPanel}`}
className={classes.widgetPanelDelimiter}
style={{ width: `calc(${perc}% - 20px)` }}
>
{panelInfo ? (
<Fragment>
{panelInfo.mergedPanels ? (
<Fragment>
<MergedWidgets
title={panelInfo.title}
leftComponent={componentToUse(
panelInfo.mergedPanels[0],
0
)}
rightComponent={componentToUse(
panelInfo.mergedPanels[1],
1
)}
/>
</Fragment>
) : (
componentToUse(panelInfo, indexPanel)
)}
</Fragment>
) : null}
</div>
);
})}
</Grid>
);
});
},
[panelInformation, dashboardDistr, timeEnd, timeStart, loading, apiPrefix]
[
timeStart,
timeEnd,
loading,
apiPrefix,
classes.dashboardRow,
classes.widgetPanelDelimiter,
panelInformation,
]
);
const fetchUsage = useCallback(() => {
@@ -235,145 +291,91 @@ const PrDashboard = ({
}
}, [loading, fetchUsage]);
const a11yProps = (index: any) => {
return {
id: `simple-tab-${index}`,
"aria-controls": `simple-tabpanel-${index}`,
};
};
const summaryPanels = [
1, 64, 65, 68, 52, 44, 61, 80, 81, 66, 62, 53, 63, 50, 69, 70, 9, 78,
[80, 81, 1],
[68, 52],
[63, 70],
[66, 50, 44, 500],
[501, 502, 61, 62],
];
const resourcesPanels = [76, 77, 11, 8, 82, 74];
const requestsPanels = [60, 71, 17, 73];
const resourcesPanels = [
[76, 77],
[11, 8],
[82, 74],
];
const requestsPanels = [[60], [71, 17], [73]];
return (
<React.Fragment>
<Fragment>
<Grid
item
xs={12}
className={`${classes.actionsTray} ${classes.timeContainers}`}
>
<span className={classes.label}>Start Time</span>
<span className={classes.filterTitle}>Filter:</span>
<span className={`${classes.filterTitle} ${classes.schedulerIcon}`}>
<ScheduleIcon />
</span>
<span className={classes.label}>Start Time:</span>
<DateTimePickerWrapper
value={timeStart}
onChange={setTimeStart}
forSearchBlock
id="stTime"
noInputIcon
/>
<span className={classes.label}>End Time</span>
<span className={`${classes.filterTitle} ${classes.schedulerIcon}`}>
<WatchLaterIcon />
</span>
<span className={classes.label}>End Time:</span>
<DateTimePickerWrapper
value={timeEnd}
onChange={setTimeEnd}
forSearchBlock
id="endTime"
noInputIcon
/>
<Button
type="button"
variant="contained"
color="primary"
onClick={triggerLoad}
startIcon={<SyncIcon />}
endIcon={<SyncIcon />}
className={classes.syncButton}
>
Sync
</Button>
</Grid>
<Grid item xs={12}>
<Tabs
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
variant="scrollable"
scrollButtons="auto"
value={curTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
<TabSelector
selectedTab={curTab}
onChange={(newValue: number) => {
setCurTab(newValue);
}}
>
<Tab label="Summary" {...a11yProps(0)} />
<Tab label="Traffic" {...a11yProps(1)} />
<Tab label="Resources" {...a11yProps(2)} />
</Tabs>
tabOptions={[
{ label: "Summary" },
{ label: "Traffic" },
{ label: "Resources" },
]}
/>
</Grid>
<Grid item xs={12} className={classes.widgetsContainer}>
<TabPanel index={0} value={curTab}>
<AutoSizer style={autoSizerStyleProp}>
{({ width, height }: any) => {
let hpanel = height < minHeight ? minHeight : height;
if (hpanel > 380) {
hpanel = 480;
}
const totalWidth = width > 1920 ? 1920 : width;
return (
<ReactGridLayout
width={totalWidth}
cols={colsInGrid}
containerPadding={[xSpacing, ySpacing]}
onLayoutChange={saveDashboardDistribution}
layout={dashboardDistr}
rowHeight={hpanel / 6}
style={{ margin: "0 auto", width: totalWidth }}
>
{panels(width, summaryPanels)}
</ReactGridLayout>
);
}}
</AutoSizer>
{panels("Summary", summaryPanels)}
</TabPanel>
<TabPanel index={1} value={curTab}>
<AutoSizer style={autoSizerStyleProp}>
{({ width, height }: any) => {
let hpanel = height < minHeight ? minHeight : height;
if (hpanel > 380) {
hpanel = 480;
}
const totalWidth = width > 1920 ? 1920 : width;
return (
<ReactGridLayout
width={totalWidth}
cols={colsInGrid}
containerPadding={[xSpacing, ySpacing]}
onLayoutChange={saveDashboardDistribution}
layout={dashboardDistr}
rowHeight={hpanel / 6}
style={{ margin: "0 auto", width: totalWidth }}
>
{panels(width, requestsPanels)}
</ReactGridLayout>
);
}}
</AutoSizer>
{panels("Traffic", requestsPanels)}
</TabPanel>
<TabPanel index={2} value={curTab}>
<AutoSizer style={autoSizerStyleProp}>
{({ width, height }: any) => {
let hpanel = height < minHeight ? minHeight : height;
if (hpanel > 380) {
hpanel = 480;
}
const totalWidth = width > 1920 ? 1920 : width;
return (
<ReactGridLayout
width={totalWidth}
cols={colsInGrid}
containerPadding={[xSpacing, ySpacing]}
onLayoutChange={saveDashboardDistribution}
layout={dashboardDistr}
rowHeight={hpanel / 6}
style={{ margin: "0 auto", width: totalWidth }}
>
{panels(width, resourcesPanels)}
</ReactGridLayout>
);
}}
</AutoSizer>
{panels("Resources", resourcesPanels)}
</TabPanel>
</Grid>
</React.Fragment>
</Fragment>
);
};
/*
<
*/
const connector = connect(null, {
displayErrorMessage: setErrorSnackMessage,

View File

@@ -14,7 +14,7 @@
// 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, useState } from "react";
import React, { useEffect, useState, Fragment } from "react";
import { connect } from "react-redux";
import {
Bar,
@@ -23,6 +23,7 @@ import {
Tooltip,
XAxis,
YAxis,
Cell,
} from "recharts";
import { MaterialUiPickersDate } from "@material-ui/pickers/typings/date";
import { CircularProgress } from "@material-ui/core";
@@ -58,14 +59,15 @@ const styles = (theme: Theme) =>
},
});
const CustomizedAxisTick = ({ x, y, payload }: any) => {
const CustomizedAxisTick = ({ y, payload }: any) => {
return (
<text
width={50}
fontSize={"63%"}
textAnchor="end"
textAnchor="start"
fill="#333"
transform={`translate(${x},${y})`}
transform={`translate(5,${y})`}
fontWeight={700}
dy={3}
>
{payload.value}
@@ -131,6 +133,19 @@ const BarChartWidget = ({
? (result.widgetConfiguration as IBarChartConfiguration[])
: [];
let greatestIndex = 0;
let currentValue = 0;
if (barChartConfiguration.length === 1) {
const dataGraph = barChartConfiguration[0];
data.forEach((item: any, index: number) => {
if (item[dataGraph.dataKey] > currentValue) {
currentValue = item[dataGraph.dataKey];
greatestIndex = index;
}
});
}
return (
<div className={classes.singleValueContainer}>
<div className={classes.titleContainer}>{title}</div>
@@ -163,7 +178,23 @@ const BarChartWidget = ({
dataKey={bar.dataKey}
fill={bar.color}
background={bar.background}
/>
barSize={12}
>
{barChartConfiguration.length === 1 ? (
<Fragment>
{data.map((_: any, index: number) => (
<Cell
key={`chart-bar-${index.toString()}`}
fill={
index === greatestIndex
? bar.greatestColor
: bar.color
}
/>
))}
</Fragment>
) : null}
</Bar>
))}
<Tooltip
cursor={{ fill: "rgba(255, 255, 255, 0.3)" }}

View File

@@ -49,7 +49,7 @@ interface ILinearGraphWidget {
hideYAxis?: boolean;
yAxisFormatter?: (item: string) => string;
xAxisFormatter?: (item: string) => string;
panelWidth?: number;
areaWidget?: boolean;
}
const styles = (theme: Theme) =>
@@ -57,24 +57,29 @@ const styles = (theme: Theme) =>
...widgetCommon,
containerElements: {
display: "flex",
flexDirection: "column",
height: "calc(100% - 18px)",
flexDirection: "row",
height: "100%",
flexGrow: 1,
},
chartCont: {
position: "relative",
flexGrow: 1,
minHeight: "65%",
height: 1,
height: 140,
width: "100%",
},
legendChart: {
display: "flex",
flexWrap: "wrap",
flexDirection: "column",
flex: "0 1 auto",
maxHeight: "35%",
height: 130,
margin: 0,
overflowY: "auto",
position: "relative",
textAlign: "center",
width: "100%",
justifyContent: "flex-start",
color: "#404143",
fontWeight: "bold",
fontSize: 12,
},
loadingAlign: {
margin: "auto",
@@ -91,9 +96,9 @@ const LinearGraphWidget = ({
panelItem,
apiPrefix,
hideYAxis = false,
areaWidget = false,
yAxisFormatter = (item: string) => item,
xAxisFormatter = (item: string) => item,
panelWidth = 0,
}: ILinearGraphWidget) => {
const [loading, setLoading] = useState<boolean>(true);
const [data, setData] = useState<object[]>([]);
@@ -153,26 +158,21 @@ const LinearGraphWidget = ({
}
}, [loading, panelItem, timeEnd, timeStart, displayErrorMessage, apiPrefix]);
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;
}
}
let intervalCount = Math.floor(data.length / 5);
const linearConfiguration = result
? (result?.widgetConfiguration as ILinearGraphConfiguration[])
: [];
const CustomizedDot = (prop: any) => {
const { cx, cy, index } = prop;
if (index % 3 !== 0) {
return null;
}
return <circle cx={cx} cy={cy} r={3} strokeWidth={0} fill="#07264A" />;
};
return (
<div className={classes.singleValueContainer}>
<div className={classes.titleContainer}>{title}</div>
@@ -191,24 +191,52 @@ const LinearGraphWidget = ({
bottom: 0,
}}
>
{areaWidget && (
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop
offset="0%"
stopColor="#ABC8F2"
stopOpacity={0.9}
/>
<stop
offset="95%"
stopColor="#ABC8F2"
stopOpacity={0}
/>
</linearGradient>
</defs>
)}
<CartesianGrid
strokeDasharray="3 3"
strokeDasharray={areaWidget ? "0 0" : "3 3"}
strokeWidth={1}
strokeOpacity={0.5}
stroke={"#07264A30"}
vertical={!areaWidget}
/>
<XAxis
dataKey="name"
tickFormatter={(value: any) => xAxisFormatter(value)}
interval={intervalCount}
tick={{ fontSize: "70%" }}
tick={{
fontSize: "70%",
fontWeight: "bold",
color: "#404143",
}}
tickCount={10}
stroke={"#082045"}
/>
<YAxis
type={"number"}
domain={[0, dataMax * 1.1]}
hide={hideYAxis}
tickFormatter={(value: any) => yAxisFormatter(value)}
tick={{ fontSize: "70%" }}
tick={{
fontSize: "70%",
fontWeight: "bold",
color: "#404143",
}}
stroke={"#082045"}
/>
{linearConfiguration.map((section, index) => {
return (
@@ -217,8 +245,10 @@ const LinearGraphWidget = ({
type="monotone"
dataKey={section.dataKey}
stroke={section.lineColor}
fill={section.fillColor}
fillOpacity={0.3}
fill={areaWidget ? "url(#colorUv)" : section.fillColor}
fillOpacity={areaWidget ? 0.3 : 0}
strokeWidth={areaWidget ? 0 : 2}
dot={areaWidget ? <CustomizedDot /> : false}
/>
);
})}
@@ -236,24 +266,26 @@ const LinearGraphWidget = ({
</AreaChart>
</ResponsiveContainer>
</div>
<div className={classes.legendChart}>
{linearConfiguration.map((section, index) => {
return (
<div
className={classes.singleLegendContainer}
key={`legend-${section.keyLabel}-${index.toString()}`}
>
{!areaWidget && (
<div className={classes.legendChart}>
{linearConfiguration.map((section, index) => {
return (
<div
className={classes.colorContainer}
style={{ backgroundColor: section.lineColor }}
/>
<div className={classes.legendLabel}>
{section.keyLabel}
className={classes.singleLegendContainer}
key={`legend-${section.keyLabel}-${index.toString()}`}
>
<div
className={classes.colorContainer}
style={{ backgroundColor: section.lineColor }}
/>
<div className={classes.legendLabel}>
{section.keyLabel}
</div>
</div>
</div>
);
})}
</div>
);
})}
</div>
)}
</React.Fragment>
)}
</div>

View File

@@ -24,7 +24,7 @@ import { IPieChartConfiguration } from "./types";
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
import { setErrorSnackMessage } from "../../../../../actions";
import { IDashboardPanel } from "../types";
import { widgetDetailsToPanel } from "../utils";
import { splitSizeMetric, widgetDetailsToPanel } from "../utils";
import { ErrorResponseHandler } from "../../../../../common/types";
import get from "lodash/get";
import api from "../../../../../common/api";
@@ -49,6 +49,20 @@ const styles = (theme: Theme) =>
textAlign: "center",
margin: "auto",
},
pieChartLabel: {
fontSize: 60,
color: "#07193E",
fontWeight: "bold",
width: "100%",
"& .unitText": {
color: "#767676",
fontSize: 12,
},
},
chartContainer: {
width: "100%",
height: 140,
},
});
const PieChartWidget = ({
@@ -125,104 +139,97 @@ const PieChartWidget = ({
)}
{!loading && (
<div className={classes.contentContainer}>
<ResponsiveContainer>
<PieChart margin={{ top: 5, bottom: 5 }}>
{dataOuter && (
<Pie
data={dataOuter as object[]}
cx={"50%"}
cy={"50%"}
dataKey="value"
innerRadius={get(
pieChartConfiguration,
"outerChart.innerRadius",
0
)}
outerRadius={get(
pieChartConfiguration,
"outerChart.outerRadius",
"80%"
)}
startAngle={get(
pieChartConfiguration,
"outerChart.startAngle",
0
)}
endAngle={get(
pieChartConfiguration,
"outerChart.endAngle",
360
)}
fill="#201763"
>
{dataOuter.map((entry, index) => (
<Cell
key={`cellOuter-${index}`}
fill={
typeof outerColors[index] == "undefined"
? "#393939"
: outerColors[index]
}
/>
))}
</Pie>
)}
{dataInner && (
<Pie
data={dataInner as object[]}
dataKey="value"
cx={"50%"}
cy={"50%"}
innerRadius={get(
pieChartConfiguration,
"innerChart.innerRadius",
0
)}
outerRadius={get(
pieChartConfiguration,
"innerChart.outerRadius",
"80%"
)}
startAngle={get(
pieChartConfiguration,
"innerChart.startAngle",
0
)}
endAngle={get(
pieChartConfiguration,
"innerChart.endAngle",
360
)}
fill="#201763"
>
{dataInner.map((entry, index) => {
return (
<span className={classes.pieChartLabel}>
{middleLabel && splitSizeMetric(middleLabel)}
</span>
<div className={classes.chartContainer}>
<ResponsiveContainer>
<PieChart margin={{ top: 5, bottom: 5 }}>
{dataOuter && (
<Pie
data={dataOuter as object[]}
cx={"50%"}
cy={"50%"}
dataKey="value"
innerRadius={get(
pieChartConfiguration,
"outerChart.innerRadius",
0
)}
outerRadius={get(
pieChartConfiguration,
"outerChart.outerRadius",
"80%"
)}
startAngle={get(
pieChartConfiguration,
"outerChart.startAngle",
0
)}
endAngle={get(
pieChartConfiguration,
"outerChart.endAngle",
360
)}
fill="#201763"
>
{dataOuter.map((entry, index) => (
<Cell
key={`cell-${index}`}
key={`cellOuter-${index}`}
fill={
typeof innerColors[index] == "undefined"
typeof outerColors[index] == "undefined"
? "#393939"
: innerColors[index]
: outerColors[index]
}
/>
);
})}
</Pie>
)}
{middleLabel && (
<text
x={"50%"}
y={"50%"}
textAnchor="middle"
dominantBaseline="middle"
fontWeight={600}
fontSize={14}
>
{middleLabel}
</text>
)}
</PieChart>
</ResponsiveContainer>
))}
</Pie>
)}
{dataInner && (
<Pie
data={dataInner as object[]}
dataKey="value"
cx={"50%"}
cy={"50%"}
innerRadius={get(
pieChartConfiguration,
"innerChart.innerRadius",
0
)}
outerRadius={get(
pieChartConfiguration,
"innerChart.outerRadius",
"80%"
)}
startAngle={get(
pieChartConfiguration,
"innerChart.startAngle",
0
)}
endAngle={get(
pieChartConfiguration,
"innerChart.endAngle",
360
)}
fill="#201763"
>
{dataInner.map((entry, index) => {
return (
<Cell
key={`cell-${index}`}
fill={
typeof innerColors[index] == "undefined"
? "#393939"
: innerColors[index]
}
/>
);
})}
</Pie>
)}
</PieChart>
</ResponsiveContainer>
</div>
</div>
)}
</div>

View File

@@ -14,14 +14,27 @@
// 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 from "react";
import React, { Fragment, useEffect, useState } from "react";
import { connect } from "react-redux";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { MaterialUiPickersDate } from "@material-ui/pickers/typings/date";
import { CircularProgress } from "@material-ui/core";
import api from "../../../../../common/api";
import { widgetDetailsToPanel } from "../utils";
import { IDashboardPanel } from "../types";
import { setErrorSnackMessage } from "../../../../../actions";
import { ErrorResponseHandler } from "../../../../../common/types";
interface ISimpleWidget {
classes: any;
iconWidget: any;
label: string;
value: string;
title: string;
panelItem: IDashboardPanel;
timeStart: MaterialUiPickersDate;
timeEnd: MaterialUiPickersDate;
propLoading: boolean;
displayErrorMessage: any;
apiPrefix: string;
}
const styles = (theme: Theme) =>
@@ -47,14 +60,80 @@ const styles = (theme: Theme) =>
},
});
const SimpleWidget = ({ classes, iconWidget, label, value }: ISimpleWidget) => {
const SimpleWidget = ({
classes,
iconWidget,
title,
panelItem,
timeStart,
timeEnd,
propLoading,
displayErrorMessage,
apiPrefix,
}: ISimpleWidget) => {
const [loading, setLoading] = useState<boolean>(true);
const [data, setData] = useState<string>("");
useEffect(() => {
if (propLoading) {
setLoading(true);
}
}, [propLoading]);
useEffect(() => {
if (loading) {
let stepCalc = 0;
if (timeStart !== null && timeEnd !== null) {
const secondsInPeriod = timeEnd.unix() - timeStart.unix();
const periods = Math.floor(secondsInPeriod / 60);
stepCalc = periods < 1 ? 15 : periods;
}
api
.invoke(
"GET",
`/api/v1/${apiPrefix}/info/widgets/${
panelItem.id
}/?step=${stepCalc}&${
timeStart !== null ? `&start=${timeStart.unix()}` : ""
}${timeStart !== null && timeEnd !== null ? "&" : ""}${
timeEnd !== null ? `end=${timeEnd.unix()}` : ""
}`
)
.then((res: any) => {
const widgetsWithValue = widgetDetailsToPanel(res, panelItem);
setData(widgetsWithValue.data);
setLoading(false);
})
.catch((err: ErrorResponseHandler) => {
displayErrorMessage(err);
setLoading(false);
});
}
}, [loading, panelItem, timeEnd, timeStart, displayErrorMessage, apiPrefix]);
return (
<span className={classes.mainWidgetContainer}>
<span className={classes.icon}>{iconWidget ? iconWidget : null}</span>
<span className={classes.widgetLabel}>{label}: </span>
<span className={classes.widgetValue}>{value}</span>
</span>
<Fragment>
{loading && (
<div className={classes.loadingAlign}>
<CircularProgress />
</div>
)}
{!loading && (
<span className={classes.mainWidgetContainer}>
<span className={classes.icon}>{iconWidget ? iconWidget : null}</span>
<span className={classes.widgetLabel}>{title}: </span>
<span className={classes.widgetValue}>{data}</span>
</span>
)}
</Fragment>
);
};
export default withStyles(styles)(SimpleWidget);
const connector = connect(null, {
displayErrorMessage: setErrorSnackMessage,
});
export default withStyles(styles)(connector(SimpleWidget));

View File

@@ -107,6 +107,8 @@ const SingleRepWidget = ({
});
}
}, [loading, panelItem, timeEnd, timeStart, displayErrorMessage, apiPrefix]);
const gradientID= `colorGradient-${title.split(" ").join('-')}`;
return (
<div className={classes.singleValueContainer}>
<div className={classes.titleContainer}>{title}</div>
@@ -119,6 +121,12 @@ const SingleRepWidget = ({
<div className={classes.contentContainer}>
<ResponsiveContainer>
<AreaChart data={data}>
<defs>
<linearGradient id={gradientID} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={fillColor} stopOpacity={1} />
<stop offset="95%" stopColor={fillColor} stopOpacity={0} />
</linearGradient>
</defs>
<YAxis
domain={[0, (dataMax: number) => dataMax * 2]}
hide={true}
@@ -127,17 +135,17 @@ const SingleRepWidget = ({
type="monotone"
dataKey={"value"}
stroke={color}
fill={fillColor}
fill={`url(#${gradientID})`}
fillOpacity={1}
/>
<text
x={"50%"}
y={"50%"}
textAnchor="middle"
dominantBaseline="middle"
fontWeight={600}
fontSize={18}
fill={color}
x={"0%"}
y={"80%"}
textAnchor="start"
dominantBaseline="auto"
fontWeight={700}
fontSize={70}
fill={"#07193E"}
>
{result ? result.innerLabel : ""}
</text>

View File

@@ -14,11 +14,11 @@
// 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, useState } from "react";
import React, { Fragment, useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
import api from "../../../../../common/api";
import { widgetDetailsToPanel } from "../utils";
import { splitSizeMetric, widgetDetailsToPanel } from "../utils";
import { MaterialUiPickersDate } from "@material-ui/pickers/typings/date";
import { IDashboardPanel } from "../types";
import { connect } from "react-redux";
@@ -49,10 +49,30 @@ const styles = (theme: Theme) =>
},
loadingAlign: {
width: "100%",
paddingTop: "15px",
textAlign: "center",
margin: "auto",
},
metric: {
fontSize: 60,
lineHeight: 1,
color: "#07193E",
fontWeight: 700,
},
titleElement: {
fontSize: 10,
color: "#767676",
fontWeight: 700,
},
containerAlignment: {
display: "flex",
height: 140,
flexDirection: "column",
justifyContent: "center",
"& .unitText": {
color: "#767676",
fontSize: 12,
}
},
});
const SingleValueWidget = ({
@@ -107,14 +127,18 @@ const SingleValueWidget = ({
}
}, [loading, panelItem, timeEnd, timeStart, displayErrorMessage, apiPrefix]);
return (
<div className={classes.singleValueContainer}>
<div className={classes.titleContainer}>{title}</div>
<div className={classes.containerAlignment}>
{loading && (
<div className={classes.loadingAlign}>
<CircularProgress />
</div>
)}
{!loading && <div className={classes.contentContainer}>{data}</div>}
{!loading && (
<Fragment>
<div className={classes.metric}>{splitSizeMetric(data)}</div>
<div className={classes.titleElement}>{title}</div>
</Fragment>
)}
</div>
);
};

View File

@@ -25,6 +25,7 @@ export interface IBarChartConfiguration {
dataKey: string;
color: string;
background?: object;
greatestColor?: string;
}
export interface IPieChartConfiguration {

View File

@@ -25,18 +25,21 @@ import {
export enum widgetType {
singleValue = "singleValue",
linearGraph = "linearGraph",
areaGraph = "areaGraph",
barChart = "barChart",
pieChart = "pieChart",
singleRep = "singleRep",
simpleWidget = "simpleWidget",
}
export interface IDashboardPanel {
id: number;
mergedPanels?: IDashboardPanel[];
title: string;
data: string | object[] | IDataSRep[];
data?: string | object[] | IDataSRep[];
dataOuter?: string | object[];
type: widgetType;
layoutIdentifier: string;
type?: widgetType;
widgetIcon?: any;
widgetConfiguration?:
| ILinearGraphConfiguration[]
| IBarChartConfiguration[]

View File

@@ -14,311 +14,32 @@
// 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, { Fragment } from "react";
import get from "lodash/get";
import { Layout } from "react-grid-layout";
import { IDashboardPanel, widgetType } from "./types";
import {
getTimeFromTimestamp,
niceBytes,
niceDays,
textToRGBColor,
units,
} from "../../../../common/utils";
import HealIcon from "../../../../icons/HealIcon";
import DiagnosticsIcon from "../../../../icons/DiagnosticsIcon";
import HistoryIcon from "../../../../icons/HistoryIcon";
const dLocalStorageV = "dashboardConfig";
export const defaultWidgetsLayout: Layout[] = [
{
w: 1,
h: 2,
x: 0,
y: 0,
i: "panel-0",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 1,
y: 2,
i: "panel-1",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 1,
y: 3,
i: "panel-2",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 2,
x: 2,
y: 0,
i: "panel-3",
minW: 1,
moved: false,
static: false,
},
{
w: 4,
h: 2,
x: 4,
y: 2,
i: "panel-4",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 2,
x: 4,
y: 0,
i: "panel-5",
minW: 2,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 0,
y: 2,
i: "panel-6",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 0,
y: 3,
i: "panel-7",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 2,
y: 2,
i: "panel-8",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 2,
y: 3,
i: "panel-9",
minW: 1,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 0,
y: 4,
i: "panel-10",
minW: 2,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 3,
y: 0,
i: "panel-11",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 3,
y: 1,
i: "panel-12",
minW: 1,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 0,
y: 10,
i: "panel-13",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 0,
y: 4,
i: "panel-14",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 4,
y: 4,
i: "panel-15",
minW: 2,
moved: false,
static: false,
},
{
w: 8,
h: 3,
x: 0,
y: 7,
i: "panel-16",
minW: 2,
moved: false,
static: false,
},
{
w: 8,
h: 3,
x: 0,
y: 19,
i: "panel-19",
minW: 2,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 3,
y: 2,
i: "panel-20",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 3,
y: 3,
i: "panel-21",
minW: 1,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 4,
y: 4,
i: "panel-22",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 4,
y: 10,
i: "panel-23",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 0,
y: 13,
i: "panel-24",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 4,
y: 13,
i: "panel-25",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 0,
y: 16,
i: "panel-26",
minW: 2,
moved: false,
static: false,
},
{
w: 4,
h: 3,
x: 4,
y: 16,
i: "panel-27",
minW: 2,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 1,
y: 0,
i: "panel-28",
minW: 1,
moved: false,
static: false,
},
{
w: 1,
h: 1,
x: 1,
y: 1,
i: "panel-29",
minW: 1,
moved: false,
static: false,
},
];
const colorsMain = [
"#6992B7",
"#E2AD17",
"#22B573",
"#F7655E",
"#0071BC",
"#C4D4E9",
"#DCD1EE",
"#D1EEE7",
"#EEDED1",
"#AAF38F",
"#F9E6C5",
"#A6E8C4",
"#C83B51",
"#F4CECE",
"#ADD5E0",
"#D6D6D6",
];
const niceDaysFromNS = (seconds: string) => {
@@ -334,24 +55,10 @@ export const panelsConfiguration: IDashboardPanel[] = [
id: 1,
title: "Uptime",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-0",
type: widgetType.simpleWidget,
widgetIcon: <HistoryIcon />,
labelDisplayFunction: niceDays,
},
{
id: 9,
title: "Total Online Disks",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-1",
},
{
id: 78,
title: "Total Offline Disks",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-2",
},
{
id: 50,
title: "Current Usable Capacity",
@@ -360,21 +67,20 @@ export const panelsConfiguration: IDashboardPanel[] = [
widgetConfiguration: {
outerChart: {
colorList: ["#9c9c9c"],
innerRadius: 51,
outerRadius: 54,
startAngle: -15,
endAngle: 195,
innerRadius: 0,
outerRadius: 0,
startAngle: 0,
endAngle: 0,
},
innerChart: {
colorList: colorsMain,
innerRadius: 35,
innerRadius: 20,
outerRadius: 50,
startAngle: -15,
endAngle: 195,
startAngle: 90,
endAngle: -200,
},
},
type: widgetType.pieChart,
layoutIdentifier: "panel-3",
innerLabel: "N/A",
labelDisplayFunction: niceBytes,
},
@@ -390,8 +96,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
fillColor: "#000",
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-4",
type: widgetType.areaGraph,
yAxisFormatter: niceBytes,
xAxisFormatter: getTimeFromTimestamp,
},
@@ -404,8 +109,9 @@ export const panelsConfiguration: IDashboardPanel[] = [
dataKey: "a",
color: colorsMain[0],
background: {
fill: "rgba(0,0,0,0.1)",
fill: "#EEF1F4",
},
greatestColor: "#081C42",
},
],
customStructure: [
@@ -436,21 +142,6 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.barChart,
layoutIdentifier: "panel-5",
},
{
id: 53,
title: "Total Online Servers",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-6",
},
{
id: 69,
title: "Total Offline Servers",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-7",
},
{
id: 66,
@@ -460,7 +151,6 @@ export const panelsConfiguration: IDashboardPanel[] = [
type: widgetType.singleRep,
color: "#0071BC",
fillColor: "#ADD5E0",
layoutIdentifier: "panel-8",
},
{
id: 44,
@@ -470,7 +160,6 @@ export const panelsConfiguration: IDashboardPanel[] = [
type: widgetType.singleRep,
color: "#0071BC",
fillColor: "#ADD5E0",
layoutIdentifier: "panel-9",
},
{
id: 63,
@@ -485,7 +174,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-10",
xAxisFormatter: getTimeFromTimestamp,
yAxisFormatter: niceBytes,
},
@@ -495,7 +184,6 @@ export const panelsConfiguration: IDashboardPanel[] = [
data: [],
innerLabel: "N/A",
type: widgetType.singleRep,
layoutIdentifier: "panel-11",
color: "#22B573",
fillColor: "#A6E8C4",
},
@@ -505,7 +193,6 @@ export const panelsConfiguration: IDashboardPanel[] = [
data: [],
innerLabel: "N/A",
type: widgetType.singleRep,
layoutIdentifier: "panel-12",
color: "#F7655E",
fillColor: "#F4CECE",
},
@@ -522,7 +209,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-13",
yAxisFormatter: roundNumber,
xAxisFormatter: getTimeFromTimestamp,
},
@@ -539,7 +226,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-14",
xAxisFormatter: getTimeFromTimestamp,
},
{
@@ -555,7 +242,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-15",
xAxisFormatter: getTimeFromTimestamp,
yAxisFormatter: niceBytes,
},
@@ -572,7 +259,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-16",
yAxisFormatter: niceBytes,
xAxisFormatter: getTimeFromTimestamp,
},
@@ -589,7 +276,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-19",
yAxisFormatter: niceBytes,
xAxisFormatter: getTimeFromTimestamp,
},
@@ -597,16 +284,16 @@ export const panelsConfiguration: IDashboardPanel[] = [
id: 80,
title: "Time Since Last Heal Activity",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-20",
type: widgetType.simpleWidget,
widgetIcon: <HealIcon />,
labelDisplayFunction: niceDaysFromNS,
},
{
id: 81,
title: "Time Since Last Scan Activity",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-21",
type: widgetType.simpleWidget,
widgetIcon: <DiagnosticsIcon />,
labelDisplayFunction: niceDaysFromNS,
},
{
@@ -622,7 +309,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-22",
xAxisFormatter: getTimeFromTimestamp,
},
{
@@ -638,7 +325,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-23",
xAxisFormatter: getTimeFromTimestamp,
yAxisFormatter: niceBytes,
},
@@ -655,7 +342,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-24",
xAxisFormatter: getTimeFromTimestamp,
yAxisFormatter: niceBytes,
},
@@ -672,7 +359,7 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-25",
disableYAxis: true,
xAxisFormatter: getTimeFromTimestamp,
},
@@ -689,7 +376,6 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-26",
yAxisFormatter: roundNumber,
xAxisFormatter: getTimeFromTimestamp,
},
@@ -706,25 +392,66 @@ export const panelsConfiguration: IDashboardPanel[] = [
},
],
type: widgetType.linearGraph,
layoutIdentifier: "panel-27",
yAxisFormatter: roundNumber,
xAxisFormatter: getTimeFromTimestamp,
},
{
id: 65,
title: "Total S3 Traffic Inbound",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-28",
labelDisplayFunction: niceBytes,
id: 500,
mergedPanels: [
{
id: 53,
title: "Online Servers",
data: "N/A",
type: widgetType.singleValue,
},
{
id: 69,
title: "Offline Servers",
data: "N/A",
type: widgetType.singleValue,
},
],
title: "Servers",
},
{
id: 64,
title: "Total S3 Traffic Outbound",
data: "N/A",
type: widgetType.singleValue,
layoutIdentifier: "panel-29",
labelDisplayFunction: niceBytes,
id: 501,
mergedPanels: [
{
id: 9,
title: "Online Disks",
data: "N/A",
type: widgetType.singleValue,
},
{
id: 78,
title: "Offline Disks",
data: "N/A",
type: widgetType.singleValue,
},
],
title: "Disks",
},
{
id: 502,
mergedPanels: [
{
id: 65,
title: "Inbound Traffic",
data: "N/A",
type: widgetType.singleValue,
labelDisplayFunction: niceBytes,
},
{
id: 64,
title: "Outbound Traffic",
data: "N/A",
type: widgetType.singleValue,
labelDisplayFunction: niceBytes,
},
],
title: "Total S3 Traffic",
},
];
@@ -804,6 +531,7 @@ export const widgetDetailsToPanel = (
switch (panelItem.type) {
case widgetType.singleValue:
case widgetType.simpleWidget:
if (typeOfPayload === "stat" || typeOfPayload === "singlestat") {
// We sort values & get the last value
let elements = get(payloadData, "targets[0].result[0].values", []);
@@ -873,6 +601,7 @@ export const widgetDetailsToPanel = (
}
break;
case widgetType.linearGraph:
case widgetType.areaGraph:
if (typeOfPayload === "graph") {
let targets = get(payloadData, "targets", []);
if (targets === null) {
@@ -1064,30 +793,21 @@ export const widgetDetailsToPanel = (
return panelItem;
};
export const saveDashboardDistribution = (configuration: Layout[]) => {
localStorage.setItem(dLocalStorageV, btoa(JSON.stringify(configuration)));
};
export const getDashboardDistribution = (currentItems: number) => {
const storedConfiguration = localStorage.getItem(dLocalStorageV);
if (!storedConfiguration) {
return defaultWidgetsLayout;
}
const parsedConfig = JSON.parse(atob(storedConfiguration));
if (
parsedConfig.length === 0 ||
(parsedConfig.length > 0 && !parsedConfig[0].minW)
) {
return defaultWidgetsLayout;
}
// Stored Widgets length is not the same as the currentItems, then we return the new configuration
if (currentItems !== 0 && parsedConfig.length !== currentItems) {
return defaultWidgetsLayout;
}
return parsedConfig;
export const splitSizeMetric = (val: string) => {
const splittedText = val.split(" ");
// Value is not a size metric, we return as common string
if (splittedText.length !== 2) {
return <Fragment>{val}</Fragment>;
}
if (!units.includes(splittedText[1])) {
return <Fragment>{val}</Fragment>;
}
return (
<span className="commonValue">
{splittedText[0]}
<span className="unitText">{splittedText[1]}</span>
</span>
);
};

View File

@@ -1831,13 +1831,6 @@
dependencies:
"@types/d3-time" "*"
"@types/d3-shape@^1":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.3.5.tgz#c0164c1be1429473016f855871d487f806c4e968"
integrity sha512-aPEax03owTAKynoK8ZkmkZEDZvvT4Y5pWgii4Jp4oQt0gH45j6siDl9gNDVC5kl64XHN2goN9jbYoHK88tFAcA==
dependencies:
"@types/d3-path" "^1"
"@types/d3-shape@^2.0.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-2.1.0.tgz#cc7bbc9fc2c25f092bd457887a3224a21a55ca55"
@@ -2070,14 +2063,6 @@
"@types/prop-types" "*"
csstype "^3.0.2"
"@types/recharts@^1.8.19":
version "1.8.19"
resolved "https://registry.yarnpkg.com/@types/recharts/-/recharts-1.8.19.tgz#047f72cf4c25df545aa1085fe3a085e58a2483c1"
integrity sha512-Fd2cYnBlWz/ga8rLmjwsZNBAc4CzXZiuTYPPqMIgrtQ02yI/OTm8WPM6ZyUuYlSdyangtsvFmHWzZ7W4tuknDA==
dependencies:
"@types/d3-shape" "^1"
"@types/react" "*"
"@types/resize-observer-browser@^0.1.5":
version "0.1.5"
resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.5.tgz#36d897708172ac2380cd486da7a3daf1161c1e23"
@@ -9970,10 +9955,10 @@ recharts-scale@^0.4.4:
dependencies:
decimal.js-light "^2.4.1"
recharts@^2.0.3:
version "2.0.9"
resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.0.9.tgz#048068eb01383291104548388712026948275f70"
integrity sha512-JNsXE80PuF3hugUCE7JqDOMSvu5xQLxtjOaqFKKZI2pCJ1PVJzhwDv4TWk0nO4AvADbeWzYEHbg8C5Hcrh42UA==
recharts@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/recharts/-/recharts-2.1.2.tgz#ceeb01e53fb46da0d946a1e0f82d783ddf5b9d06"
integrity sha512-rwFQT6T4imhLzD1kYtg9ql8YOesbFRdSwZi95KWgi5udbBdLGRCR4SgaPO8kf0URHcC23mdRbLLTMYCnXng7zQ==
dependencies:
"@types/d3-scale" "^3.0.0"
"@types/d3-shape" "^2.0.0"