UX basic dashboard (#1866)
This commit is contained in:
committed by
GitHub
parent
f36c07aa68
commit
46151a5e55
41
portal-ui/src/icons/SuccessIcon.tsx
Normal file
41
portal-ui/src/icons/SuccessIcon.tsx
Normal 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;
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -23,6 +23,10 @@ export interface Usage {
|
||||
prometheusNotReady?: boolean;
|
||||
widgets?: any;
|
||||
servers: ServerInfo[];
|
||||
//TODO
|
||||
lastScan: any;
|
||||
lastHeal: any;
|
||||
upTime: any;
|
||||
}
|
||||
|
||||
export interface ServerInfo {
|
||||
|
||||
Reference in New Issue
Block a user