Migrated Heal and Watch pages to mds (#3016)

Signed-off-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
Alex
2023-08-25 09:53:02 -06:00
committed by GitHub
parent bbf4027418
commit bf733f3822
3 changed files with 220 additions and 325 deletions

View File

@@ -16,116 +16,48 @@
import React, { Fragment, useEffect, useState } from "react";
import { useSelector } from "react-redux";
import {
FormControl,
Grid,
InputBase,
MenuItem,
Select,
TextField,
} from "@mui/material";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { Theme } from "@mui/material/styles";
import { Button, HealIcon, PageLayout } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import { wsProtocol } from "../../../utils/wsUtils";
import { colorH, HealStatus } from "./types";
import { niceBytes } from "../../../common/utils";
import {
actionsTray,
containerForHeader,
inlineCheckboxes,
searchField,
} from "../Common/FormComponents/common/styleLibrary";
import {
CONSOLE_UI_RESOURCE,
IAM_SCOPES,
} from "../../../common/SecureComponent/permissions";
import CheckboxWrapper from "../Common/FormComponents/CheckboxWrapper/CheckboxWrapper";
import { SecureComponent } from "../../../common/SecureComponent";
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import { selDistSet, setHelpName } from "../../../systemSlice";
import makeStyles from "@mui/styles/makeStyles";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
Box,
Button,
Checkbox,
Grid,
HealIcon,
InputBox,
InputLabel,
PageLayout,
Select,
} from "mds";
import {
Bar,
BarChart,
CartesianGrid,
Legend,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from "recharts";
import { Legend } from "recharts";
import HelpMenu from "../HelpMenu";
import { useAppDispatch } from "../../../store";
import { api } from "api";
import { Bucket } from "api/consoleApi";
import { errorToHandler } from "api/errors";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
graphContainer: {
backgroundColor: "#fff",
border: "#EAEDEE 1px solid",
borderRadius: 3,
padding: "19px 38px",
marginTop: 15,
},
scanInfo: {
marginTop: 20,
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
},
scanData: {
fontSize: 13,
},
formBox: {
padding: 15,
border: "1px solid #EAEAEA",
},
buttonBar: {
display: "flex",
alignItems: "center",
justifyContent: "flex-end",
},
bucketField: {
flex: 1,
},
prefixField: {
...searchField.searchField,
marginLeft: 10,
flex: 1,
},
actionsTray: {
...actionsTray.actionsTray,
marginBottom: 0,
},
...inlineCheckboxes,
...searchField,
...containerForHeader,
}),
);
const SelectStyled = withStyles((theme: Theme) =>
createStyles({
root: {
lineHeight: "50px",
marginRight: 15,
"label + &": {
marginTop: theme.spacing(3),
},
"& .MuiSelect-select:focus": {
backgroundColor: "transparent",
},
},
}),
)(InputBase);
import { wsProtocol } from "../../../utils/wsUtils";
import { colorH, HealStatus } from "./types";
import { niceBytes } from "../../../common/utils";
import { modalStyleUtils } from "../Common/FormComponents/common/styleLibrary";
import {
CONSOLE_UI_RESOURCE,
IAM_SCOPES,
} from "../../../common/SecureComponent/permissions";
import { selDistSet, setHelpName } from "../../../systemSlice";
import { SecureComponent } from "../../../common/SecureComponent";
import { useAppDispatch } from "../../../store";
import DistributedOnly from "../Common/DistributedOnly/DistributedOnly";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
const Heal = () => {
const classes = useStyles();
const distributedSetup = useSelector(selDistSet);
const [start, setStart] = useState(false);
@@ -166,13 +98,13 @@ const Heal = () => {
// forceStart and forceStop need to be mutually exclusive
useEffect(() => {
if (forceStart === true) {
if (forceStart) {
setForceStop(false);
}
}, [forceStart]);
useEffect(() => {
if (forceStop === true) {
if (forceStop) {
setForceStart(false);
}
}, [forceStop]);
@@ -285,84 +217,81 @@ const Heal = () => {
scopes={[IAM_SCOPES.ADMIN_HEAL]}
resource={CONSOLE_UI_RESOURCE}
>
<Grid xs={12} item className={classes.formBox}>
<Grid item xs={12} className={classes.actionsTray}>
<FormControl variant="outlined" className={classes.bucketField}>
<Box withBorders>
<Box
sx={{
display: "flex" as const,
alignItems: "center",
marginBottom: 15,
gap: 15,
}}
>
<Box sx={{ flexGrow: 1, width: "100%" }}>
<InputLabel>Bucket</InputLabel>
<Select
label="Bucket"
id="bucket-name"
name="bucket-name"
value={bucketName}
onChange={(e) => {
setBucketName(e.target.value as string);
onChange={(value) => {
setBucketName(value as string);
}}
className={classes.searchField}
input={<SelectStyled />}
displayEmpty
>
<MenuItem value="" key={`select-bucket-name-default`}>
Select Bucket
</MenuItem>
{bucketNames.map((option) => (
<MenuItem
value={option.value}
key={`select-bucket-name-${option.label}`}
>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<TextField
label="Prefix"
className={classes.prefixField}
id="prefix-resource"
disabled={false}
InputProps={{
disableUnderline: true,
}}
onChange={(e) => {
setPrefix(e.target.value);
}}
variant="standard"
/>
</Grid>
<Grid item xs={12} className={classes.inlineCheckboxes}>
<CheckboxWrapper
name="recursive"
id="recursive"
value="recursive"
checked={recursive}
onChange={(e) => {
setRecursive(e.target.checked);
}}
disabled={false}
label="Recursive"
/>
<CheckboxWrapper
name="forceStart"
id="forceStart"
value="forceStart"
checked={forceStart}
onChange={(e) => {
setForceStart(e.target.checked);
}}
disabled={false}
label="Force Start"
/>
<CheckboxWrapper
name="forceStop"
id="forceStop"
value="forceStop"
checked={forceStop}
onChange={(e) => {
setForceStop(e.target.checked);
}}
disabled={false}
label="Force Stop"
/>
</Grid>
<Grid item xs={12} className={classes.buttonBar}>
options={bucketNames}
placeholder={"Select Bucket"}
/>
</Box>
<Box sx={{ flexGrow: 1, width: "100%" }}>
<InputLabel>Prefix</InputLabel>
<InputBox
id="prefix-resource"
disabled={false}
onChange={(e) => {
setPrefix(e.target.value);
}}
/>
</Box>
</Box>
<Box sx={{ display: "flex", gap: 20 }}>
<Box>
<Checkbox
name="recursive"
id="recursive"
value="recursive"
checked={recursive}
onChange={() => {
setRecursive(!recursive);
}}
disabled={false}
label="Recursive"
/>
</Box>
<Box>
<Checkbox
name="forceStart"
id="forceStart"
value="forceStart"
checked={forceStart}
onChange={() => {
setForceStart(!forceStart);
}}
disabled={false}
label="Force Start"
/>
</Box>
<Box>
<Checkbox
name="forceStop"
id="forceStop"
value="forceStop"
checked={forceStop}
onChange={() => {
setForceStop(!forceStop);
}}
disabled={false}
label="Force Stop"
/>
</Box>
</Box>
<Box sx={modalStyleUtils.modalButtonBar}>
<Button
id={"start-heal"}
type="submit"
@@ -372,9 +301,18 @@ const Heal = () => {
onClick={() => setStart(true)}
label={"Start"}
/>
</Grid>
</Grid>
<Grid item xs={12} className={classes.graphContainer}>
</Box>
</Box>
<Box
withBorders
sx={{
marginTop: 15,
'& ul li:not([class*="Mui"])::before': {
listStyle: "none",
content: "' '",
},
}}
>
<ResponsiveContainer width={"90%"} height={400}>
<BarChart
width={600}
@@ -391,34 +329,48 @@ const Heal = () => {
<XAxis dataKey="name" />
<YAxis />
<Tooltip />
<Legend verticalAlign={"top"} layout={"vertical"} />
<Legend
verticalAlign={"top"}
layout={"horizontal"}
className={"noLi"}
/>
<Bar
dataKey="ah"
name={"After Healing"}
fill="rgba(0, 0, 255, 0.2)"
stroke="rgba(0, 0, 255, 1)"
fill="#2781B060"
stroke="#2781B0"
/>
<Bar
dataKey="bh"
name={"Before Healing"}
fill="rgba(153, 102, 255, 0.2)"
stroke="rgba(153, 102, 255, 1)"
fill="#C83B5160"
stroke="#C83B51"
/>
</BarChart>
</ResponsiveContainer>
<Grid item xs={12} className={classes.scanInfo}>
<div className={classes.scanData}>
<Grid
item
xs={12}
sx={{
marginTop: 20,
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
"& .scanData": {},
}}
>
<Box className={"scanData"}>
<strong>Size scanned:</strong> {hStatus.sizeScanned}
</div>
<div className={classes.scanData}>
</Box>
<Box className={"scanData"}>
<strong>Objects healed:</strong> {hStatus.objectsHealed} /{" "}
{hStatus.objectsScanned}
</div>
<div className={classes.scanData}>
</Box>
<Box className={"scanData"}>
<strong>Healing time:</strong> {hStatus.healDuration}s
</div>
</Box>
</Grid>
</Grid>
</Box>
</SecureComponent>
)}
</PageLayout>

View File

@@ -13,83 +13,33 @@
//
// 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, { useEffect, useState } from "react";
import {
FormControl,
Grid,
InputBase,
MenuItem,
Select,
TextField,
} from "@mui/material";
import React, { useEffect, useState, Fragment } from "react";
import { IMessageEvent, w3cwebsocket as W3CWebSocket } from "websocket";
import { useSelector } from "react-redux";
import { Theme } from "@mui/material/styles";
import { Button, PageLayout } from "mds";
import createStyles from "@mui/styles/createStyles";
import withStyles from "@mui/styles/withStyles";
import {
Box,
Button,
DataTable,
Grid,
InputBox,
InputLabel,
PageLayout,
Select,
} from "mds";
import { AppState, useAppDispatch } from "../../../store";
import { Bucket, BucketList, EventInfo } from "./types";
import { niceBytes, timeFromDate } from "../../../common/utils";
import { wsProtocol } from "../../../utils/wsUtils";
import {
actionsTray,
containerForHeader,
searchField,
tableStyles,
} from "../Common/FormComponents/common/styleLibrary";
import { ErrorResponseHandler } from "../../../common/types";
import TableWrapper from "../Common/TableWrapper/TableWrapper";
import api from "../../../common/api";
import makeStyles from "@mui/styles/makeStyles";
import { watchMessageReceived, watchResetMessages } from "./watchSlice";
import { setHelpName } from "../../../systemSlice";
import api from "../../../common/api";
import PageHeaderWrapper from "../Common/PageHeaderWrapper/PageHeaderWrapper";
import HelpMenu from "../HelpMenu";
import { setHelpName } from "../../../systemSlice";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
searchPrefix: {
flexGrow: 1,
marginLeft: 15,
},
watchTableHeight: {
height: "calc(100vh - 270px)",
},
bucketField: {
flexGrow: 2,
minWidth: 200,
},
...tableStyles,
...actionsTray,
...searchField,
...containerForHeader,
}),
);
const SelectStyled = withStyles((theme: Theme) =>
createStyles({
root: {
lineHeight: "50px",
"label + &": {
marginTop: theme.spacing(3),
},
"& .MuiSelect-select:focus": {
backgroundColor: "transparent",
},
},
input: {
height: 50,
fontSize: 13,
lineHeight: "50px",
},
}),
)(InputBase);
const Watch = () => {
const dispatch = useAppDispatch();
const classes = useStyles();
const messages = useSelector((state: AppState) => state.watch.messages);
const [start, setStart] = useState(false);
@@ -177,89 +127,83 @@ const Watch = () => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<React.Fragment>
<PageHeaderWrapper label="Watch" actions={<HelpMenu />} />
const optionsArray = bucketNames.map((option) => ({
label: option.label,
value: option.value,
}));
return (
<Fragment>
<PageHeaderWrapper label="Watch" actions={<HelpMenu />} />
<PageLayout>
<Grid container spacing={1} item xs={12}>
<Grid item xs={12} className={classes.actionsTray}>
<FormControl variant="outlined" className={classes.bucketField}>
<Grid container>
<Grid
item
xs={12}
sx={{
display: "flex",
gap: 10,
marginBottom: 15,
alignItems: "center",
}}
>
<Box sx={{ flexGrow: 1 }}>
<InputLabel>Bucket</InputLabel>
<Select
id="bucket-name"
name="bucket-name"
value={bucketName}
onChange={(e) => {
setBucketName(e.target.value as string);
onChange={(value) => {
setBucketName(value as string);
}}
className={classes.searchField}
disabled={start}
input={<SelectStyled />}
>
<MenuItem
value={bucketName}
key={`select-bucket-name-default`}
disabled={true}
>
Select Bucket
</MenuItem>
{bucketNames.map((option) => (
<MenuItem
value={option.value}
key={`select-bucket-name-${option.label}`}
>
{option.label}
</MenuItem>
))}
</Select>
</FormControl>
<TextField
className={`${classes.searchField} ${classes.searchPrefix}`}
id="prefix-resource"
label="Prefix"
disabled={start}
InputProps={{
disableUnderline: true,
}}
onChange={(e) => {
setPrefix(e.target.value);
}}
variant="standard"
/>
<TextField
className={`${classes.searchField} ${classes.searchPrefix}`}
id="suffix-resource"
label="Suffix"
disabled={start}
InputProps={{
disableUnderline: true,
}}
onChange={(e) => {
setSuffix(e.target.value);
}}
variant="standard"
/>
{start ? (
<Button
id={"stop-watch"}
type="submit"
variant="callAction"
onClick={() => setStart(false)}
label={"Stop"}
options={optionsArray}
placeholder={"Select Bucket"}
/>
) : (
<Button
id={"start-watch"}
type="submit"
variant="callAction"
onClick={() => setStart(true)}
label={"Start"}
</Box>
<Box sx={{ flexGrow: 1 }}>
<InputLabel>Prefix</InputLabel>
<InputBox
id="prefix-resource"
disabled={start}
onChange={(e) => {
setPrefix(e.target.value);
}}
/>
)}
</Box>
<Box sx={{ flexGrow: 1 }}>
<InputLabel>Suffix</InputLabel>
<InputBox
id="suffix-resource"
disabled={start}
onChange={(e) => {
setSuffix(e.target.value);
}}
/>
</Box>
<Box sx={{ alignSelf: "flex-end", paddingBottom: 4 }}>
{start ? (
<Button
id={"stop-watch"}
type="submit"
variant="callAction"
onClick={() => setStart(false)}
label={"Stop"}
/>
) : (
<Button
id={"start-watch"}
type="submit"
variant="callAction"
onClick={() => setStart(true)}
label={"Start"}
/>
)}
</Box>
</Grid>
<Grid item xs={12} className={classes.tableBlock}>
<TableWrapper
<Grid item xs={12}>
<DataTable
columns={[
{
label: "Time",
@@ -279,12 +223,12 @@ const Watch = () => {
customEmptyMessage={"No Changes at this time"}
idField={"watch_table"}
isLoading={false}
customPaperHeight={classes.watchTableHeight}
customPaperHeight={"calc(100vh - 270px)"}
/>
</Grid>
</Grid>
</PageLayout>
</React.Fragment>
</Fragment>
);
};

View File

@@ -59,7 +59,7 @@ export const deleteAllVersions =
//----------------------------------------------------
// Inputs
//----------------------------------------------------
export const bucketNameInput = Selector("#bucket-name");
export const bucketNameInput = Selector("#bucket-name-select");
export const bucketsPrefixInput = Selector("#prefix");
export const bucketsAccessInput = Selector("div.selectContainer");
export const bucketsAccessReadOnlyInput = Selector("li").withText("readonly");
@@ -85,8 +85,7 @@ export const groupUserCheckbox = Selector(".ReactVirtualized__Table__row input")
// Dropdowns and options
//----------------------------------------------------
export const bucketDropdownOptionFor = (modifier) => {
return Selector("li").withAttribute(
"data-value",
return Selector("#bucket-name-options-selector li").withText(
`${constants.TEST_BUCKET_NAME}-${modifier}`,
);
};