UI: Dashboard (#24)
* UI: Dashboard * h1 MinIO Console * UI: Dashboard * h1 MinIO Console * Formatting * paramter fix Co-authored-by: Lenin Alevski <alevsk.8772@gmail.com>
This commit is contained in:
@@ -126,9 +126,13 @@ class AddEvent extends React.Component<IAddEventProps, IAddEventState> {
|
|||||||
api
|
api
|
||||||
.invoke("GET", `/api/v1/admin/arns`)
|
.invoke("GET", `/api/v1/admin/arns`)
|
||||||
.then((res: ArnList) => {
|
.then((res: ArnList) => {
|
||||||
|
let arns: string[] = [];
|
||||||
|
if (res.arns !== null) {
|
||||||
|
arns = res.arns;
|
||||||
|
}
|
||||||
this.setState({
|
this.setState({
|
||||||
addLoading: false,
|
addLoading: false,
|
||||||
arnList: res.arns,
|
arnList: arns,
|
||||||
addError: ""
|
addError: ""
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -14,232 +14,210 @@
|
|||||||
// You should have received a copy of the GNU Affero General Public License
|
// 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/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { makeStyles, useTheme } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import {
|
import NetworkCheckIcon from "@material-ui/icons/NetworkCheck";
|
||||||
CartesianGrid,
|
import PieChartIcon from "@material-ui/icons/PieChart";
|
||||||
Line,
|
import ViewHeadlineIcon from "@material-ui/icons/ViewHeadline";
|
||||||
LineChart,
|
import { Usage } from "./types";
|
||||||
ResponsiveContainer,
|
import api from "../../../common/api";
|
||||||
Legend,
|
|
||||||
Tooltip,
|
|
||||||
XAxis,
|
|
||||||
YAxis
|
|
||||||
} from "recharts";
|
|
||||||
import NetworkCheckIcon from '@material-ui/icons/NetworkCheck';
|
|
||||||
import PieChartIcon from '@material-ui/icons/PieChart';
|
|
||||||
import ViewHeadlineIcon from '@material-ui/icons/ViewHeadline';
|
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const styles = (theme: Theme) =>
|
||||||
root: {
|
createStyles({
|
||||||
display: "flex"
|
root: {
|
||||||
},
|
display: "flex"
|
||||||
toolbar: {
|
|
||||||
paddingRight: 24 // keep right padding when drawer closed
|
|
||||||
},
|
|
||||||
toolbarIcon: {
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
justifyContent: "flex-end",
|
|
||||||
padding: "0 8px",
|
|
||||||
...theme.mixins.toolbar
|
|
||||||
},
|
|
||||||
appBar: {
|
|
||||||
zIndex: theme.zIndex.drawer + 1,
|
|
||||||
transition: theme.transitions.create(["width", "margin"], {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
menuButton: {
|
|
||||||
marginRight: 36
|
|
||||||
},
|
|
||||||
menuButtonHidden: {
|
|
||||||
display: "none"
|
|
||||||
},
|
|
||||||
title: {
|
|
||||||
flexGrow: 1
|
|
||||||
},
|
|
||||||
drawerPaperClose: {
|
|
||||||
overflowX: "hidden",
|
|
||||||
transition: theme.transitions.create("width", {
|
|
||||||
easing: theme.transitions.easing.sharp,
|
|
||||||
duration: theme.transitions.duration.leavingScreen
|
|
||||||
}),
|
|
||||||
width: theme.spacing(7),
|
|
||||||
[theme.breakpoints.up("sm")]: {
|
|
||||||
width: theme.spacing(9)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
appBarSpacer: theme.mixins.toolbar,
|
|
||||||
content: {
|
|
||||||
flexGrow: 1,
|
|
||||||
height: "100vh",
|
|
||||||
overflow: "auto"
|
|
||||||
},
|
|
||||||
container: {
|
|
||||||
paddingBottom: theme.spacing(4),
|
|
||||||
"& h6": {
|
|
||||||
color: "#777777",
|
|
||||||
fontSize: 14,
|
|
||||||
},
|
},
|
||||||
"& p": {
|
toolbar: {
|
||||||
"& span": {
|
paddingRight: 24 // keep right padding when drawer closed
|
||||||
fontSize: 16,
|
},
|
||||||
|
toolbarIcon: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
padding: "0 8px",
|
||||||
|
...theme.mixins.toolbar
|
||||||
|
},
|
||||||
|
appBar: {
|
||||||
|
zIndex: theme.zIndex.drawer + 1,
|
||||||
|
transition: theme.transitions.create(["width", "margin"], {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
menuButton: {
|
||||||
|
marginRight: 36
|
||||||
|
},
|
||||||
|
menuButtonHidden: {
|
||||||
|
display: "none"
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
flexGrow: 1
|
||||||
|
},
|
||||||
|
drawerPaperClose: {
|
||||||
|
overflowX: "hidden",
|
||||||
|
transition: theme.transitions.create("width", {
|
||||||
|
easing: theme.transitions.easing.sharp,
|
||||||
|
duration: theme.transitions.duration.leavingScreen
|
||||||
|
}),
|
||||||
|
width: theme.spacing(7),
|
||||||
|
[theme.breakpoints.up("sm")]: {
|
||||||
|
width: theme.spacing(9)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
appBarSpacer: theme.mixins.toolbar,
|
||||||
|
content: {
|
||||||
|
flexGrow: 1,
|
||||||
|
height: "100vh",
|
||||||
|
overflow: "auto"
|
||||||
|
},
|
||||||
|
container: {
|
||||||
|
paddingBottom: theme.spacing(4),
|
||||||
|
"& h6": {
|
||||||
|
color: "#777777",
|
||||||
|
fontSize: 14
|
||||||
},
|
},
|
||||||
|
"& p": {
|
||||||
|
"& span": {
|
||||||
|
fontSize: 16
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
paper: {
|
||||||
paper: {
|
padding: theme.spacing(2),
|
||||||
padding: theme.spacing(2),
|
display: "flex",
|
||||||
display: "flex",
|
overflow: "auto",
|
||||||
overflow: "auto",
|
flexDirection: "column"
|
||||||
flexDirection: "column"
|
},
|
||||||
},
|
fixedHeight: {
|
||||||
fixedHeight: {
|
minHeight: 240
|
||||||
minHeight: 240,
|
},
|
||||||
},
|
consumptionValue: {
|
||||||
consumptionValue: {
|
color: "#000000",
|
||||||
color: "#000000",
|
fontSize: "60px",
|
||||||
fontSize: "60px",
|
fontWeight: "bold"
|
||||||
fontWeight: "bold",
|
},
|
||||||
},
|
icon: {
|
||||||
icon: {
|
marginRight: 10,
|
||||||
marginRight: 10,
|
color: "#777777"
|
||||||
color: "#777777",
|
}
|
||||||
},
|
});
|
||||||
}));
|
|
||||||
|
|
||||||
export default function Dashboard() {
|
interface IDashboardProps {
|
||||||
const theme = useTheme();
|
classes: any;
|
||||||
const classes = useStyles(theme);
|
}
|
||||||
|
|
||||||
|
const Dashboard = ({ classes }: IDashboardProps) => {
|
||||||
const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);
|
const fixedHeightPaper = clsx(classes.paper, classes.fixedHeight);
|
||||||
|
const [usage, setUsage] = useState<Usage | null>(null);
|
||||||
|
const [loading, isLoading] = useState<boolean>(true);
|
||||||
|
const [error, setError] = useState<string>("");
|
||||||
|
|
||||||
const data = [39,31,37,29,28,31,31,34,39,40,35,40,24,25,30,20,28,38,23,28,22,39,37,37,40,28,28,28,24,31].map((usage, day ) => ({ usage, day }));
|
useEffect(() => {
|
||||||
const data2 = [25,32,21,40,31,30,23,40,26,32].map((usage, day ) => ({ usage, day }));
|
if (loading) {
|
||||||
|
fetchUsage();
|
||||||
|
}
|
||||||
|
}, [loading]);
|
||||||
|
|
||||||
|
const fetchUsage = () => {
|
||||||
|
api
|
||||||
|
.invoke("GET", `/api/v1/admin/info`)
|
||||||
|
.then((res: Usage) => {
|
||||||
|
setUsage(res);
|
||||||
|
setError("");
|
||||||
|
isLoading(false);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
setError(err);
|
||||||
|
isLoading(false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const prettyUsage = (usage: string | undefined) => {
|
||||||
|
if (usage == undefined) {
|
||||||
|
return "0";
|
||||||
|
}
|
||||||
|
return niceBytes(usage);
|
||||||
|
};
|
||||||
|
const units = ["bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
||||||
|
|
||||||
|
const niceBytes = (x: string) => {
|
||||||
|
let l = 0,
|
||||||
|
n = parseInt(x, 10) || 0;
|
||||||
|
|
||||||
|
while (n >= 1024 && ++l) {
|
||||||
|
n = n / 1024;
|
||||||
|
}
|
||||||
|
//include a decimal point and a tenths-place digit if presenting
|
||||||
|
//less than ten of KB or greater units
|
||||||
|
return n.toFixed(n < 10 && l > 0 ? 1 : 0) + " " + units[l];
|
||||||
|
};
|
||||||
|
const prettyNumber = (usage: number | undefined) => {
|
||||||
|
if (usage == undefined) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usage;
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Grid container xs={12}>
|
<React.Fragment>
|
||||||
<Grid container xs={12} spacing={3} className={classes.container}>
|
<Grid container xs={12}>
|
||||||
<Grid item xs={12} md={4} lg={4}>
|
<Grid container xs={12} spacing={3} className={classes.container}>
|
||||||
<Paper className={fixedHeightPaper}>
|
<Grid container xs={12}>
|
||||||
<Grid container direction="row" alignItems="center">
|
<Typography variant="h2">MinIO Console</Typography>
|
||||||
<Grid item className={classes.icon}>
|
</Grid>
|
||||||
<ViewHeadlineIcon/>
|
<Grid item xs={12} md={4} lg={4}>
|
||||||
|
<Paper className={fixedHeightPaper}>
|
||||||
|
<Grid container direction="row" alignItems="center">
|
||||||
|
<Grid item className={classes.icon}>
|
||||||
|
<ViewHeadlineIcon />
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6">Total Buckets</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Typography className={classes.consumptionValue}>
|
||||||
<Typography variant="h6">All Buckets</Typography>
|
{usage ? prettyNumber(usage.buckets) : 0}
|
||||||
|
</Typography>
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} md={4} lg={4}>
|
||||||
|
<Paper className={fixedHeightPaper}>
|
||||||
|
<Grid container direction="row" alignItems="center">
|
||||||
|
<Grid item className={classes.icon}>
|
||||||
|
<NetworkCheckIcon />
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6"> Total Objects</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
<Typography className={classes.consumptionValue}>
|
||||||
<Typography className={classes.consumptionValue}>238</Typography>
|
{usage ? prettyNumber(usage.objects) : 0}
|
||||||
</Paper>
|
</Typography>
|
||||||
</Grid>
|
</Paper>
|
||||||
<Grid item xs={12} md={4} lg={4}>
|
</Grid>
|
||||||
<Paper className={fixedHeightPaper}>
|
<Grid item xs={12} md={4} lg={4}>
|
||||||
<Grid container direction="row" alignItems="center">
|
<Paper className={fixedHeightPaper}>
|
||||||
<Grid item className={classes.icon}>
|
<Grid container direction="row" alignItems="center">
|
||||||
<PieChartIcon/>
|
<Grid item className={classes.icon}>
|
||||||
|
<PieChartIcon />
|
||||||
|
</Grid>
|
||||||
|
<Grid item>
|
||||||
|
<Typography variant="h6">Usage</Typography>
|
||||||
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item>
|
<Typography className={classes.consumptionValue}>
|
||||||
<Typography variant="h6">Usage</Typography>
|
{usage ? prettyUsage(usage.usage + "") : 0}
|
||||||
</Grid>
|
</Typography>
|
||||||
</Grid>
|
</Paper>
|
||||||
<Typography className={classes.consumptionValue}>375<span>TB</span></Typography>
|
</Grid>
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} md={4} lg={4}>
|
|
||||||
<Paper className={fixedHeightPaper}>
|
|
||||||
<Grid container direction="row" alignItems="center">
|
|
||||||
<Grid item className={classes.icon}>
|
|
||||||
<NetworkCheckIcon/>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Typography variant="h6"> Egress this Month</Typography>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Typography className={classes.consumptionValue}>1.5<span>TB</span></Typography>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid container xs={12} spacing={3} className={classes.container}>
|
</React.Fragment>
|
||||||
<Grid item xs={8}>
|
|
||||||
<Paper className={fixedHeightPaper}>
|
|
||||||
<Typography variant="h6">Daily Average Usage</Typography>
|
|
||||||
<ResponsiveContainer width="100%" height={400}>
|
|
||||||
<LineChart
|
|
||||||
data={data}
|
|
||||||
>
|
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
|
||||||
<XAxis
|
|
||||||
type="number"
|
|
||||||
domain={[1, 31]}
|
|
||||||
interval={0}
|
|
||||||
dataKey="day"
|
|
||||||
axisLine={false}
|
|
||||||
tickLine={false}
|
|
||||||
/>
|
|
||||||
<YAxis
|
|
||||||
dataKey="usage"
|
|
||||||
width={80}
|
|
||||||
tick={{ fill: "#737373" }}
|
|
||||||
dx={-5}
|
|
||||||
axisLine={false}
|
|
||||||
tickLine={false}
|
|
||||||
/>
|
|
||||||
<Tooltip />
|
|
||||||
<Legend />
|
|
||||||
<Line
|
|
||||||
strokeWidth={2}
|
|
||||||
yAxisId={0}
|
|
||||||
type="monotone"
|
|
||||||
dataKey="usage"
|
|
||||||
stroke="#8884d8"
|
|
||||||
activeDot={{ r: 8 }}
|
|
||||||
/>
|
|
||||||
</LineChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={4}>
|
|
||||||
<Paper className={fixedHeightPaper}>
|
|
||||||
<Typography variant="h6">Daily Network Egress</Typography>
|
|
||||||
<ResponsiveContainer width="100%" height={400}>
|
|
||||||
<LineChart
|
|
||||||
data={data2}
|
|
||||||
>
|
|
||||||
<CartesianGrid strokeDasharray="3 3" />
|
|
||||||
<XAxis
|
|
||||||
type="number"
|
|
||||||
dataKey="day"
|
|
||||||
axisLine={false}
|
|
||||||
tickLine={false}
|
|
||||||
/>
|
|
||||||
<YAxis
|
|
||||||
dataKey="usage"
|
|
||||||
tick={{ fill: "#737373" }}
|
|
||||||
dx={-5}
|
|
||||||
axisLine={false}
|
|
||||||
tickLine={false}
|
|
||||||
/>
|
|
||||||
<Tooltip />
|
|
||||||
<Legend />
|
|
||||||
<Line
|
|
||||||
strokeWidth={2}
|
|
||||||
yAxisId={0}
|
|
||||||
type="monotone"
|
|
||||||
dataKey="usage"
|
|
||||||
stroke="#8884d8"
|
|
||||||
activeDot={{ r: 8 }}
|
|
||||||
/>
|
|
||||||
</LineChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(Dashboard);
|
||||||
|
|||||||
21
portal-ui/src/screens/Console/Dashboard/types.tsx
Normal file
21
portal-ui/src/screens/Console/Dashboard/types.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// This file is part of MinIO Console Server
|
||||||
|
// Copyright (c) 2020 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 Usage {
|
||||||
|
usage: number;
|
||||||
|
buckets: number;
|
||||||
|
objects: number;
|
||||||
|
}
|
||||||
@@ -32,7 +32,7 @@ import api from "../../../common/api";
|
|||||||
import "codemirror/lib/codemirror.css";
|
import "codemirror/lib/codemirror.css";
|
||||||
import "codemirror/theme/material.css";
|
import "codemirror/theme/material.css";
|
||||||
import PolicyBuilder from "./PolicyBuilder";
|
import PolicyBuilder from "./PolicyBuilder";
|
||||||
import {Policy} from "./types";
|
import { Policy } from "./types";
|
||||||
require("codemirror/mode/javascript/javascript");
|
require("codemirror/mode/javascript/javascript");
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
@@ -144,7 +144,6 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
|
|||||||
id="standard-basic"
|
id="standard-basic"
|
||||||
fullWidth
|
fullWidth
|
||||||
label="Policy Name"
|
label="Policy Name"
|
||||||
value={policyEdit ? policyEdit.name : ""}
|
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
this.setState({ policyName: e.target.value });
|
this.setState({ policyName: e.target.value });
|
||||||
}}
|
}}
|
||||||
@@ -170,17 +169,19 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
|
|||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
{!policyEdit && <Grid item xs={12}>
|
{!policyEdit && (
|
||||||
<Button
|
<Grid item xs={12}>
|
||||||
type="submit"
|
<Button
|
||||||
variant="contained"
|
type="submit"
|
||||||
color="primary"
|
variant="contained"
|
||||||
fullWidth
|
color="primary"
|
||||||
disabled={addLoading}
|
fullWidth
|
||||||
>
|
disabled={addLoading}
|
||||||
Save
|
>
|
||||||
</Button>
|
Save
|
||||||
</Grid>}
|
</Button>
|
||||||
|
</Grid>
|
||||||
|
)}
|
||||||
{addLoading && (
|
{addLoading && (
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<LinearProgress />
|
<LinearProgress />
|
||||||
|
|||||||
@@ -120,7 +120,7 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
|
|||||||
deleteOpen: false,
|
deleteOpen: false,
|
||||||
selectedPolicy: "",
|
selectedPolicy: "",
|
||||||
filterPolicies: "",
|
filterPolicies: "",
|
||||||
policyEdit: null,
|
policyEdit: null
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchRecords() {
|
fetchRecords() {
|
||||||
@@ -187,7 +187,7 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
|
|||||||
deleteOpen,
|
deleteOpen,
|
||||||
selectedPolicy,
|
selectedPolicy,
|
||||||
filterPolicies,
|
filterPolicies,
|
||||||
policyEdit,
|
policyEdit
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const offset = page * rowsPerPage;
|
const offset = page * rowsPerPage;
|
||||||
@@ -250,7 +250,7 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
addScreenOpen: true,
|
addScreenOpen: true,
|
||||||
policyEdit: null,
|
policyEdit: null
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -294,7 +294,7 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
addScreenOpen: true,
|
addScreenOpen: true,
|
||||||
policyEdit: row,
|
policyEdit: row
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -15,16 +15,24 @@
|
|||||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import {createStyles, Theme, withStyles} from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import remove from "lodash/remove";
|
import remove from "lodash/remove";
|
||||||
import {Checkbox, FormControlLabel, FormGroup, FormLabel, Paper, Radio, RadioGroup} from "@material-ui/core";
|
import {
|
||||||
import {Statement} from "./types";
|
Checkbox,
|
||||||
|
FormControlLabel,
|
||||||
|
FormGroup,
|
||||||
|
FormLabel,
|
||||||
|
Paper,
|
||||||
|
Radio,
|
||||||
|
RadioGroup
|
||||||
|
} from "@material-ui/core";
|
||||||
|
import { Statement } from "./types";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
root: {
|
root: {
|
||||||
flexGrow: 1,
|
flexGrow: 1
|
||||||
},
|
},
|
||||||
errorBlock: {
|
errorBlock: {
|
||||||
color: "red"
|
color: "red"
|
||||||
@@ -38,9 +46,9 @@ const styles = (theme: Theme) =>
|
|||||||
},
|
},
|
||||||
paper: {
|
paper: {
|
||||||
padding: theme.spacing(1),
|
padding: theme.spacing(1),
|
||||||
textAlign: 'center',
|
textAlign: "center",
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
interface IPolicyBuilderProps {
|
interface IPolicyBuilderProps {
|
||||||
@@ -56,22 +64,29 @@ interface IPolicyBuilderState {
|
|||||||
currentStatementRead: boolean;
|
currentStatementRead: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PolicyBuilder extends React.Component<IPolicyBuilderProps, IPolicyBuilderState> {
|
class PolicyBuilder extends React.Component<
|
||||||
|
IPolicyBuilderProps,
|
||||||
|
IPolicyBuilderState
|
||||||
|
> {
|
||||||
state: IPolicyBuilderState = {
|
state: IPolicyBuilderState = {
|
||||||
policyString: "",
|
policyString: "",
|
||||||
statements: [],
|
statements: [],
|
||||||
currentStatement: {
|
currentStatement: {
|
||||||
effect: "",
|
effect: "",
|
||||||
actions: [],
|
actions: [],
|
||||||
resources: [],
|
resources: []
|
||||||
},
|
},
|
||||||
currentStatementWrite: false,
|
currentStatementWrite: false,
|
||||||
currentStatementRead: false,
|
currentStatementRead: false
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes, policyDefinition } = this.props;
|
const { classes, policyDefinition } = this.props;
|
||||||
const { currentStatement,currentStatementWrite, currentStatementRead } = this.state;
|
const {
|
||||||
|
currentStatement,
|
||||||
|
currentStatementWrite,
|
||||||
|
currentStatementRead
|
||||||
|
} = this.state;
|
||||||
console.log(currentStatement);
|
console.log(currentStatement);
|
||||||
return (
|
return (
|
||||||
<div className={classes.root}>
|
<div className={classes.root}>
|
||||||
@@ -85,12 +100,25 @@ class PolicyBuilder extends React.Component<IPolicyBuilderProps, IPolicyBuilderS
|
|||||||
aria-label="effect"
|
aria-label="effect"
|
||||||
name="effect"
|
name="effect"
|
||||||
value={currentStatement.effect}
|
value={currentStatement.effect}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>, value: string) => {
|
onChange={(
|
||||||
this.setState( { currentStatement: { ...currentStatement, effect: value }});
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
value: string
|
||||||
|
) => {
|
||||||
|
this.setState({
|
||||||
|
currentStatement: { ...currentStatement, effect: value }
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<FormControlLabel value="Deny" control={<Radio />} label="Deny" />
|
<FormControlLabel
|
||||||
<FormControlLabel value="Allow" control={<Radio />} label="Allow" />
|
value="Deny"
|
||||||
|
control={<Radio />}
|
||||||
|
label="Deny"
|
||||||
|
/>
|
||||||
|
<FormControlLabel
|
||||||
|
value="Allow"
|
||||||
|
control={<Radio />}
|
||||||
|
label="Allow"
|
||||||
|
/>
|
||||||
</RadioGroup>
|
</RadioGroup>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
@@ -102,16 +130,30 @@ class PolicyBuilder extends React.Component<IPolicyBuilderProps, IPolicyBuilderS
|
|||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={currentStatementRead}
|
checked={currentStatementRead}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
|
onChange={(
|
||||||
const readActions = ["s3:ListBucket", "s3:GetObject", "s3:GetBucketLocation"];
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
checked: boolean
|
||||||
|
) => {
|
||||||
|
const readActions = [
|
||||||
|
"s3:ListBucket",
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:GetBucketLocation"
|
||||||
|
];
|
||||||
let actions = currentStatement.actions;
|
let actions = currentStatement.actions;
|
||||||
if (checked) {
|
if (checked) {
|
||||||
actions.push(...readActions)
|
actions.push(...readActions);
|
||||||
} else {
|
} else {
|
||||||
actions = remove(actions, (action) => readActions.includes(action))
|
actions = remove(actions, action =>
|
||||||
|
readActions.includes(action)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.setState( { currentStatement: { ...currentStatement, actions: actions }});
|
this.setState({
|
||||||
this.setState( { currentStatementRead: checked });
|
currentStatement: {
|
||||||
|
...currentStatement,
|
||||||
|
actions: actions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({ currentStatementRead: checked });
|
||||||
}}
|
}}
|
||||||
name="read"
|
name="read"
|
||||||
/>
|
/>
|
||||||
@@ -122,16 +164,26 @@ class PolicyBuilder extends React.Component<IPolicyBuilderProps, IPolicyBuilderS
|
|||||||
control={
|
control={
|
||||||
<Checkbox
|
<Checkbox
|
||||||
checked={currentStatementWrite}
|
checked={currentStatementWrite}
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>, checked: boolean) => {
|
onChange={(
|
||||||
|
e: React.ChangeEvent<HTMLInputElement>,
|
||||||
|
checked: boolean
|
||||||
|
) => {
|
||||||
const writeActions = ["s3:PutObject"];
|
const writeActions = ["s3:PutObject"];
|
||||||
let actions = currentStatement.actions;
|
let actions = currentStatement.actions;
|
||||||
if (checked) {
|
if (checked) {
|
||||||
actions.push(...writeActions)
|
actions.push(...writeActions);
|
||||||
} else {
|
} else {
|
||||||
actions = remove(actions, (action) => writeActions.includes(action))
|
actions = remove(actions, action =>
|
||||||
|
writeActions.includes(action)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.setState( { currentStatement: { ...currentStatement, actions: actions }});
|
this.setState({
|
||||||
this.setState( { currentStatementWrite: checked });
|
currentStatement: {
|
||||||
|
...currentStatement,
|
||||||
|
actions: actions
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.setState({ currentStatementWrite: checked });
|
||||||
}}
|
}}
|
||||||
name="write"
|
name="write"
|
||||||
/>
|
/>
|
||||||
@@ -150,7 +202,7 @@ class PolicyBuilder extends React.Component<IPolicyBuilderProps, IPolicyBuilderS
|
|||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user