Updated form dialog components to be using ModalBoxWrapper component (#66)
Updated all form dialog components in mcs to be using the new ModalBoxWrapper component, This doesn't affect delete dialogs since we are going to create an independent component for those. Co-authored-by: Benjamin Perez <benjamin@bexsoft.net>
This commit is contained in:
@@ -16,23 +16,20 @@
|
||||
|
||||
import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Title from "../../../../common/Title";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
LinearProgress,
|
||||
TextField
|
||||
} from "@material-ui/core";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import api from "../../../../common/api";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
errorBlock: {
|
||||
color: "red"
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -88,10 +85,11 @@ class AddBucket extends React.Component<IAddBucketProps, IAddBucketState> {
|
||||
|
||||
render() {
|
||||
const { classes, open } = this.props;
|
||||
const { addLoading, addError } = this.state;
|
||||
const { addLoading, addError, bucketName } = this.state;
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
<ModalWrapper
|
||||
title="Create Bucket"
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
this.props.closeModalAndRefresh();
|
||||
@@ -100,63 +98,58 @@ class AddBucket extends React.Component<IAddBucketProps, IAddBucketState> {
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
<Title>Create Bucket</Title>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
fullWidth
|
||||
label="Bucket Name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ bucketName: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={addLoading}
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="bucket-name"
|
||||
name="bucket-name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ bucketName: e.target.value });
|
||||
}}
|
||||
label="Bucket Name"
|
||||
value={bucketName}
|
||||
/>
|
||||
</Grid>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
<br />
|
||||
</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 />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,12 +210,23 @@ class ListBuckets extends React.Component<
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AddBucket
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
this.closeAddModalAndRefresh();
|
||||
}}
|
||||
/>
|
||||
{addScreenOpen && (
|
||||
<AddBucket
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
this.closeAddModalAndRefresh();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeleteBucket
|
||||
deleteOpen={deleteOpen}
|
||||
selectedBucket={selectedBucket}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Buckets</Typography>
|
||||
@@ -334,14 +345,6 @@ class ListBuckets extends React.Component<
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<DeleteBucket
|
||||
deleteOpen={deleteOpen}
|
||||
selectedBucket={selectedBucket}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -16,20 +16,8 @@
|
||||
|
||||
import React, { ChangeEvent } from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Title from "../../../../common/Title";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
LinearProgress,
|
||||
MenuItem,
|
||||
Select,
|
||||
TextField
|
||||
} from "@material-ui/core";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import api from "../../../../common/api";
|
||||
import TableHead from "@material-ui/core/TableHead";
|
||||
@@ -39,6 +27,9 @@ import TableBody from "@material-ui/core/TableBody";
|
||||
import Checkbox from "@material-ui/core/Checkbox";
|
||||
import Table from "@material-ui/core/Table";
|
||||
import { ArnList } from "../types";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -52,6 +43,9 @@ const styles = (theme: Theme) =>
|
||||
fontWeight: "bold"
|
||||
}
|
||||
}
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -148,7 +142,15 @@ class AddEvent extends React.Component<IAddEventProps, IAddEventState> {
|
||||
|
||||
render() {
|
||||
const { classes, open } = this.props;
|
||||
const { addLoading, addError, arn, selectedEvents, arnList } = this.state;
|
||||
const {
|
||||
addLoading,
|
||||
addError,
|
||||
arn,
|
||||
selectedEvents,
|
||||
arnList,
|
||||
prefix,
|
||||
suffix
|
||||
} = this.state;
|
||||
|
||||
const events = [
|
||||
{ label: "PUT - Object Uploaded", value: "put" },
|
||||
@@ -179,134 +181,131 @@ class AddEvent extends React.Component<IAddEventProps, IAddEventState> {
|
||||
this.setState({ selectedEvents: newSelected });
|
||||
};
|
||||
|
||||
const arnValues = arnList.map(arnConstant => ({
|
||||
label: arnConstant,
|
||||
value: arnConstant
|
||||
}));
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
this.props.closeModalAndRefresh();
|
||||
});
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
title="Subscribe To Event"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
<Title>Subscribe To Event</Title>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<FormControl className={classes.formControl} fullWidth>
|
||||
<InputLabel id="select-access-policy">ARN</InputLabel>
|
||||
<Select
|
||||
labelId="select-access-policy"
|
||||
id="select-access-policy"
|
||||
value={arn}
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
this.setState({ arn: e.target.value as string });
|
||||
}}
|
||||
>
|
||||
{arnList.map(arn => (
|
||||
<MenuItem value={arn}>{arn}</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</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}>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
fullWidth
|
||||
label="Prefix"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ prefix: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
fullWidth
|
||||
label="Suffix"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ suffix: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={addLoading}
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
)}
|
||||
<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>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<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 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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,21 +15,12 @@
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Title from "../../../../common/Title";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControl,
|
||||
InputLabel,
|
||||
LinearProgress,
|
||||
MenuItem,
|
||||
Select
|
||||
} from "@material-ui/core";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import api from "../../../../common/api";
|
||||
import ModalWrapper from "../../Common/ModalWrapper/ModalWrapper";
|
||||
import SelectWrapper from "../../Common/FormComponents/SelectWrapper/SelectWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -97,80 +88,71 @@ class SetAccessPolicy extends React.Component<
|
||||
const { classes, open } = this.props;
|
||||
const { addLoading, addError, accessPolicy } = this.state;
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
<ModalWrapper
|
||||
title="Change Access Policy"
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
this.props.closeModalAndRefresh();
|
||||
});
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
<Title>Change Access Policy</Title>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<FormControl className={classes.formControl} fullWidth>
|
||||
<InputLabel id="select-access-policy">
|
||||
Access Policy
|
||||
</InputLabel>
|
||||
<Select
|
||||
labelId="select-access-policy"
|
||||
id="select-access-policy"
|
||||
value={accessPolicy}
|
||||
onChange={(e: React.ChangeEvent<{ value: unknown }>) => {
|
||||
this.setState({ accessPolicy: e.target.value as string });
|
||||
}}
|
||||
>
|
||||
<MenuItem value="PRIVATE">Private</MenuItem>
|
||||
<MenuItem value="PUBLIC">Public</MenuItem>
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={addLoading}
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
Set
|
||||
</Button>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</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 }>) => {
|
||||
this.setState({ accessPolicy: e.target.value as string });
|
||||
}}
|
||||
options={[
|
||||
{ value: "PRIVATE", label: "Private" },
|
||||
{ value: "PUBLIC", label: "Public" }
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,24 +315,28 @@ class ViewBucket extends React.Component<IViewBucketProps, IViewBucketState> {
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{records.slice(offset, offset + rowsPerPage).map(row => (
|
||||
<TableRow key={row.id}>
|
||||
<TableCell>{row.arn}</TableCell>
|
||||
<TableCell>{row.events.join(", ")}</TableCell>
|
||||
<TableCell>{row.prefix}</TableCell>
|
||||
<TableCell>{row.suffix}</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
onClick={() => {
|
||||
confirmDeleteEvent(row);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
{records
|
||||
.slice(offset, offset + rowsPerPage)
|
||||
.map((row, index) => (
|
||||
<TableRow
|
||||
key={`bucket-evt-${row.id}-${index.toString()}`}
|
||||
>
|
||||
<TableCell>{row.arn}</TableCell>
|
||||
<TableCell>{row.events.join(", ")}</TableCell>
|
||||
<TableCell>{row.prefix}</TableCell>
|
||||
<TableCell>{row.suffix}</TableCell>
|
||||
<TableCell align="right">
|
||||
<IconButton
|
||||
aria-label="delete"
|
||||
onClick={() => {
|
||||
confirmDeleteEvent(row);
|
||||
}}
|
||||
>
|
||||
<DeleteIcon />
|
||||
</IconButton>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
|
||||
@@ -27,7 +27,7 @@ import { fieldBasic } from "../common/styleLibrary";
|
||||
interface InputBoxProps {
|
||||
label: string;
|
||||
classes: any;
|
||||
onChangeFunc: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
value: string;
|
||||
id: string;
|
||||
name: string;
|
||||
@@ -71,7 +71,7 @@ function InputField(props: TextFieldProps) {
|
||||
|
||||
const InputBoxWrapper = ({
|
||||
label,
|
||||
onChangeFunc,
|
||||
onChange,
|
||||
value,
|
||||
id,
|
||||
name,
|
||||
@@ -95,7 +95,7 @@ const InputBoxWrapper = ({
|
||||
fullWidth
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
onChange={onChangeFunc}
|
||||
onChange={onChange}
|
||||
type={type}
|
||||
autoComplete={autoComplete}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
// This file is part of MinIO Console Server
|
||||
// Copyright (c) 2020 MinIO, Inc.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import {
|
||||
FormControl,
|
||||
InputLabel,
|
||||
MenuItem,
|
||||
Select,
|
||||
InputBase
|
||||
} from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { fieldBasic } from "../common/styleLibrary";
|
||||
|
||||
interface selectorTypes {
|
||||
label: string;
|
||||
value: string;
|
||||
}
|
||||
|
||||
interface SelectProps {
|
||||
options: selectorTypes[];
|
||||
value: string;
|
||||
label: string;
|
||||
id: string;
|
||||
name: string;
|
||||
onChange: (
|
||||
e: React.ChangeEvent<{ name?: string | undefined; value: unknown }>
|
||||
) => void;
|
||||
classes: any;
|
||||
}
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
...fieldBasic
|
||||
});
|
||||
|
||||
const SelectStyled = withStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
root: {
|
||||
"label + &": {
|
||||
marginTop: theme.spacing(3)
|
||||
}
|
||||
},
|
||||
input: {
|
||||
borderRadius: 0,
|
||||
position: "relative",
|
||||
color: "#393939",
|
||||
fontSize: 14,
|
||||
padding: "11px 20px",
|
||||
border: "1px solid #c4c4c4",
|
||||
"&:hover": {
|
||||
borderColor: "#393939"
|
||||
},
|
||||
"&:focus": {
|
||||
backgroundColor: "#fff"
|
||||
}
|
||||
}
|
||||
})
|
||||
)(InputBase);
|
||||
|
||||
const SelectWrapper = ({
|
||||
classes,
|
||||
id,
|
||||
name,
|
||||
onChange,
|
||||
options,
|
||||
label,
|
||||
value
|
||||
}: SelectProps) => {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12} className={classes.fieldContainer}>
|
||||
<InputLabel htmlFor={id} className={classes.inputLabel}>
|
||||
{label}
|
||||
</InputLabel>
|
||||
<FormControl variant="outlined" fullWidth>
|
||||
<Select
|
||||
id={id}
|
||||
name={name}
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
input={<SelectStyled />}
|
||||
>
|
||||
{options.map(option => (
|
||||
<MenuItem
|
||||
value={option.value}
|
||||
key={`select-${name}-${option.label}`}
|
||||
>
|
||||
{option.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default withStyles(styles)(SelectWrapper);
|
||||
@@ -19,7 +19,7 @@ export const fieldBasic = {
|
||||
inputLabel: {
|
||||
fontWeight: 500,
|
||||
marginRight: 16,
|
||||
minWidth: 80,
|
||||
minWidth: 90,
|
||||
fontSize: 14,
|
||||
color: "#393939"
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
|
||||
interface IModalProps {
|
||||
classes: any;
|
||||
closeModalAndRefresh: () => void;
|
||||
onClose: () => void;
|
||||
modalOpen: boolean;
|
||||
title: string;
|
||||
children: any;
|
||||
@@ -85,7 +85,7 @@ const styles = (theme: Theme) =>
|
||||
});
|
||||
|
||||
const ModalWrapper = ({
|
||||
closeModalAndRefresh,
|
||||
onClose,
|
||||
modalOpen,
|
||||
title,
|
||||
children,
|
||||
@@ -94,7 +94,7 @@ const ModalWrapper = ({
|
||||
return (
|
||||
<Dialog
|
||||
open={modalOpen}
|
||||
onClose={closeModalAndRefresh}
|
||||
onClose={onClose}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
maxWidth={"md"}
|
||||
@@ -105,7 +105,7 @@ const ModalWrapper = ({
|
||||
<IconButton
|
||||
aria-label="close"
|
||||
className={classes.closeButton}
|
||||
onClick={closeModalAndRefresh}
|
||||
onClick={onClose}
|
||||
disableRipple
|
||||
>
|
||||
<span className={classes.closeIcon} />
|
||||
|
||||
@@ -16,22 +16,14 @@
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
LinearProgress,
|
||||
TextField
|
||||
} from "@material-ui/core";
|
||||
import Radio from "@material-ui/core/Radio";
|
||||
import RadioGroup from "@material-ui/core/RadioGroup";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import api from "../../../common/api";
|
||||
import UsersSelectors from "./UsersSelectors";
|
||||
import Title from "../../../common/Title";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
import RadioGroupSelector from "../Common/FormComponents/RadioGroupSelector/RadioGroupSelector";
|
||||
|
||||
interface IGroupProps {
|
||||
open: boolean;
|
||||
@@ -56,6 +48,9 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
keyName: {
|
||||
marginLeft: 5
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -157,102 +152,88 @@ const AddGroup = ({
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={closeModalAndRefresh}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
title={selectedGroup !== null ? `Group Edit - ${groupName}` : "Add Group"}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{selectedGroup !== null ? `Group Edit - ${groupName}` : "Add Group"}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<form noValidate autoComplete="off" onSubmit={setSaving}>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
|
||||
{selectedGroup !== null ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<Title>Status</Title>
|
||||
<RadioGroup
|
||||
aria-label="status"
|
||||
name="status"
|
||||
value={groupEnabled}
|
||||
onChange={e => {
|
||||
setGroupEnabled(e.target.value);
|
||||
}}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="enabled"
|
||||
control={<Radio color={"primary"} />}
|
||||
label="Enabled"
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="disabled"
|
||||
control={<Radio color={"primary"} />}
|
||||
label="Disabled"
|
||||
/>
|
||||
</RadioGroup>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
fullWidth
|
||||
label="Name"
|
||||
value={groupName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setGroupName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<form noValidate autoComplete="off" onSubmit={setSaving}>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<UsersSelectors
|
||||
selectedUsers={selectedUsers}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={saving}
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
{saving && (
|
||||
)}
|
||||
|
||||
{selectedGroup !== null ? (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
<RadioGroupSelector
|
||||
currentSelection={groupEnabled}
|
||||
id="group-status"
|
||||
name="group-status"
|
||||
label="Status"
|
||||
onChange={e => {
|
||||
setGroupEnabled(e.target.value);
|
||||
}}
|
||||
selectorOptions={[
|
||||
{ label: "Enabled", value: "enabled" },
|
||||
{ label: "Disabled", value: "disabled" }
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
)}
|
||||
</React.Fragment>
|
||||
) : (
|
||||
<React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="group-name"
|
||||
name="group-name"
|
||||
label="Name"
|
||||
value={groupName}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
setGroupName(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</React.Fragment>
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Grid item xs={12}>
|
||||
<UsersSelectors
|
||||
selectedUsers={selectedUsers}
|
||||
setSelectedUsers={setSelectedUsers}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12} className={classes.buttonContainer}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={saving}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{saving && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -18,20 +18,14 @@ import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import { UnControlled as CodeMirror } from "react-codemirror2";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
LinearProgress,
|
||||
TextField
|
||||
} from "@material-ui/core";
|
||||
import { Button, LinearProgress } from "@material-ui/core";
|
||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import Title from "../../../common/Title";
|
||||
import api from "../../../common/api";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import "codemirror/theme/material.css";
|
||||
import { Policy } from "./types";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
require("codemirror/mode/javascript/javascript");
|
||||
|
||||
@@ -46,6 +40,9 @@ const styles = (theme: Theme) =>
|
||||
},
|
||||
codeMirror: {
|
||||
fontSize: 14
|
||||
},
|
||||
buttonContainer: {
|
||||
textAlign: "right"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -103,100 +100,103 @@ class AddPolicy extends React.Component<IAddPolicyProps, IAddPolicyState> {
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const { policyEdit } = this.props;
|
||||
|
||||
if (policyEdit) {
|
||||
this.setState({
|
||||
policyName: policyEdit.name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, open, policyEdit } = this.props;
|
||||
const { addLoading, addError } = this.state;
|
||||
const { addLoading, addError, policyName } = this.state;
|
||||
return (
|
||||
<Dialog
|
||||
fullWidth
|
||||
open={open}
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
this.props.closeModalAndRefresh();
|
||||
});
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
title={`${policyEdit ? "Info" : "Create"} Policy`}
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
<Title>{policyEdit ? "Info" : "Create"} Policy</Title>
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.addRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
defaultValue={policyEdit ? policyEdit.name : ""}
|
||||
id="standard-basic"
|
||||
fullWidth
|
||||
label="Policy Name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ policyName: e.target.value });
|
||||
}}
|
||||
/>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<CodeMirror
|
||||
className={classes.codeMirror}
|
||||
value={
|
||||
policyEdit
|
||||
? JSON.stringify(JSON.parse(policyEdit.policy), null, 4)
|
||||
: ""
|
||||
}
|
||||
options={{
|
||||
mode: "javascript",
|
||||
lineNumbers: true
|
||||
}}
|
||||
onChange={(editor, data, value) => {
|
||||
this.setState({ policyDefinition: value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{!policyEdit && (
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
fullWidth
|
||||
disabled={addLoading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
)}
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="policy-name"
|
||||
name="policy-name"
|
||||
label="Policy Name"
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ policyName: e.target.value });
|
||||
}}
|
||||
value={policyName}
|
||||
disabled={!!policyEdit}
|
||||
/>
|
||||
</Grid>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<CodeMirror
|
||||
className={classes.codeMirror}
|
||||
value={
|
||||
policyEdit
|
||||
? JSON.stringify(JSON.parse(policyEdit.policy), null, 4)
|
||||
: ""
|
||||
}
|
||||
options={{
|
||||
mode: "javascript",
|
||||
lineNumbers: true
|
||||
}}
|
||||
onChange={(editor, data, value) => {
|
||||
this.setState({ policyDefinition: value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
{!policyEdit && (
|
||||
<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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -208,13 +208,24 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AddPolicy
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
this.closeAddModalAndRefresh();
|
||||
}}
|
||||
policyEdit={policyEdit}
|
||||
/>
|
||||
{addScreenOpen && (
|
||||
<AddPolicy
|
||||
open={addScreenOpen}
|
||||
closeModalAndRefresh={() => {
|
||||
this.closeAddModalAndRefresh();
|
||||
}}
|
||||
policyEdit={policyEdit}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeletePolicy
|
||||
deleteOpen={deleteOpen}
|
||||
selectedPolicy={selectedPolicy}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">IAM Policies</Typography>
|
||||
@@ -336,14 +347,6 @@ class Policies extends React.Component<IPoliciesProps, IPoliciesState> {
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<DeletePolicy
|
||||
deleteOpen={deleteOpen}
|
||||
selectedPolicy={selectedPolicy}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,15 +17,7 @@
|
||||
import React from "react";
|
||||
import Grid from "@material-ui/core/Grid";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogTitle,
|
||||
FormControlLabel,
|
||||
LinearProgress,
|
||||
TextField
|
||||
} from "@material-ui/core";
|
||||
import { Button, FormControlLabel, LinearProgress } from "@material-ui/core";
|
||||
import {
|
||||
createStyles,
|
||||
lighten,
|
||||
@@ -54,6 +46,8 @@ import {
|
||||
ServiceAccountDetails
|
||||
} from "./types";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
import InputBoxWrapper from "../Common/FormComponents/InputBoxWrapper/InputBoxWrapper";
|
||||
|
||||
const useToolbarStyles = makeStyles((theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -209,7 +203,7 @@ class AddServiceAccountContent extends React.Component<
|
||||
|
||||
saveRecord(event: React.FormEvent) {
|
||||
event.preventDefault();
|
||||
const { name, addLoading, selectedPermissions,enabled } = this.state;
|
||||
const { name, addLoading, selectedPermissions, enabled } = this.state;
|
||||
const { selectedServiceAccount } = this.props;
|
||||
if (addLoading) {
|
||||
return;
|
||||
@@ -272,7 +266,12 @@ class AddServiceAccountContent extends React.Component<
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, selectedServiceAccount } = this.props;
|
||||
const {
|
||||
classes,
|
||||
selectedServiceAccount,
|
||||
open,
|
||||
closeModalAndRefresh
|
||||
} = this.props;
|
||||
const {
|
||||
addLoading,
|
||||
addError,
|
||||
@@ -342,177 +341,175 @@ class AddServiceAccountContent extends React.Component<
|
||||
const handleChange = (name: string) => (
|
||||
event: React.ChangeEvent<HTMLInputElement>
|
||||
) => {
|
||||
this.setState({enabled:event.target.checked})
|
||||
this.setState({ enabled: event.target.checked });
|
||||
};
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<DialogTitle id="alert-dialog-title">
|
||||
{selectedServiceAccount !== null ? (
|
||||
<span>Edit Service Account</span>
|
||||
) : (
|
||||
<span>Create Service Account</span>
|
||||
)}
|
||||
</DialogTitle>
|
||||
<DialogContent>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{loadingServiceAccount && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
)}
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
closeModalAndRefresh(null);
|
||||
});
|
||||
}}
|
||||
title={
|
||||
selectedServiceAccount !== null
|
||||
? "Edit Service Account"
|
||||
: "Create Service Account"
|
||||
}
|
||||
>
|
||||
<form
|
||||
noValidate
|
||||
autoComplete="off"
|
||||
onSubmit={(e: React.FormEvent<HTMLFormElement>) => {
|
||||
this.saveRecord(e);
|
||||
}}
|
||||
>
|
||||
<Grid container>
|
||||
{loadingServiceAccount && (
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
id="standard-basic"
|
||||
fullWidth
|
||||
label="Name"
|
||||
value={name}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ name: e.target.value });
|
||||
}}
|
||||
/>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
{addError !== "" && (
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.root}>
|
||||
<EnhancedTableToolbar
|
||||
numSelected={selectedPermissions.length}
|
||||
/>
|
||||
<TableContainer>
|
||||
<Table
|
||||
className={classes.table}
|
||||
aria-labelledby="tableTitle"
|
||||
size={"small"}
|
||||
aria-label="enhanced table"
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
indeterminate={
|
||||
selectedPermissions.length > 0 &&
|
||||
selectedPermissions.length < permissions.length
|
||||
}
|
||||
checked={
|
||||
selectedPermissions.length > 0 &&
|
||||
selectedPermissions.length ===
|
||||
permissions.length
|
||||
}
|
||||
onChange={handleSelectAllClick}
|
||||
inputProps={{
|
||||
"aria-label": "select all desserts"
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>Permission</TableCell>
|
||||
<TableCell>Description</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{permissions
|
||||
.slice(
|
||||
page * rowsPerPage,
|
||||
page * rowsPerPage + rowsPerPage
|
||||
)
|
||||
.map((row, index) => {
|
||||
const isItemSelected = isSelected(row);
|
||||
const labelId = `enhanced-table-checkbox-${index}`;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={event => handleClick(event, row)}
|
||||
role="checkbox"
|
||||
aria-checked={isItemSelected}
|
||||
tabIndex={-1}
|
||||
key={row.name}
|
||||
selected={isItemSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isItemSelected}
|
||||
inputProps={{ "aria-labelledby": labelId }}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell id={labelId}>{row.name}</TableCell>
|
||||
<TableCell>{row.description}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow style={{ height: 33 * emptyRows }}>
|
||||
<TableCell colSpan={6} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
count={permissions.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
labelRowsPerPage={null}
|
||||
onChangePage={handleChangePage}
|
||||
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch
|
||||
onChange={handleChange("enabled")}
|
||||
value="checkedA"
|
||||
/>
|
||||
}
|
||||
label="Enabled"
|
||||
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
{addError}
|
||||
</Typography>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
)}
|
||||
<Grid item xs={12}>
|
||||
<InputBoxWrapper
|
||||
id="service-account-name"
|
||||
name="service-account-name"
|
||||
label="Name"
|
||||
value={name}
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ name: e.target.value });
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</React.Fragment>
|
||||
<Grid item xs={12}>
|
||||
<div className={classes.root}>
|
||||
<EnhancedTableToolbar
|
||||
numSelected={selectedPermissions.length}
|
||||
/>
|
||||
<TableContainer>
|
||||
<Table
|
||||
className={classes.table}
|
||||
aria-labelledby="tableTitle"
|
||||
size={"small"}
|
||||
aria-label="enhanced table"
|
||||
>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
indeterminate={
|
||||
selectedPermissions.length > 0 &&
|
||||
selectedPermissions.length < permissions.length
|
||||
}
|
||||
checked={
|
||||
selectedPermissions.length > 0 &&
|
||||
selectedPermissions.length === permissions.length
|
||||
}
|
||||
onChange={handleSelectAllClick}
|
||||
inputProps={{
|
||||
"aria-label": "select all desserts"
|
||||
}}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell>Permission</TableCell>
|
||||
<TableCell>Description</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{permissions
|
||||
.slice(
|
||||
page * rowsPerPage,
|
||||
page * rowsPerPage + rowsPerPage
|
||||
)
|
||||
.map((row, index) => {
|
||||
const isItemSelected = isSelected(row);
|
||||
const labelId = `enhanced-table-checkbox-${index}`;
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
hover
|
||||
onClick={event => handleClick(event, row)}
|
||||
role="checkbox"
|
||||
aria-checked={isItemSelected}
|
||||
tabIndex={-1}
|
||||
key={row.name}
|
||||
selected={isItemSelected}
|
||||
>
|
||||
<TableCell padding="checkbox">
|
||||
<Checkbox
|
||||
checked={isItemSelected}
|
||||
inputProps={{ "aria-labelledby": labelId }}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell id={labelId}>{row.name}</TableCell>
|
||||
<TableCell>{row.description}</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
{emptyRows > 0 && (
|
||||
<TableRow style={{ height: 33 * emptyRows }}>
|
||||
<TableCell colSpan={6} />
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25]}
|
||||
component="div"
|
||||
count={permissions.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
labelRowsPerPage={null}
|
||||
onChangePage={handleChangePage}
|
||||
onChangeRowsPerPage={handleChangeRowsPerPage}
|
||||
/>
|
||||
</div>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={
|
||||
<Switch onChange={handleChange("enabled")} value="checkedA" />
|
||||
}
|
||||
label="Enabled"
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<br />
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={addLoading}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
</Grid>
|
||||
{addLoading && (
|
||||
<Grid item xs={12}>
|
||||
<LinearProgress />
|
||||
</Grid>
|
||||
)}
|
||||
</Grid>
|
||||
</form>
|
||||
</ModalWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -534,21 +531,7 @@ class AddServiceAccount extends React.Component<
|
||||
state: IAddServiceAccountState = {};
|
||||
|
||||
render() {
|
||||
const { open } = this.props;
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onClose={() => {
|
||||
this.setState({ addError: "" }, () => {
|
||||
this.props.closeModalAndRefresh(null);
|
||||
});
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
>
|
||||
<AddServiceAccountWrapper {...this.props} />
|
||||
</Dialog>
|
||||
);
|
||||
return <AddServiceAccountWrapper {...this.props} />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,13 +19,12 @@ import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||
import { NewServiceAccount } from "./types";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogActions,
|
||||
DialogContent,
|
||||
DialogContentText,
|
||||
DialogTitle
|
||||
DialogContentText
|
||||
} from "@material-ui/core";
|
||||
import Typography from "@material-ui/core/Typography";
|
||||
import ModalWrapper from "../Common/ModalWrapper/ModalWrapper";
|
||||
|
||||
const styles = (theme: Theme) =>
|
||||
createStyles({
|
||||
@@ -49,18 +48,21 @@ class CredentialsPrompt extends React.Component<
|
||||
> {
|
||||
state: ICredentialsPromptState = {};
|
||||
|
||||
download(filename:string, text:string) {
|
||||
var element = document.createElement('a');
|
||||
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
|
||||
element.setAttribute('download', filename);
|
||||
download(filename: string, text: string) {
|
||||
var element = document.createElement("a");
|
||||
element.setAttribute(
|
||||
"href",
|
||||
"data:text/plain;charset=utf-8," + encodeURIComponent(text)
|
||||
);
|
||||
element.setAttribute("download", filename);
|
||||
|
||||
element.style.display = 'none';
|
||||
document.body.appendChild(element);
|
||||
element.style.display = "none";
|
||||
document.body.appendChild(element);
|
||||
|
||||
element.click();
|
||||
element.click();
|
||||
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
document.body.removeChild(element);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { classes, open, newServiceAccount } = this.props;
|
||||
@@ -70,60 +72,63 @@ class CredentialsPrompt extends React.Component<
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
<ModalWrapper
|
||||
modalOpen={open}
|
||||
onClose={() => {
|
||||
this.props.closeModal();
|
||||
}}
|
||||
aria-labelledby="alert-dialog-title"
|
||||
aria-describedby="alert-dialog-description"
|
||||
title="New Service Account"
|
||||
>
|
||||
<DialogTitle id="alert-dialog-title">New Service Account</DialogTitle>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
A new service account has been created with the following details:
|
||||
<ul>
|
||||
<li>
|
||||
<b>Access Key:</b>{" "}
|
||||
{newServiceAccount.service_account.access_key}
|
||||
</li>
|
||||
<li>
|
||||
<b>Secret Key:</b> {newServiceAccount.secret_key}
|
||||
</li>
|
||||
</ul>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
<React.Fragment>
|
||||
<DialogContent>
|
||||
<DialogContentText id="alert-dialog-description">
|
||||
A new service account has been created with the following details:
|
||||
<ul>
|
||||
<li>
|
||||
<b>Access Key:</b>{" "}
|
||||
{newServiceAccount.service_account.access_key}
|
||||
</li>
|
||||
<li>
|
||||
<b>Secret Key:</b> {newServiceAccount.secret_key}
|
||||
</li>
|
||||
</ul>
|
||||
<Typography
|
||||
component="p"
|
||||
variant="body1"
|
||||
className={classes.errorBlock}
|
||||
>
|
||||
Write these down, as this is the only time the secret will be
|
||||
displayed.
|
||||
</Typography>
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.download(
|
||||
"credentials.json",
|
||||
JSON.stringify({
|
||||
access_key: newServiceAccount.service_account.access_key,
|
||||
secret_key: newServiceAccount.secret_key
|
||||
})
|
||||
);
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Write these down, as this is the only time the secret will be
|
||||
displayed.
|
||||
</Typography>
|
||||
</DialogContentText>
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.download("credentials.json",JSON.stringify({
|
||||
access_key: newServiceAccount.service_account.access_key,
|
||||
secret_key: newServiceAccount.secret_key,
|
||||
}))
|
||||
}}
|
||||
color="primary"
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.props.closeModal();
|
||||
}}
|
||||
color="secondary"
|
||||
autoFocus
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</Dialog>
|
||||
Download
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
this.props.closeModal();
|
||||
}}
|
||||
color="secondary"
|
||||
autoFocus
|
||||
>
|
||||
Done
|
||||
</Button>
|
||||
</DialogActions>
|
||||
</React.Fragment>
|
||||
</ModalWrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,7 +80,7 @@ const styles = (theme: Theme) =>
|
||||
color: "#393939",
|
||||
"& tr": {
|
||||
"& th": {
|
||||
fontWeight:'bold'
|
||||
fontWeight: "bold"
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -93,14 +93,14 @@ const styles = (theme: Theme) =>
|
||||
actionsTray: {
|
||||
textAlign: "right",
|
||||
"& button": {
|
||||
marginLeft: 10,
|
||||
},
|
||||
marginLeft: 10
|
||||
}
|
||||
},
|
||||
searchField: {
|
||||
background: "#FFFFFF",
|
||||
padding: 12,
|
||||
borderRadius: 5,
|
||||
boxShadow: "0px 3px 6px #00000012",
|
||||
boxShadow: "0px 3px 6px #00000012"
|
||||
}
|
||||
});
|
||||
|
||||
@@ -250,14 +250,33 @@ class ServiceAccounts extends React.Component<
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<AddServiceAccount
|
||||
open={addScreenOpen}
|
||||
selectedServiceAccount={selectedServiceAccount}
|
||||
closeModalAndRefresh={(res: NewServiceAccount | null) => {
|
||||
this.closeAddModalAndRefresh(res);
|
||||
}}
|
||||
/>
|
||||
|
||||
{addScreenOpen && (
|
||||
<AddServiceAccount
|
||||
open={addScreenOpen}
|
||||
selectedServiceAccount={selectedServiceAccount}
|
||||
closeModalAndRefresh={(res: NewServiceAccount | null) => {
|
||||
this.closeAddModalAndRefresh(res);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{deleteOpen && (
|
||||
<DeleteServiceAccount
|
||||
deleteOpen={deleteOpen}
|
||||
selectedServiceAccount={selectedServiceAccount}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{showNewCredentials && (
|
||||
<CredentialsPrompt
|
||||
newServiceAccount={newServiceAccount}
|
||||
open={showNewCredentials}
|
||||
closeModal={() => {
|
||||
this.closeCredentialsModal();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Grid container>
|
||||
<Grid item xs={12}>
|
||||
<Typography variant="h6">Service Accounts</Typography>
|
||||
@@ -277,7 +296,7 @@ class ServiceAccounts extends React.Component<
|
||||
<InputAdornment position="start">
|
||||
<SearchIcon />
|
||||
</InputAdornment>
|
||||
),
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
@@ -331,7 +350,7 @@ class ServiceAccounts extends React.Component<
|
||||
<Checkbox
|
||||
value="secondary"
|
||||
color="primary"
|
||||
inputProps={{ 'aria-label': 'secondary checkbox' }}
|
||||
inputProps={{ "aria-label": "secondary checkbox" }}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className={classes.wrapCell}>
|
||||
@@ -387,21 +406,6 @@ class ServiceAccounts extends React.Component<
|
||||
</Paper>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<DeleteServiceAccount
|
||||
deleteOpen={deleteOpen}
|
||||
selectedServiceAccount={selectedServiceAccount}
|
||||
closeDeleteModalAndRefresh={(refresh: boolean) => {
|
||||
this.closeDeleteModalAndRefresh(refresh);
|
||||
}}
|
||||
/>
|
||||
<CredentialsPrompt
|
||||
newServiceAccount={newServiceAccount}
|
||||
open={showNewCredentials}
|
||||
closeModal={() => {
|
||||
this.closeCredentialsModal();
|
||||
}}
|
||||
/>
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ class AddUserContent extends React.Component<
|
||||
|
||||
return (
|
||||
<ModalWrapper
|
||||
closeModalAndRefresh={() => {
|
||||
onClose={() => {
|
||||
this.props.closeModalAndRefresh();
|
||||
}}
|
||||
modalOpen={this.props.open}
|
||||
@@ -227,7 +227,7 @@ class AddUserContent extends React.Component<
|
||||
name="accesskey-input"
|
||||
label="Access Key"
|
||||
value={accessKey}
|
||||
onChangeFunc={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ accessKey: e.target.value });
|
||||
}}
|
||||
disabled={selectedUser !== null}
|
||||
@@ -254,7 +254,7 @@ class AddUserContent extends React.Component<
|
||||
label="Secret Key"
|
||||
type="password"
|
||||
value={secretKey}
|
||||
onChangeFunc={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
this.setState({ secretKey: e.target.value });
|
||||
}}
|
||||
autoComplete="current-password"
|
||||
|
||||
Reference in New Issue
Block a user