Migrated Metrics pages and components to mds (#3042)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-09-13 12:01:43 -06:00
committed by GitHub
parent 1697c826c0
commit d30c0c8cbb
35 changed files with 1515 additions and 1890 deletions

View File

@@ -17,6 +17,7 @@
// This object contains variables that will be used across form components.
import { breakPoints } from "mds";
import get from "lodash/get";
const inputLabelBase = {
fontWeight: 600,
@@ -416,16 +417,16 @@ export const typesSelection = {
},
};
export const widgetCommon = {
singleValueContainer: {
export const widgetCommon = (theme: any) => ({
"& .singleValueContainer": {
height: 200,
border: "#eaeaea 1px solid",
backgroundColor: "#fff",
borderRadius: "3px",
border: `${get(theme, "borderColor", "#eaeaea")} 1px solid`,
borderRadius: 2,
backgroundColor: get(theme, "bgColor", "#fff"),
padding: 16,
},
titleContainer: {
color: "#404143",
"& .titleContainer": {
color: get(theme, "mutedText", "#87888d"),
fontSize: 16,
fontWeight: 600,
paddingBottom: 14,
@@ -433,38 +434,38 @@ export const widgetCommon = {
display: "flex" as const,
justifyContent: "space-between" as const,
},
contentContainer: {
"& .contentContainer": {
justifyContent: "center" as const,
alignItems: "center" as const,
display: "flex" as const,
width: "100%",
height: 140,
},
singleLegendContainer: {
"& .singleLegendContainer": {
display: "flex",
alignItems: "center",
padding: "0 10px",
maxWidth: "100%",
},
colorContainer: {
"& .colorContainer": {
width: 8,
height: 8,
minWidth: 8,
marginRight: 5,
},
legendLabel: {
"& .legendLabel": {
fontSize: "80%",
color: "#393939",
color: get(theme, "mutedText", "#87888d"),
whiteSpace: "nowrap" as const,
overflow: "hidden" as const,
textOverflow: "ellipsis" as const,
},
zoomChartCont: {
"& .zoomChartCont": {
position: "relative" as const,
height: 340,
width: "100%",
},
};
});
export const tooltipCommon = {
customTooltip: {
@@ -646,44 +647,6 @@ export const inputFieldStyles = {
},
};
const commonStateIcon = {
marginRight: 10,
lineHeight: 1,
display: "inline-flex",
marginTop: 6,
};
export const commonDashboardInfocard: any = {
redState: {
color: "#F55B5B",
...commonStateIcon,
},
greenState: {
color: "#9FF281",
...commonStateIcon,
},
yellowState: {
color: "#F7A25A",
...commonStateIcon,
},
greyState: {
color: "grey",
...commonStateIcon,
},
healthStatusIcon: {
position: "absolute" as const,
fontSize: 8,
left: 18,
height: 10,
bottom: 2,
marginRight: 10,
"& .min-icon": {
width: 5,
height: 5,
},
},
};
export const tableStyles: any = {
tableBlock: {
display: "flex",

View File

@@ -1,123 +0,0 @@
// 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 makeStyles from "@mui/styles/makeStyles";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { ITabOption } from "./types";
interface ITabSelector {
selectedTab: number;
onChange: (newValue: number) => void;
tabOptions: ITabOption[];
}
const tabSubStyles = makeStyles({
tabRoot: {
height: "40px",
borderBottom: "1px solid #eaeaea",
},
root: {
width: "120px",
backgroundColor: "transparent",
paddingTop: 0,
paddingBottom: 0,
fontSize: "14px",
fontWeight: 600,
color: "#07193E",
height: "40px",
},
selected: {
"&.MuiTab-selected": {
backgroundColor: "#F6F7F7 !important",
},
"&.MuiTab-wrapper": {
color: "#07193E",
fontWeight: 600,
},
},
indicator: {
background:
"transparent linear-gradient(90deg, #072B4E 0%, #081C42 100%) 0% 0% no-repeat padding-box;",
height: 2,
},
scroller: {
maxWidth: 1185,
position: "relative",
"&::after": {
content: '" "',
backgroundColor: "#EEF1F4",
height: 2,
width: "100%",
display: "block",
},
},
});
const TabSelector = ({ selectedTab, onChange, tabOptions }: ITabSelector) => {
const subStyles = tabSubStyles();
return (
<Fragment>
<Tabs
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
variant="scrollable"
scrollButtons="auto"
value={selectedTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
onChange(newValue);
}}
classes={{
root: subStyles.tabRoot,
indicator: subStyles.indicator,
scroller: subStyles.scroller,
}}
>
{tabOptions.map((option, index) => {
let tabOptions: ITabOption = {
label: option.label,
};
if (option.value) {
tabOptions = { ...tabOptions, value: option.value };
}
if (option.disabled) {
tabOptions = { ...tabOptions, disabled: option.disabled };
}
return (
<Tab
{...tabOptions}
classes={{
root: subStyles.root,
selected: subStyles.selected,
}}
id={`simple-tab-${index}`}
aria-controls={`simple-tabpanel-${index}`}
key={`tab-${index}-${option.label}`}
/>
);
})}
</Tabs>
</Fragment>
);
};
export default TabSelector;

View File

@@ -1,21 +0,0 @@
// 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/>.
export interface ITabOption {
label: string;
value?: string;
disabled?: boolean;
}

View File

@@ -15,9 +15,10 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box, Grid } from "@mui/material";
import {
ArrowRightIcon,
Box,
breakPoints,
BucketsIcon,
Button,
DiagnosticsMenuIcon,
@@ -46,15 +47,14 @@ import { AdminInfoResponse, ServerDrives } from "api/consoleApi";
const BoxItem = ({ children }: { children: any }) => {
return (
<Box
withBorders
sx={{
border: "1px solid #f1f1f1",
padding: {
md: "15px",
xs: "5px",
},
padding: 15,
height: "136px",
maxWidth: {
sm: "100%",
maxWidth: "100%",
[`@media (max-width: ${breakPoints.sm}px)`]: {
padding: 5,
maxWidth: "initial",
},
}}
>
@@ -124,8 +124,8 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
display: "grid",
gridTemplateRows: "1fr",
gridTemplateColumns: "1fr",
gap: "27px",
marginBottom: "40px",
gap: 27,
marginBottom: 40,
}}
>
<Box
@@ -139,13 +139,13 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
sx={{
display: "grid",
gridTemplateRows: "136px",
gridTemplateColumns: {
sm: "1fr 1fr 1fr",
xs: "1fr",
gridTemplateColumns: "1fr 1fr 1fr",
gap: 20,
[`@media (max-width: ${breakPoints.sm}px)`]: {
gridTemplateColumns: "1fr",
},
gap: {
md: "20px",
xs: "20px",
[`@media (max-width: ${breakPoints.md}px)`]: {
marginBottom: 0,
},
}}
>
@@ -214,12 +214,12 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
</BoxItem>
<Box
withBorders
sx={{
gridRowStart: "1",
gridRowEnd: "3",
gridColumnStart: "3",
border: "1px solid #f1f1f1",
padding: "15px",
padding: 15,
display: "grid",
justifyContent: "stretch",
}}
@@ -243,9 +243,9 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
<Box>
<Box
sx={{
display: {
md: "inline",
xs: "none",
display: "inline",
[`@media (max-width: ${breakPoints.sm}px)`]: {
display: "none",
},
}}
>
@@ -262,9 +262,9 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
<Box>
<Box
sx={{
display: {
md: "inline",
xs: "none",
display: "inline",
[`@media (max-width: ${breakPoints.sm}px)`]: {
display: "none",
},
}}
>
@@ -283,29 +283,32 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
</Box>
</Box>
</Box>
<Grid container spacing={1}>
<Grid item xs={4}>
<TimeStatItem
icon={<StorageIcon />}
label={"Backend type"}
value={usage?.backend?.backendType ?? "Unknown"}
/>
</Grid>
<Grid item xs={4}>
<TimeStatItem
icon={<FormatDrivesIcon />}
label={"Standard storage class parity"}
value={usage?.backend?.standardSCParity?.toString() ?? "n/a"}
/>
</Grid>
<Grid item xs={4}>
<TimeStatItem
icon={<FormatDrivesIcon />}
label={"Reduced redundancy storage class parity"}
value={usage?.backend?.rrSCParity?.toString() ?? "n/a"}
/>
</Grid>
</Grid>
<Box
sx={{
display: "grid",
gridTemplateColumns: "1fr 1fr 1fr",
gap: "14px",
[`@media (max-width: ${breakPoints.lg}px)`]: {
gridTemplateColumns: "1fr",
},
}}
>
<TimeStatItem
icon={<StorageIcon />}
label={"Backend type"}
value={usage?.backend?.backendType ?? "Unknown"}
/>
<TimeStatItem
icon={<FormatDrivesIcon />}
label={"Standard storage class parity"}
value={usage?.backend?.standardSCParity?.toString() ?? "n/a"}
/>
<TimeStatItem
icon={<FormatDrivesIcon />}
label={"Reduced redundancy storage class parity"}
value={usage?.backend?.rrSCParity?.toString() ?? "n/a"}
/>
</Box>
<Box
sx={{
@@ -338,11 +341,8 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
</Box>
<Box
sx={{
paddingTop: "20px",
fontSize: "14px",
"& a": {
color: (theme) => theme.colors.link,
},
paddingTop: 20,
fontSize: 14,
}}
>
<a

View File

@@ -14,46 +14,46 @@
// 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 { Box, Tooltip } from "@mui/material";
import React from "react";
import get from "lodash/get";
import styled from "styled-components";
import { Box, breakPoints, Tooltip } from "mds";
const CounterCardMain = styled.div(({ theme }) => ({
fontFamily: "Inter,sans-serif",
color: get(theme, "signalColors.main", "#07193E"),
maxWidth: "300px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
position: "relative",
width: "100%",
}));
const CounterCard = ({
counterValue,
label = "",
icon = null,
actions = null,
loading = false,
}: {
counterValue: string | number;
label?: any;
icon?: any;
actions?: any;
loading?: boolean;
}) => {
return (
<Box
sx={{
fontFamily: "Inter,sans-serif",
color: "#07193E",
maxWidth: "300px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
position: "relative",
width: "100%",
}}
>
<CounterCardMain>
<Box
sx={{
flex: 1,
display: "flex",
width: "100%",
padding: {
sm: "0 8px 0 8px",
xs: "0 10px 0 10px",
},
padding: "0 8px 0 8px",
position: "absolute",
[`@media (max-width: ${breakPoints.md}px)`]: {
padding: "0 10px 0 10px",
},
}}
>
<Box
@@ -75,36 +75,28 @@ const CounterCard = ({
{label}
</Box>
<Tooltip title={counterValue} placement="bottom" enterDelay={500}>
<Tooltip tooltip={counterValue} placement="bottom">
<Box
sx={{
fontSize:
counterValue.toString().length >= 5
? {
xl: "50px",
lg: "45px",
md: "28px",
sm: "28px",
xs: "20px",
}
: {
xl: "55px",
lg: "50px",
md: "36px",
sm: "35px",
xs: "35px",
},
fontWeight: 600,
overflow: "hidden",
textOverflow: "ellipsis",
maxWidth: {
md: 187,
xs: 200,
maxWidth: 187,
flexFlow: "row",
fontSize: counterValue.toString().length >= 5 ? 50 : 55,
[`@media (max-width: ${breakPoints.sm}px)`]: {
flexFlow: "column",
maxWidth: 200,
fontSize: counterValue.toString().length >= 5 ? 20 : 35,
},
flexFlow: {
md: "row",
xs: "column",
[`@media (max-width: ${breakPoints.md}px)`]: {
fontSize: counterValue.toString().length >= 5 ? 28 : 35,
},
[`@media (max-width: ${breakPoints.lg}px)`]: {
fontSize: counterValue.toString().length >= 5 ? 28 : 36,
},
[`@media (max-width: ${breakPoints.xl}px)`]: {
fontSize: counterValue.toString().length >= 5 ? 45 : 50,
},
}}
>
@@ -137,7 +129,7 @@ const CounterCard = ({
</Box>
</Box>
</Box>
</Box>
</CounterCardMain>
);
};

View File

@@ -15,28 +15,14 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
capacityColors,
niceBytes,
niceBytesInt,
} from "../../../../common/utils";
import { Box } from "@mui/material";
import { Cell, Pie, PieChart } from "recharts";
import { CircleIcon } from "mds";
import { commonDashboardInfocard } from "../../Common/FormComponents/common/styleLibrary";
import { STATUS_COLORS } from "./Utils";
import get from "lodash/get";
import styled from "styled-components";
import { niceBytes } from "../../../../common/utils";
import { Box, breakPoints, CircleIcon, SizeChart } from "mds";
import { ServerDrives } from "api/consoleApi";
const styles = (theme: Theme) =>
createStyles({
...commonDashboardInfocard,
});
import { STATUS_COLORS } from "./Utils";
interface ICardProps {
classes?: any;
drive: ServerDrives;
}
@@ -51,29 +37,45 @@ const driveStatusColor = (health_status: string) => {
}
};
const DataContainerMain = styled.div(({ theme }) => ({
flex: 1,
display: "flex",
alignItems: "center",
paddingLeft: "20px",
marginTop: "10px",
flexFlow: "row",
"& .info-label": {
color: get(theme, "mutedText", "#87888d"),
fontSize: "12px",
textAlign: "center",
},
"& .info-value": {
fontSize: "18px",
color: get(theme, "signalColors.main", "#07193E"),
display: "flex",
fontWeight: 500,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
[`@media (max-width: ${breakPoints.sm}px)`]: {
flexFlow: "column",
},
}));
const DriveInfoItem = ({ drive }: ICardProps) => {
const totalSpace = drive.totalSpace || 0;
const usedSpace = drive.usedSpace || 0;
const freeSpace = totalSpace - usedSpace;
const plotValues = [
{ value: freeSpace, color: "#D6D6D6", label: "Free Space" },
{
value: drive.usedSpace,
color: capacityColors(usedSpace, totalSpace),
label: "Used Space",
},
];
return (
<Box
withBorders
sx={{
display: "flex",
flex: 1,
alignItems: "center",
paddingBottom: "10px",
padding: "20px",
border: "1px solid #eaeaea",
}}
>
<Box
@@ -106,9 +108,9 @@ const DriveInfoItem = ({ drive }: ICardProps) => {
wordBreak: "break-all",
marginRight: "8px",
fontWeight: 600,
fontSize: {
md: "16px",
xs: "10px",
fontSize: 16,
[`@media (max-width: ${breakPoints.sm}px)`]: {
fontSize: 10,
},
},
}}
@@ -117,68 +119,15 @@ const DriveInfoItem = ({ drive }: ICardProps) => {
{drive.state && <CircleIcon />}
</Box>
<Box
sx={{
flex: 1,
display: "flex",
alignItems: "center",
paddingLeft: "20px",
marginTop: "10px",
flexFlow: {
sm: "row",
xs: "column",
},
"& .info-label": {
color: "#5E5E5E",
fontSize: "12px",
textAlign: "center",
},
"& .info-value": {
fontSize: "18px",
color: "#07193E",
display: "flex",
fontWeight: 500,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
}}
>
<DataContainerMain>
<Box sx={{ flex: 1 }}>
<div style={{ position: "relative", width: 110, height: 110 }}>
<span
style={{
position: "absolute",
top: "50%",
left: "50%",
transform: "translate(-50%, -50%)",
fontWeight: "bold",
color: "#000",
fontSize: 12,
}}
>
{drive.usedSpace ? niceBytesInt(drive.usedSpace) : "-"}
</span>
<div>
<PieChart width={110} height={110}>
<Pie
data={plotValues}
cx={"50%"}
cy={"50%"}
dataKey="value"
outerRadius={50}
innerRadius={40}
startAngle={-70}
endAngle={360}
animationDuration={1}
>
{plotValues.map((entry, index) => (
<Cell key={`cellCapacity-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</div>
</div>
<SizeChart
label={true}
usedBytes={usedSpace}
totalBytes={totalSpace}
width={"120"}
height={"120"}
/>
</Box>
<Box
@@ -229,10 +178,10 @@ const DriveInfoItem = ({ drive }: ICardProps) => {
<label className="info-label">Available</label>
</Box>
</Box>
</Box>
</DataContainerMain>
</Box>
</Box>
);
};
export default withStyles(styles)(DriveInfoItem);
export default DriveInfoItem;

View File

@@ -14,10 +14,46 @@
// 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 { Box, Tooltip } from "@mui/material";
import React from "react";
import get from "lodash/get";
import styled from "styled-components";
import { Box, Tooltip } from "mds";
import { Cell, Pie, PieChart } from "recharts";
const ReportedUsageMain = styled.div(({ theme }) => ({
maxHeight: "110px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
fontSize: "19px",
padding: "10px",
"& .unit-value": {
fontSize: "50px",
color: get(theme, "signalColors.main", "#07193E"),
},
"& .unit-type": {
fontSize: "18px",
color: get(theme, "mutedText", "#87888d"),
marginTop: "20px",
marginLeft: "5px",
},
"& .usage-label": {
display: "flex",
alignItems: "center",
fontSize: "16px",
fontWeight: 600,
marginRight: "20px",
marginTop: "-10px",
"& .min-icon": {
marginLeft: "10px",
height: 16,
width: 16,
},
},
}));
const ReportedUsage = ({
usageValue,
total,
@@ -37,47 +73,13 @@ const ReportedUsage = ({
];
return (
<Box
sx={{
maxHeight: "110px",
display: "flex",
alignItems: "center",
justifyContent: "space-between",
fontSize: "19px",
padding: "10px",
"& .unit-value": {
fontSize: "50px",
color: "#07193E",
},
"& .unit-type": {
fontSize: "18px",
color: "#5E5E5E",
marginTop: "20px",
marginLeft: "5px",
},
"& .usage-label": {
display: "flex",
alignItems: "center",
fontSize: "16px",
fontWeight: 600,
marginRight: "20px",
marginTop: "-10px",
"& .min-icon": {
marginLeft: "10px",
height: 16,
width: 16,
},
},
}}
>
<ReportedUsageMain>
<Box>
<div className="usage-label">
<span>Reported Usage</span>
</div>
<Tooltip title={`${usageValue} Bytes`}>
<Tooltip tooltip={`${usageValue} Bytes`}>
<label
className={"unit-value"}
style={{
@@ -122,7 +124,7 @@ const ReportedUsage = ({
</div>
</Box>
</Box>
</Box>
</ReportedUsageMain>
);
};

View File

@@ -14,14 +14,10 @@
// 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 { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { niceDays } from "../../../../common/utils";
import { Box } from "@mui/material";
import { CircleIcon } from "mds";
import styled from "styled-components";
import get from "lodash/get";
import { commonDashboardInfocard } from "../../Common/FormComponents/common/styleLibrary";
import { Box, breakPoints, CircleIcon } from "mds";
import { niceDays } from "../../../../common/utils";
import {
getDriveStatusColor,
getNetworkStatusColor,
@@ -29,84 +25,121 @@ import {
} from "./Utils";
import { ServerProperties } from "api/consoleApi";
const styles = (theme: Theme) =>
createStyles({
...commonDashboardInfocard,
});
interface ICardProps {
classes?: any;
server: ServerProperties;
index: number;
}
const ServerStatItemMain = styled.div(({ theme }) => ({
alignItems: "baseline",
padding: "5px",
display: "flex",
gap: "5px",
"& .StatBox": {
display: "flex",
alignItems: "center",
justifyContent: "center",
flexFlow: "column",
"& .stat-text": {
color: get(theme, "mutedText", "#87888d"),
fontSize: "12px",
},
"& .stat-value": {
fontSize: "18px",
color: get(theme, "signalColors.main", "#07193E"),
display: "flex",
fontWeight: 500,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
"& .stat-container": {
display: "flex",
alignItems: "center",
justifyContent: "center",
flexFlow: "column",
marginLeft: "5px",
maxWidth: "40px",
"&:first-of-type(svg)": {
fill: get(theme, "mutedText", "#87888d"),
},
"& .stat-indicator": {
marginRight: "0px",
justifyContent: "center",
alignItems: "center",
textAlign: "center",
"& svg.min-icon": {
width: "10px",
height: "10px",
},
"&.good": {
"& svg.min-icon": {
fill: get(theme, "signalColors.good", "#4CCB92"),
},
},
"&.warn": {
"& svg.min-icon": {
fill: get(theme, "signalColors.warning", "#FFBD62"),
},
},
"&.bad": {
"& svg.min-icon": {
fill: get(theme, "signalColors.danger", "#C51B3F"),
},
},
},
},
},
},
}));
const ServerInfoItemMain = styled.div(({ theme }) => ({
display: "flex",
alignItems: "flex-start",
flexFlow: "column",
flex: 1,
"& .server-state": {
marginLeft: "8px",
"& .min-icon": {
height: "14px",
width: "14px",
},
"&.good": {
"& svg.min-icon": {
fill: get(theme, "signalColors.good", "#4CCB92"),
},
},
"&.warn": {
"& svg.min-icon": {
fill: get(theme, "signalColors.warning", "#FFBD62"),
},
},
"&.bad": {
"& svg.min-icon": {
fill: get(theme, "signalColors.danger", "#C51B3F"),
},
},
},
}));
const ServerStatItem = ({
label = "",
value = "",
statusColor = "",
statusColor = "warn",
hasStatus = false,
}: {
label?: string;
value?: any;
hasStatus?: boolean;
statusColor: string | undefined;
statusColor?: "good" | "warn" | "bad";
}) => {
return (
<Box
sx={{
alignItems: "baseline",
padding: "5px",
display: "flex",
gap: "5px",
}}
>
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexFlow: "column",
"& .stat-text": { color: "#5E5E5E", fontSize: "12px" },
"& .stat-value": {
fontSize: "18px",
color: "#07193E",
display: "flex",
fontWeight: 500,
overflow: "hidden",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
},
}}
>
<ServerStatItemMain>
<Box className={"StatBox"}>
<div className="stat-value">
{value}{" "}
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
flexFlow: "column",
marginLeft: "5px",
maxWidth: "40px",
"&:first-of-type(svg)": {
fill: "#848484",
},
}}
>
<Box className={"stat-container"}>
{hasStatus ? (
<Box
sx={{
marginRight: "0px",
justifyContent: "center",
alignItems: "center",
textAlign: "center",
"& svg.min-icon": {
fill: statusColor,
width: "10px",
height: "10px",
},
}}
>
<Box className={`stat-indicator ${statusColor}`}>
<CircleIcon />
</Box>
) : (
@@ -116,7 +149,7 @@ const ServerStatItem = ({
</div>
<div className="stat-text">{label}</div>
</Box>
</Box>
</ServerStatItemMain>
);
};
@@ -135,14 +168,7 @@ const ServerInfoItem = ({ server }: ICardProps) => {
? server.drives.filter((element) => element.state === "ok").length
: 0;
return (
<Box
sx={{
display: "flex",
alignItems: "flex-start",
flexFlow: "column",
flex: 1,
}}
>
<ServerInfoItemMain>
<Box
sx={{
display: "flex",
@@ -152,10 +178,9 @@ const ServerInfoItem = ({ server }: ICardProps) => {
justifyContent: "space-between",
width: "100%",
paddingLeft: "20px",
flexFlow: {
sm: "row",
xs: "column",
flexFlow: "row",
[`@media (max-width: ${breakPoints.md}px)`]: {
flexFlow: "column",
},
}}
>
@@ -174,16 +199,7 @@ const ServerInfoItem = ({ server }: ICardProps) => {
{server.endpoint || ""}
</Box>
{server?.state && (
<Box
sx={{
marginLeft: "8px",
"& .min-icon": {
fill: serverStatusColor(server.state),
height: "14px",
width: "14px",
},
}}
>
<Box className={`server-state ${serverStatusColor(server.state)}`}>
<CircleIcon />
</Box>
)}
@@ -195,10 +211,7 @@ const ServerInfoItem = ({ server }: ICardProps) => {
alignItems: "center",
justifyContent: "center",
flex: "1.5",
gap: {
md: "5%",
xs: "5%",
},
gap: "5%",
}}
>
<ServerStatItem
@@ -213,15 +226,14 @@ const ServerInfoItem = ({ server }: ICardProps) => {
hasStatus={true}
value={`${activeNetwork}/${networkTotal}`}
/>
<ServerStatItem
statusColor={"green"}
statusColor={"good"}
label={"Up time"}
value={server?.uptime ? niceDays(`${server.uptime}`) : "N/A"}
/>
</Box>
<ServerStatItem
statusColor={"green"}
statusColor={"good"}
label={""}
value={
<Box
@@ -246,7 +258,7 @@ const ServerInfoItem = ({ server }: ICardProps) => {
}
/>
</Box>
</Box>
</ServerInfoItemMain>
);
};
export default withStyles(styles)(ServerInfoItem);
export default ServerInfoItem;

View File

@@ -15,14 +15,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import ListSubheader from "@mui/material/ListSubheader";
import List from "@mui/material/List";
import ListItemButton from "@mui/material/ListItemButton";
import Collapse from "@mui/material/Collapse";
import { Accordion, Box, breakPoints } from "mds";
import ServerInfoItem from "./ServerInfoItem";
import { Box } from "@mui/material";
import DriveInfoItem from "./DriveInfoItem";
import { MenuCollapsedIcon, MenuExpandedIcon } from "mds";
import { ServerProperties } from "api/consoleApi";
const ServersList = ({ data }: { data: ServerProperties[] }) => {
@@ -38,128 +33,63 @@ const ServersList = ({ data }: { data: ServerProperties[] }) => {
<Box>
<Box
sx={{
marginBottom: "10px",
fontSize: 18,
lineHeight: 2,
fontWeight: 700,
}}
>
Servers ({data.length})
</Box>
<List
sx={{ width: "100%", flex: 1, padding: "0" }}
component="nav"
aria-labelledby="nested-list-subheader"
>
<Box>
{data.map((serverInfo, index) => {
const key = `${serverInfo.endpoint}-${index}`;
const isExpanded = expanded === key;
return (
<React.Fragment key={key}>
<ListItemButton
disableRipple
onClick={() => {
if (!isExpanded) {
handleClick(key);
} else {
handleClick("");
}
}}
className={isExpanded ? "expanded" : ""}
<Accordion
key={key}
expanded={isExpanded}
onTitleClick={() => {
if (!isExpanded) {
handleClick(key);
} else {
handleClick("");
}
}}
id={"key"}
title={<ServerInfoItem server={serverInfo} index={index} />}
sx={{ marginBottom: 15 }}
>
<Box
useBackground
sx={{ padding: "10px 30px", fontWeight: "bold" }}
>
Drives ({serverInfo.drives?.length})
</Box>
<Box
sx={{
flex: 1,
display: "flex",
alignItems: "center",
justifyContent: "space-between",
borderTop: index === 0 ? "1px solid #f1f1f1" : "",
borderBottom: "1px solid #f1f1f1",
borderLeft: "1px solid #f1f1f1",
borderRight: "1px solid #f1f1f1",
padding: "3px 10px 3px 10px",
"&:hover": {
background: "#bebbbb0d",
flexDirection: "column",
padding: "15px 30px",
gap: 15,
[`@media (max-width: ${breakPoints.sm}px)`]: {
padding: "10px 10px",
},
}}
>
<ServerInfoItem server={serverInfo} index={index} />
<Box
sx={{
height: "25px",
width: "25px",
background: "#FBFAFA",
borderRadius: "2px",
"&:hover": {
background: "#fafafa",
},
display: {
md: "block",
xs: "none",
},
"& .collapse-icon": {
fill: "#494949",
"& g rect": {
fill: "#ffffff",
},
},
"& .expand-icon": {
fill: "#494949",
"& rect": {
fill: "#ffffff",
},
},
}}
>
{isExpanded ? (
<MenuCollapsedIcon className="collapse-icon" />
) : (
<MenuExpandedIcon className="expand-icon" />
)}
</Box>
</ListItemButton>
{isExpanded ? (
<Box
key={`${serverInfo.endpoint}-${index}`}
sx={{
border: "1px solid #f1f1f1",
borderTop: "0",
}}
>
<ListSubheader
key={`${index}-drive-details`}
component="div"
sx={{ paddingLeft: "30px" }}
>
Drives ({serverInfo.drives?.length})
</ListSubheader>
<Collapse
in={isExpanded}
timeout="auto"
unmountOnExit
sx={{
width: "100%",
flex: 1,
display: "flex",
padding: { md: "15px 30px", xs: "10px 10px" },
"& .MuiCollapse-wrapperInner": {
display: "flex",
flexFlow: "column",
gap: "15px",
},
}}
>
{serverInfo.drives?.map((driveInfo, index) => {
return (
<DriveInfoItem
drive={driveInfo}
key={`${driveInfo.endpoint}-${index}`}
/>
);
})}
</Collapse>
</Box>
) : null}
</React.Fragment>
{serverInfo.drives?.map((driveInfo, index) => {
return (
<DriveInfoItem
drive={driveInfo}
key={`${driveInfo.endpoint}-${index}`}
/>
);
})}
</Box>
</Accordion>
);
})}
</List>
</Box>
</Box>
);
};

View File

@@ -17,7 +17,7 @@
export const STATUS_COLORS = {
RED: "#C83B51",
GREEN: "#4CCB92",
YELLOW: "#E7A219",
YELLOW: "#FFBD62",
};
export const getDriveStatusColor = (
@@ -25,24 +25,24 @@ export const getDriveStatusColor = (
totalDrives: number,
) => {
if (activeDisks <= totalDrives / 2) {
return STATUS_COLORS.RED;
return "bad";
}
if (totalDrives !== 2 && activeDisks === totalDrives / 2 + 1) {
return STATUS_COLORS.YELLOW;
return "warn";
}
if (activeDisks === totalDrives) {
return STATUS_COLORS.GREEN;
return "good";
}
};
export const serverStatusColor = (health_status: string) => {
switch (health_status) {
case "offline":
return STATUS_COLORS.RED;
return "bad";
case "online":
return STATUS_COLORS.GREEN;
return "good";
default:
return STATUS_COLORS.YELLOW;
return "warn";
}
};
export const getNetworkStatusColor = (
@@ -50,12 +50,12 @@ export const getNetworkStatusColor = (
networkTotal: number,
) => {
if (activeNetwork <= networkTotal / 2) {
return STATUS_COLORS.RED;
return "bad";
}
if (activeNetwork === networkTotal / 2 + 1) {
return STATUS_COLORS.YELLOW;
return "warn";
}
if (activeNetwork === networkTotal) {
return STATUS_COLORS.GREEN;
return "good";
}
};

View File

@@ -14,13 +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 { Card, CardHeader } from "@mui/material";
import { Link } from "react-router-dom";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import makeStyles from "@mui/styles/makeStyles";
import React, { Fragment } from "react";
import styled from "styled-components";
import get from "lodash/get";
import { Box } from "mds";
import { Link } from "react-router-dom";
import { widgetCommon } from "../Common/FormComponents/common/styleLibrary";
export interface ISubInterface {
@@ -36,69 +34,48 @@ interface ICommonCard {
moreLink?: string;
rightComponent?: any;
extraMargin?: boolean;
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
...widgetCommon,
cardRoot: {
...widgetCommon.singleValueContainer,
"&.MuiPaper-root": {
borderRadius: 10,
},
},
metricText: {
fontSize: 70,
lineHeight: 1.1,
color: "#07193E",
const CommonCardItem = styled.div(({ theme }) => ({
...widgetCommon(theme),
"& .metricText": {
fontSize: 70,
lineHeight: 1.1,
color: get(theme, "signalColors.main", "#07193E"),
fontWeight: "bold",
},
"& .unitText": {
fontSize: 10,
color: get(theme, "mutedText", "#87888d"),
fontWeight: "normal",
},
"& .subHeaderContainer": {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
"& .subMessage": {
fontSize: 10,
color: get(theme, "mutedText", "#87888d"),
"&.bold": {
fontWeight: "bold",
},
unitText: {
fontSize: 10,
color: "#767676",
fontWeight: "normal",
},
subHearderContainer: {
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
},
subMessage: {
fontSize: 10,
color: "#767676",
"&.bold": {
fontWeight: "bold",
},
},
headerContainer: {
display: "flex",
justifyContent: "space-between",
},
viewAll: {
fontSize: 10,
color: "#C83B51",
textTransform: "capitalize",
"& a, & a:hover, & a:visited, & a:active": {
color: "#C83B51",
},
},
extraMargin: {
margin: "10px 20px 10px 0",
},
});
const cardSubStyles = makeStyles({
root: { backgroundColor: "#fff", padding: 0 },
title: {
...widgetCommon.titleContainer,
},
content: {
maxWidth: "100%",
"& .headerContainer": {
display: "flex",
justifyContent: "space-between",
},
});
"& .viewAll": {
fontSize: 10,
color: get(theme, "signalColors.danger", "#C83B51"),
textTransform: "capitalize",
"& a, & a:hover, & a:visited, & a:active": {
color: get(theme, "signalColors.danger", "#C83B51"),
},
},
}));
const CommonCard = ({
title,
@@ -108,31 +85,29 @@ const CommonCard = ({
moreLink,
rightComponent,
extraMargin = false,
classes,
}: ICommonCard) => {
const subStyles = cardSubStyles();
const SubHeader = () => {
return (
<Fragment>
<div className={classes.subHearderContainer}>
<div className={classes.leftSide}>
<div className={"subHeaderContainer"}>
<div className={"leftSide"}>
<div>
<span className={classes.metricText}>
<span className={"metricText"}>
{metricValue}
<span className={classes.unitText}>{metricUnit}</span>
<span className={"unitText"}>{metricUnit}</span>
</span>
</div>
{subMessage && (
<div
className={`${classes.subMessage} ${
subMessage.fontWeight ? subMessage.fontWeight : ""
}`}
<Box
sx={{
fontWeight: subMessage.fontWeight || "normal",
}}
>
{subMessage.message}
</div>
</Box>
)}
</div>
<div className={classes.rightSide}>{rightComponent}</div>
<div className={"rightSide"}>{rightComponent}</div>
</div>
</Fragment>
);
@@ -141,11 +116,11 @@ const CommonCard = ({
const Header = () => {
return (
<Fragment>
<div className={classes.headerContainer}>
<span className={classes.title}>{title}</span>
<div className={"headerContainer"}>
<span className={"titleContainer"}>{title}</span>
{moreLink && (
<Fragment>
<span className={classes.viewAll}>
<span className={"viewAll"}>
<Link to={moreLink}>View All</Link>
</span>
</Fragment>
@@ -157,29 +132,23 @@ const CommonCard = ({
return (
<Fragment>
<Card
className={`${classes.cardRoot} ${
extraMargin ? classes.extraMargin : ""
}`}
<Box
withBorders
sx={{
height: 200,
padding: 16,
margin: extraMargin ? "10px 20px 10px 0" : "",
}}
>
{metricValue !== "" && (
<CardHeader
title={<Header />}
subheader={
<Fragment>
<SubHeader />
</Fragment>
}
classes={{
root: subStyles.root,
title: subStyles.title,
content: subStyles.content,
}}
/>
<CommonCardItem>
<Header />
<SubHeader />
</CommonCardItem>
)}
</Card>
</Box>
</Fragment>
);
};
export default withStyles(styles)(CommonCard);
export default CommonCard;

View File

@@ -15,32 +15,17 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import PrDashboard from "./Prometheus/PrDashboard";
import Grid from "@mui/material/Grid";
import { containerForHeader } from "../Common/FormComponents/common/styleLibrary";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Grid, ProgressBar } from "mds";
import { useSelector } from "react-redux";
import { AppState, useAppDispatch } from "../../../store";
import { getUsageAsync } from "./dashboardThunks";
import { useSelector } from "react-redux";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import { selFeatures } from "../consoleSlice";
import HelpMenu from "../HelpMenu";
import { setHelpName } from "../../../systemSlice";
import { ProgressBar } from "mds";
import PrDashboard from "./Prometheus/PrDashboard";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
interface IDashboardSimple {
classes: any;
}
const styles = (theme: Theme) =>
createStyles({
...containerForHeader,
});
const Dashboard = ({ classes }: IDashboardSimple) => {
const Dashboard = () => {
const dispatch = useAppDispatch();
const [loading, setLoading] = useState<boolean>(true);
@@ -73,7 +58,7 @@ const Dashboard = ({ classes }: IDashboardSimple) => {
)}
{loading ? (
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12}>
<ProgressBar />
</Grid>
</Grid>
@@ -84,4 +69,4 @@ const Dashboard = ({ classes }: IDashboardSimple) => {
);
};
export default withStyles(styles)(Dashboard);
export default Dashboard;

View File

@@ -15,24 +15,25 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Box, breakPoints } from "mds";
const DashboardItemBox = ({ children }: { children: any }) => {
return (
<Box
withBorders
sx={{
border: "1px solid #f1f1f1",
borderRadius: "3px",
padding: {
md: "15px",
xs: "5px",
padding: 15,
height: 136,
maxWidth: "100%",
[`@media (max-width: ${breakPoints.sm}px)`]: {
padding: 5,
height: "auto",
},
height: {
md: "136px",
xs: "auto",
},
maxWidth: {
sm: "100%",
[`@media (max-width: ${breakPoints.md}px)`]: {
display: "flex",
flexFlow: "column",
maxWidth: "initial",
},
}}
>

View File

@@ -16,21 +16,22 @@
import React, { Fragment, useState } from "react";
import { useSelector } from "react-redux";
import Grid from "@mui/material/Grid";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Box } from "@mui/material";
import { actionsTray } from "../../Common/FormComponents/common/styleLibrary";
import {
Box,
Button,
Grid,
HelpBox,
PageLayout,
ProgressBar,
PrometheusErrorIcon,
SyncIcon,
TabItemProps,
Tabs,
} from "mds";
import { IDashboardPanel } from "./types";
import { panelsConfiguration } from "./utils";
import { TabPanel } from "../../../shared/tabs";
import TabSelector from "../../Common/TabSelector/TabSelector";
import { componentToUse } from "./widgetUtils";
import ZoomWidget from "./ZoomWidget";
import { AppState, useAppDispatch } from "../../../../store";
import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector";
import {
DLayoutColumnProps,
DLayoutRowProps,
@@ -40,33 +41,20 @@ import {
summaryPanelsLayout,
trafficPanelsLayout,
} from "./Widgets/LayoutUtil";
import MergedWidgetsRenderer from "./Widgets/MergedWidgetsRenderer";
import BasicDashboard from "../BasicDashboard/BasicDashboard";
import {
Button,
HelpBox,
PageLayout,
ProgressBar,
PrometheusErrorIcon,
SyncIcon,
} from "mds";
import { ITabOption } from "../../Common/TabSelector/types";
import { getUsageAsync } from "../dashboardThunks";
import { reloadWidgets } from "../dashboardSlice";
import { selFeatures } from "../../consoleSlice";
import { AdminInfoResponse } from "api/consoleApi";
import ZoomWidget from "./ZoomWidget";
import DateRangeSelector from "../../Common/FormComponents/DateRangeSelector/DateRangeSelector";
import MergedWidgetsRenderer from "./Widgets/MergedWidgetsRenderer";
import BasicDashboard from "../BasicDashboard/BasicDashboard";
interface IPrDashboard {
classes?: any;
apiPrefix?: string;
usage: AdminInfoResponse | null;
}
const styles = (theme: Theme) =>
createStyles({
...actionsTray,
});
const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
const dispatch = useAppDispatch();
const loadingUsage = useSelector(
@@ -90,7 +78,9 @@ const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
const [timeStart, setTimeStart] = useState<any>(null);
const [timeEnd, setTimeEnd] = useState<any>(null);
const panelInformation = panelsConfiguration;
const [curTab, setCurTab] = useState<number>(0);
const [curTab, setCurTab] = useState<string>(
usage?.advancedMetricsStatus === "not configured" ? "info" : "usage",
);
const getPanelDetails = (id: number) => {
return panelInformation.find((panel) => panel.id === id);
@@ -168,110 +158,79 @@ const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
return renderPanelItems(resourcesPanelsLayoutAdvanced);
};
let tabs: ITabOption[];
if (usage?.advancedMetricsStatus !== "not configured") {
tabs = [
{ label: "Usage" },
{ label: "Traffic" },
{ label: "Resources" },
{ label: "Info" },
];
} else {
tabs = [
{ label: "Info" },
{ label: "Usage", disabled: true },
{ label: "Traffic", disabled: true },
{ label: "Resources", disabled: true },
];
}
const prometheusOptionsDisabled =
usage?.advancedMetricsStatus === "not configured";
return (
<PageLayout
sx={{
padding: hideMenu ? 0 : "2rem",
}}
>
{zoomOpen && (
<ZoomWidget
modalOpen={zoomOpen}
timeStart={timeStart}
timeEnd={timeEnd}
widgetRender={0}
value={zoomWidget}
apiPrefix={apiPrefix}
/>
)}
<Grid item xs={12}>
<TabSelector
selectedTab={curTab}
onChange={(newValue: number) => {
setCurTab(newValue);
}}
tabOptions={tabs}
/>
</Grid>
<Grid
item
xs={12}
sx={{
paddingTop: "20px",
}}
>
<Box
sx={{
marginBottom: "20px",
}}
>
{curTab ===
(usage?.advancedMetricsStatus === "not configured" ? 0 : 3) ? (
<Grid container>
const searchBox = (
<Box sx={{ marginBottom: 20 }}>
{curTab === "info" ? (
<Grid container>
<Grid item>
<Box
sx={{
fontSize: 18,
lineHeight: 2,
fontWeight: 700,
}}
>
Server Information
</Box>
</Grid>
<Grid item xs>
<Grid container direction="row-reverse">
<Grid item>
<Box
sx={{
color: "#000",
fontSize: 18,
lineHeight: 2,
fontWeight: 700,
marginLeft: "21px",
display: "flex",
<Button
id={"sync"}
type="button"
variant="callAction"
onClick={() => {
dispatch(getUsageAsync());
}}
>
Server Information
</Box>
</Grid>
<Grid item xs>
<Grid container direction="row-reverse">
<Grid item>
<Button
id={"sync"}
type="button"
variant="callAction"
onClick={() => {
dispatch(getUsageAsync());
}}
disabled={loadingUsage}
icon={<SyncIcon />}
label={"Sync"}
/>
</Grid>
</Grid>
disabled={loadingUsage}
icon={<SyncIcon />}
label={"Sync"}
/>
</Grid>
</Grid>
) : (
<DateRangeSelector
timeStart={timeStart}
setTimeStart={setTimeStart}
timeEnd={timeEnd}
setTimeEnd={setTimeEnd}
triggerSync={triggerLoad}
/>
)}
</Box>
<TabPanel
index={usage?.advancedMetricsStatus === "not configured" ? 3 : 0}
value={curTab}
>
</Grid>
</Grid>
) : (
<DateRangeSelector
timeStart={timeStart}
setTimeStart={setTimeStart}
timeEnd={timeEnd}
setTimeEnd={setTimeEnd}
triggerSync={triggerLoad}
/>
)}
</Box>
);
const infoTab: TabItemProps = {
tabConfig: { label: "Info", id: "info", disabled: false },
content: (
<Fragment>
{(!usage || loadingUsage) && <ProgressBar />}
{usage && !loadingUsage && (
<Fragment>
{searchBox}
<BasicDashboard usage={usage} />
</Fragment>
)}
</Fragment>
),
};
const prometheusTabs: TabItemProps[] = [
{
tabConfig: {
label: "Usage",
id: "usage",
disabled: prometheusOptionsDisabled,
},
content: (
<Fragment>
{searchBox}
<RowPanelLayout>
{usage?.advancedMetricsStatus === "unavailable" && (
<HelpBox
@@ -291,8 +250,18 @@ const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
)}
{panelInformation.length ? renderSummaryPanels() : null}
</RowPanelLayout>
</TabPanel>
<TabPanel index={1} value={curTab}>
</Fragment>
),
},
{
tabConfig: {
label: "Traffic",
id: "traffic",
disabled: prometheusOptionsDisabled,
},
content: (
<Fragment>
{searchBox}
<RowPanelLayout>
{usage?.advancedMetricsStatus === "unavailable" && (
<HelpBox
@@ -312,8 +281,18 @@ const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
)}
{panelInformation.length ? renderTrafficPanels() : null}
</RowPanelLayout>
</TabPanel>
<TabPanel index={2} value={curTab}>
</Fragment>
),
},
{
tabConfig: {
label: "Resources",
id: "resources",
disabled: prometheusOptionsDisabled,
},
content: (
<Fragment>
{searchBox}
<RowPanelLayout>
{usage?.advancedMetricsStatus === "unavailable" && (
<HelpBox
@@ -337,17 +316,46 @@ const PrDashboard = ({ apiPrefix = "admin", usage }: IPrDashboard) => {
</h2>
{panelInformation.length ? renderAdvancedResourcesPanels() : null}
</RowPanelLayout>
</TabPanel>
<TabPanel
index={usage?.advancedMetricsStatus === "not configured" ? 0 : 3}
value={curTab}
>
{(!usage || loadingUsage) && <ProgressBar />}
{usage && !loadingUsage && <BasicDashboard usage={usage} />}
</TabPanel>
</Grid>
</Fragment>
),
},
];
let tabsOptions: TabItemProps[];
if (!prometheusOptionsDisabled) {
tabsOptions = [...prometheusTabs, infoTab];
} else {
tabsOptions = [infoTab, ...prometheusTabs];
}
return (
<PageLayout
sx={{
padding: hideMenu ? 0 : "2rem",
}}
>
{zoomOpen && (
<ZoomWidget
modalOpen={zoomOpen}
timeStart={timeStart}
timeEnd={timeEnd}
widgetRender={0}
value={zoomWidget}
apiPrefix={apiPrefix}
/>
)}
<Tabs
horizontal
options={tabsOptions}
currentTabOrPath={curTab}
onTabClick={(newValue) => {
setCurTab(newValue);
}}
/>
</PageLayout>
);
};
export default withStyles(styles)(PrDashboard);
export default PrDashboard;

View File

@@ -15,7 +15,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import { Box, breakPoints, Grid, Loader } from "mds";
import { useSelector } from "react-redux";
import {
Bar,
BarChart,
@@ -25,46 +27,36 @@ import {
XAxis,
YAxis,
} from "recharts";
import { Grid, useMediaQuery } from "@mui/material";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { IBarChartConfiguration } from "./types";
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
import BarChartTooltip from "./tooltips/BarChartTooltip";
import { IDashboardPanel } from "../types";
import { widgetDetailsToPanel } from "../utils";
import { ErrorResponseHandler } from "../../../../../common/types";
import api from "../../../../../common/api";
import { useTheme } from "@mui/styles";
import { Loader } from "mds";
import ExpandGraphLink from "./ExpandGraphLink";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
import ExpandGraphLink from "./ExpandGraphLink";
import DownloadWidgetDataButton from "../../DownloadWidgetDataButton";
import { useSelector } from "react-redux";
import BarChartTooltip from "./tooltips/BarChartTooltip";
import api from "../../../../../common/api";
interface IBarChartWidget {
classes: any;
title: string;
panelItem: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
zoomActivated?: boolean;
}
const styles = (theme: Theme) =>
createStyles({
...widgetCommon,
loadingAlign: {
width: "100%",
paddingTop: "15px",
textAlign: "center",
margin: "auto",
},
});
const BarChartMain = styled.div(({ theme }) => ({
...widgetCommon(theme),
loadingAlign: {
width: "100%",
paddingTop: "15px",
textAlign: "center",
margin: "auto",
},
}));
const CustomizedAxisTick = ({ y, payload }: any) => {
return (
@@ -83,12 +75,10 @@ const CustomizedAxisTick = ({ y, payload }: any) => {
};
const BarChartWidget = ({
classes,
title,
panelItem,
timeStart,
timeEnd,
propLoading,
apiPrefix,
zoomActivated = false,
}: IBarChartWidget) => {
@@ -97,10 +87,15 @@ const BarChartWidget = ({
const [data, setData] = useState<any>([]);
const [result, setResult] = useState<IDashboardPanel | null>(null);
const [hover, setHover] = useState<boolean>(false);
const [biggerThanMd, setBiggerThanMd] = useState<boolean>(
window.innerWidth >= breakPoints.md,
);
const componentRef = useRef<HTMLElement>();
const widgetVersion = useSelector(
(state: AppState) => state.dashboard.widgetLoadVersion,
);
const onHover = () => {
setHover(true);
};
@@ -112,6 +107,23 @@ const BarChartWidget = ({
setLoading(true);
}, [widgetVersion]);
useEffect(() => {
const handleWindowResize = () => {
let extMD = false;
if (window.innerWidth >= breakPoints.md) {
console.log("Bigger");
extMD = true;
}
setBiggerThanMd(extMD);
};
window.addEventListener("resize", handleWindowResize);
return () => {
window.removeEventListener("resize", handleWindowResize);
};
}, []);
useEffect(() => {
if (loading) {
let stepCalc = 0;
@@ -164,103 +176,112 @@ const BarChartWidget = ({
});
}
const theme = useTheme();
const biggerThanMd = useMediaQuery(theme.breakpoints.up("md"));
return (
<div
className={zoomActivated ? "" : classes.singleValueContainer}
onMouseOver={onHover}
onMouseLeave={onStopHover}
>
{!zoomActivated && (
<Grid container>
<Grid item xs={10} alignItems={"start"} justifyItems={"start"}>
<div className={classes.titleContainer}>{title}</div>
</Grid>
<Grid item xs={1} display={"flex"} justifyContent={"flex-end"}>
{hover && <ExpandGraphLink panelItem={panelItem} />}
</Grid>
<Grid item xs={1} display={"flex"} justifyContent={"flex-end"}>
<DownloadWidgetDataButton
title={title}
componentRef={componentRef}
data={data}
/>
</Grid>
</Grid>
)}
{loading && (
<div className={classes.loadingAlign}>
<Loader />
</div>
)}
{!loading && (
<div
ref={componentRef as React.RefObject<HTMLDivElement>}
className={
zoomActivated ? classes.zoomChartCont : classes.contentContainer
}
>
<ResponsiveContainer width="99%">
<BarChart
data={data as object[]}
layout={"vertical"}
barCategoryGap={1}
<BarChartMain>
<Box
className={zoomActivated ? "" : "singleValueContainer"}
onMouseOver={onHover}
onMouseLeave={onStopHover}
>
{!zoomActivated && (
<Grid container>
<Grid
item
xs={10}
sx={{ alignItems: "start", justifyItems: "start" }}
>
<XAxis type="number" hide />
<YAxis
dataKey="name"
type="category"
interval={0}
tick={<CustomizedAxisTick />}
tickLine={false}
axisLine={false}
width={150}
hide={!biggerThanMd}
style={{
fontSize: "12px",
fontWeight: 100,
}}
<div className={"titleContainer"}>{title}</div>
</Grid>
<Grid
item
xs={1}
sx={{ display: "flex", justifyContent: "flex-end" }}
>
{hover && <ExpandGraphLink panelItem={panelItem} />}
</Grid>
<Grid
item
xs={1}
sx={{ display: "flex", justifyContent: "flex-end" }}
>
<DownloadWidgetDataButton
title={title}
componentRef={componentRef}
data={data}
/>
{barChartConfiguration.map((bar) => (
<Bar
key={`bar-${bar.dataKey}`}
dataKey={bar.dataKey}
fill={bar.color}
background={bar.background}
barSize={zoomActivated ? 25 : 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)" }}
content={
<BarChartTooltip
barChartConfiguration={barChartConfiguration}
/>
}
/>
</BarChart>
</ResponsiveContainer>
</div>
)}
</div>
</Grid>
</Grid>
)}
{loading && (
<Box className={"loadingAlign"}>
<Loader />
</Box>
)}
{!loading && (
<div
ref={componentRef as React.RefObject<HTMLDivElement>}
className={zoomActivated ? "zoomChartCont" : "contentContainer"}
>
<ResponsiveContainer width="99%">
<BarChart
data={data as object[]}
layout={"vertical"}
barCategoryGap={1}
>
<XAxis type="number" hide />
<YAxis
dataKey="name"
type="category"
interval={0}
tick={<CustomizedAxisTick />}
tickLine={false}
axisLine={false}
width={150}
hide={!biggerThanMd}
style={{
fontSize: "12px",
fontWeight: 100,
}}
/>
{barChartConfiguration.map((bar) => (
<Bar
key={`bar-${bar.dataKey}`}
dataKey={bar.dataKey}
fill={bar.color}
background={bar.background}
barSize={zoomActivated ? 25 : 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)" }}
content={
<BarChartTooltip
barChartConfiguration={barChartConfiguration}
/>
}
/>
</BarChart>
</ResponsiveContainer>
</div>
)}
</Box>
</BarChartMain>
);
};
export default withStyles(styles)(BarChartWidget);
export default BarChartWidget;

View File

@@ -15,34 +15,81 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useEffect, useState } from "react";
import { IDashboardPanel } from "../types";
import { Box } from "@mui/material";
import styled from "styled-components";
import get from "lodash/get";
import { Box, breakPoints, Loader, ReportedUsageIcon } from "mds";
import { Cell, Pie, PieChart } from "recharts";
import { useSelector } from "react-redux";
import api from "../../../../../common/api";
import { IDashboardPanel } from "../types";
import { widgetDetailsToPanel } from "../utils";
import { ErrorResponseHandler } from "../../../../../common/types";
import {
calculateBytes,
capacityColors,
niceBytesInt,
} from "../../../../../common/utils";
import { Cell, Pie, PieChart } from "recharts";
import { Loader, ReportedUsageIcon } from "mds";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
import { useSelector } from "react-redux";
const CapacityItemMain = styled.div(({ theme }) => ({
flex: 1,
display: "flex",
alignItems: "center",
flexFlow: "row",
"& .usableLabel": {
color: get(theme, "mutedText", "#87888d"),
fontSize: "10px",
display: "flex",
flexFlow: "column",
alignItems: "center",
textAlign: "center",
},
"& .usedLabel": {
color: get(theme, "mutedText", "#87888d"),
fontWeight: "bold",
fontSize: "14px",
},
"& .totalUsed": {
display: "flex",
"& .value": {
fontSize: "50px",
fontFamily: "Inter",
fontWeight: 600,
alignSelf: "flex-end",
lineHeight: 1,
},
"& .unit": {
color: get(theme, "mutedText", "#87888d"),
fontWeight: "bold",
fontSize: "14px",
marginLeft: "12px",
alignSelf: "flex-end",
},
},
"& .ofUsed": {
marginTop: "5px",
"& .value": {
color: get(theme, "mutedText", "#87888d"),
fontWeight: "bold",
fontSize: "14px",
textAlign: "right",
},
},
[`@media (max-width: ${breakPoints.sm}px)`]: {
flexFlow: "column",
},
}));
const CapacityItem = ({
value,
timeStart,
timeEnd,
propLoading,
apiPrefix,
}: {
value: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
}) => {
const dispatch = useAppDispatch();
@@ -134,23 +181,13 @@ const CapacityItem = ({
},
];
return (
<Box
sx={{
flex: 1,
display: "flex",
alignItems: "center",
flexFlow: {
sm: "row",
xs: "column",
},
}}
>
<CapacityItemMain>
<Box
sx={{
fontSize: "16px",
fontWeight: 600,
alignSelf: {
xs: "flex-start",
[`@media (max-width: ${breakPoints.sm}px)`]: {
alignSelf: "flex-start",
},
}}
>
@@ -161,9 +198,9 @@ const CapacityItem = ({
position: "relative",
width: 110,
height: 110,
marginLeft: {
sm: "auto",
xs: "",
marginLeft: "auto",
[`@media (max-width: ${breakPoints.sm}px)`]: {
marginLeft: "",
},
}}
>
@@ -177,24 +214,12 @@ const CapacityItem = ({
left: "50%",
transform: "translate(-50%, -50%)",
fontWeight: "bold",
color: "#000",
fontSize: 12,
}}
>
{`${totalUsableFreeRatio}%`}
<br />
<Box
sx={{
color: "#8F9090",
fontSize: "10px",
display: "flex",
flexFlow: "column",
alignItems: "center",
textAlign: "center",
}}
>
Free
</Box>
<Box className={"usableLabel"}>Free</Box>
</Box>
<PieChart width={110} height={110}>
<Pie
@@ -218,55 +243,19 @@ const CapacityItem = ({
sx={{
display: "flex",
alignItems: "center",
marginLeft: {
sm: "auto",
xs: "",
marginLeft: "auto",
[`@media (max-width: ${breakPoints.sm}px)`]: {
marginLeft: "",
},
}}
>
<Box>
<Box
sx={{
color: "#5E5E5E",
fontWeight: "bold",
fontSize: "14px",
}}
>
Used:
</Box>
<Box
sx={{
display: "flex",
"& .value": {
fontSize: "50px",
fontFamily: "Inter",
fontWeight: 600,
alignSelf: "flex-end",
lineHeight: 1,
},
"& .unit": {
color: "#5E5E5E",
fontWeight: "bold",
fontSize: "14px",
marginLeft: "12px",
alignSelf: "flex-end",
},
}}
>
<Box className={"usedLabel"}>Used:</Box>
<Box className={"totalUsed"}>
<div className="value">{usedConvert.total}</div>
<div className="unit">{usedConvert.unit}</div>
</Box>
<Box
sx={{
marginTop: "5px",
"& .value": {
color: "#5E5E5E",
fontWeight: "bold",
fontSize: "14px",
textAlign: "right",
},
}}
>
<Box className={"ofUsed"}>
<div className="value">Of: {niceBytesInt(totalUsable)}</div>
</Box>
</Box>
@@ -288,7 +277,7 @@ const CapacityItem = ({
</Box>
</Box>
</Box>
</Box>
</CapacityItemMain>
);
};

View File

@@ -15,7 +15,24 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import styled from "styled-components";
import get from "lodash/get";
import { Box, breakPoints } from "mds";
const DualSTCardContent = styled.div(({ theme }) => ({
fontFamily: "Inter,sans-serif",
color: get(theme, "signalColors.main", "#07193E"),
maxWidth: "321px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
"& .stat-text": {
color: get(theme, "mutedText", "#87888d"),
fontSize: "12px",
marginTop: "8px",
},
}));
const DualStatCard = ({
statItemLeft = null,
@@ -34,9 +51,9 @@ const DualStatCard = ({
sx={{
flex: 1,
display: "flex",
padding: {
sm: "0 8px 0 8px",
xs: "0 10px 0 10px",
padding: "0 8px 0 8px",
[`@media (max-width: ${breakPoints.sm}px)`]: {
padding: "0 10px 0 10px",
},
}}
>
@@ -60,26 +77,12 @@ const DualStatCard = ({
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
gap: 5,
justifyContent: "space-between",
paddingBottom: {
md: "0px",
xs: "10px",
},
fontSize: {
xl: "55px",
lg: "50px",
md: "45px",
xs: "35px",
},
paddingBottom: 0,
fontSize: 55,
flexFlow: "row",
fontWeight: 600,
"& .stat-text": {
color: "#696969",
fontSize: "12px",
marginTop: "8px",
},
"& .stat-value": {
textAlign: "center",
height: "50px",
@@ -90,6 +93,15 @@ const DualStatCard = ({
height: "10px",
width: "10px",
},
[`@media (max-width: ${breakPoints.sm}px)`]: {
fontSize: 35,
},
[`@media (max-width: ${breakPoints.lg}px)`]: {
fontSize: 45,
},
[`@media (max-width: ${breakPoints.xl}px)`]: {
fontSize: 50,
},
}}
>
{statItemLeft}
@@ -114,21 +126,7 @@ const DualStatCard = ({
);
};
return (
<Box
sx={{
fontFamily: "Inter,sans-serif",
color: "#07193E",
maxWidth: "321px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
}}
>
{getContent()}
</Box>
);
return <DualSTCardContent>{getContent()}</DualSTCardContent>;
};
export default DualStatCard;

View File

@@ -15,23 +15,45 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import styled from "styled-components";
import get from "lodash/get";
import { CircleIcon, DrivesIcon, ServersIcon, Box } from "mds";
import EntityStateStatItem from "./EntityStateStatItem";
import { Box } from "@mui/material";
import { CircleIcon, DrivesIcon, ServersIcon } from "mds";
import DualStatCard from "./DualStatCard";
import { IDashboardPanel } from "../types";
const StateIndicator = styled.div(({ theme }) => ({
display: "flex",
alignItems: "center",
marginTop: "5px",
gap: 8,
"&.online": {
"& .min-icon": {
margin: 0,
fill: get(theme, "signalColors.good", "#4CCB92"),
},
},
"&.offline": {
"& .min-icon": {
margin: 0,
fill: get(theme, "signalColors.danger", "#C51B3F"),
},
},
"& .indicatorText": {
color: get(theme, "mutedText", "#C51B3F"),
fontSize: 12,
},
}));
const EntityStateItemRenderer = ({
info,
timeStart,
timeEnd,
loading,
apiPrefix,
}: {
info: IDashboardPanel;
timeStart: any;
timeEnd: any;
loading: boolean;
apiPrefix: string;
}) => {
const { mergedPanels = [], id } = info;
@@ -42,22 +64,12 @@ const EntityStateItemRenderer = ({
panelItem={leftPanel}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
statLabel={
<Box
sx={{
display: "flex",
alignItems: "center",
marginTop: "5px",
"& .min-icon": {
fill: "#4CCB92",
},
}}
>
<StateIndicator className={"online"}>
<CircleIcon />
<div className="stat-text">Online</div>
</Box>
<Box className="indicatorText">Online</Box>
</StateIndicator>
}
/>
);
@@ -66,22 +78,12 @@ const EntityStateItemRenderer = ({
panelItem={rightPanel}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
statLabel={
<Box
sx={{
display: "flex",
alignItems: "center",
marginTop: "5px",
"& .min-icon": {
fill: "#C83B51",
},
}}
>
<StateIndicator className={"offline"}>
<CircleIcon />
<div className="stat-text">Offline</div>
</Box>
<Box className="indicatorText">Offline</Box>
</StateIndicator>
}
/>
);

View File

@@ -15,29 +15,25 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useEffect, useState } from "react";
import { Box } from "@mui/material";
import api from "../../../../../common/api";
import { Loader, Box } from "mds";
import { useSelector } from "react-redux";
import { widgetDetailsToPanel } from "../utils";
import { ErrorResponseHandler } from "../../../../../common/types";
import { IDashboardPanel } from "../types";
import { Loader } from "mds";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
import { useSelector } from "react-redux";
import api from "../../../../../common/api";
const EntityStateStatItem = ({
panelItem,
timeStart,
timeEnd,
propLoading,
apiPrefix,
statLabel,
}: {
panelItem: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
statLabel: any;
}) => {

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Box, breakPoints } from "mds";
import TimeStatItem from "../../TimeStatItem";
export type SimpleWidgetRenderProps = {
@@ -50,9 +50,9 @@ const HealActivityRenderer = ({
<Box>
<Box
sx={{
display: {
md: "inline",
xs: "none",
display: "inline",
[`@media (max-width: ${breakPoints.sm}px)`]: {
display: "none",
},
}}
>

View File

@@ -14,15 +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 { Box } from "@mui/material";
import { SxProps, Theme } from "@mui/material/styles";
import { Box } from "mds";
import { CSSObject } from "styled-components";
import { breakPoints } from "mds";
export type DLayoutColumnProps = {
componentId: number;
sx?: SxProps<Theme>;
sx?: CSSObject;
};
export type DLayoutRowProps = {
sx?: SxProps<Theme>;
sx?: CSSObject;
columns: DLayoutColumnProps[];
};
@@ -31,12 +32,14 @@ export const summaryPanelsLayout: DLayoutRowProps[] = [
sx: {
minWidth: 0,
display: "grid",
gridTemplateColumns: {
md: "1fr 1fr 1fr 1fr",
sm: "1fr 1fr",
xs: "1fr",
},
gap: "30px",
gridTemplateColumns: "1fr 1fr 1fr 1fr",
[`@media (max-width: ${breakPoints.sm}px)`]: {
gridTemplateColumns: "1fr",
},
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr 1fr",
},
},
columns: [
{
@@ -57,11 +60,11 @@ export const summaryPanelsLayout: DLayoutRowProps[] = [
sx: {
display: "grid",
minWidth: 0, // important to avoid css grid blow out.
gridTemplateColumns: {
md: "1fr 1fr",
xs: "1fr",
},
gap: "30px",
gridTemplateColumns: "1fr 1fr",
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr",
},
},
columns: [
{
@@ -76,11 +79,11 @@ export const summaryPanelsLayout: DLayoutRowProps[] = [
sx: {
display: "grid",
minWidth: 0,
gridTemplateColumns: {
md: "1fr 1fr 1fr",
xs: "1fr",
},
gap: "30px",
gridTemplateColumns: "1fr 1fr 1fr",
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr",
},
},
columns: [
{
@@ -98,11 +101,11 @@ export const summaryPanelsLayout: DLayoutRowProps[] = [
sx: {
display: "grid",
minWidth: 0,
gridTemplateColumns: {
sm: "1fr 1fr",
xs: "1fr",
},
gap: "30px",
gridTemplateColumns: "1fr 1fr",
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr",
},
},
columns: [
{
@@ -117,11 +120,11 @@ export const summaryPanelsLayout: DLayoutRowProps[] = [
sx: {
display: "grid",
minWidth: 0,
gridTemplateColumns: {
sm: "1fr 1fr",
xs: "1fr",
},
gap: "30px",
gridTemplateColumns: "1fr 1fr",
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr",
},
},
columns: [
{
@@ -151,11 +154,11 @@ export const trafficPanelsLayout: DLayoutRowProps[] = [
sx: {
display: "grid",
minWidth: 0,
gridTemplateColumns: {
sm: "1fr 1fr",
xs: "1fr",
},
gap: "30px",
gridTemplateColumns: "1fr 1fr",
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr",
},
},
columns: [
{

View File

@@ -15,6 +15,9 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useRef, useState } from "react";
import styled from "styled-components";
import get from "lodash/get";
import { useSelector } from "react-redux";
import {
Area,
AreaChart,
@@ -24,32 +27,24 @@ import {
XAxis,
YAxis,
} from "recharts";
import { Box, Grid, useMediaQuery } from "@mui/material";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { Box, breakPoints, Grid, Loader } from "mds";
import { ILinearGraphConfiguration } from "./types";
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
import { IDashboardPanel } from "../types";
import { widgetDetailsToPanel } from "../utils";
import { ErrorResponseHandler } from "../../../../../common/types";
import api from "../../../../../common/api";
import LineChartTooltip from "./tooltips/LineChartTooltip";
import { useTheme } from "@mui/styles";
import { Loader } from "mds";
import ExpandGraphLink from "./ExpandGraphLink";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
import api from "../../../../../common/api";
import LineChartTooltip from "./tooltips/LineChartTooltip";
import ExpandGraphLink from "./ExpandGraphLink";
import DownloadWidgetDataButton from "../../DownloadWidgetDataButton";
import { useSelector } from "react-redux";
interface ILinearGraphWidget {
classes: any;
title: string;
panelItem: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
hideYAxis?: boolean;
yAxisFormatter?: (item: string) => string;
@@ -58,43 +53,43 @@ interface ILinearGraphWidget {
zoomActivated?: boolean;
}
const styles = (theme: Theme) =>
createStyles({
...widgetCommon,
chartCont: {
position: "relative",
height: 140,
width: "100%",
const LinearGraphMain = styled.div(({ theme }) => ({
...widgetCommon(theme),
"& .chartCont": {
position: "relative",
height: 140,
width: "100%",
},
"& .legendChart": {
display: "flex",
flexDirection: "column",
flex: "0 1 auto",
maxHeight: 130,
margin: 0,
overflowY: "auto",
position: "relative",
textAlign: "center",
width: "100%",
justifyContent: "flex-start",
color: get(theme, "mutedText", "#87888d"),
fontWeight: "bold",
fontSize: 12,
[`@media (max-width: ${breakPoints.md}px)`]: {
display: "none",
},
legendChart: {
display: "flex",
flexDirection: "column",
flex: "0 1 auto",
maxHeight: 130,
margin: 0,
overflowY: "auto",
position: "relative",
textAlign: "center",
width: "100%",
justifyContent: "flex-start",
color: "#404143",
fontWeight: "bold",
fontSize: 12,
},
loadingAlign: {
width: 40,
height: 40,
textAlign: "center",
margin: "15px auto",
},
});
},
"& .loadingAlign": {
width: 40,
height: 40,
textAlign: "center",
margin: "15px auto",
},
}));
const LinearGraphWidget = ({
classes,
title,
timeStart,
timeEnd,
propLoading,
panelItem,
apiPrefix,
hideYAxis = false,
@@ -114,7 +109,7 @@ const LinearGraphWidget = ({
(state: AppState) => state.dashboard.widgetLoadVersion,
);
const componentRef = useRef<HTMLElement>();
const componentRef = useRef(null);
useEffect(() => {
setLoading(true);
@@ -208,9 +203,6 @@ const LinearGraphWidget = ({
return <circle cx={cx} cy={cy} r={3} strokeWidth={0} fill="#07264A" />;
};
const theme = useTheme();
const biggerThanMd = useMediaQuery(theme.breakpoints.up("md"));
let dspLongDate = false;
if (zoomActivated) {
@@ -218,185 +210,205 @@ const LinearGraphWidget = ({
}
return (
<Box
className={zoomActivated ? "" : classes.singleValueContainer}
onMouseOver={onHover}
onMouseLeave={onStopHover}
>
{!zoomActivated && (
<Grid container alignItems={"left"}>
<Grid item xs={10} alignItems={"start"}>
<div className={classes.titleContainer}>{title}</div>
</Grid>
<Grid
item
xs={1}
display={"flex"}
justifyContent={"flex-end"}
alignContent={"flex-end"}
>
{hover && <ExpandGraphLink panelItem={panelItem} />}
</Grid>
<Grid item xs={1} display={"flex"} justifyContent={"flex-end"}>
<DownloadWidgetDataButton
title={title}
componentRef={componentRef}
data={csvData}
/>
</Grid>
</Grid>
)}
<LinearGraphMain>
<Box
sx={
zoomActivated
? { flexDirection: "column" }
: {
height: "100%",
display: "grid",
gridTemplateColumns: {
md: "1fr 1fr",
sm: "1fr",
},
}
}
style={areaWidget ? { gridTemplateColumns: "1fr" } : {}}
ref={componentRef}
className={zoomActivated ? "" : "singleValueContainer"}
onMouseOver={onHover}
onMouseLeave={onStopHover}
>
{loading && <Loader className={classes.loadingAlign} />}
{!loading && (
<React.Fragment>
<div
className={
zoomActivated ? classes.zoomChartCont : classes.chartCont
}
{!zoomActivated && (
<Grid container>
<Grid item xs={10} sx={{ alignItems: "start" }}>
<Box className={"titleContainer"}>{title}</Box>
</Grid>
<Grid
item
xs={1}
sx={{
display: "flex",
justifyContent: "flex-end",
alignContent: "flex-end",
}}
>
<ResponsiveContainer width="99%">
<AreaChart
data={data}
margin={{
top: 5,
right: 20,
left: hideYAxis ? 20 : 5,
bottom: 0,
}}
>
{areaWidget && (
<defs>
<linearGradient id="colorUv" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor="#2781B0" stopOpacity={1} />
<stop
offset="100%"
stopColor="#ffffff"
stopOpacity={0}
/>
<stop
offset="95%"
stopColor="#ffffff"
stopOpacity={0.8}
/>
</linearGradient>
</defs>
)}
<CartesianGrid
strokeDasharray={areaWidget ? "2 2" : "5 5"}
strokeWidth={1}
strokeOpacity={1}
stroke={"#eee0e0"}
vertical={!areaWidget}
/>
<XAxis
dataKey="name"
tickFormatter={(value: any) =>
xAxisFormatter(value, dspLongDate, true)
}
interval={intervalCount}
tick={{
fontSize: "68%",
fontWeight: "normal",
color: "#404143",
}}
tickCount={10}
stroke={"#082045"}
/>
<YAxis
type={"number"}
domain={[0, dataMax * 1.1]}
hide={hideYAxis}
tickFormatter={(value: any) => yAxisFormatter(value)}
tick={{
fontSize: "68%",
fontWeight: "normal",
color: "#404143",
}}
stroke={"#082045"}
/>
{linearConfiguration.map((section, index) => {
return (
<Area
key={`area-${section.dataKey}-${index.toString()}`}
type="monotone"
dataKey={section.dataKey}
isAnimationActive={false}
stroke={!areaWidget ? section.lineColor : "#D7E5F8"}
fill={areaWidget ? "url(#colorUv)" : section.fillColor}
fillOpacity={areaWidget ? 0.65 : 0}
strokeWidth={!areaWidget ? 3 : 0}
strokeLinecap={"round"}
dot={areaWidget ? <CustomizedDot /> : false}
/>
);
})}
<Tooltip
content={
<LineChartTooltip
linearConfiguration={linearConfiguration}
yAxisFormatter={yAxisFormatter}
/>
}
wrapperStyle={{
zIndex: 5000,
}}
/>
</AreaChart>
</ResponsiveContainer>
</div>
{!areaWidget && (
{hover && <ExpandGraphLink panelItem={panelItem} />}
</Grid>
<Grid
item
xs={1}
sx={{ display: "flex", justifyContent: "flex-end" }}
>
{componentRef !== null && (
<DownloadWidgetDataButton
title={title}
componentRef={componentRef}
data={csvData}
/>
)}
</Grid>
</Grid>
)}
<div ref={componentRef}>
<Box
sx={
zoomActivated
? { flexDirection: "column" }
: {
height: "100%",
display: "grid",
gridTemplateColumns: "1fr 1fr",
[`@media (max-width: ${breakPoints.md}px)`]: {
gridTemplateColumns: "1fr",
},
}
}
style={areaWidget ? { gridTemplateColumns: "1fr" } : {}}
>
{loading && <Loader className={"loadingAlign"} />}
{!loading && (
<Fragment>
{zoomActivated && (
<Fragment>
<strong>Series</strong>
<br />
<br />
</Fragment>
)}
{biggerThanMd && (
<div className={classes.legendChart}>
{linearConfiguration.map((section, index) => {
return (
<div
className={classes.singleLegendContainer}
key={`legend-${section.keyLabel}-${index.toString()}`}
>
<div
className={classes.colorContainer}
style={{ backgroundColor: section.lineColor }}
<Box className={zoomActivated ? "zoomChartCont" : "chartCont"}>
<ResponsiveContainer width="99%">
<AreaChart
data={data}
margin={{
top: 5,
right: 20,
left: hideYAxis ? 20 : 5,
bottom: 0,
}}
>
{areaWidget && (
<defs>
<linearGradient
id="colorUv"
x1="0"
y1="0"
x2="0"
y2="1"
>
<stop
offset="0%"
stopColor="#2781B0"
stopOpacity={1}
/>
<stop
offset="100%"
stopColor="#ffffff"
stopOpacity={0}
/>
<stop
offset="95%"
stopColor="#ffffff"
stopOpacity={0.8}
/>
</linearGradient>
</defs>
)}
<CartesianGrid
strokeDasharray={areaWidget ? "2 2" : "5 5"}
strokeWidth={1}
strokeOpacity={1}
stroke={"#eee0e0"}
vertical={!areaWidget}
/>
<XAxis
dataKey="name"
tickFormatter={(value: any) =>
xAxisFormatter(value, dspLongDate, true)
}
interval={intervalCount}
tick={{
fontSize: "68%",
fontWeight: "normal",
color: "#404143",
}}
tickCount={10}
stroke={"#082045"}
/>
<YAxis
type={"number"}
domain={[0, dataMax * 1.1]}
hide={hideYAxis}
tickFormatter={(value: any) => yAxisFormatter(value)}
tick={{
fontSize: "68%",
fontWeight: "normal",
color: "#404143",
}}
stroke={"#082045"}
/>
{linearConfiguration.map((section, index) => {
return (
<Area
key={`area-${section.dataKey}-${index.toString()}`}
type="monotone"
dataKey={section.dataKey}
isAnimationActive={false}
stroke={!areaWidget ? section.lineColor : "#D7E5F8"}
fill={
areaWidget ? "url(#colorUv)" : section.fillColor
}
fillOpacity={areaWidget ? 0.65 : 0}
strokeWidth={!areaWidget ? 3 : 0}
strokeLinecap={"round"}
dot={areaWidget ? <CustomizedDot /> : false}
/>
<div className={classes.legendLabel}>
{section.keyLabel}
</div>
</div>
);
})}
</div>
);
})}
<Tooltip
content={
<LineChartTooltip
linearConfiguration={linearConfiguration}
yAxisFormatter={yAxisFormatter}
/>
}
wrapperStyle={{
zIndex: 5000,
}}
/>
</AreaChart>
</ResponsiveContainer>
</Box>
{!areaWidget && (
<Fragment>
{zoomActivated && (
<Fragment>
<strong>Series</strong>
<br />
<br />
</Fragment>
)}
<Box className={"legendChart"}>
{linearConfiguration.map((section, index) => {
return (
<Box
className={"singleLegendContainer"}
key={`legend-${
section.keyLabel
}-${index.toString()}`}
>
<Box
className={"colorContainer"}
style={{ backgroundColor: section.lineColor }}
/>
<Box className={"legendLabel"}>
{section.keyLabel}
</Box>
</Box>
);
})}
</Box>
</Fragment>
)}
</Fragment>
)}
</React.Fragment>
)}
</Box>
</div>
</Box>
</Box>
</LinearGraphMain>
);
};
export default withStyles(styles)(LinearGraphWidget);
export default LinearGraphWidget;

View File

@@ -16,8 +16,8 @@
import React from "react";
import { componentToUse } from "../widgetUtils";
import MergedWidgets from "../MergedWidgets";
import { IDashboardPanel } from "../types";
import MergedWidgets from "../MergedWidgets";
import EntityStateItemRenderer from "./EntityStateItemRenderer";
import NetworkItem from "./NetworkItem";
import DashboardItemBox from "../../DashboardItemBox";
@@ -46,7 +46,6 @@ const MergedWidgetsRenderer = ({
info={info}
timeStart={timeStart}
timeEnd={timeEnd}
loading={loading}
apiPrefix={apiPrefix}
/>
</DashboardItemBox>
@@ -61,7 +60,6 @@ const MergedWidgetsRenderer = ({
timeEnd={timeEnd}
timeStart={timeStart}
value={info}
propLoading={loading}
/>
</DashboardItemBox>
);

View File

@@ -15,8 +15,35 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Loader, NetworkGetIcon } from "mds";
import styled from "styled-components";
import get from "lodash/get";
import { Loader, NetworkGetIcon, Box } from "mds";
const NetworkGetBase = styled.div(({ theme }) => ({
"& .putLabel": {
display: "flex",
gap: 10,
alignItems: "center",
marginTop: "10px",
"& .min-icon": {
height: 15,
width: 15,
fill: get(theme, "signalColors.good", "#4CCB92"),
},
"& .getText": {
fontSize: "18px",
color: get(theme, "mutedText", "#87888d"),
fontWeight: "bold",
},
"& .valueText": {
fontSize: 50,
fontFamily: "Inter",
fontWeight: 600,
},
},
}));
const NetworkGetItem = ({
value,
@@ -28,44 +55,17 @@ const NetworkGetItem = ({
id?: number;
}) => {
return (
<Box>
<Box
sx={{
display: "flex",
alignItems: "center",
marginTop: "10px",
gap: "10px",
"& .min-icon": {
height: "15px",
width: "15px",
fill: "#4ccb92",
},
}}
>
<Box
sx={{
fontSize: "18px",
color: "#696969",
}}
>
GET
</Box>
<NetworkGetBase>
<Box className={"putLabel"}>
<Box className={"getText"}>GET</Box>
{loading ? (
<Loader style={{ width: "15px", height: "15px" }} />
) : (
<NetworkGetIcon />
)}
</Box>
<Box
sx={{
fontSize: "50px",
fontFamily: "Inter",
fontWeight: 600,
}}
>
{value}
</Box>
</Box>
<Box className={"valueText"}>{value}</Box>
</NetworkGetBase>
);
};

View File

@@ -15,24 +15,45 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import styled from "styled-components";
import get from "lodash/get";
import { Box, breakPoints, SpeedtestIcon } from "mds";
import { IDashboardPanel } from "../types";
import { Box } from "@mui/material";
import { SpeedtestIcon } from "mds";
import SingleValueWidget from "./SingleValueWidget";
import NetworkGetItem from "./NetworkGetItem";
import NetworkPutItem from "./NetworkPutItem";
const NetworkItemBase = styled.div(({ theme }) => ({
flex: 1,
display: "flex",
alignItems: "center",
flexFlow: "row",
gap: "15px",
"& .unitText": {
fontSize: "14px",
color: get(theme, "mutedText", "#87888d"),
marginLeft: "5px",
},
"& .unit": {
color: get(theme, "mutedText", "#87888d"),
fontSize: "18px",
marginLeft: "12px",
marginTop: "10px",
},
[`@media (max-width: ${breakPoints.sm}px)`]: {
flexFlow: "column",
},
}));
const NetworkItem = ({
value,
timeStart,
timeEnd,
propLoading,
apiPrefix,
}: {
value: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
}) => {
const { mergedPanels = [] } = value;
@@ -44,7 +65,6 @@ const NetworkItem = ({
panelItem={leftPanel}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={propLoading}
apiPrefix={apiPrefix}
renderFn={({ valueToRender, loading, title, id }) => {
return (
@@ -64,7 +84,6 @@ const NetworkItem = ({
panelItem={rightPanel}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={propLoading}
apiPrefix={apiPrefix}
renderFn={({ valueToRender, loading, title, id }) => {
return (
@@ -80,23 +99,7 @@ const NetworkItem = ({
);
return (
<Box
sx={{
flex: 1,
display: "flex",
alignItems: "center",
flexFlow: {
sm: "row",
xs: "column",
},
gap: "15px",
"& .unitText": {
fontSize: "14px",
color: "#5E5E5E",
marginLeft: "5px",
},
}}
>
<NetworkItemBase>
<Box
sx={{
fontSize: "16px",
@@ -110,9 +113,9 @@ const NetworkItem = ({
position: "relative",
width: 110,
height: 110,
marginLeft: {
sm: "auto",
xs: "",
marginLeft: "auto",
[`@media (max-width: ${breakPoints.sm}px)`]: {
marginLeft: "0",
},
}}
>
@@ -126,7 +129,6 @@ const NetworkItem = ({
left: "50%",
transform: "translate(-50%, -50%)",
fontWeight: "bold",
color: "#000",
fontSize: 12,
}}
>
@@ -137,9 +139,9 @@ const NetworkItem = ({
sx={{
display: "flex",
alignItems: "center",
marginLeft: {
sm: "auto",
xs: "",
marginLeft: "auto",
[`@media (max-width: ${breakPoints.sm}px)`]: {
marginLeft: "0",
},
}}
>
@@ -148,12 +150,6 @@ const NetworkItem = ({
display: "flex",
alignItems: "center",
"& .value": { fontSize: "50px", fontFamily: "Inter" },
"& .unit": {
color: "#5E5E5E",
fontSize: "18px",
marginLeft: "12px",
marginTop: "10px",
},
}}
>
{rightCmp}
@@ -173,7 +169,7 @@ const NetworkItem = ({
>
<SpeedtestIcon />
</Box>
</Box>
</NetworkItemBase>
);
};

View File

@@ -15,8 +15,35 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Loader, NetworkPutIcon } from "mds";
import styled from "styled-components";
import get from "lodash/get";
import { Box, Loader, NetworkPutIcon } from "mds";
const NetworkPutBase = styled.div(({ theme }) => ({
"& .putLabel": {
display: "flex",
gap: 10,
alignItems: "center",
marginTop: "10px",
"& .min-icon": {
height: 15,
width: 15,
fill: get(theme, "signalColors.info", "#2781B0"),
},
"& .putText": {
fontSize: "18px",
color: get(theme, "mutedText", "#87888d"),
fontWeight: "bold",
},
"& .valueText": {
fontSize: 50,
fontFamily: "Inter",
fontWeight: 600,
},
},
}));
const NetworkPutItem = ({
value,
@@ -28,46 +55,17 @@ const NetworkPutItem = ({
id?: number;
}) => {
return (
<Box>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
marginTop: "10px",
"& .min-icon": {
height: "15px",
width: "15px",
fill: "#2781b0",
},
}}
>
<Box
sx={{
fontSize: "18px",
color: "#696969",
fontWeight: "normal",
}}
>
PUT
</Box>
<NetworkPutBase>
<Box className={"putLabel"}>
<Box className={"putText"}>PUT</Box>
{loading ? (
<Loader style={{ width: "15px", height: "15px" }} />
) : (
<NetworkPutIcon />
)}
</Box>
<Box
sx={{
fontSize: "50px",
fontFamily: "Inter",
fontWeight: 600,
}}
>
{value}
</Box>
</Box>
<Box className={"valueText"}>{value}</Box>
</NetworkPutBase>
);
};

View File

@@ -15,8 +15,21 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box, Tooltip } from "@mui/material";
import { Loader } from "mds";
import styled from "styled-components";
import get from "lodash/get";
import { Box, breakPoints, Loader, Tooltip } from "mds";
const StatCardMain = styled.div(({ theme }) => ({
fontFamily: "Inter,sans-serif",
color: get(theme, "signalColors.main", "#07193E"),
maxWidth: "300px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
position: "relative",
width: "100%",
}));
const NumericStatCard = ({
value,
@@ -36,9 +49,9 @@ const NumericStatCard = ({
flex: 1,
display: "flex",
width: "100%",
padding: {
sm: "0 8px 0 8px",
xs: "0 10px 0 10px",
padding: "0 8px 0 8px",
[`@media (max-width: ${breakPoints.sm}px)`]: {
padding: "0 10px 0 10px",
},
}}
>
@@ -61,26 +74,28 @@ const NumericStatCard = ({
{label}
</Box>
<Tooltip title={value} placement="bottom" enterDelay={500}>
<Tooltip tooltip={value} placement="bottom">
<Box
sx={{
fontSize: {
xl: "55px",
lg: "50px",
md: "36px",
sm: "35px",
xs: "35px",
},
fontWeight: 600,
overflow: "hidden",
textOverflow: "ellipsis",
maxWidth: {
md: 187,
xs: 200,
maxWidth: 187,
flexFlow: "row",
fontSize: 55,
[`@media (max-width: ${breakPoints.sm}px)`]: {
fontSize: 35,
maxWidth: 200,
flexFlow: "column",
},
flexFlow: {
md: "row",
xs: "column",
[`@media (max-width: ${breakPoints.md}px)`]: {
fontSize: 35,
},
[`@media (max-width: ${breakPoints.lg}px)`]: {
fontSize: 36,
},
[`@media (max-width: ${breakPoints.xl}px)`]: {
fontSize: 50,
},
}}
>
@@ -113,23 +128,7 @@ const NumericStatCard = ({
);
};
return (
<Box
sx={{
fontFamily: "Inter,sans-serif",
color: "#07193E",
maxWidth: "300px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
position: "relative",
width: "100%",
}}
>
{getContent()}
</Box>
);
return <StatCardMain>{getContent()}</StatCardMain>;
};
export default NumericStatCard;

View File

@@ -15,68 +15,57 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { useEffect, useState } from "react";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import get from "lodash/get";
import styled from "styled-components";
import { Box, Loader } from "mds";
import { Cell, Pie, PieChart, ResponsiveContainer } from "recharts";
import { useSelector } from "react-redux";
import api from "../../../../../common/api";
import { IPieChartConfiguration } from "./types";
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
import { IDashboardPanel } from "../types";
import { splitSizeMetric, widgetDetailsToPanel } from "../utils";
import { ErrorResponseHandler } from "../../../../../common/types";
import get from "lodash/get";
import api from "../../../../../common/api";
import { Loader } from "mds";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
import { useSelector } from "react-redux";
interface IPieChartWidget {
classes: any;
title: string;
panelItem: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
}
const styles = (theme: Theme) =>
createStyles({
...widgetCommon,
loadingAlign: {
width: "100%",
paddingTop: "15px",
textAlign: "center",
margin: "auto",
const PieChartMain = styled.div(({ theme }) => ({
...widgetCommon(theme),
"& .loadingAlign": {
width: "100%",
paddingTop: "15px",
textAlign: "center",
margin: "auto",
},
"& .pieChartLabel": {
fontSize: 60,
color: get(theme, "signalColors.main", "#07193E"),
fontWeight: "bold",
width: "100%",
"& .unitText": {
color: get(theme, "mutedText", "#87888d"),
fontSize: 12,
},
pieChartLabel: {
fontSize: 60,
color: "#07193E",
fontWeight: "bold",
width: "100%",
"& .unitText": {
color: "#767676",
fontSize: 12,
},
},
chartContainer: {
width: "100%",
height: 140,
},
});
},
"& .chartContainer": {
width: "100%",
height: 140,
},
}));
const PieChartWidget = ({
classes,
title,
panelItem,
timeStart,
timeEnd,
propLoading,
apiPrefix,
}: IPieChartWidget) => {
const dispatch = useAppDispatch();
@@ -137,110 +126,112 @@ const PieChartWidget = ({
const outerColors = get(pieChartConfiguration, "outerChart.colorList", []);
return (
<div className={classes.singleValueContainer}>
<div className={classes.titleContainer}>{title}</div>
{loading && (
<div className={classes.loadingAlign}>
<Loader />
</div>
)}
{!loading && (
<div className={classes.contentContainer}>
<span className={classes.pieChartLabel}>
{middleLabel && splitSizeMetric(middleLabel)}
</span>
<div className={classes.chartContainer}>
<ResponsiveContainer width="99%">
<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 (
<PieChartMain>
<Box className={"singleValueContainer"}>
<Box className={"titleContainer"}>{title}</Box>
{loading && (
<Box className={"loadingAlign"}>
<Loader />
</Box>
)}
{!loading && (
<Box className={"contentContainer"}>
<span className={"pieChartLabel"}>
{middleLabel && splitSizeMetric(middleLabel)}
</span>
<Box className={"chartContainer"}>
<ResponsiveContainer width="99%">
<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>
)}
</PieChart>
</ResponsiveContainer>
</div>
</div>
)}
</div>
))}
</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>
</Box>
</Box>
)}
</Box>
</PieChartMain>
);
};
export default withStyles(styles)(PieChartWidget);
export default PieChartWidget;

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Box, breakPoints } from "mds";
import TimeStatItem from "../../TimeStatItem";
import { SimpleWidgetRenderProps } from "./HealActivityRenderer";
@@ -44,9 +44,9 @@ const ScanActivityRenderer = ({
<Box>
<Box
sx={{
display: {
md: "inline",
xs: "none",
display: "inline",
[`@media (max-width: ${breakPoints.sm}px)`]: {
display: "none",
},
}}
>

View File

@@ -15,63 +15,53 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect, useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import styled from "styled-components";
import get from "lodash/get";
import { useSelector } from "react-redux";
import { Loader } from "mds";
import api from "../../../../../common/api";
import { widgetDetailsToPanel } from "../utils";
import { IDashboardPanel } from "../types";
import { ErrorResponseHandler } from "../../../../../common/types";
import { Loader } from "mds";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
interface ISimpleWidget {
classes: any;
iconWidget: any;
title: string;
panelItem: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
apiPrefix: string;
renderFn?: undefined | null | ((arg: Record<string, any>) => any);
}
const styles = (theme: Theme) =>
createStyles({
mainWidgetContainer: {
display: "inline-flex",
color: "#072A4D",
alignItems: "center",
},
icon: {
color: "#072A4D",
fill: "#072A4D",
marginRight: 5,
marginLeft: 12,
},
widgetLabel: {
fontWeight: "bold",
textTransform: "uppercase",
marginRight: 10,
},
widgetValue: {
marginRight: 25,
},
});
const SimpleWidgetMain = styled.span(({ theme }) => ({
display: "inline-flex",
color: get(theme, "signalColors.main", "#07193E"),
alignItems: "center",
"& .icon": {
color: get(theme, "signalColors.main", "#07193E"),
fill: get(theme, "signalColors.main", "#07193E"),
marginRight: 5,
marginLeft: 12,
},
"& .widgetLabel": {
fontWeight: "bold",
textTransform: "uppercase",
marginRight: 10,
},
"& .widgetValue": {
marginRight: 25,
},
}));
const SimpleWidget = ({
classes,
iconWidget,
title,
panelItem,
timeStart,
timeEnd,
propLoading,
apiPrefix,
renderFn,
}: ISimpleWidget) => {
@@ -132,23 +122,19 @@ const SimpleWidget = ({
return (
<Fragment>
{loading && (
<div className={classes.loadingAlign}>
<div className={"loadingAlign"}>
<Loader />
</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>
<SimpleWidgetMain>
<span className={"icon"}>{iconWidget ? iconWidget : null}</span>
<span className={"widgetLabel"}>{title}: </span>
<span className={"widgetValue"}>{data}</span>
</SimpleWidgetMain>
)}
</Fragment>
);
};
const connector = connect(null, {
setErrorSnackMessage: setErrorSnackMessage,
});
export default withStyles(styles)(connector(SimpleWidget));
export default SimpleWidget;

View File

@@ -15,73 +15,65 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useEffect, useState } from "react";
import { connect, useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import api from "../../../../../common/api";
import { Loader } from "mds";
import { useSelector } from "react-redux";
import { Box, Loader } from "mds";
import styled from "styled-components";
import get from "lodash/get";
import { widgetCommon } from "../../../Common/FormComponents/common/styleLibrary";
import { splitSizeMetric, widgetDetailsToPanel } from "../utils";
import { IDashboardPanel } from "../types";
import { ErrorResponseHandler } from "../../../../../common/types";
import { setErrorSnackMessage } from "../../../../../systemSlice";
import { AppState, useAppDispatch } from "../../../../../store";
import api from "../../../../../common/api";
interface ISingleValueWidget {
title: string;
panelItem: IDashboardPanel;
timeStart: any;
timeEnd: any;
propLoading: boolean;
classes: any;
apiPrefix: string;
renderFn?: (arg: Record<string, any>) => any;
}
const styles = (theme: Theme) =>
createStyles({
...widgetCommon,
loadingAlign: {
width: "100%",
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 SingleValueWidgetMain = styled.div(({ theme }) => ({
display: "flex",
height: 140,
flexDirection: "column",
justifyContent: "center",
"& .unitText": {
color: get(theme, "mutedText", "#87888d"),
fontSize: 12,
},
"& .loadingAlign": {
width: "100%",
textAlign: "center",
margin: "auto",
},
"& .metric": {
fontSize: 60,
lineHeight: 1,
color: get(theme, "signalColors.main", "#07193E"),
fontWeight: 700,
},
"& .titleElement": {
fontSize: 10,
color: get(theme, "mutedText", "#87888d"),
fontWeight: 700,
},
...widgetCommon(theme),
}));
const SingleValueWidget = ({
title,
panelItem,
timeStart,
timeEnd,
propLoading,
classes,
apiPrefix,
renderFn,
}: ISingleValueWidget) => {
const dispatch = useAppDispatch();
const [loading, setLoading] = useState<boolean>(false);
const [data, setData] = useState<string>("");
const widgetVersion = useSelector(
@@ -132,24 +124,20 @@ const SingleValueWidget = ({
return renderFn({ valueToRender, loading, title, id: panelItem.id });
}
return (
<div className={classes.containerAlignment}>
<SingleValueWidgetMain>
{loading && (
<div className={classes.loadingAlign}>
<Box className={"loadingAlign"}>
<Loader />
</div>
</Box>
)}
{!loading && (
<Fragment>
<div className={classes.metric}>{splitSizeMetric(data)}</div>
<div className={classes.titleElement}>{title}</div>
<Box className={"metric"}>{splitSizeMetric(data)}</Box>
<Box className={"titleElement"}>{title}</Box>
</Fragment>
)}
</div>
</SingleValueWidgetMain>
);
};
const connector = connect(null, {
setErrorSnackMessage: setErrorSnackMessage,
});
export default withStyles(styles)(connector(SingleValueWidget));
export default SingleValueWidget;

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Box } from "mds";
import TimeStatItem from "../../TimeStatItem";
export type SimpleWidgetRenderProps = {
@@ -34,8 +34,8 @@ const UptimeActivityRenderer = ({
<Box
sx={{
display: "flex",
height: "47px",
borderRadius: "2px",
height: 47,
borderRadius: 2,
"& .dashboard-time-stat-item": {
height: "100%",

View File

@@ -46,7 +46,6 @@ export const componentToUse = (
panelItem={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
/>
);
@@ -86,7 +85,6 @@ export const componentToUse = (
panelItem={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
iconWidget={value.widgetIcon}
renderFn={renderFn}
@@ -100,7 +98,6 @@ export const componentToUse = (
value={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
/>
</DashboardItemBox>
@@ -112,7 +109,6 @@ export const componentToUse = (
panelItem={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
/>
);
@@ -124,7 +120,6 @@ export const componentToUse = (
panelItem={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
hideYAxis={value.disableYAxis}
xAxisFormatter={value.xAxisFormatter}
yAxisFormatter={value.yAxisFormatter}
@@ -140,7 +135,6 @@ export const componentToUse = (
panelItem={value}
timeStart={timeStart}
timeEnd={timeEnd}
propLoading={loading}
apiPrefix={apiPrefix}
zoomActivated={zoomActivated}
/>

View File

@@ -15,8 +15,39 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import { Box } from "@mui/material";
import { Loader, SuccessIcon } from "mds";
import styled from "styled-components";
import get from "lodash/get";
import { Box, Loader, SuccessIcon } from "mds";
const TimeStatBase = styled.div(({ theme }) => ({
display: "grid",
alignItems: "center",
gap: 8,
height: 33,
paddingLeft: 15,
gridTemplateColumns: "20px 1.5fr .5fr 20px",
background: get(theme, "boxBackground", "#FBFAFA"), // #EBF9EE
"& .min-icon": {
height: "12px",
width: "12px",
fill: get(theme, "signalColors.good", "#4CCB92"),
},
"& .ok-icon": {
height: "8px",
width: "8px",
fill: get(theme, "signalColors.good", "#4CCB92"),
color: get(theme, "signalColors.good", "#4CCB92"),
},
"& .timeStatLabel": {
fontSize: "12px",
color: get(theme, "signalColors.good", "#4CCB92"),
fontWeight: 600,
},
"& .timeStatValue": {
fontSize: "12px",
color: get(theme, "signalColors.good", "#4CCB92"),
},
}));
const TimeStatItem = ({
icon,
@@ -30,46 +61,12 @@ const TimeStatItem = ({
loading?: boolean;
}) => {
return (
<Box
sx={{
display: "grid",
alignItems: "center",
gap: "8px",
height: "33px",
paddingLeft: "15px",
gridTemplateColumns: {
xs: "20px 1.5fr .5fr 20px",
},
background: "#EBF9EE",
"& .min-icon": {
height: "12px",
width: "12px",
fill: "#4CCB92",
},
"& .ok-icon": {
height: "8px",
width: "8px",
fill: "#4CCB92",
color: "#4CCB92",
},
}}
className="dashboard-time-stat-item"
>
<TimeStatBase className="dashboard-time-stat-item">
{loading ? <Loader style={{ width: 10, height: 10 }} /> : icon}
<Box
sx={{
fontSize: "12px",
color: "#4CCB92",
fontWeight: 600,
}}
>
{label}
</Box>
<Box sx={{ fontSize: "12px", color: "#4CCB92" }}>{value}</Box>
<Box className={"timeStatLabel"}>{label}</Box>
<Box className={"timeStatValue"}>{value}</Box>
{value !== "n/a" ? <SuccessIcon className="ok-icon" /> : null}
</Box>
</TimeStatBase>
);
};

View File

@@ -15,21 +15,11 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { Fragment, useState } from "react";
import SearchBox from "../Common/SearchBox";
import { Theme } from "@mui/material/styles";
import { searchField } from "../Common/FormComponents/common/styleLibrary";
import { DisabledIcon, EnabledIcon, Box, Grid } from "mds";
import SearchBox from "../Common/SearchBox";
import { STATUS_COLORS } from "../Dashboard/BasicDashboard/Utils";
import makeStyles from "@mui/styles/makeStyles";
import { IAMStatement } from "./types";
const useStyles = makeStyles((theme: Theme) => ({
searchField: {
...searchField.searchField,
maxWidth: 380,
},
}));
const rowGridStyle = {
display: "grid",
gridTemplateColumns: "70px 1fr",
@@ -57,8 +47,6 @@ const PolicyView = ({
}: {
policyStatements: IAMStatement[];
}) => {
const classes = useStyles();
const [filter, setFilter] = useState<string>("");
return (
@@ -77,8 +65,10 @@ const PolicyView = ({
<SearchBox
placeholder={"Search"}
onChange={setFilter}
overrideClass={classes.searchField}
value={filter}
sx={{
maxWidth: 380,
}}
/>
</Grid>
</Grid>