Refactor React Classes to Functions (#483)

This commit is contained in:
Daniel Valdivia
2020-12-08 10:31:04 -08:00
committed by GitHub
parent e541446631
commit b24d62a695
42 changed files with 2403 additions and 2788 deletions

View File

@@ -16,19 +16,17 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class AddIcon extends React.Component {
render() {
return (
<SvgIcon viewBox="0 0 12 12">
<path
fill="#081c42"
className="a"
d="M-13160.269,1885.114h-3.235v-4.381h-4.382V1877.5h4.382v-4.381h3.235v4.381h4.383v3.238h-4.383v4.38Z"
transform="translate(13167.886 -1873.114)"
/>
</SvgIcon>
);
}
}
const AddIcon = () => {
return (
<SvgIcon viewBox="0 0 12 12">
<path
fill="#081c42"
className="a"
d="M-13160.269,1885.114h-3.235v-4.381h-4.382V1877.5h4.382v-4.381h3.235v4.381h4.383v3.238h-4.383v4.38Z"
transform="translate(13167.886 -1873.114)"
/>
</SvgIcon>
);
};
export default AddIcon;

View File

@@ -16,60 +16,59 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class AllBucketsIcon extends React.Component {
render() {
return (
<SvgIcon viewBox="0 0 15.834 17.375">
<defs>
<linearGradient
id="a"
y1="0.5"
x2="1"
y2="0.5"
gradientUnits="objectBoundingBox"
>
<stop offset="0.044" stopColor="#362585" />
<stop offset="0.301" stopColor="#281b6f" />
<stop offset="1" stopColor="#1e1560" />
</linearGradient>
</defs>
<g transform="translate(0 0.375)">
<circle
style={{ opacity: 0.1, fill: "url(#a)" }}
cx="6.625"
cy="6.625"
r="6.625"
transform="translate(0 3.75)"
const AllBucketsIcon = () => {
return (
<SvgIcon viewBox="0 0 15.834 17.375">
<defs>
<linearGradient
id="a"
y1="0.5"
x2="1"
y2="0.5"
gradientUnits="objectBoundingBox"
>
<stop offset="0.044" stopColor="#362585" />
<stop offset="0.301" stopColor="#281b6f" />
<stop offset="1" stopColor="#1e1560" />
</linearGradient>
</defs>
<g transform="translate(0 0.375)">
<circle
style={{ opacity: 0.1, fill: "url(#a)" }}
cx="6.625"
cy="6.625"
r="6.625"
transform="translate(0 3.75)"
/>
<g transform="translate(3.092)">
<ellipse
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.75px",
}}
cx="6.183"
cy="1.244"
rx="6.183"
ry="1.244"
transform="translate(0)"
/>
<path
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.75px",
}}
d="M-3722.174,1225.225l-1.687,10.292a.858.858,0,0,1-.578.669,12.182,12.182,0,0,1-3.918.647,12.187,12.187,0,0,1-3.894-.639.878.878,0,0,1-.6-.678q-.843-5.145-1.687-10.291"
transform="translate(3734.541 -1223.981)"
/>
<g transform="translate(3.092)">
<ellipse
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.75px",
}}
cx="6.183"
cy="1.244"
rx="6.183"
ry="1.244"
transform="translate(0)"
/>
<path
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.75px",
}}
d="M-3722.174,1225.225l-1.687,10.292a.858.858,0,0,1-.578.669,12.182,12.182,0,0,1-3.918.647,12.187,12.187,0,0,1-3.894-.639.878.878,0,0,1-.6-.678q-.843-5.145-1.687-10.291"
transform="translate(3734.541 -1223.981)"
/>
</g>
</g>
</SvgIcon>
);
}
}
</g>
</SvgIcon>
);
};
export default AllBucketsIcon;

View File

@@ -16,16 +16,15 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class BucketsIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<path d="M8.392,10H1.608L0,0H10Z" />
</svg>
</SvgIcon>
);
}
}
const BucketsIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<path d="M8.392,10H1.608L0,0H10Z" />
</svg>
</SvgIcon>
);
};
export default BucketsIcon;

View File

@@ -16,108 +16,106 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class ClustersIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 9">
<g transform="translate(79 438.479)">
const ClustersIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 9">
<g transform="translate(79 438.479)">
<g>
<g>
<g>
<rect x="-77.9" y="-434.5" width="7.8" height="1" />
</g>
</g>
<g>
<g>
<rect
x="-77.9"
y="-434.5"
transform="matrix(0.4999 -0.8661 0.8661 0.4999 338.8698 -281.1237)"
width="7.8"
height="1"
/>
</g>
</g>
<g>
<g>
<rect
x="-74.5"
y="-437.9"
transform="matrix(0.866 -0.5001 0.5001 0.866 207.1129 -95.1668)"
width="1"
height="7.8"
/>
</g>
</g>
<g>
<g>
<path
d="M-71.8-430.1h-4.5l-2.2-3.9l2.2-3.9h4.5l2.2,3.9L-71.8-430.1z M-75.7-431.1h3.3l1.7-2.9l-1.7-2.9h-3.3
l-1.7,2.9L-75.7-431.1z"
/>
</g>
</g>
<g>
<g>
<path
d="M-72.3-434c0,0.9-0.7,1.7-1.7,1.7c-0.9,0-1.7-0.7-1.7-1.7c0-0.9,0.7-1.7,1.7-1.7
C-73.1-435.7-72.3-434.9-72.3-434z"
/>
</g>
</g>
<g>
<g>
<path
d="M-76.8-434c0,0.6-0.5,1.1-1.1,1.1c0,0,0,0,0,0c-0.6,0-1.1-0.5-1.1-1.1c0,0,0,0,0,0c0-0.6,0.5-1.1,1.1-1.1
c0,0,0,0,0,0C-77.3-435.1-76.8-434.6-76.8-434C-76.8-434-76.8-434-76.8-434z"
/>
</g>
</g>
<g>
<g>
<path
d="M-69-434c0,0.6-0.5,1.1-1.1,1.1c0,0,0,0,0,0c-0.6,0-1.1-0.5-1.1-1.1c0,0,0,0,0,0c0-0.6,0.5-1.1,1.1-1.1
c0,0,0,0,0,0C-69.5-435.1-69-434.6-69-434C-69-434-69-434-69-434z"
/>
</g>
</g>
<g>
<g>
<path
d="M-75.4-431.6c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c0,0,0,0,0,0c-0.5-0.3-0.7-1-0.4-1.5
C-76.6-431.7-75.9-431.9-75.4-431.6C-75.4-431.6-75.4-431.6-75.4-431.6z"
/>
</g>
</g>
<g>
<g>
<path
d="M-71.5-438.3c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c0,0,0,0,0,0c-0.5-0.3-0.7-1-0.4-1.5
C-72.7-438.5-72-438.6-71.5-438.3C-71.5-438.3-71.5-438.3-71.5-438.3z"
/>
</g>
</g>
<g>
<g>
<path
d="M-72.6-431.6c0.5-0.3,1.2-0.1,1.5,0.4c0,0,0,0,0,0c0.3,0.5,0.1,1.2-0.4,1.5c-0.5,0.3-1.2,0.1-1.5-0.4
c0,0,0,0,0,0C-73.3-430.6-73.1-431.3-72.6-431.6z"
/>
</g>
</g>
<g>
<g>
<path
d="M-76.5-438.3c0.5-0.3,1.2-0.1,1.5,0.4c0,0,0,0,0,0c0.3,0.5,0.1,1.2-0.4,1.5c-0.5,0.3-1.2,0.1-1.5-0.4
c0,0,0,0,0,0C-77.2-437.3-77-438-76.5-438.3z"
/>
</g>
<rect x="-77.9" y="-434.5" width="7.8" height="1" />
</g>
</g>
</svg>
</SvgIcon>
);
}
}
<g>
<g>
<rect
x="-77.9"
y="-434.5"
transform="matrix(0.4999 -0.8661 0.8661 0.4999 338.8698 -281.1237)"
width="7.8"
height="1"
/>
</g>
</g>
<g>
<g>
<rect
x="-74.5"
y="-437.9"
transform="matrix(0.866 -0.5001 0.5001 0.866 207.1129 -95.1668)"
width="1"
height="7.8"
/>
</g>
</g>
<g>
<g>
<path
d="M-71.8-430.1h-4.5l-2.2-3.9l2.2-3.9h4.5l2.2,3.9L-71.8-430.1z M-75.7-431.1h3.3l1.7-2.9l-1.7-2.9h-3.3
l-1.7,2.9L-75.7-431.1z"
/>
</g>
</g>
<g>
<g>
<path
d="M-72.3-434c0,0.9-0.7,1.7-1.7,1.7c-0.9,0-1.7-0.7-1.7-1.7c0-0.9,0.7-1.7,1.7-1.7
C-73.1-435.7-72.3-434.9-72.3-434z"
/>
</g>
</g>
<g>
<g>
<path
d="M-76.8-434c0,0.6-0.5,1.1-1.1,1.1c0,0,0,0,0,0c-0.6,0-1.1-0.5-1.1-1.1c0,0,0,0,0,0c0-0.6,0.5-1.1,1.1-1.1
c0,0,0,0,0,0C-77.3-435.1-76.8-434.6-76.8-434C-76.8-434-76.8-434-76.8-434z"
/>
</g>
</g>
<g>
<g>
<path
d="M-69-434c0,0.6-0.5,1.1-1.1,1.1c0,0,0,0,0,0c-0.6,0-1.1-0.5-1.1-1.1c0,0,0,0,0,0c0-0.6,0.5-1.1,1.1-1.1
c0,0,0,0,0,0C-69.5-435.1-69-434.6-69-434C-69-434-69-434-69-434z"
/>
</g>
</g>
<g>
<g>
<path
d="M-75.4-431.6c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c0,0,0,0,0,0c-0.5-0.3-0.7-1-0.4-1.5
C-76.6-431.7-75.9-431.9-75.4-431.6C-75.4-431.6-75.4-431.6-75.4-431.6z"
/>
</g>
</g>
<g>
<g>
<path
d="M-71.5-438.3c0.5,0.3,0.7,1,0.4,1.5c-0.3,0.5-1,0.7-1.5,0.4c0,0,0,0,0,0c-0.5-0.3-0.7-1-0.4-1.5
C-72.7-438.5-72-438.6-71.5-438.3C-71.5-438.3-71.5-438.3-71.5-438.3z"
/>
</g>
</g>
<g>
<g>
<path
d="M-72.6-431.6c0.5-0.3,1.2-0.1,1.5,0.4c0,0,0,0,0,0c0.3,0.5,0.1,1.2-0.4,1.5c-0.5,0.3-1.2,0.1-1.5-0.4
c0,0,0,0,0,0C-73.3-430.6-73.1-431.3-72.6-431.6z"
/>
</g>
</g>
<g>
<g>
<path
d="M-76.5-438.3c0.5-0.3,1.2-0.1,1.5,0.4c0,0,0,0,0,0c0.3,0.5,0.1,1.2-0.4,1.5c-0.5,0.3-1.2,0.1-1.5-0.4
c0,0,0,0,0,0C-77.2-437.3-77-438-76.5-438.3z"
/>
</g>
</g>
</g>
</svg>
</SvgIcon>
);
};
export default ClustersIcon;

View File

@@ -16,27 +16,26 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class ConfigurationsListIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<rect width="1.433" height="1" />
<rect width="7.828" height="1" transform="translate(2.172)" />
<rect width="1.433" height="1" transform="translate(0 6)" />
<rect width="1.433" height="1" transform="translate(0 3)" />
<rect width="1.433" height="1" transform="translate(0 9)" />
<rect width="1.368" height="0.569" transform="translate(6.316 9)" />
<path d="M5.566,9.569v-.31l-.238-.138-.269.155-.65.375L4.034,9V9H2.172v1H5.566Z" />
<path d="M9.966,9l-.375.65-.65-.375-.269-.155-.238.138V10H10V9H9.967Z" />
<path d="M3.625,6.793l.269-.155V6.362l-.269-.155L3.266,6H2.172V7H3.266Z" />
<path d="M8.434,3.431v.31l.238.138.269-.155.649-.375L9.966,4V4H10V3H8.434Z" />
<path d="M4.034,4l.375-.65.65.375.269.155.238-.138V3H2.172V4H4.033Z" />
<path d="M9.356,5.929,10,5.558,9.316,4.373l-.644.372-.988-.571V3.431H6.316v.743l-.988.571-.644-.372L4,5.558l.644.371V7.071L4,7.442l.684,1.185.644-.372.988.571v.743H7.684V8.826l.988-.571.644.372L10,7.442l-.644-.371ZM7,7.278A.778.778,0,1,1,7.778,6.5.779.779,0,0,1,7,7.278Z" />
</svg>
</SvgIcon>
);
}
}
const ConfigurationsListIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<rect width="1.433" height="1" />
<rect width="7.828" height="1" transform="translate(2.172)" />
<rect width="1.433" height="1" transform="translate(0 6)" />
<rect width="1.433" height="1" transform="translate(0 3)" />
<rect width="1.433" height="1" transform="translate(0 9)" />
<rect width="1.368" height="0.569" transform="translate(6.316 9)" />
<path d="M5.566,9.569v-.31l-.238-.138-.269.155-.65.375L4.034,9V9H2.172v1H5.566Z" />
<path d="M9.966,9l-.375.65-.65-.375-.269-.155-.238.138V10H10V9H9.967Z" />
<path d="M3.625,6.793l.269-.155V6.362l-.269-.155L3.266,6H2.172V7H3.266Z" />
<path d="M8.434,3.431v.31l.238.138.269-.155.649-.375L9.966,4V4H10V3H8.434Z" />
<path d="M4.034,4l.375-.65.65.375.269.155.238-.138V3H2.172V4H4.033Z" />
<path d="M9.356,5.929,10,5.558,9.316,4.373l-.644.372-.988-.571V3.431H6.316v.743l-.988.571-.644-.372L4,5.558l.644.371V7.071L4,7.442l.684,1.185.644-.372.988.571v.743H7.684V8.826l.988-.571.644.372L10,7.442l-.644-.371ZM7,7.278A.778.778,0,1,1,7.778,6.5.779.779,0,0,1,7,7.278Z" />
</svg>
</SvgIcon>
);
};
export default ConfigurationsListIcon;

View File

@@ -16,22 +16,20 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class ConsoleIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(-518 -361)">
<path
d="M-126,0V10h10V0Zm1.5,8.5V2.95h7V8.5Z"
transform="translate(644 361)"
/>
<rect width="2" height="1" transform="translate(520.272 364.772)" />
</g>
</svg>
</SvgIcon>
);
}
}
const ConsoleIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(-518 -361)">
<path
d="M-126,0V10h10V0Zm1.5,8.5V2.95h7V8.5Z"
transform="translate(644 361)"
/>
<rect width="2" height="1" transform="translate(520.272 364.772)" />
</g>
</svg>
</SvgIcon>
);
};
export default ConsoleIcon;

View File

@@ -16,24 +16,23 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class CopyIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<title>ic_h_copy-new_sl</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
d="M0,0V16H16V0ZM11.886,9.048H9.048v2.838h-2.1V9.048H4.114v-2.1H6.952V4.114h2.1V6.952h2.838Z"
/>
</g>
const CopyIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
<title>ic_h_copy-new_sl</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
d="M0,0V16H16V0ZM11.886,9.048H9.048v2.838h-2.1V9.048H4.114v-2.1H6.952V4.114h2.1V6.952h2.838Z"
/>
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default CopyIcon;

View File

@@ -16,35 +16,29 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class CreateIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
<g
id="Group_55"
data-name="Group 55"
transform="translate(1002 -2555)"
>
<rect
id="Rectangle_29"
width="2"
height="12"
transform="translate(-997 2555)"
fill="#fff"
/>
<rect
id="Rectangle_30"
width="2"
height="12"
transform="translate(-990 2560) rotate(90)"
fill="#fff"
/>
</g>
</svg>
</SvgIcon>
);
}
}
const CreateIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12 12">
<g id="Group_55" data-name="Group 55" transform="translate(1002 -2555)">
<rect
id="Rectangle_29"
width="2"
height="12"
transform="translate(-997 2555)"
fill="#fff"
/>
<rect
id="Rectangle_30"
width="2"
height="12"
transform="translate(-990 2560) rotate(90)"
fill="#fff"
/>
</g>
</svg>
</SvgIcon>
);
};
export default CreateIcon;

View File

@@ -16,33 +16,32 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class DashboardIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(249 720)">
<rect
width="6"
height="5"
transform="translate(-244 -720) rotate(90)"
/>
<rect width="4" height="4" transform="translate(-243 -720)" />
<rect
width="5"
height="4"
transform="translate(-239 -715) rotate(90)"
/>
<rect
width="5"
height="3"
transform="translate(-244 -710) rotate(180)"
/>
</g>
</svg>
</SvgIcon>
);
}
}
const DashboardIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(249 720)">
<rect
width="6"
height="5"
transform="translate(-244 -720) rotate(90)"
/>
<rect width="4" height="4" transform="translate(-243 -720)" />
<rect
width="5"
height="4"
transform="translate(-239 -715) rotate(90)"
/>
<rect
width="5"
height="3"
transform="translate(-244 -710) rotate(180)"
/>
</g>
</svg>
</SvgIcon>
);
};
export default DashboardIcon;

View File

@@ -16,19 +16,18 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class DeleteIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.402 13">
<path
d="M6.761 1V0H3.64v1H.004v1h10.4V1zM.004 2.998l1.672 10h7.052l1.673-10zm3.412 8.243l-.552-6.478h.653l.553 6.472zm3.569 0h-.653l.551-6.472h.654z"
className="a"
></path>
</svg>
</SvgIcon>
);
}
}
const DeleteIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.402 13">
<path
d="M6.761 1V0H3.64v1H.004v1h10.4V1zM.004 2.998l1.672 10h7.052l1.673-10zm3.412 8.243l-.552-6.478h.653l.553 6.472zm3.569 0h-.653l.551-6.472h.654z"
className="a"
></path>
</svg>
</SvgIcon>
);
};
export default DeleteIcon;

View File

@@ -17,17 +17,15 @@
import React from "react";
import SvgIcon from "@material-ui/core/SvgIcon";
class DownloadIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
<path d="M11.05 9.096v1.95h-9.1v-1.95H0v3.9h13v-3.9z"></path>
<path d="M6.5 9.75L9 6.672H7.475V0h-1.95v6.672H4z"></path>
</svg>
</SvgIcon>
);
}
}
const DownloadIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
<path d="M11.05 9.096v1.95h-9.1v-1.95H0v3.9h13v-3.9z"></path>
<path d="M6.5 9.75L9 6.672H7.475V0h-1.95v6.672H4z"></path>
</svg>
</SvgIcon>
);
};
export default DownloadIcon;

View File

@@ -16,58 +16,56 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class EgressIcon extends React.Component {
render() {
return (
<SvgIcon viewBox="0 0 18.344 17.009">
<defs>
<linearGradient
id="a"
y1="0.5"
x2="1"
y2="0.5"
gradientUnits="objectBoundingBox"
>
<stop offset="0.044" stopColor="#362585" />
<stop offset="0.301" stopColor="#281b6f" />
<stop offset="1" stopColor="#1e1560" />
</linearGradient>
</defs>
<g transform="translate(0 0.25)">
<ellipse
style={{ opacity: 0.1, fill: "url(#a)" }}
cx="7.462"
cy="7.462"
rx="7.462"
ry="7.462"
transform="translate(0 1.835)"
/>
<rect
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.5px",
}}
width="9.323"
height="9.323"
transform="translate(4.083)"
/>
<rect
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.5px",
}}
width="8.223"
height="8.223"
transform="translate(9.871 5.307)"
/>
</g>
</SvgIcon>
);
}
}
const EgressIcon = () => {
return (
<SvgIcon viewBox="0 0 18.344 17.009">
<defs>
<linearGradient
id="a"
y1="0.5"
x2="1"
y2="0.5"
gradientUnits="objectBoundingBox"
>
<stop offset="0.044" stopColor="#362585" />
<stop offset="0.301" stopColor="#281b6f" />
<stop offset="1" stopColor="#1e1560" />
</linearGradient>
</defs>
<g transform="translate(0 0.25)">
<ellipse
style={{ opacity: 0.1, fill: "url(#a)" }}
cx="7.462"
cy="7.462"
rx="7.462"
ry="7.462"
transform="translate(0 1.835)"
/>
<rect
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.5px",
}}
width="9.323"
height="9.323"
transform="translate(4.083)"
/>
<rect
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.5px",
}}
width="8.223"
height="8.223"
transform="translate(9.871 5.307)"
/>
</g>
</SvgIcon>
);
};
export default EgressIcon;

View File

@@ -16,26 +16,25 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class GroupsIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 9.787">
<g transform="translate(177 719.787)">
<g transform="translate(-105 -720)">
<path d="M-65,5a3,3,0,0,0-1.131.224A3.981,3.981,0,0,1-65,8v2h3V8A3,3,0,0,0-65,5Z" />
<path d="M-72,10h6V8a3,3,0,0,0-3-3,3,3,0,0,0-3,3Z" />
<path
className="a"
d="M-65,.213a1.993,1.993,0,0,0-1.384.561A2.967,2.967,0,0,1-66,2.213a2.964,2.964,0,0,1-.384,1.439A1.989,1.989,0,0,0-65,4.213a2,2,0,0,0,2-2A2,2,0,0,0-65,.213Z"
/>
<circle cx="2" cy="2" r="2" transform="translate(-71 0.213)" />
</g>
const GroupsIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 9.787">
<g transform="translate(177 719.787)">
<g transform="translate(-105 -720)">
<path d="M-65,5a3,3,0,0,0-1.131.224A3.981,3.981,0,0,1-65,8v2h3V8A3,3,0,0,0-65,5Z" />
<path d="M-72,10h6V8a3,3,0,0,0-3-3,3,3,0,0,0-3,3Z" />
<path
className="a"
d="M-65,.213a1.993,1.993,0,0,0-1.384.561A2.967,2.967,0,0,1-66,2.213a2.964,2.964,0,0,1-.384,1.439A1.989,1.989,0,0,0-65,4.213a2,2,0,0,0,2-2A2,2,0,0,0-65,.213Z"
/>
<circle cx="2" cy="2" r="2" transform="translate(-71 0.213)" />
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default GroupsIcon;

View File

@@ -16,38 +16,21 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class HealIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.014 9.993">
<path
className="a"
d="M9.162,5.971h0L8.192,5,9.346,3.846a2.257,2.257,0,0,0,0-3.192,2.311,2.311,0,0,0-3.192,0L5,1.808,4.029.837,3.846.654a2.311,2.311,0,0,0-3.192,0,2.257,2.257,0,0,0,0,3.192l.184.183h0L1.808,5,.654,6.154A2.257,2.257,0,0,0,3.846,9.346L5,8.192l.971.971.183.183A2.257,2.257,0,0,0,9.346,6.154Zm-2.29-4.6a1.27,1.27,0,0,1,1.757,0,1.242,1.242,0,0,1,0,1.757L7.475,4.283,5.717,2.525Zm-5.5,1.757A1.243,1.243,0,0,1,3.129,1.371l.183.183L1.555,3.312Zm1.757,5.5a1.27,1.27,0,0,1-1.757,0,1.242,1.242,0,0,1,0-1.757L2.525,5.717,4.283,7.475Zm2.843-.9-.254-.253L2.525,4.283l-.253-.254L4.029,2.272l.254.253L7.475,5.717l.253.254Zm2.657.9a1.271,1.271,0,0,1-1.757,0l-.183-.183L8.446,6.688l.183.183h0a1.241,1.241,0,0,1,0,1.757Z"
transform="translate(0.007 -0.014)"
/>
<circle
cx="0.5"
cy="0.5"
r="0.5"
transform="translate(4.507 4.486)"
/>
<circle
cx="0.5"
cy="0.5"
r="0.5"
transform="translate(3.507 3.486)"
/>
<circle
cx="0.5"
cy="0.5"
r="0.5"
transform="translate(5.507 5.486)"
/>
</svg>
</SvgIcon>
);
}
}
const HealIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10.014 9.993">
<path
className="a"
d="M9.162,5.971h0L8.192,5,9.346,3.846a2.257,2.257,0,0,0,0-3.192,2.311,2.311,0,0,0-3.192,0L5,1.808,4.029.837,3.846.654a2.311,2.311,0,0,0-3.192,0,2.257,2.257,0,0,0,0,3.192l.184.183h0L1.808,5,.654,6.154A2.257,2.257,0,0,0,3.846,9.346L5,8.192l.971.971.183.183A2.257,2.257,0,0,0,9.346,6.154Zm-2.29-4.6a1.27,1.27,0,0,1,1.757,0,1.242,1.242,0,0,1,0,1.757L7.475,4.283,5.717,2.525Zm-5.5,1.757A1.243,1.243,0,0,1,3.129,1.371l.183.183L1.555,3.312Zm1.757,5.5a1.27,1.27,0,0,1-1.757,0,1.242,1.242,0,0,1,0-1.757L2.525,5.717,4.283,7.475Zm2.843-.9-.254-.253L2.525,4.283l-.253-.254L4.029,2.272l.254.253L7.475,5.717l.253.254Zm2.657.9a1.271,1.271,0,0,1-1.757,0l-.183-.183L8.446,6.688l.183.183h0a1.241,1.241,0,0,1,0,1.757Z"
transform="translate(0.007 -0.014)"
/>
<circle cx="0.5" cy="0.5" r="0.5" transform="translate(4.507 4.486)" />
<circle cx="0.5" cy="0.5" r="0.5" transform="translate(3.507 3.486)" />
<circle cx="0.5" cy="0.5" r="0.5" transform="translate(5.507 5.486)" />
</svg>
</SvgIcon>
);
};
export default HealIcon;

View File

@@ -16,19 +16,18 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class BucketsIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8.75 10">
<path
d="M-44.625,10l-4.353-2.419L-53.375,10V0h8.75Z"
transform="translate(53.375)"
/>
</svg>
</SvgIcon>
);
}
}
const BucketsIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 8.75 10">
<path
d="M-44.625,10l-4.353-2.419L-53.375,10V0h8.75Z"
transform="translate(53.375)"
/>
</svg>
</SvgIcon>
);
};
export default BucketsIcon;

View File

@@ -16,19 +16,17 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class LambdaNotificationsIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<path
d="M0,0v10l2.8-2.2H10V0H0z M6.6,6L5.6,6.4l-0.8-2l-1.5,2L2.5,5.9l1.9-2.6L4.1,2.4H3.2v-1h1.5l1.4,3.7l0.9-0.4
const LambdaNotificationsIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<path
d="M0,0v10l2.8-2.2H10V0H0z M6.6,6L5.6,6.4l-0.8-2l-1.5,2L2.5,5.9l1.9-2.6L4.1,2.4H3.2v-1h1.5l1.4,3.7l0.9-0.4
l0.4,0.9L6.6,6z"
/>
</svg>
</SvgIcon>
);
}
}
/>
</svg>
</SvgIcon>
);
};
export default LambdaNotificationsIcon;

View File

@@ -17,31 +17,29 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class LicenseIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 11">
<path fill="#fff" d="M11 11H0V2h11v9zM2 8v1h7V8zm0-3v1h5V5z"></path>
<g
fill="#07274a"
stroke="#fdfdfd"
strokeWidth="0.5"
transform="translate(7)"
>
<circle cx="3" cy="3" r="3" stroke="none"></circle>
<circle cx="3" cy="3" r="2.75" fill="none"></circle>
</g>
<path
fill="none"
stroke="#fff"
strokeWidth="0.5"
d="M8.73 2.794l.954.953 1.471-1.471"
></path>
</svg>
</SvgIcon>
);
}
}
const LicenseIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 11">
<path fill="#fff" d="M11 11H0V2h11v9zM2 8v1h7V8zm0-3v1h5V5z"></path>
<g
fill="#07274a"
stroke="#fdfdfd"
strokeWidth="0.5"
transform="translate(7)"
>
<circle cx="3" cy="3" r="3" stroke="none"></circle>
<circle cx="3" cy="3" r="2.75" fill="none"></circle>
</g>
<path
fill="none"
stroke="#fff"
strokeWidth="0.5"
d="M8.73 2.794l.954.953 1.471-1.471"
></path>
</svg>
</SvgIcon>
);
};
export default LicenseIcon;

View File

@@ -16,32 +16,30 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class LogoutIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.122 10.571">
<g transform="translate(0 0.5)">
<path
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
d="M4816.27,3755.205v-2.939h8.539v9.571h-8.539v-2.932"
transform="translate(-4813.187 -3752.266)"
/>
<path
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
d="M4813.187,3757.052h8.081"
transform="translate(-4813.187 -3752.266)"
/>
<path
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
d="M4806.5,3756.511l2.265,2.063-2.265,2.063"
transform="translate(-4800.808 -3753.863)"
/>
</g>
</svg>
</SvgIcon>
);
}
}
const LogoutIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.122 10.571">
<g transform="translate(0 0.5)">
<path
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
d="M4816.27,3755.205v-2.939h8.539v9.571h-8.539v-2.932"
transform="translate(-4813.187 -3752.266)"
/>
<path
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
d="M4813.187,3757.052h8.081"
transform="translate(-4813.187 -3752.266)"
/>
<path
style={{ fill: "none", stroke: "rgba(255,255,255,0.8)" }}
d="M4806.5,3756.511l2.265,2.063-2.265,2.063"
transform="translate(-4800.808 -3753.863)"
/>
</g>
</svg>
</SvgIcon>
);
};
export default LogoutIcon;

View File

@@ -16,20 +16,18 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class MirroringIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(61 439)">
<rect width="1.5" height="10" transform="translate(-56.75 -439)" />
<path d="M6.5,10V0h.572L10,10Z" transform="translate(-61 -439)" />
<path d="M3.5,10V0H2.928L0,10Z" transform="translate(-61 -439)" />
</g>
</svg>
</SvgIcon>
);
}
}
const MirroringIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(61 439)">
<rect width="1.5" height="10" transform="translate(-56.75 -439)" />
<path d="M6.5,10V0h.572L10,10Z" transform="translate(-61 -439)" />
<path d="M3.5,10V0H2.928L0,10Z" transform="translate(-61 -439)" />
</g>
</svg>
</SvgIcon>
);
};
export default MirroringIcon;

View File

@@ -16,24 +16,22 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class PermissionIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 16">
<title>ic_permissions</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<polygon
className="cls-1"
points="14 16 7.035 12.13 0 16 0 0 14 0 14 16"
/>
</g>
const PermissionIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 16">
<title>ic_permissions</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<polygon
className="cls-1"
points="14 16 7.035 12.13 0 16 0 0 14 0 14 16"
/>
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default PermissionIcon;

View File

@@ -16,18 +16,17 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class RemoveIcon extends React.Component {
render() {
return (
<SvgIcon viewBox="0 0 11.656 3.101">
<path
fill="#081c42"
d="M-13157.172,1879.551h-11.656v-3.1h11.656v3.1Z"
transform="translate(13168.828 -1876.449)"
/>
</SvgIcon>
);
}
}
const RemoveIcon = () => {
return (
<SvgIcon viewBox="0 0 11.656 3.101">
<path
fill="#081c42"
d="M-13157.172,1879.551h-11.656v-3.1h11.656v3.1Z"
transform="translate(13168.828 -1876.449)"
/>
</SvgIcon>
);
};
export default RemoveIcon;

View File

@@ -16,24 +16,22 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class ServiceAccountIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.856 16">
<title>ic_service-accounts</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
d="M6.928,0,0,4v8l6.928,4,6.928-4V4Zm0,10.286A2.286,2.286,0,1,1,9.215,8,2.286,2.286,0,0,1,6.928,10.286Z"
/>
</g>
const ServiceAccountIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13.856 16">
<title>ic_service-accounts</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Layer_1-2" data-name="Layer 1">
<path
className="cls-1"
d="M6.928,0,0,4v8l6.928,4,6.928-4V4Zm0,10.286A2.286,2.286,0,1,1,9.215,8,2.286,2.286,0,0,1,6.928,10.286Z"
/>
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default ServiceAccountIcon;

View File

@@ -16,26 +16,25 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class ServiceAccountsIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 9.5">
<g transform="translate(231 719.516)">
<path
d="M-125.5,7.984a4.5,4.5,0,0,1,4.5-4.5,4.5,4.5,0,0,1,4.5,4.5Z"
transform="translate(-105 -720)"
/>
<rect width="10" height="1" transform="translate(-231 -711.016)" />
<path
d="M-119.5.484h-3v1h1v1h1v-1h1Z"
transform="translate(-105 -720)"
/>
</g>
</svg>
</SvgIcon>
);
}
}
const ServiceAccountsIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 9.5">
<g transform="translate(231 719.516)">
<path
d="M-125.5,7.984a4.5,4.5,0,0,1,4.5-4.5,4.5,4.5,0,0,1,4.5,4.5Z"
transform="translate(-105 -720)"
/>
<rect width="10" height="1" transform="translate(-231 -711.016)" />
<path
d="M-119.5.484h-3v1h1v1h1v-1h1Z"
transform="translate(-105 -720)"
/>
</g>
</svg>
</SvgIcon>
);
};
export default ServiceAccountsIcon;

View File

@@ -17,23 +17,21 @@
import React from "react";
import SvgIcon from "@material-ui/core/SvgIcon";
class ShareIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 13">
<path
d="M11.05 8.617v2.429h-9.1v-9.1h2.429v-1.95H0v13h13V8.617z"
className="a"
></path>
<path
d="M3.854 9.256h1.95a4.945 4.945 0 013.6-4.74v1.3l.6-.487 2.474-2.012L9.4.817v1.7a6.9 6.9 0 00-5.546 6.739z"
className="a"
></path>
</svg>
</SvgIcon>
);
}
}
const ShareIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 13">
<path
d="M11.05 8.617v2.429h-9.1v-9.1h2.429v-1.95H0v13h13V8.617z"
className="a"
></path>
<path
d="M3.854 9.256h1.95a4.945 4.945 0 013.6-4.74v1.3l.6-.487 2.474-2.012L9.4.817v1.7a6.9 6.9 0 00-5.546 6.739z"
className="a"
></path>
</svg>
</SvgIcon>
);
};
export default ShareIcon;

View File

@@ -16,47 +16,38 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class TraceIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9.998 10">
<g transform="translate(140.999 720)">
<g transform="translate(-105 -720)">
<rect
width="1.114"
height="1.667"
transform="translate(-27.116 8.333)"
/>
<path d="M-28.184,10H-29.3V8.154l2.182-3.037V3.147H-26V5.476l-2.182,3.037Z" />
<rect
width="1.114"
height="2.963"
transform="translate(-31.531)"
/>
<rect
width="1.114"
height="2.132"
transform="translate(-27.115 0)"
/>
<rect
width="1.114"
height="5.389"
transform="translate(-29.298)"
/>
<path d="M-30.417,10h-1.114V5.722l-2.233-3V0h1.114V2.353l2.233,3Z" />
<path d="M-32.65,10h-1.114V6.185l-2.234-3V0h1.114V2.815l2.234,3Z" />
<rect
width="1.114"
height="4.463"
transform="translate(-35.999 5.537)"
/>
</g>
const TraceIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 9.998 10">
<g transform="translate(140.999 720)">
<g transform="translate(-105 -720)">
<rect
width="1.114"
height="1.667"
transform="translate(-27.116 8.333)"
/>
<path d="M-28.184,10H-29.3V8.154l2.182-3.037V3.147H-26V5.476l-2.182,3.037Z" />
<rect width="1.114" height="2.963" transform="translate(-31.531)" />
<rect
width="1.114"
height="2.132"
transform="translate(-27.115 0)"
/>
<rect width="1.114" height="5.389" transform="translate(-29.298)" />
<path d="M-30.417,10h-1.114V5.722l-2.233-3V0h1.114V2.353l2.233,3Z" />
<path d="M-32.65,10h-1.114V6.185l-2.234-3V0h1.114V2.815l2.234,3Z" />
<rect
width="1.114"
height="4.463"
transform="translate(-35.999 5.537)"
/>
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default TraceIcon;

View File

@@ -17,25 +17,23 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class UploadFile extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
<g transform="translate(-63.686 -70.783)">
<path
className="a"
d="M74.736,79.879v1.95h-9.1v-1.95h-1.95v3.9h13v-3.9Z"
/>
<path
className="a"
d="M69.211,80.533h1.95V73.861h1.525l-2.5-3.078-2.5,3.078h1.525Z"
/>
</g>
</svg>
</SvgIcon>
);
}
}
const UploadFile = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 12.996">
<g transform="translate(-63.686 -70.783)">
<path
className="a"
d="M74.736,79.879v1.95h-9.1v-1.95h-1.95v3.9h13v-3.9Z"
/>
<path
className="a"
d="M69.211,80.533h1.95V73.861h1.525l-2.5-3.078-2.5,3.078h1.525Z"
/>
</g>
</svg>
</SvgIcon>
);
};
export default UploadFile;

View File

@@ -16,49 +16,48 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class UsageIcon extends React.Component {
render() {
return (
<SvgIcon viewBox="0 0 16.172 17.187">
<defs>
<linearGradient
id="a"
y1="0.5"
x2="1"
y2="0.5"
gradientUnits="objectBoundingBox"
>
<stop offset="0.044" stopColor="#362585" />
<stop offset="0.301" stopColor="#281b6f" />
<stop offset="1" stopColor="#1e1560" />
</linearGradient>
</defs>
<path
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.5px",
}}
d="M-4778.1,2239.582v6.425h6.425"
transform="translate(4787.594 -2239.582)"
/>
<path
fill={"#707070"}
d="M-4784.238,2247.532v-.581c0-.027.009-.054.012-.081.039-.313.055-.632.121-.939a6.744,6.744,0,0,1,3.064-4.441,6.514,6.514,0,0,1,3.293-1.032,6.923,6.923,0,0,1,2.667.423,6.793,6.793,0,0,1,4.119,4.333,6.053,6.053,0,0,1,.279,1.337c.006.083.014.164.021.247v.86c-.011.131-.018.261-.032.392a6.494,6.494,0,0,1-.626,2.147,6.807,6.807,0,0,1-4.044,3.528,6.052,6.052,0,0,1-1.663.3,6.576,6.576,0,0,1-2.565-.325,6.73,6.73,0,0,1-3.947-3.451,6.627,6.627,0,0,1-.658-2.288C-4784.212,2247.816-4784.225,2247.674-4784.238,2247.532Zm13.025-.306c-.024-.309-.021-.661-.082-1a6.206,6.206,0,0,0-1.658-3.293,6.153,6.153,0,0,0-4.1-1.9,5.984,5.984,0,0,0-2.476.355,6.188,6.188,0,0,0-4.134,5.708,6.453,6.453,0,0,0,.228,1.881,6.127,6.127,0,0,0,1.984,3.052,6.046,6.046,0,0,0,3.806,1.445,6.043,6.043,0,0,0,1.235-.065,6.249,6.249,0,0,0,3.783-2.2,6.2,6.2,0,0,0,1.352-3.048C-4771.228,2247.863-4771.233,2247.563-4771.212,2247.226Z"
transform="translate(4786.834 -2240.452)"
/>
<ellipse
style={{ opacity: 0.1, fill: "url(#a)" }}
cx="6.151"
cy="6.151"
rx="6.151"
ry="6.151"
transform="translate(0 4.886)"
/>
</SvgIcon>
);
}
}
const UsageIcon = () => {
return (
<SvgIcon viewBox="0 0 16.172 17.187">
<defs>
<linearGradient
id="a"
y1="0.5"
x2="1"
y2="0.5"
gradientUnits="objectBoundingBox"
>
<stop offset="0.044" stopColor="#362585" />
<stop offset="0.301" stopColor="#281b6f" />
<stop offset="1" stopColor="#1e1560" />
</linearGradient>
</defs>
<path
style={{
fill: "none",
stroke: "#707070",
strokeMiterlimit: 10,
strokeWidth: "0.5px",
}}
d="M-4778.1,2239.582v6.425h6.425"
transform="translate(4787.594 -2239.582)"
/>
<path
fill={"#707070"}
d="M-4784.238,2247.532v-.581c0-.027.009-.054.012-.081.039-.313.055-.632.121-.939a6.744,6.744,0,0,1,3.064-4.441,6.514,6.514,0,0,1,3.293-1.032,6.923,6.923,0,0,1,2.667.423,6.793,6.793,0,0,1,4.119,4.333,6.053,6.053,0,0,1,.279,1.337c.006.083.014.164.021.247v.86c-.011.131-.018.261-.032.392a6.494,6.494,0,0,1-.626,2.147,6.807,6.807,0,0,1-4.044,3.528,6.052,6.052,0,0,1-1.663.3,6.576,6.576,0,0,1-2.565-.325,6.73,6.73,0,0,1-3.947-3.451,6.627,6.627,0,0,1-.658-2.288C-4784.212,2247.816-4784.225,2247.674-4784.238,2247.532Zm13.025-.306c-.024-.309-.021-.661-.082-1a6.206,6.206,0,0,0-1.658-3.293,6.153,6.153,0,0,0-4.1-1.9,5.984,5.984,0,0,0-2.476.355,6.188,6.188,0,0,0-4.134,5.708,6.453,6.453,0,0,0,.228,1.881,6.127,6.127,0,0,0,1.984,3.052,6.046,6.046,0,0,0,3.806,1.445,6.043,6.043,0,0,0,1.235-.065,6.249,6.249,0,0,0,3.783-2.2,6.2,6.2,0,0,0,1.352-3.048C-4771.228,2247.863-4771.233,2247.563-4771.212,2247.226Z"
transform="translate(4786.834 -2240.452)"
/>
<ellipse
style={{ opacity: 0.1, fill: "url(#a)" }}
cx="6.151"
cy="6.151"
rx="6.151"
ry="6.151"
transform="translate(0 4.886)"
/>
</SvgIcon>
);
};
export default UsageIcon;

View File

@@ -16,30 +16,29 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class UsersIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6.131 10">
<g transform="translate(193 719.787)">
<g transform="translate(-193 -719.787)">
<path
d="M3,0h.131a3,3,0,0,1,3,3V5a0,0,0,0,1,0,0H0A0,0,0,0,1,0,5V3A3,3,0,0,1,3,0Z"
transform="translate(0 5)"
/>
<ellipse
cx="2.065"
cy="2"
rx="2.065"
ry="2"
transform="translate(1 0)"
/>
</g>
const UsersIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6.131 10">
<g transform="translate(193 719.787)">
<g transform="translate(-193 -719.787)">
<path
d="M3,0h.131a3,3,0,0,1,3,3V5a0,0,0,0,1,0,0H0A0,0,0,0,1,0,5V3A3,3,0,0,1,3,0Z"
transform="translate(0 5)"
/>
<ellipse
cx="2.065"
cy="2"
rx="2.065"
ry="2"
transform="translate(1 0)"
/>
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default UsersIcon;

View File

@@ -16,24 +16,23 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class WarpIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(43 439)">
<path d="M27.5,10" transform="translate(-61 -439)" />
<rect width="1.5" height="2" transform="translate(-43 -431)" />
<rect width="1.5" height="6" transform="translate(-38.75 -435)" />
<rect width="1.5" height="8" transform="translate(-36.625 -437)" />
<rect width="1.5" height="4" transform="translate(-40.875 -433)" />
<rect width="1.5" height="10" transform="translate(-34.5 -439)" />
<path d="M18.5,10" transform="translate(-61 -439)" />
</g>
</svg>
</SvgIcon>
);
}
}
const WarpIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(43 439)">
<path d="M27.5,10" transform="translate(-61 -439)" />
<rect width="1.5" height="2" transform="translate(-43 -431)" />
<rect width="1.5" height="6" transform="translate(-38.75 -435)" />
<rect width="1.5" height="8" transform="translate(-36.625 -437)" />
<rect width="1.5" height="4" transform="translate(-40.875 -433)" />
<rect width="1.5" height="10" transform="translate(-34.5 -439)" />
<path d="M18.5,10" transform="translate(-61 -439)" />
</g>
</svg>
</SvgIcon>
);
};
export default WarpIcon;

View File

@@ -16,44 +16,43 @@
import React from "react";
import { SvgIcon } from "@material-ui/core";
class WatchIcon extends React.Component {
render() {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(213 720)">
<g transform="translate(-105 -720)">
<rect width="1.5" height="4" transform="translate(-108)" />
<rect width="1.5" height="4" transform="translate(-108 6)" />
<rect width="1.5" height="4" transform="translate(-99.5 6)" />
<rect width="1.5" height="4" transform="translate(-99.5)" />
<rect
width="1.5"
height="4"
transform="translate(-98) rotate(90)"
/>
<rect
width="1.5"
height="4"
transform="translate(-104) rotate(90)"
/>
<rect
width="1.5"
height="4"
transform="translate(-104 8.5) rotate(90)"
/>
<rect
width="1.5"
height="4"
transform="translate(-98 8.5) rotate(90)"
/>
<circle cx="2" cy="2" r="2" transform="translate(-105 3)" />
</g>
const WatchIcon = () => {
return (
<SvgIcon>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 10">
<g transform="translate(213 720)">
<g transform="translate(-105 -720)">
<rect width="1.5" height="4" transform="translate(-108)" />
<rect width="1.5" height="4" transform="translate(-108 6)" />
<rect width="1.5" height="4" transform="translate(-99.5 6)" />
<rect width="1.5" height="4" transform="translate(-99.5)" />
<rect
width="1.5"
height="4"
transform="translate(-98) rotate(90)"
/>
<rect
width="1.5"
height="4"
transform="translate(-104) rotate(90)"
/>
<rect
width="1.5"
height="4"
transform="translate(-104 8.5) rotate(90)"
/>
<rect
width="1.5"
height="4"
transform="translate(-98 8.5) rotate(90)"
/>
<circle cx="2" cy="2" r="2" transform="translate(-105 3)" />
</g>
</svg>
</SvgIcon>
);
}
}
</g>
</svg>
</SvgIcon>
);
};
export default WatchIcon;

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import React from "react";
import React, { useState } from "react";
import {
Button,
Dialog,
@@ -48,106 +48,89 @@ interface IDeleteBucketState {
deleteError: string;
}
class DeleteBucket extends React.Component<
IDeleteBucketProps,
IDeleteBucketState
> {
state: IDeleteBucketState = {
deleteLoading: false,
deleteError: "",
};
const DeleteBucket = ({
classes,
closeDeleteModalAndRefresh,
deleteOpen,
selectedBucket,
}: IDeleteBucketProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const [deleteError, setDeleteError] = useState<string>("");
removeRecord() {
const { deleteLoading } = this.state;
const { selectedBucket } = this.props;
const removeRecord = () => {
if (deleteLoading) {
return;
}
this.setState({ deleteLoading: true }, () => {
api
.invoke("DELETE", `/api/v1/buckets/${selectedBucket}`, {
name: selectedBucket,
})
.then((res: BucketList) => {
this.setState(
{
deleteLoading: false,
deleteError: "",
},
() => {
this.props.closeDeleteModalAndRefresh(true);
}
);
})
.catch((err) => {
this.setState({
deleteLoading: false,
deleteError: err,
});
});
});
}
setDeleteLoading(true);
render() {
const { classes, deleteOpen, selectedBucket } = this.props;
const { deleteLoading, deleteError } = this.state;
api
.invoke("DELETE", `/api/v1/buckets/${selectedBucket}`, {
name: selectedBucket,
})
.then((res: BucketList) => {
setDeleteLoading(false);
setDeleteError("");
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setDeleteError(err);
});
};
return (
<Dialog
open={deleteOpen}
onClose={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Bucket</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete bucket <b>{selectedBucket}</b>?{" "}
<br />A bucket can only be deleted if it's empty.
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
this.removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
}
}
return (
<Dialog
open={deleteOpen}
onClose={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Bucket</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete bucket <b>{selectedBucket}</b>? <br />
A bucket can only be deleted if it's empty.
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(DeleteBucket);

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import React from "react";
import React, { useState } from "react";
import {
Button,
Dialog,
@@ -48,18 +48,17 @@ interface IDeleteObjectState {
deleteError: string;
}
class DeleteObject extends React.Component<
IDeleteObjectProps,
IDeleteObjectState
> {
state: IDeleteObjectState = {
deleteLoading: false,
deleteError: "",
};
const DeleteObject = ({
classes,
closeDeleteModalAndRefresh,
deleteOpen,
selectedBucket,
selectedObject,
}: IDeleteObjectProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const [deleteError, setDeleteError] = useState<string>("");
removeRecord() {
const { deleteLoading } = this.state;
const { selectedObject, selectedBucket } = this.props;
const removeRecord = () => {
if (deleteLoading) {
return;
}
@@ -67,94 +66,77 @@ class DeleteObject extends React.Component<
if (selectedObject.endsWith("/")) {
recursive = true;
}
setDeleteLoading(true);
api
.invoke(
"DELETE",
`/api/v1/buckets/${selectedBucket}/objects?path=${selectedObject}&recursive=${recursive}`
)
.then((res: any) => {
setDeleteLoading(false);
setDeleteError("");
this.setState({ deleteLoading: true }, () => {
api
.invoke(
"DELETE",
`/api/v1/buckets/${selectedBucket}/objects?path=${selectedObject}&recursive=${recursive}`
)
.then((res: any) => {
this.setState(
{
deleteLoading: false,
deleteError: "",
},
() => {
this.props.closeDeleteModalAndRefresh(true);
}
);
})
.catch((err) => {
this.setState({
deleteLoading: false,
deleteError: err,
});
});
});
}
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setDeleteError(err);
});
};
render() {
const { classes, deleteOpen, selectedObject } = this.props;
const { deleteLoading, deleteError } = this.state;
return (
<Dialog
open={deleteOpen}
onClose={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete: <b>{selectedObject}</b>?{" "}
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
this.setState({ deleteError: "" }, () => {
this.removeRecord();
});
}}
color="secondary"
disabled={deleteLoading}
>
Delete
</Button>
</DialogActions>
</Dialog>
);
}
}
return (
<Dialog
open={deleteOpen}
onClose={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete: <b>{selectedObject}</b>?{" "}
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
setDeleteError("");
removeRecord();
}}
color="secondary"
disabled={deleteLoading}
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(DeleteObject);

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React, { ChangeEvent } from "react";
import React, { ChangeEvent, useEffect, useState } from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Button, LinearProgress } from "@material-ui/core";
@@ -68,250 +68,225 @@ interface IAddEventState {
arnList: string[];
}
class AddEvent extends React.Component<IAddEventProps, IAddEventState> {
state: IAddEventState = {
addLoading: false,
addError: "",
prefix: "",
suffix: "",
arn: "",
selectedEvents: [],
arnList: [],
};
const AddEvent = ({
classes,
open,
selectedBucket,
closeModalAndRefresh,
}: IAddEventProps) => {
const [addLoading, setAddLoading] = useState<boolean>(false);
const [addError, setAddError] = useState<string>("");
const [prefix, setPrefix] = useState<string>("");
const [suffix, setSuffix] = useState<string>("");
const [arn, setArn] = useState<string>("");
const [selectedEvents, setSelectedEvents] = useState<string[]>([]);
const [arnList, setArnList] = useState<string[]>([]);
addRecord(event: React.FormEvent) {
const addRecord = (event: React.FormEvent) => {
event.preventDefault();
const { prefix, suffix, addLoading, arn, selectedEvents } = this.state;
const { selectedBucket } = this.props;
if (addLoading) {
return;
}
this.setState({ addLoading: true }, () => {
api
.invoke("POST", `/api/v1/buckets/${selectedBucket}/events`, {
configuration: {
arn: arn,
events: selectedEvents,
prefix: prefix,
suffix: suffix,
},
ignoreExisting: true,
})
.then((res) => {
this.setState(
{
addLoading: false,
addError: "",
},
() => {
this.props.closeModalAndRefresh();
}
);
})
.catch((err) => {
this.setState({
addLoading: false,
addError: err,
});
});
});
}
setAddLoading(true);
api
.invoke("POST", `/api/v1/buckets/${selectedBucket}/events`, {
configuration: {
arn: arn,
events: selectedEvents,
prefix: prefix,
suffix: suffix,
},
ignoreExisting: true,
})
.then((res) => {
setAddLoading(false);
setAddError("");
closeModalAndRefresh();
})
.catch((err) => {
setAddLoading(false);
setAddError(err);
});
};
fetchArnList() {
this.setState({ addLoading: true }, () => {
api
.invoke("GET", `/api/v1/admin/arns`)
.then((res: ArnList) => {
let arns: string[] = [];
if (res.arns !== null) {
arns = res.arns;
}
this.setState({
addLoading: false,
arnList: arns,
addError: "",
});
})
.catch((err: any) => {
this.setState({ addLoading: false, addError: err });
});
});
}
const fetchArnList = () => {
setAddLoading(true);
api
.invoke("GET", `/api/v1/admin/arns`)
.then((res: ArnList) => {
let arns: string[] = [];
if (res.arns !== null) {
arns = res.arns;
}
setAddLoading(false);
setAddError("");
setArnList(arns);
})
.catch((err: any) => {
setAddLoading(false);
setAddError(err);
});
};
componentDidMount(): void {
this.fetchArnList();
}
useEffect(() => {
fetchArnList();
}, []);
render() {
const { classes, open } = this.props;
const {
addLoading,
addError,
arn,
selectedEvents,
arnList,
prefix,
suffix,
} = this.state;
const events = [
{ label: "PUT - Object Uploaded", value: "put" },
{ label: "GET - Object accessed", value: "get" },
{ label: "DELETE - Object Deleted", value: "delete" },
];
const events = [
{ label: "PUT - Object Uploaded", value: "put" },
{ label: "GET - Object accessed", value: "get" },
{ label: "DELETE - Object Deleted", value: "delete" },
];
const handleClick = (
event: React.MouseEvent<unknown> | ChangeEvent<unknown>,
name: string
) => {
const selectedIndex = selectedEvents.indexOf(name);
let newSelected: string[] = [];
const handleClick = (
event: React.MouseEvent<unknown> | ChangeEvent<unknown>,
name: string
) => {
const selectedIndex = selectedEvents.indexOf(name);
let newSelected: string[] = [];
if (selectedIndex === -1) {
newSelected = newSelected.concat(selectedEvents, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selectedEvents.slice(1));
} else if (selectedIndex === selectedEvents.length - 1) {
newSelected = newSelected.concat(selectedEvents.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selectedEvents.slice(0, selectedIndex),
selectedEvents.slice(selectedIndex + 1)
);
}
setSelectedEvents(newSelected);
};
if (selectedIndex === -1) {
newSelected = newSelected.concat(selectedEvents, name);
} else if (selectedIndex === 0) {
newSelected = newSelected.concat(selectedEvents.slice(1));
} else if (selectedIndex === selectedEvents.length - 1) {
newSelected = newSelected.concat(selectedEvents.slice(0, -1));
} else if (selectedIndex > 0) {
newSelected = newSelected.concat(
selectedEvents.slice(0, selectedIndex),
selectedEvents.slice(selectedIndex + 1)
);
}
const arnValues = arnList.map((arnConstant) => ({
label: arnConstant,
value: arnConstant,
}));
this.setState({ selectedEvents: newSelected });
};
const arnValues = arnList.map((arnConstant) => ({
label: arnConstant,
value: arnConstant,
}));
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
this.setState({ addError: "" }, () => {
this.props.closeModalAndRefresh();
});
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
setAddError("");
closeModalAndRefresh();
}}
title="Subscribe To Event"
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
addRecord(e);
}}
title="Subscribe To Event"
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
this.addRecord(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<SelectWrapper
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
this.setState({ arn: e.target.value as string });
}}
id="select-access-policy"
name="select-access-policy"
label={"ARN"}
value={arn}
options={arnValues}
/>
</Grid>
<Grid item xs={12}>
<Table size="medium">
<TableHead className={classes.minTableHeader}>
<TableRow>
<TableCell>Select</TableCell>
<TableCell>Event</TableCell>
</TableRow>
</TableHead>
<TableBody>
{events.map((row) => (
<TableRow
key={`group-${row.value}`}
onClick={(event) => handleClick(event, row.value)}
>
<TableCell padding="checkbox">
<Checkbox
value={row.value}
color="primary"
inputProps={{
"aria-label": "secondary checkbox",
}}
onChange={(event) => handleClick(event, row.value)}
checked={selectedEvents.includes(row.value)}
/>
</TableCell>
<TableCell className={classes.wrapCell}>
{row.label}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="prefix-input"
name="prefix-input"
label="Prefix"
value={prefix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ prefix: e.target.value });
}}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="suffix-input"
name="suffix-input"
label="Suffix"
value={suffix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ suffix: e.target.value });
}}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Grid item xs={12}>
<SelectWrapper
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setArn(e.target.value as string);
}}
id="select-access-policy"
name="select-access-policy"
label={"ARN"}
value={arn}
options={arnValues}
/>
</Grid>
<Grid item xs={12}>
<Table size="medium">
<TableHead className={classes.minTableHeader}>
<TableRow>
<TableCell>Select</TableCell>
<TableCell>Event</TableCell>
</TableRow>
</TableHead>
<TableBody>
{events.map((row) => (
<TableRow
key={`group-${row.value}`}
onClick={(event) => handleClick(event, row.value)}
>
<TableCell padding="checkbox">
<Checkbox
value={row.value}
color="primary"
inputProps={{
"aria-label": "secondary checkbox",
}}
onChange={(event) => handleClick(event, row.value)}
checked={selectedEvents.includes(row.value)}
/>
</TableCell>
<TableCell className={classes.wrapCell}>
{row.label}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="prefix-input"
name="prefix-input"
label="Prefix"
value={prefix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setPrefix(e.target.value);
}}
/>
</Grid>
<Grid item xs={12}>
<InputBoxWrapper
id="suffix-input"
name="suffix-input"
label="Suffix"
value={suffix}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSuffix(e.target.value);
}}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
</Grid>
</form>
</ModalWrapper>
);
}
}
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(AddEvent);

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, { useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import get from "lodash/get";
import {
@@ -45,23 +45,17 @@ interface IDeleteEventProps {
bucketEvent: BucketEvent | null;
}
interface IDeleteEventState {
deleteLoading: boolean;
deleteError: string;
}
const DeleteEvent = ({
classes,
closeDeleteModalAndRefresh,
deleteOpen,
selectedBucket,
bucketEvent,
}: IDeleteEventProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const [deleteError, setDeleteError] = useState<string>("");
class DeleteEvent extends React.Component<
IDeleteEventProps,
IDeleteEventState
> {
state: IDeleteEventState = {
deleteLoading: false,
deleteError: "",
};
removeRecord() {
const { deleteLoading } = this.state;
const { selectedBucket, bucketEvent } = this.props;
const removeRecord = () => {
if (deleteLoading) {
return;
}
@@ -69,99 +63,84 @@ class DeleteEvent extends React.Component<
return;
}
this.setState({ deleteLoading: true }, () => {
const events = get(bucketEvent, "events", []);
const prefix = get(bucketEvent, "prefix", "");
const suffix = get(bucketEvent, "suffix", "");
api
.invoke(
"DELETE",
`/api/v1/buckets/${selectedBucket}/events/${bucketEvent.arn}`,
{
events,
prefix,
suffix,
}
)
.then((res: BucketList) => {
this.setState(
{
deleteLoading: false,
deleteError: "",
},
() => {
this.props.closeDeleteModalAndRefresh(true);
}
);
})
.catch((err) => {
this.setState({
deleteLoading: false,
deleteError: err,
});
});
});
}
setDeleteLoading(true);
render() {
const { classes, deleteOpen } = this.props;
const { deleteLoading, deleteError } = this.state;
const events = get(bucketEvent, "events", []);
const prefix = get(bucketEvent, "prefix", "");
const suffix = get(bucketEvent, "suffix", "");
api
.invoke(
"DELETE",
`/api/v1/buckets/${selectedBucket}/events/${bucketEvent.arn}`,
{
events,
prefix,
suffix,
}
)
.then((res: BucketList) => {
setDeleteLoading(false);
setDeleteError("");
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setDeleteError(err);
});
};
return (
<Dialog
open={deleteOpen}
onClose={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Bucket</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this event?
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
this.removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
}
}
return (
<Dialog
open={deleteOpen}
onClose={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Bucket</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete this event?
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(DeleteEvent);

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, { useState } from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Button, LinearProgress } from "@material-ui/core";
@@ -51,30 +51,19 @@ interface IEnableBucketEncryptionProps {
closeModalAndRefresh: () => void;
}
interface IEnableBucketEncryptionState {
loading: boolean;
encryptionError: string;
kmsKeyID: string;
suffix: string;
encryptionType: string;
}
const EnableBucketEncryption = ({
classes,
open,
selectedBucket,
closeModalAndRefresh,
}: IEnableBucketEncryptionProps) => {
const [loading, setLoading] = useState<boolean>(false);
const [encryptionError, setEncryptionError] = useState<string>("");
const [kmsKeyID, setKmsKeyID] = useState<string>("");
const [encryptionType, setEncryptionType] = useState<string>("sse-s3");
class EnableBucketEncryption extends React.Component<
IEnableBucketEncryptionProps,
IEnableBucketEncryptionState
> {
state: IEnableBucketEncryptionState = {
loading: false,
encryptionError: "",
kmsKeyID: "",
suffix: "",
encryptionType: "sse-s3",
};
enableBucketEncryption(event: React.FormEvent) {
const enableBucketEncryption = (event: React.FormEvent) => {
event.preventDefault();
const { kmsKeyID, loading, encryptionType } = this.state;
const { selectedBucket } = this.props;
if (loading) {
return;
}
@@ -84,116 +73,106 @@ class EnableBucketEncryption extends React.Component<
kmsKeyID: kmsKeyID,
})
.then(() => {
this.setState(
{
loading: false,
encryptionError: "",
},
() => {
this.props.closeModalAndRefresh();
}
);
setLoading(false);
setEncryptionError("");
closeModalAndRefresh();
})
.catch((err: any) => {
this.setState({ encryptionError: err });
setLoading(false);
setEncryptionError(err);
});
}
};
render() {
const { classes, open } = this.props;
const { loading, encryptionError, kmsKeyID, encryptionType } = this.state;
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
this.setState({ encryptionError: "" }, () => {
this.props.closeModalAndRefresh();
});
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
setEncryptionError("");
closeModalAndRefresh();
}}
title="Enable Bucket Encryption"
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
enableBucketEncryption(e);
}}
title="Enable Bucket Encryption"
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
this.enableBucketEncryption(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{encryptionError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{encryptionError}
</Typography>
</Grid>
)}
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{encryptionError !== "" && (
<Grid item xs={12}>
<SelectWrapper
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
this.setState({ encryptionType: e.target.value as string });
}}
id="select-encryption-type"
name="select-encryption-type"
label={"Encryption Type"}
value={encryptionType}
options={[
{
label: "SSE-S3",
value: "sse-s3",
},
{
label: "SSE-KMS",
value: "sse-kms",
},
]}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
{encryptionType === "sse-kms" && (
<Grid item xs={12}>
<InputBoxWrapper
id="kms-key-id"
name="kms-key-id"
label="KMS Key ID"
value={kmsKeyID}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ kmsKeyID: e.target.value });
}}
/>
</Grid>
)}
<Grid item xs={12}>
<br />
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading}
>
Save
</Button>
</Grid>
{loading && (
<Grid item xs={12}>
<LinearProgress />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{encryptionError}
</Typography>
</Grid>
)}
<Grid item xs={12}>
<SelectWrapper
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setEncryptionType(e.target.value as string);
}}
id="select-encryption-type"
name="select-encryption-type"
label={"Encryption Type"}
value={encryptionType}
options={[
{
label: "SSE-S3",
value: "sse-s3",
},
{
label: "SSE-KMS",
value: "sse-kms",
},
]}
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
{encryptionType === "sse-kms" && (
<Grid item xs={12}>
<InputBoxWrapper
id="kms-key-id"
name="kms-key-id"
label="KMS Key ID"
value={kmsKeyID}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setKmsKeyID(e.target.value);
}}
/>
</Grid>
)}
<Grid item xs={12}>
<br />
</Grid>
</Grid>
</form>
</ModalWrapper>
);
}
}
<Grid item xs={12} className={classes.buttonContainer}>
<Button
type="submit"
variant="contained"
color="primary"
disabled={loading}
>
Save
</Button>
</Grid>
{loading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(EnableBucketEncryption);

View File

@@ -13,7 +13,7 @@
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, { useEffect, useState } from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Button, LinearProgress } from "@material-ui/core";
@@ -39,130 +39,106 @@ interface ISetAccessPolicyProps {
closeModalAndRefresh: () => void;
}
interface ISetAccessPolicyState {
addLoading: boolean;
addError: string;
accessPolicy: string;
}
class SetAccessPolicy extends React.Component<
ISetAccessPolicyProps,
ISetAccessPolicyState
> {
state: ISetAccessPolicyState = {
addLoading: false,
addError: "",
accessPolicy: "",
};
addRecord(event: React.FormEvent) {
const SetAccessPolicy = ({
classes,
open,
bucketName,
actualPolicy,
closeModalAndRefresh,
}: ISetAccessPolicyProps) => {
const [addLoading, setAddLoading] = useState<boolean>(false);
const [addError, setAddError] = useState<string>("");
const [accessPolicy, setAccessPolicy] = useState<string>("");
const addRecord = (event: React.FormEvent) => {
event.preventDefault();
const { addLoading, accessPolicy } = this.state;
const { bucketName } = this.props;
if (addLoading) {
return;
}
this.setState({ addLoading: true }, () => {
api
.invoke("PUT", `/api/v1/buckets/${bucketName}/set-policy`, {
access: accessPolicy,
})
.then((res) => {
this.setState(
{
addLoading: false,
addError: "",
},
() => {
this.props.closeModalAndRefresh();
}
);
})
.catch((err) => {
this.setState({
addLoading: false,
addError: err,
});
});
});
}
setAddLoading(true);
api
.invoke("PUT", `/api/v1/buckets/${bucketName}/set-policy`, {
access: accessPolicy,
})
.then((res) => {
setAddLoading(false);
setAddError("");
closeModalAndRefresh();
})
.catch((err) => {
setAddLoading(false);
setAddError(err);
});
};
componentDidMount() {
const { actualPolicy } = this.props;
useEffect(() => {
setAccessPolicy(actualPolicy);
}, []);
this.setState({ accessPolicy: actualPolicy });
}
render() {
const { classes, open } = this.props;
const { addLoading, addError, accessPolicy } = this.state;
return (
<ModalWrapper
title="Change Access Policy"
modalOpen={open}
onClose={() => {
this.setState({ addError: "" }, () => {
this.props.closeModalAndRefresh();
});
return (
<ModalWrapper
title="Change Access Policy"
modalOpen={open}
onClose={() => {
setAddError("");
closeModalAndRefresh();
}}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
addRecord(e);
}}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
this.addRecord(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<SelectWrapper
value={accessPolicy}
label="Access Policy"
id="select-access-policy"
name="select-access-policy"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
this.setState({ accessPolicy: e.target.value as string });
}}
options={[
{ value: "PRIVATE", label: "Private" },
{ value: "PUBLIC", label: "Public" },
]}
/>
</Grid>
</Grid>
<Grid item xs={12}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={addLoading}
>
Set
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Grid item xs={12}>
<SelectWrapper
value={accessPolicy}
label="Access Policy"
id="select-access-policy"
name="select-access-policy"
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
setAccessPolicy(e.target.value as string);
}}
options={[
{ value: "PRIVATE", label: "Private" },
{ value: "PUBLIC", label: "Public" },
]}
/>
</Grid>
</Grid>
</form>
</ModalWrapper>
);
}
}
<Grid item xs={12}>
<Button
type="submit"
variant="contained"
color="primary"
fullWidth
disabled={addLoading}
>
Set
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(SetAccessPolicy);

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, { useEffect, useState } from "react";
import get from "lodash/get";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import Paper from "@material-ui/core/Paper";
@@ -201,455 +201,417 @@ interface IViewBucketState {
encryptionEnabled: boolean;
}
class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
state: IViewBucketState = {
info: null,
records: [],
replicationRules: [],
loadingBucket: true,
loadingEvents: true,
loadingSize: true,
error: "",
deleteError: "",
errBucket: "",
setAccessPolicyScreenOpen: false,
curTab: 0,
addScreenOpen: false,
enableEncryptionScreenOpen: false,
deleteOpen: false,
selectedBucket: "",
selectedEvent: null,
bucketSize: "0",
errorSize: "",
replicationSet: false,
openSetReplication: false,
isVersioned: false,
encryptionEnabled: false,
const ViewBucket = ({ classes, match }: IViewBucketProps) => {
const [info, setInfo] = useState<BucketInfo | null>(null);
const [records, setRecords] = useState<BucketEvent[]>([]);
const [replicationRules, setReplicationRules] = useState<
BucketReplicationRule[]
>([]);
const [loadingBucket, setLoadingBucket] = useState<boolean>(true);
const [loadingEvents, setLoadingEvents] = useState<boolean>(true);
const [loadingSize, setLoadingSize] = useState<boolean>(true);
const [error, setError] = useState<string>("");
const [deleteError, setDeleteError] = useState<string>("");
const [errBucket, setErrBucket] = useState<string>("");
const [accessPolicyScreenOpen, setAccessPolicyScreenOpen] = useState<boolean>(
false
);
const [curTab, setCurTab] = useState<number>(0);
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
const [
enableEncryptionScreenOpen,
setEnableEncryptionScreenOpen,
] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [selectedBucket, setSelectedBucket] = useState<string>("");
const [selectedEvent, setSelectedEvent] = useState<BucketEvent | null>(null);
const [bucketSize, setBucketSize] = useState<string>("0");
const [errorSize, setErrorSize] = useState<string>("");
const [replicationSet, setReplicationSet] = useState<boolean>(false);
const [openSetReplication, setOpenSetReplication] = useState<boolean>(false);
const [isVersioned, setIsVersioned] = useState<boolean>(false);
const [encryptionEnabled, setEncryptionEnabled] = useState<boolean>(false);
const fetchEvents = () => {
setLoadingBucket(true);
const bucketName = match.params["bucketName"];
api
.invoke("GET", `/api/v1/buckets/${bucketName}/events`)
.then((res: BucketEventList) => {
const events = get(res, "events", []);
setLoadingEvents(false);
setError("");
setRecords(events || []);
})
.catch((err: any) => {
setLoadingEvents(false);
setError(err);
});
api
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
.then((res: BucketVersioning) => {
setIsVersioned(res.is_versioned);
})
.catch((err: any) => {
setError(err);
});
api
.invoke("GET", `/api/v1/buckets/${bucketName}/replication`)
.then((res: BucketReplication) => {
const r = res.rules ? res.rules : [];
setReplicationRules(r);
})
.catch((err: any) => {
setError(err);
});
};
fetchEvents() {
this.setState({ loadingBucket: true }, () => {
const { match } = this.props;
const bucketName = match.params["bucketName"];
api
.invoke("GET", `/api/v1/buckets/${bucketName}/events`)
.then((res: BucketEventList) => {
const events = get(res, "events", []);
this.setState({
loadingEvents: false,
records: events || [],
error: "",
});
})
.catch((err: any) => {
this.setState({ loadingEvents: false, error: err });
});
api
.invoke("GET", `/api/v1/buckets/${bucketName}/versioning`)
.then((res: BucketVersioning) => {
this.setState({
isVersioned: res.is_versioned,
});
})
.catch((err: any) => {
this.setState({ error: err });
});
api
.invoke("GET", `/api/v1/buckets/${bucketName}/replication`)
.then((res: BucketReplication) => {
const r = res.rules ? res.rules : [];
this.setState({
replicationRules: r,
});
})
.catch((err: any) => {
this.setState({ error: err });
});
});
}
fetchBucketsSize() {
const { match } = this.props;
const fetchBucketsSize = () => {
const bucketName = match.params["bucketName"];
setLoadingSize(true);
api
.invoke("GET", `/api/v1/buckets`)
.then((res: BucketList) => {
const resBuckets = get(res, "buckets", []);
this.setState({ loadingSize: true }, () => {
api
.invoke("GET", `/api/v1/buckets`)
.then((res: BucketList) => {
const resBuckets = get(res, "buckets", []);
const bucketInfo = resBuckets.find(
(bucket) => bucket.name === bucketName
);
const bucketInfo = resBuckets.find(
(bucket) => bucket.name === bucketName
);
const size = get(bucketInfo, "size", "0");
const size = get(bucketInfo, "size", "0");
setLoadingSize(false);
setErrorSize("");
setBucketSize(size);
})
.catch((err: any) => {
setLoadingSize(false);
setErrorSize(err);
});
};
this.setState({
loadingSize: false,
errorSize: "",
bucketSize: size,
});
})
.catch((err: any) => {
this.setState({ loadingSize: false, errorSize: err });
});
});
}
loadInfo() {
const { match } = this.props;
const loadInfo = () => {
const bucketName = match.params["bucketName"];
this.setState({ loadingBucket: true }, () => {
api
.invoke("GET", `/api/v1/buckets/${bucketName}`)
.then((res: BucketInfo) => {
this.setState({ loadingBucket: false, info: res });
})
.catch((err) => {
this.setState({ loadingBucket: false, errBucket: err });
});
});
}
setLoadingBucket(true);
fetchBucketEncryptionInfo() {
const { match } = this.props;
api
.invoke("GET", `/api/v1/buckets/${bucketName}`)
.then((res: BucketInfo) => {
setLoadingBucket(false);
setInfo(res);
})
.catch((err) => {
setLoadingBucket(false);
setErrBucket(err);
});
};
const fetchBucketEncryptionInfo = () => {
const bucketName = match.params["bucketName"];
api
.invoke("GET", `/api/v1/buckets/${bucketName}/encryption/info`)
.then((res: BucketEncryptionInfo) => {
if (res.algorithm) {
this.setState({ encryptionEnabled: true });
setEncryptionEnabled(true);
}
})
.catch((err) => {
console.log(err);
});
}
};
closeAddModalAndRefresh() {
this.setState({ setAccessPolicyScreenOpen: false }, () => {
this.loadInfo();
});
}
const closeAddModalAndRefresh = () => {
setAccessPolicyScreenOpen(false);
loadInfo();
};
closeDeleteModalAndRefresh(refresh: boolean) {
this.setState({ deleteOpen: false }, () => {
if (refresh) {
this.fetchEvents();
}
});
}
componentDidMount(): void {
this.loadInfo();
this.fetchEvents();
this.fetchBucketsSize();
this.fetchBucketEncryptionInfo();
}
bucketFilter(): void {}
render() {
const { classes, match } = this.props;
const {
info,
records,
setAccessPolicyScreenOpen,
loadingEvents,
loadingBucket,
deleteOpen,
addScreenOpen,
enableEncryptionScreenOpen,
selectedEvent,
bucketSize,
loadingSize,
openSetReplication,
isVersioned,
replicationRules,
curTab,
encryptionEnabled,
} = this.state;
const bucketName = match.params["bucketName"];
const confirmDeleteEvent = (evnt: BucketEvent) => {
this.setState({ deleteOpen: true, selectedEvent: evnt });
};
let accessPolicy = "n/a";
if (info !== null) {
accessPolicy = info.access;
const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
fetchEvents();
}
};
const eventsDisplay = (events: string[]) => {
return <React.Fragment>{events.join(", ")}</React.Fragment>;
};
useEffect(() => {
loadInfo();
fetchEvents();
fetchBucketsSize();
fetchBucketEncryptionInfo();
}, []);
const ruleDestDisplay = (events: BucketReplicationDestination) => {
return (
<React.Fragment>
{events.bucket.replace("arn:aws:s3:::", "")}
</React.Fragment>
);
};
const bucketName = match.params["bucketName"];
const ruleDelDisplay = (events: BucketReplicationRuleDeleteMarker) => {
return <React.Fragment>{events.status}</React.Fragment>;
};
const confirmDeleteEvent = (evnt: BucketEvent) => {
setDeleteOpen(true);
setSelectedEvent(evnt);
};
const setOpenReplicationOpen = (open = false) => {
this.setState({ openSetReplication: open });
};
let accessPolicy = "n/a";
const handleEncryptionCheckbox = (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (event.target.checked) {
this.setState({ enableEncryptionScreenOpen: true });
} else {
api
.invoke("POST", `/api/v1/buckets/${bucketName}/encryption/disable`)
.then(() => {
this.setState({ encryptionEnabled: false });
})
.catch((err: any) => {
this.setState({ error: err });
});
}
};
if (info !== null) {
accessPolicy = info.access;
}
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
const eventsDisplay = (events: string[]) => {
return <React.Fragment>{events.join(", ")}</React.Fragment>;
};
const ruleDestDisplay = (events: BucketReplicationDestination) => {
return (
<React.Fragment>
{addScreenOpen && (
<AddEvent
open={addScreenOpen}
selectedBucket={bucketName}
closeModalAndRefresh={() => {
this.setState({ addScreenOpen: false });
this.fetchEvents();
}}
/>
)}
{enableEncryptionScreenOpen && (
<EnableBucketEncryption
open={enableEncryptionScreenOpen}
selectedBucket={bucketName}
closeModalAndRefresh={() => {
this.setState({ enableEncryptionScreenOpen: false });
this.fetchBucketEncryptionInfo();
}}
/>
)}
{setAccessPolicyScreenOpen && (
<SetAccessPolicy
bucketName={bucketName}
open={setAccessPolicyScreenOpen}
actualPolicy={accessPolicy}
closeModalAndRefresh={() => {
this.closeAddModalAndRefresh();
}}
/>
)}
{openSetReplication && (
<AddReplicationModal
closeModalAndRefresh={() => {
setOpenReplicationOpen(false);
this.fetchEvents();
}}
open={openSetReplication}
bucketName={bucketName}
/>
)}
<PageHeader label={`Bucket > ${match.params["bucketName"]}`} />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12}>
<div className={classes.headerContainer}>
<div>
<Paper className={classes.paperContainer}>
<div className={classes.gridContainer}>
<div>Access Policy:</div>
<div className={classes.capitalizeFirst}>
{loadingBucket ? (
<CircularProgress
color="primary"
size={16}
variant="indeterminate"
/>
) : (
accessPolicy.toLowerCase()
)}
</div>
<div>Reported Usage:</div>
<div>
{loadingSize ? (
<CircularProgress
color="primary"
size={16}
variant="indeterminate"
/>
) : (
niceBytes(bucketSize)
)}
</div>
<div>Replication:</div>
<div className={classes.doubleElement}>
<span>{replicationRules.length ? "Yes" : "No"}</span>
</div>
<div>Versioning:</div>
<div>{isVersioned ? "Yes" : "No"}&nbsp;</div>
<div>Encryption:</div>
<div>
<Checkbox
color="primary"
inputProps={{
"aria-label": "secondary checkbox",
}}
onChange={(event) => handleEncryptionCheckbox(event)}
checked={encryptionEnabled}
/>
</div>
</div>
</Paper>
</div>
<div className={classes.masterActions}>
<div>
<Button
variant="contained"
color="primary"
fullWidth
size="medium"
onClick={() => {
this.setState({
setAccessPolicyScreenOpen: true,
});
}}
>
Change Access Policy
</Button>
</div>
</div>
</div>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
<Grid item xs={6}>
<Tabs
value={curTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
this.setState({ curTab: newValue });
}}
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
>
<Tab label="Events" {...a11yProps(0)} />
<Tab label="Replication" {...a11yProps(1)} />
</Tabs>
</Grid>
<Grid item xs={6} className={classes.actionsTray}>
{curTab === 0 && (
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
size="medium"
onClick={() => {
this.setState({
addScreenOpen: true,
});
}}
>
Subscribe to Event
</Button>
)}
{curTab === 1 && (
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
size="medium"
onClick={() => {
this.setState({
openSetReplication: true,
});
}}
>
Add Replication Rule
</Button>
)}
</Grid>
</Grid>
<Grid item xs={12}>
<TabPanel index={0} value={curTab}>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "SQS", elementKey: "arn" },
{
label: "Events",
elementKey: "events",
renderFunction: eventsDisplay,
},
{ label: "Prefix", elementKey: "prefix" },
{ label: "Suffix", elementKey: "suffix" },
]}
isLoading={loadingEvents}
records={records}
entityName="Events"
idField="id"
/>
</TabPanel>
<TabPanel index={1} value={curTab}>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "ID", elementKey: "id" },
{
label: "Priority",
elementKey: "priority",
},
{
label: "Destination",
elementKey: "destination",
renderFunction: ruleDestDisplay,
},
{
label: "Delete Replication",
elementKey: "delete_marker_replication",
renderFunction: ruleDelDisplay,
},
{ label: "Status", elementKey: "status" },
]}
isLoading={loadingEvents}
records={replicationRules}
entityName="Replication Rules"
idField="id"
/>
</TabPanel>
</Grid>
</Grid>
</Grid>
<DeleteEvent
deleteOpen={deleteOpen}
selectedBucket={bucketName}
bucketEvent={selectedEvent}
closeDeleteModalAndRefresh={(refresh: boolean) => {
this.closeDeleteModalAndRefresh(refresh);
}}
/>
{events.bucket.replace("arn:aws:s3:::", "")}
</React.Fragment>
);
}
}
};
const ruleDelDisplay = (events: BucketReplicationRuleDeleteMarker) => {
return <React.Fragment>{events.status}</React.Fragment>;
};
const setOpenReplicationOpen = (open = false) => {
setOpenSetReplication(open);
};
const handleEncryptionCheckbox = (
event: React.ChangeEvent<HTMLInputElement>
) => {
if (event.target.checked) {
setEnableEncryptionScreenOpen(true);
} else {
api
.invoke("POST", `/api/v1/buckets/${bucketName}/encryption/disable`)
.then(() => {
setEncryptionEnabled(false);
})
.catch((err: any) => {
setError(err);
});
}
};
const tableActions = [{ type: "delete", onClick: confirmDeleteEvent }];
return (
<React.Fragment>
{addScreenOpen && (
<AddEvent
open={addScreenOpen}
selectedBucket={bucketName}
closeModalAndRefresh={() => {
setAddScreenOpen(false);
fetchEvents();
}}
/>
)}
{enableEncryptionScreenOpen && (
<EnableBucketEncryption
open={enableEncryptionScreenOpen}
selectedBucket={bucketName}
closeModalAndRefresh={() => {
setEnableEncryptionScreenOpen(false);
fetchBucketEncryptionInfo();
}}
/>
)}
{accessPolicyScreenOpen && (
<SetAccessPolicy
bucketName={bucketName}
open={accessPolicyScreenOpen}
actualPolicy={accessPolicy}
closeModalAndRefresh={() => {
closeAddModalAndRefresh();
}}
/>
)}
{openSetReplication && (
<AddReplicationModal
closeModalAndRefresh={() => {
setOpenReplicationOpen(false);
fetchEvents();
}}
open={openSetReplication}
bucketName={bucketName}
/>
)}
<PageHeader label={`Bucket > ${match.params["bucketName"]}`} />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12}>
<div className={classes.headerContainer}>
<div>
<Paper className={classes.paperContainer}>
<div className={classes.gridContainer}>
<div>Access Policy:</div>
<div className={classes.capitalizeFirst}>
{loadingBucket ? (
<CircularProgress
color="primary"
size={16}
variant="indeterminate"
/>
) : (
accessPolicy.toLowerCase()
)}
</div>
<div>Reported Usage:</div>
<div>
{loadingSize ? (
<CircularProgress
color="primary"
size={16}
variant="indeterminate"
/>
) : (
niceBytes(bucketSize)
)}
</div>
<div>Replication:</div>
<div className={classes.doubleElement}>
<span>{replicationRules.length ? "Yes" : "No"}</span>
</div>
<div>Versioning:</div>
<div>{isVersioned ? "Yes" : "No"}&nbsp;</div>
<div>Encryption:</div>
<div>
<Checkbox
color="primary"
inputProps={{
"aria-label": "secondary checkbox",
}}
onChange={(event) => handleEncryptionCheckbox(event)}
checked={encryptionEnabled}
/>
</div>
</div>
</Paper>
</div>
<div className={classes.masterActions}>
<div>
<Button
variant="contained"
color="primary"
fullWidth
size="medium"
onClick={() => {
setAccessPolicyScreenOpen(true);
}}
>
Change Access Policy
</Button>
</div>
</div>
</div>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid container item xs={12}>
<Grid item xs={6}>
<Tabs
value={curTab}
onChange={(e: React.ChangeEvent<{}>, newValue: number) => {
setCurTab(newValue);
}}
indicatorColor="primary"
textColor="primary"
aria-label="cluster-tabs"
>
<Tab label="Events" {...a11yProps(0)} />
<Tab label="Replication" {...a11yProps(1)} />
</Tabs>
</Grid>
<Grid item xs={6} className={classes.actionsTray}>
{curTab === 0 && (
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
size="medium"
onClick={() => {
setAddScreenOpen(true);
}}
>
Subscribe to Event
</Button>
)}
{curTab === 1 && (
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
size="medium"
onClick={() => {
setOpenReplicationOpen(true);
}}
>
Add Replication Rule
</Button>
)}
</Grid>
</Grid>
<Grid item xs={12}>
<TabPanel index={0} value={curTab}>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "SQS", elementKey: "arn" },
{
label: "Events",
elementKey: "events",
renderFunction: eventsDisplay,
},
{ label: "Prefix", elementKey: "prefix" },
{ label: "Suffix", elementKey: "suffix" },
]}
isLoading={loadingEvents}
records={records}
entityName="Events"
idField="id"
/>
</TabPanel>
<TabPanel index={1} value={curTab}>
<TableWrapper
itemActions={tableActions}
columns={[
{ label: "ID", elementKey: "id" },
{
label: "Priority",
elementKey: "priority",
},
{
label: "Destination",
elementKey: "destination",
renderFunction: ruleDestDisplay,
},
{
label: "Delete Replication",
elementKey: "delete_marker_replication",
renderFunction: ruleDelDisplay,
},
{ label: "Status", elementKey: "status" },
]}
isLoading={loadingEvents}
records={replicationRules}
entityName="Replication Rules"
idField="id"
/>
</TabPanel>
</Grid>
</Grid>
</Grid>
<DeleteEvent
deleteOpen={deleteOpen}
selectedBucket={bucketName}
bucketEvent={selectedEvent}
closeDeleteModalAndRefresh={(refresh: boolean) => {
closeDeleteModalAndRefresh(refresh);
}}
/>
</React.Fragment>
);
};
export default withStyles(styles)(ViewBucket);

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, { useEffect, useState } from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Button, LinearProgress } from "@material-ui/core";
@@ -59,159 +59,141 @@ interface IAddPolicyState {
policyDefinition: string;
}
class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
state: IAddPolicyState = {
addLoading: false,
addError: "",
policyName: "",
policyDefinition: "",
};
const AddPolicy = ({
classes,
open,
closeModalAndRefresh,
policyEdit,
}: IAddPolicyProps) => {
const [addLoading, setAddLoading] = useState<boolean>(false);
const [addError, setAddError] = useState<string>("");
const [policyName, setPolicyName] = useState<string>("");
const [policyDefinition, setPolicyDefinition] = useState<string>("");
addRecord(event: React.FormEvent) {
const addRecord = (event: React.FormEvent) => {
event.preventDefault();
const { policyName, addLoading, policyDefinition } = this.state;
if (addLoading) {
return;
}
this.setState({ addLoading: true }, () => {
api
.invoke("POST", "/api/v1/policies", {
name: policyName,
policy: policyDefinition,
})
.then((res) => {
this.setState(
{
addLoading: false,
addError: "",
},
() => {
this.props.closeModalAndRefresh(true);
}
);
})
.catch((err) => {
this.setState({
addLoading: false,
addError: err,
});
});
});
}
setAddLoading(true);
api
.invoke("POST", "/api/v1/policies", {
name: policyName,
policy: policyDefinition,
})
.then((res) => {
setAddLoading(false);
setAddError("");
componentDidMount() {
const { policyEdit } = this.props;
if (policyEdit) {
this.setState({
policyName: policyEdit.name,
policyDefinition: policyEdit
? JSON.stringify(JSON.parse(policyEdit.policy), null, 4)
: "",
closeModalAndRefresh(true);
})
.catch((err) => {
setAddLoading(false);
setAddError(err);
});
};
useEffect(() => {
if (policyEdit) {
setPolicyName(policyEdit.name);
setPolicyDefinition(
policyEdit ? JSON.stringify(JSON.parse(policyEdit.policy), null, 4) : ""
);
}
}
}, []);
resetForm() {
this.setState({
policyName: "",
policyDefinition: "",
});
}
const resetForm = () => {
setPolicyName("");
setPolicyDefinition("");
};
render() {
const { classes, open, policyEdit } = this.props;
const { addLoading, addError, policyName, policyDefinition } = this.state;
const validSave = policyName.trim() !== "";
const validSave = policyName.trim() !== "";
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
this.setState({ addError: "" }, () => {
this.props.closeModalAndRefresh(false);
});
return (
<ModalWrapper
modalOpen={open}
onClose={() => {
setAddError("");
closeModalAndRefresh(false);
}}
title={`${policyEdit ? "Info" : "Create"} Policy`}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
addRecord(e);
}}
title={`${policyEdit ? "Info" : "Create"} Policy`}
>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
this.addRecord(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<InputBoxWrapper
id="policy-name"
name="policy-name"
label="Policy Name"
placeholder="Enter Policy Name"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ policyName: e.target.value });
}}
value={policyName}
disabled={!!policyEdit}
/>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<CodeMirrorWrapper
label="Write Policy"
value={policyDefinition}
onBeforeChange={(editor, data, value) => {
this.setState({ policyDefinition: value });
)}
<Grid item xs={12}>
<InputBoxWrapper
id="policy-name"
name="policy-name"
label="Policy Name"
placeholder="Enter Policy Name"
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setPolicyName(e.target.value);
}}
readOnly={!!policyEdit}
value={policyName}
disabled={!!policyEdit}
/>
</Grid>
{!policyEdit && (
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={() => {
this.resetForm();
}}
>
Clear
</button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !validSave}
>
Save
</Button>
</Grid>
)}
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
<Grid item xs={12}>
<br />
</Grid>
<CodeMirrorWrapper
label="Write Policy"
value={policyDefinition}
onBeforeChange={(editor, data, value) => {
setPolicyDefinition(value);
}}
readOnly={!!policyEdit}
/>
</Grid>
</form>
</ModalWrapper>
);
}
}
{!policyEdit && (
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={() => {
resetForm();
}}
>
Clear
</button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !validSave}
>
Save
</Button>
</Grid>
)}
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</ModalWrapper>
);
};
export default withStyles(styles)(AddPolicy);

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import React from "react";
import React, { useState } from "react";
import {
Button,
Dialog,
@@ -48,100 +48,84 @@ interface IDeletePolicyState {
deleteError: string;
}
class DeletePolicy extends React.Component<
IDeletePolicyProps,
IDeletePolicyState
> {
state: IDeletePolicyState = {
deleteLoading: false,
deleteError: "",
};
removeRecord() {
const { deleteLoading } = this.state;
const { selectedPolicy } = this.props;
const DeletePolicy = ({
classes,
closeDeleteModalAndRefresh,
deleteOpen,
selectedPolicy,
}: IDeletePolicyProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const [deleteError, setDeleteError] = useState<string>("");
const removeRecord = () => {
if (deleteLoading) {
return;
}
this.setState({ deleteLoading: true }, () => {
api
.invoke("DELETE", `/api/v1/policies/${selectedPolicy}`)
.then((res: PolicyList) => {
this.setState(
{
deleteLoading: false,
deleteError: "",
},
() => {
this.props.closeDeleteModalAndRefresh(true);
}
);
})
.catch((err) => {
this.setState({
deleteLoading: false,
deleteError: err,
});
});
});
}
render() {
const { classes, deleteOpen, selectedPolicy } = this.props;
const { deleteLoading, deleteError } = this.state;
return (
<Dialog
open={deleteOpen}
onClose={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Policy</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete policy <b>{selectedPolicy}</b>?.
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
this.removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
}
}
setDeleteLoading(true);
api
.invoke("DELETE", `/api/v1/policies/${selectedPolicy}`)
.then((res: PolicyList) => {
setDeleteLoading(false);
setDeleteError("");
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setDeleteError(err);
});
};
return (
<Dialog
open={deleteOpen}
onClose={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete Policy</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete policy <b>{selectedPolicy}</b>?.
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(DeletePolicy);

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import React from "react";
import React, { useEffect, useState } from "react";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import { Button, LinearProgress } from "@material-ui/core";
@@ -52,112 +52,74 @@ interface IAddUserContentProps {
open: boolean;
}
interface IAddUserContentState {
addLoading: boolean;
addError: string;
accessKey: string;
secretKey: string;
selectedGroups: string[];
currentGroups: string[];
enabled: boolean;
}
const AddUserContent = ({
classes,
closeModalAndRefresh,
selectedUser,
open,
}: IAddUserContentProps) => {
const [addLoading, setAddLoading] = useState<boolean>(false);
const [addError, setAddError] = useState<string>("");
const [accessKey, setAccessKey] = useState<string>("");
const [secretKey, setSecretKey] = useState<string>("");
const [enabled, setEnabled] = useState<boolean>(false);
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
const [currentGroups, setCurrentGroups] = useState<string[]>([]);
class AddUserContent extends React.Component<
IAddUserContentProps,
IAddUserContentState
> {
state: IAddUserContentState = {
addLoading: false,
addError: "",
accessKey: "",
secretKey: "",
enabled: false,
selectedGroups: [],
currentGroups: [],
};
componentDidMount(): void {
const { selectedUser } = this.props;
useEffect(() => {
if (selectedUser == null) {
this.setState({
accessKey: "",
secretKey: "",
selectedGroups: [],
});
setAccessKey("");
setSecretKey("");
setSelectedGroups([]);
} else {
this.getUserInformation();
getUserInformation();
}
}
}, []);
saveRecord(event: React.FormEvent) {
const saveRecord = (event: React.FormEvent) => {
event.preventDefault();
const {
accessKey,
addLoading,
secretKey,
selectedGroups,
enabled,
} = this.state;
const { selectedUser } = this.props;
if (addLoading) {
return;
}
this.setState({ addLoading: true }, () => {
if (selectedUser !== null) {
api
.invoke("PUT", `/api/v1/users/${selectedUser.accessKey}`, {
status: enabled ? "enabled" : "disabled",
groups: selectedGroups,
})
.then((res) => {
this.setState(
{
addLoading: false,
addError: "",
},
() => {
this.props.closeModalAndRefresh();
}
);
})
.catch((err) => {
this.setState({
addLoading: false,
addError: err,
});
});
} else {
api
.invoke("POST", "/api/v1/users", {
accessKey,
secretKey,
groups: selectedGroups,
})
.then((res) => {
this.setState(
{
addLoading: false,
addError: "",
},
() => {
this.props.closeModalAndRefresh();
}
);
})
.catch((err) => {
console.log(err);
this.setState({
addLoading: false,
addError: err,
});
});
}
});
}
setAddLoading(true);
if (selectedUser !== null) {
api
.invoke("PUT", `/api/v1/users/${selectedUser.accessKey}`, {
status: enabled ? "enabled" : "disabled",
groups: selectedGroups,
})
.then((res) => {
setAddLoading(false);
setAddError("");
getUserInformation() {
const { selectedUser } = this.props;
closeModalAndRefresh();
})
.catch((err) => {
setAddLoading(false);
setAddError(err);
});
} else {
api
.invoke("POST", "/api/v1/users", {
accessKey,
secretKey,
groups: selectedGroups,
})
.then((res) => {
setAddLoading(false);
setAddError("");
closeModalAndRefresh();
})
.catch((err) => {
console.log(err);
setAddLoading(false);
setAddError(err);
});
}
};
const getUserInformation = () => {
if (!selectedUser) {
return null;
}
@@ -165,167 +127,148 @@ class AddUserContent extends React.Component<
api
.invoke("GET", `/api/v1/users/${selectedUser.accessKey}`)
.then((res) => {
this.setState({
addLoading: false,
addError: "",
accessKey: res.accessKey,
selectedGroups: res.memberOf || [],
currentGroups: res.memberOf || [],
enabled: res.status === "enabled",
});
setAddLoading(false);
setAddError("");
setAccessKey(res.accessKey);
setSelectedGroups(res.memberOf || []);
setCurrentGroups(res.memberOf || []);
setEnabled(res.status === "enabled");
})
.catch((err) => {
this.setState({
addLoading: false,
addError: err,
});
setAddLoading(false);
setAddError(err);
});
}
resetForm() {
if (this.props.selectedUser !== null) {
this.setState({ selectedGroups: [] });
};
const resetForm = () => {
if (selectedUser !== null) {
setSelectedGroups([]);
return;
}
setAccessKey("");
setSecretKey("");
setSelectedGroups([]);
};
this.setState({ accessKey: "", secretKey: "", selectedGroups: [] });
}
render() {
const { classes, selectedUser } = this.props;
const {
addLoading,
addError,
accessKey,
secretKey,
selectedGroups,
currentGroups,
enabled,
} = this.state;
const sendEnabled =
accessKey.trim() !== "" &&
((secretKey.trim() !== "" && selectedUser === null) ||
selectedUser !== null);
return (
<ModalWrapper
onClose={() => {
this.props.closeModalAndRefresh();
}}
modalOpen={this.props.open}
title={selectedUser !== null ? "Edit User" : "Create User"}
>
{selectedUser !== null && (
<div className={classes.floatingEnabled}>
<FormSwitchWrapper
indicatorLabels={["Enabled", "Disabled"]}
checked={enabled}
value={"user_enabled"}
id="user-status"
name="user-status"
onChange={(e) => {
this.setState({ enabled: e.target.checked });
}}
switchOnly
/>
</div>
)}
<React.Fragment>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
this.saveRecord(e);
const sendEnabled =
accessKey.trim() !== "" &&
((secretKey.trim() !== "" && selectedUser === null) ||
selectedUser !== null);
return (
<ModalWrapper
onClose={() => {
closeModalAndRefresh();
}}
modalOpen={open}
title={selectedUser !== null ? "Edit User" : "Create User"}
>
{selectedUser !== null && (
<div className={classes.floatingEnabled}>
<FormSwitchWrapper
indicatorLabels={["Enabled", "Disabled"]}
checked={enabled}
value={"user_enabled"}
id="user-status"
name="user-status"
onChange={(e) => {
setEnabled(e.target.checked);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
switchOnly
/>
</div>
)}
<InputBoxWrapper
id="accesskey-input"
name="accesskey-input"
label="Access Key"
value={accessKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ accessKey: e.target.value });
}}
disabled={selectedUser !== null}
/>
{selectedUser !== null ? (
<PredefinedList
label={"Current Groups"}
content={currentGroups.join(", ")}
/>
) : (
<InputBoxWrapper
id="standard-multiline-static"
name="standard-multiline-static"
label="Secret Key"
type="password"
value={secretKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
this.setState({ secretKey: e.target.value });
}}
autoComplete="current-password"
/>
)}
<React.Fragment>
<form
noValidate
autoComplete="off"
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
saveRecord(e);
}}
>
<Grid container>
<Grid item xs={12} className={classes.formScrollable}>
{addError !== "" && (
<Grid item xs={12}>
<GroupsSelectors
selectedGroups={selectedGroups}
setSelectedGroups={(elements: string[]) => {
this.setState({
selectedGroups: elements,
});
}}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={() => {
this.resetForm();
}}
>
Clear
</button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !sendEnabled}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{addError}
</Typography>
</Grid>
)}
<InputBoxWrapper
id="accesskey-input"
name="accesskey-input"
label="Access Key"
value={accessKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setAccessKey(e.target.value);
}}
disabled={selectedUser !== null}
/>
{selectedUser !== null ? (
<PredefinedList
label={"Current Groups"}
content={currentGroups.join(", ")}
/>
) : (
<InputBoxWrapper
id="standard-multiline-static"
name="standard-multiline-static"
label="Secret Key"
type="password"
value={secretKey}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
setSecretKey(e.target.value);
}}
autoComplete="current-password"
/>
)}
<Grid item xs={12}>
<GroupsSelectors
selectedGroups={selectedGroups}
setSelectedGroups={(elements: string[]) => {
setSelectedGroups(elements);
}}
/>
</Grid>
</Grid>
</form>
</React.Fragment>
</ModalWrapper>
);
}
}
<Grid item xs={12} className={classes.buttonContainer}>
<button
type="button"
color="primary"
className={classes.clearButton}
onClick={() => {
resetForm();
}}
>
Clear
</button>
<Button
type="submit"
variant="contained"
color="primary"
disabled={addLoading || !sendEnabled}
>
Save
</Button>
</Grid>
{addLoading && (
<Grid item xs={12}>
<LinearProgress />
</Grid>
)}
</Grid>
</form>
</React.Fragment>
</ModalWrapper>
);
};
const AddUserWrapper = withStyles(styles)(AddUserContent);
@@ -335,14 +278,8 @@ interface IAddUserProps {
selectedUser: User | null;
}
interface IAddUserState {}
class AddUser extends React.Component<IAddUserProps, IAddUserState> {
state: IAddUserState = {};
render() {
return <AddUserWrapper {...this.props} />;
}
}
const AddUser = (props: IAddUserProps) => {
return <AddUserWrapper {...props} />;
};
export default AddUser;

View File

@@ -15,7 +15,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import React from "react";
import React, { useState } from "react";
import {
Button,
Dialog,
@@ -48,110 +48,94 @@ interface IDeleteUserState {
deleteError: string;
}
class DeleteUser extends React.Component<IDeleteUserProps, IDeleteUserState> {
state: IDeleteUserState = {
deleteLoading: false,
deleteError: "",
};
const DeleteUser = ({
classes,
closeDeleteModalAndRefresh,
deleteOpen,
selectedUser,
}: IDeleteUserProps) => {
const [deleteLoading, setDeleteLoading] = useState<boolean>(false);
const [deleteError, setDeleteError] = useState<string>("");
removeRecord() {
const { deleteLoading } = this.state;
const { selectedUser } = this.props;
const removeRecord = () => {
if (deleteLoading) {
return;
}
if (selectedUser == null) {
return;
}
this.setState({ deleteLoading: true }, () => {
api
.invoke("DELETE", `/api/v1/users/${selectedUser.accessKey}`, {
id: selectedUser.id,
})
.then((res: UsersList) => {
this.setState(
{
deleteLoading: false,
deleteError: "",
},
() => {
this.props.closeDeleteModalAndRefresh(true);
}
);
})
.catch((err) => {
this.setState({
deleteLoading: false,
deleteError: err,
});
});
});
setDeleteLoading(true);
api
.invoke("DELETE", `/api/v1/users/${selectedUser.accessKey}`, {
id: selectedUser.id,
})
.then((res: UsersList) => {
setDeleteLoading(false);
setDeleteError("");
closeDeleteModalAndRefresh(true);
})
.catch((err) => {
setDeleteLoading(false);
setDeleteError(err);
});
};
if (selectedUser === null) {
return <div />;
}
render() {
const { classes, deleteOpen, selectedUser } = this.props;
const { deleteLoading, deleteError } = this.state;
if (selectedUser === null) {
return <div />;
}
return (
<Dialog
open={deleteOpen}
onClose={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete User</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete user <b>{selectedUser.accessKey}</b>
?
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
this.setState({ deleteError: "" }, () => {
this.props.closeDeleteModalAndRefresh(false);
});
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
this.removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
}
}
return (
<Dialog
open={deleteOpen}
onClose={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">Delete User</DialogTitle>
<DialogContent>
{deleteLoading && <LinearProgress />}
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete user <b>{selectedUser.accessKey}</b>?
{deleteError !== "" && (
<React.Fragment>
<br />
<Typography
component="p"
variant="body1"
className={classes.errorBlock}
>
{deleteError}
</Typography>
</React.Fragment>
)}
</DialogContentText>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
setDeleteError("");
closeDeleteModalAndRefresh(false);
}}
color="primary"
disabled={deleteLoading}
>
Cancel
</Button>
<Button
onClick={() => {
removeRecord();
}}
color="secondary"
autoFocus
>
Delete
</Button>
</DialogActions>
</Dialog>
);
};
export default withStyles(styles)(DeleteUser);

View File

@@ -14,10 +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 React, { useEffect, useState } from "react";
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
import api from "../../../common/api";
import { Button, Grid, TextField, InputAdornment } from "@material-ui/core";
import { Button, Grid, InputAdornment, TextField } from "@material-ui/core";
import SearchIcon from "@material-ui/icons/Search";
import GroupIcon from "@material-ui/icons/Group";
import { User, UsersList } from "./types";
@@ -93,246 +93,210 @@ interface IUsersState {
setPolicyOpen: boolean;
}
class Users extends React.Component<IUsersProps, IUsersState> {
state: IUsersState = {
records: [],
loading: false,
error: "",
deleteError: "",
addScreenOpen: false,
deleteOpen: false,
selectedUser: null,
addGroupOpen: false,
filter: "",
checkedUsers: [],
setPolicyOpen: false,
const Users = ({ classes }: IUsersProps) => {
const [records, setRecords] = useState<User[]>([]);
const [loading, setLoading] = useState<boolean>(false);
const [error, setError] = useState<string>("");
const [deleteError, setDeleteError] = useState<string>("");
const [addScreenOpen, setAddScreenOpen] = useState<boolean>(false);
const [deleteOpen, setDeleteOpen] = useState<boolean>(false);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [addGroupOpen, setAddGroupOpen] = useState<boolean>(false);
const [filter, setFilter] = useState<string>("");
const [checkedUsers, setCheckedUsers] = useState<string[]>([]);
const [policyOpen, setPolicyOpen] = useState<boolean>(false);
const fetchRecords = () => {
setLoading(true);
api
.invoke("GET", `/api/v1/users`)
.then((res: UsersList) => {
const users = res.users === null ? [] : res.users;
setLoading(false);
setError("");
setRecords(users.sort(usersSort));
})
.catch((err) => {
setLoading(false);
setError(err);
});
};
fetchRecords() {
this.setState({ loading: true }, () => {
api
.invoke("GET", `/api/v1/users`)
.then((res: UsersList) => {
const users = res.users === null ? [] : res.users;
this.setState({
loading: false,
records: users.sort(usersSort),
error: "",
});
})
.catch((err) => {
this.setState({ loading: false, error: err });
});
});
}
const closeAddModalAndRefresh = () => {
setAddScreenOpen(false);
fetchRecords();
};
closeAddModalAndRefresh() {
this.setState({ addScreenOpen: false }, () => {
this.fetchRecords();
});
}
closeDeleteModalAndRefresh(refresh: boolean) {
this.setState({ deleteOpen: false }, () => {
if (refresh) {
this.fetchRecords();
}
});
}
closeAddGroupBulk(unCheckAll: boolean = false) {
let newStates = { addGroupOpen: false };
let addUsers = {};
const closeDeleteModalAndRefresh = (refresh: boolean) => {
setDeleteOpen(false);
if (refresh) {
fetchRecords();
}
};
const closeAddGroupBulk = (unCheckAll: boolean = false) => {
setAddGroupOpen(false);
if (unCheckAll) {
addUsers = { checkedUsers: [] };
setCheckedUsers([]);
}
};
useEffect(() => {
fetchRecords();
}, []);
const filteredRecords = records.filter((elementItem) =>
elementItem.accessKey.includes(filter)
);
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;
let elements: string[] = [...checkedUsers]; // We clone the checkedUsers array
if (checked) {
// If the user has checked this field we need to push this to checkedUsersList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter((element) => element !== value);
}
this.setState({ ...newStates, ...addUsers });
}
setCheckedUsers(elements);
componentDidMount(): void {
this.fetchRecords();
}
return elements;
};
render() {
const { classes } = this.props;
const {
records,
addScreenOpen,
loading,
deleteOpen,
selectedUser,
filter,
checkedUsers,
addGroupOpen,
setPolicyOpen,
} = this.state;
const viewAction = (selectionElement: any): void => {
setAddScreenOpen(true);
setSelectedUser(selectionElement);
};
const filteredRecords = records.filter((elementItem) =>
elementItem.accessKey.includes(filter)
);
const setPolicyAction = (selectionElement: any): void => {
setPolicyOpen(true);
setSelectedUser(selectionElement);
};
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const targetD = e.target;
const value = targetD.value;
const checked = targetD.checked;
const deleteAction = (selectionElement: any): void => {
setDeleteOpen(true);
setSelectedUser(selectionElement);
};
let elements: string[] = [...checkedUsers]; // We clone the checkedUsers array
const tableActions = [
{ type: "view", onClick: viewAction },
{ type: "description", onClick: setPolicyAction },
{ type: "delete", onClick: deleteAction },
];
if (checked) {
// If the user has checked this field we need to push this to checkedUsersList
elements.push(value);
} else {
// User has unchecked this field, we need to remove it from the list
elements = elements.filter((element) => element !== value);
}
return (
<React.Fragment>
{addScreenOpen && (
<AddUser
open={addScreenOpen}
selectedUser={selectedUser}
closeModalAndRefresh={() => {
closeAddModalAndRefresh();
}}
/>
)}
{policyOpen && (
<SetPolicy
open={policyOpen}
selectedUser={selectedUser}
selectedGroup={null}
closeModalAndRefresh={() => {
setPolicyOpen(false);
fetchRecords();
}}
/>
)}
{deleteOpen && (
<DeleteUser
deleteOpen={deleteOpen}
selectedUser={selectedUser}
closeDeleteModalAndRefresh={(refresh: boolean) => {
closeDeleteModalAndRefresh(refresh);
}}
/>
)}
{addGroupOpen && (
<AddToGroup
open={addGroupOpen}
checkedUsers={checkedUsers}
closeModalAndRefresh={(close: boolean) => {
closeAddGroupBulk(close);
}}
/>
)}
<PageHeader label={"Users"} />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Search Users"
className={classes.searchField}
id="search-resource"
label=""
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
onChange={(e) => {
setFilter(e.target.value);
}}
/>
<Button
variant="contained"
color="primary"
startIcon={<GroupIcon />}
disabled={checkedUsers.length <= 0}
onClick={() => {
if (checkedUsers.length > 0) {
setAddGroupOpen(true);
}
}}
>
Add to Group
</Button>
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
onClick={() => {
setAddScreenOpen(true);
setSelectedUser(null);
}}
>
Create User
</Button>
</Grid>
this.setState({
checkedUsers: elements,
});
return elements;
};
const viewAction = (selectionElement: any): void => {
this.setState({
addScreenOpen: true,
selectedUser: selectionElement,
});
};
const setPolicyAction = (selectionElement: any): void => {
this.setState({
setPolicyOpen: true,
selectedUser: selectionElement,
});
};
const deleteAction = (selectionElement: any): void => {
this.setState({
deleteOpen: true,
selectedUser: selectionElement,
});
};
const tableActions = [
{ type: "view", onClick: viewAction },
{ type: "description", onClick: setPolicyAction },
{ type: "delete", onClick: deleteAction },
];
return (
<React.Fragment>
{addScreenOpen && (
<AddUser
open={addScreenOpen}
selectedUser={selectedUser}
closeModalAndRefresh={() => {
this.closeAddModalAndRefresh();
}}
/>
)}
{setPolicyOpen && (
<SetPolicy
open={setPolicyOpen}
selectedUser={selectedUser}
selectedGroup={null}
closeModalAndRefresh={() => {
this.setState({ setPolicyOpen: false });
this.fetchRecords();
}}
/>
)}
{deleteOpen && (
<DeleteUser
deleteOpen={deleteOpen}
selectedUser={selectedUser}
closeDeleteModalAndRefresh={(refresh: boolean) => {
this.closeDeleteModalAndRefresh(refresh);
}}
/>
)}
{addGroupOpen && (
<AddToGroup
open={addGroupOpen}
checkedUsers={checkedUsers}
closeModalAndRefresh={(close: boolean) => {
this.closeAddGroupBulk(close);
}}
/>
)}
<PageHeader label={"Users"} />
<Grid container>
<Grid item xs={12} className={classes.container}>
<Grid item xs={12} className={classes.actionsTray}>
<TextField
placeholder="Search Users"
className={classes.searchField}
id="search-resource"
label=""
InputProps={{
disableUnderline: true,
startAdornment: (
<InputAdornment position="start">
<SearchIcon />
</InputAdornment>
),
}}
onChange={(e) => {
this.setState({ filter: e.target.value });
}}
/>
<Button
variant="contained"
color="primary"
startIcon={<GroupIcon />}
disabled={checkedUsers.length <= 0}
onClick={() => {
if (checkedUsers.length > 0) {
this.setState({
addGroupOpen: true,
});
}
}}
>
Add to Group
</Button>
<Button
variant="contained"
color="primary"
startIcon={<CreateIcon />}
onClick={() => {
this.setState({
addScreenOpen: true,
selectedUser: null,
});
}}
>
Create User
</Button>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={tableActions}
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
onSelect={selectionChanged}
selectedItems={checkedUsers}
isLoading={loading}
records={filteredRecords}
entityName="Users"
idField="accessKey"
/>
</Grid>
<Grid item xs={12}>
<br />
</Grid>
<Grid item xs={12}>
<TableWrapper
itemActions={tableActions}
columns={[{ label: "Access Key", elementKey: "accessKey" }]}
onSelect={selectionChanged}
selectedItems={checkedUsers}
isLoading={loading}
records={filteredRecords}
entityName="Users"
idField="accessKey"
/>
</Grid>
</Grid>
</React.Fragment>
);
}
}
</Grid>
</React.Fragment>
);
};
export default withStyles(styles)(Users);