UX basic dashboard (#1866)

This commit is contained in:
Prakash Senthil Vel
2022-04-19 15:16:26 +00:00
committed by GitHub
parent f36c07aa68
commit 46151a5e55
8 changed files with 297 additions and 42 deletions

View File

@@ -0,0 +1,41 @@
// This file is part of MinIO Console Server
// Copyright (c) 2022 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, { SVGProps } from "react";
const SuccessIcon = (props: SVGProps<SVGSVGElement>) => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
className={`min-icon`}
fill={"currentcolor"}
viewBox="0 0 10.155 8.367"
{...props}
>
<path
id="Intersección_8"
data-name="Intersección 8"
d="M14368.751,22047.6a1.045,1.045,0,1,1,1.467-1.488l1.411,1.395,3.98-3.918h0c.008-.01.017-.018.025-.027a1.048,1.048,0,0,1,1.451,1.514l-5.456,5.361Z"
transform="translate(-14367.849 -22042.768)"
fill={"currentcolor"}
stroke="rgba(0,0,0,0)"
strokeWidth="1"
/>
</svg>
);
};
export default SuccessIcon;

View File

@@ -183,3 +183,4 @@ export { default as BackIcon } from "./BackIcon";
export { default as DeleteNonCurrentIcon } from "./DeleteNonCurrentIcon";
export { default as FilterIcon } from "./FilterIcon";
export { default as EditTenantIcon } from "./EditTenantIcon";
export { default as SuccessIcon } from "./SuccessIcon";

View File

@@ -1105,6 +1105,11 @@ const IconsScreen = ({ classes }: IIconsScreenSimple) => {
<br />
FilterIcon
</Grid>
<Grid item xs={3} sm={2} md={1}>
<cicons.SuccessIcon />
<br />
SuccessIcon
</Grid>
</Grid>
<h1>Menu Icons</h1>
<Grid

View File

@@ -17,11 +17,15 @@
import React, { Fragment } from "react";
import { Box } from "@mui/material";
import {
ArrowRightIcon,
BucketsIcon,
DrivesIcon,
HealIcon,
PrometheusErrorIcon,
ServersIcon,
SuccessIcon,
TotalObjectsIcon,
UptimeIcon,
} from "../../../../icons";
import HelpBox from "../../../../common/HelpBox";
import { calculateBytes, representationNumber } from "../../../../common/utils";
@@ -31,13 +35,21 @@ import groupBy from "lodash/groupBy";
import ServersList from "./ServersList";
import CounterCard from "./CounterCard";
import ReportedUsage from "./ReportedUsage";
import { DiagnosticsMenuIcon } from "../../../../icons/SidebarMenus";
import RBIconButton from "../../Buckets/BucketDetails/SummaryItems/RBIconButton";
import { Link } from "react-router-dom";
import { IAM_PAGES } from "../../../../common/SecureComponent/permissions";
const BoxItem = ({ children }: { children: any }) => {
return (
<Box
sx={{
border: "1px solid #f1f1f1",
padding: "25px",
padding: {
md: "15px",
xs: "5px",
},
height: "136px",
maxWidth: {
sm: "100%",
},
@@ -48,6 +60,63 @@ const BoxItem = ({ children }: { children: any }) => {
);
};
const TimeStatItem = ({
icon,
label,
value,
}: {
icon: any;
label: any;
value: string;
}) => {
return (
<Box
sx={{
display: "grid",
alignItems: "center",
gap: "8px",
height: "33px",
paddingLeft: "15px",
gridTemplateColumns: {
md: "20px 1.5fr .5fr 20px",
xs: "20px 1fr 1fr",
},
background: "#EBF9EE",
"& .min-icon": {
height: "12px",
width: "12px",
fill: "#4CCB92",
},
"& .ok-icon": {
height: "8px",
width: "8px",
fill: "#4CCB92",
color: "#4CCB92",
display: {
md: "block",
xs: "none",
},
},
}}
>
{icon}
<Box
sx={{
fontSize: "12px",
color: "#4CCB92",
fontWeight: 600,
}}
>
{label}
</Box>
<Box sx={{ fontSize: "12px", color: "#4CCB92" }}>{value}</Box>
{value !== "n/a" ? <SuccessIcon className="ok-icon" /> : null}
</Box>
);
};
interface IDashboardProps {
usage: Usage | null;
}
@@ -82,6 +151,8 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
const usageValue = usage && usage.usage ? usage.usage.toString() : "0";
const usageToRepresent = prettyUsage(usageValue);
const { lastScan = "n/a", lastHeal = "n/a", upTime = "n/a" } = usage || {};
const serverList = getServersList(usage || null);
let allDrivesArray: IDriveInfo[] = [];
@@ -180,7 +251,6 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
<Box
sx={{
display: "grid",
gridTemplateRows: "1fr .2fr auto",
gridTemplateColumns: "1fr",
gap: "40px",
}}
@@ -188,13 +258,15 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
<Box
sx={{
display: "grid",
gridTemplateRows: "1fr",
gridTemplateRows: "136px",
gridTemplateColumns: {
lg: "1fr 1fr 1fr 1fr ",
sm: "1fr 1fr",
sm: "1fr 1fr 1fr",
xs: "1fr",
},
gap: "40px",
gap: {
md: "40px",
xs: "20px",
},
}}
>
<BoxItem>
@@ -202,6 +274,26 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
label={"Buckets"}
icon={<BucketsIcon />}
counterValue={usage ? representationNumber(usage.buckets) : 0}
actions={
<Link
to={IAM_PAGES.BUCKETS}
style={{
textDecoration: "none",
top: "40px",
position: "relative",
marginRight: "75px",
}}
>
<RBIconButton
tooltip={"Browse"}
onClick={() => {}}
text={"Browse"}
icon={<ArrowRightIcon />}
color={"primary"}
variant={"outlined"}
/>
</Link>
}
/>
</BoxItem>
<BoxItem>
@@ -211,6 +303,7 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
counterValue={usage ? representationNumber(usage.objects) : 0}
/>
</BoxItem>
<BoxItem>
<StatusCountCard
onlineCount={onlineServers.length}
@@ -227,15 +320,78 @@ const BasicDashboard = ({ usage }: IDashboardProps) => {
icon={<DrivesIcon />}
/>
</BoxItem>
<Box
sx={{
gridRowStart: "1",
gridRowEnd: "3",
gridColumnStart: "3",
border: "1px solid #f1f1f1",
padding: "15px",
display: "grid",
justifyContent: "stretch",
}}
>
<ReportedUsage
usageValue={usageValue}
total={usageToRepresent.total}
unit={usageToRepresent.unit}
/>
<Box
sx={{
display: "flex",
flexFlow: "column",
gap: "14px",
}}
>
<TimeStatItem
icon={<HealIcon />}
label={
<Box>
<Box
sx={{
display: {
md: "inline",
xs: "none",
},
}}
>
Time since last
</Box>{" "}
Heal Activity
</Box>
}
value={lastHeal}
/>
<TimeStatItem
icon={<DiagnosticsMenuIcon />}
label={
<Box>
<Box
sx={{
display: {
md: "inline",
xs: "none",
},
}}
>
Time since last
</Box>{" "}
Scan Activity
</Box>
}
value={lastScan}
/>
<TimeStatItem
icon={<UptimeIcon />}
label={"Uptime"}
value={upTime}
/>
</Box>
</Box>
</Box>
<BoxItem>
<ReportedUsage
usageValue={usageValue}
total={usageToRepresent.total}
unit={usageToRepresent.unit}
/>
</BoxItem>
<Box
sx={{
display: "grid",

View File

@@ -21,10 +21,12 @@ const CounterCard = ({
counterValue,
label = "",
icon = null,
actions = null,
}: {
counterValue: string | number;
label?: any;
icon?: any;
actions?: any;
}) => {
return (
<Box
@@ -32,20 +34,17 @@ const CounterCard = ({
fontFamily: "Lato,sans-serif",
color: "#07193E",
maxWidth: "300px",
minHeight: "143px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
cursor: "default",
position: "relative",
width: "100%",
//marginLeft: "25px",
}}
>
<Box
sx={{
flex: 1,
minHeight: "200px",
display: "flex",
width: "100%",
padding: {
@@ -60,7 +59,7 @@ const CounterCard = ({
flex: 1,
display: "flex",
flexFlow: "column",
marginTop: "22px",
marginTop: "8px",
zIndex: 10,
overflow: "hidden",
}}
@@ -103,9 +102,11 @@ const CounterCard = ({
</Box>
<Box
sx={{
width: "20px",
height: "20px",
marginTop: "26px",
display: "flex",
flexFlow: "column",
alignItems: "center",
justifyContent: "flex-start",
marginTop: "8px",
maxWidth: "26px",
"& .min-icon": {
width: "16px",
@@ -114,6 +115,8 @@ const CounterCard = ({
}}
>
{icon}
<Box>{actions}</Box>
</Box>
</Box>
</Box>

View File

@@ -14,9 +14,9 @@
// 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 { ReportedUsageIcon } from "../../../../icons";
import { Box, Tooltip } from "@mui/material";
import React from "react";
import { Cell, Pie, PieChart } from "recharts";
const ReportedUsage = ({
usageValue,
@@ -27,13 +27,22 @@ const ReportedUsage = ({
total: number | string;
unit: string;
}) => {
const plotValues = [
{ value: total, color: "#D6D6D6", label: "Free Space" },
{
value: usageValue,
color: "#073052",
label: "Used Space",
},
];
return (
<Box
sx={{
maxHeight: "110px",
display: "flex",
alignItems: "center",
justifyContent: "center",
justifyContent: "space-between",
fontSize: "19px",
padding: "10px",
@@ -63,21 +72,56 @@ const ReportedUsage = ({
},
}}
>
<div className="usage-label">
<span>Reported Usage</span> <ReportedUsageIcon />
</div>
<Box>
<div className="usage-label">
<span>Reported Usage</span>
</div>
<Tooltip title={`${usageValue} Bytes`}>
<label
className={"unit-value"}
style={{
fontWeight: 600,
}}
>
{total}
</label>
</Tooltip>
<label className={"unit-type"}>{unit}</label>
<Tooltip title={`${usageValue} Bytes`}>
<label
className={"unit-value"}
style={{
fontWeight: 600,
}}
>
{total}
</label>
</Tooltip>
<label className={"unit-type"}>{unit}</label>
</Box>
<Box>
<Box sx={{ flex: 1 }}>
<div
style={{
position: "relative",
width: 105,
height: 105,
top: "-8px",
}}
>
<div>
<PieChart width={105} height={105}>
<Pie
data={plotValues}
cx={"50%"}
cy={"50%"}
dataKey="value"
outerRadius={45}
innerRadius={35}
startAngle={-70}
endAngle={360}
animationDuration={1}
>
{plotValues.map((entry, index) => (
<Cell key={`cellCapacity-${index}`} fill={entry.color} />
))}
</Pie>
</PieChart>
</div>
</div>
</Box>
</Box>
</Box>
);
};

View File

@@ -38,8 +38,7 @@ export const StatusCountCard = ({
sx={{
fontFamily: "Lato,sans-serif",
color: "#07193E",
maxWidth: "260px",
minHeight: "143px",
maxWidth: "321px",
display: "flex",
marginLeft: "auto",
marginRight: "auto",
@@ -61,7 +60,6 @@ export const StatusCountCard = ({
flex: 1,
display: "flex",
flexFlow: "column",
marginTop: "22px",
}}
>
<Box
@@ -77,6 +75,7 @@ export const StatusCountCard = ({
sx={{
display: "flex",
alignItems: "center",
gap: "5px",
justifyContent: "space-between",
paddingBottom: {
md: "0px",
@@ -94,7 +93,7 @@ export const StatusCountCard = ({
"& .stat-text": {
color: "#696969",
fontSize: "12px",
marginTop: "25px",
marginTop: "8px",
},
"& .stat-value": {
textAlign: "center",
@@ -102,7 +101,7 @@ export const StatusCountCard = ({
},
"& .min-icon": {
marginRight: "8px",
marginTop: "25px",
marginTop: "8px",
height: "10px",
width: "10px",
},
@@ -114,6 +113,7 @@ export const StatusCountCard = ({
sx={{
display: "flex",
alignItems: "center",
marginTop: "5px",
"& .min-icon": {
fill: "#4CCB92",
},
@@ -129,6 +129,7 @@ export const StatusCountCard = ({
sx={{
display: "flex",
alignItems: "center",
marginTop: "8px",
"& .min-icon": {
fill: "#C83B51",
},
@@ -144,7 +145,7 @@ export const StatusCountCard = ({
sx={{
width: "20px",
height: "20px",
marginTop: "26px",
marginTop: "8px",
maxWidth: "26px",
"& .min-icon": {
width: "16px",

View File

@@ -23,6 +23,10 @@ export interface Usage {
prometheusNotReady?: boolean;
widgets?: any;
servers: ServerInfo[];
//TODO
lastScan: any;
lastHeal: any;
upTime: any;
}
export interface ServerInfo {