mirror of
https://github.com/OpenMaxIO/openmaxio-object-browser
synced 2026-07-01 07:41:18 -07:00
Groups list component creation (#14)
* Reconnected Users delete modal * Fix API path * Reconnected access name modal * Fixed title issue * Fixed reset state * Added groups selectors list control component * Change of edit form to not edit access key & access secret values Co-authored-by: Benjamin Perez <benjamin@bexsoft.net> Co-authored-by: Daniel Valdivia <hola@danielvaldivia.com>
This commit is contained in:
@@ -29,11 +29,18 @@ import {
|
|||||||
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
import { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
import api from "../../../common/api";
|
import api from "../../../common/api";
|
||||||
import { User } from "./types";
|
import { User } from "./types";
|
||||||
|
import GroupsSelectors from "./GroupsSelectors";
|
||||||
|
|
||||||
const styles = (theme: Theme) =>
|
const styles = (theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
errorBlock: {
|
errorBlock: {
|
||||||
color: "red"
|
color: "red"
|
||||||
|
},
|
||||||
|
strongText: {
|
||||||
|
fontWeight: 700,
|
||||||
|
},
|
||||||
|
keyName: {
|
||||||
|
marginLeft: 5
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,6 +55,9 @@ interface IAddUserContentState {
|
|||||||
addError: string;
|
addError: string;
|
||||||
accessKey: string;
|
accessKey: string;
|
||||||
secretKey: string;
|
secretKey: string;
|
||||||
|
selectedGroups: string[];
|
||||||
|
loadingGroups: boolean;
|
||||||
|
groupsList: any[];
|
||||||
}
|
}
|
||||||
|
|
||||||
class AddUserContent extends React.Component<
|
class AddUserContent extends React.Component<
|
||||||
@@ -58,7 +68,10 @@ class AddUserContent extends React.Component<
|
|||||||
addLoading: false,
|
addLoading: false,
|
||||||
addError: "",
|
addError: "",
|
||||||
accessKey: "",
|
accessKey: "",
|
||||||
secretKey: ""
|
secretKey: "",
|
||||||
|
selectedGroups: [],
|
||||||
|
loadingGroups: false,
|
||||||
|
groupsList: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
componentDidMount(): void {
|
componentDidMount(): void {
|
||||||
@@ -74,7 +87,7 @@ class AddUserContent extends React.Component<
|
|||||||
|
|
||||||
saveRecord(event: React.FormEvent) {
|
saveRecord(event: React.FormEvent) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const { accessKey, addLoading, secretKey } = this.state;
|
const { accessKey, addLoading, secretKey, selectedGroups } = this.state;
|
||||||
const { selectedUser } = this.props;
|
const { selectedUser } = this.props;
|
||||||
if (addLoading) {
|
if (addLoading) {
|
||||||
return;
|
return;
|
||||||
@@ -133,12 +146,12 @@ class AddUserContent extends React.Component<
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { classes, selectedUser } = this.props;
|
const { classes, selectedUser } = this.props;
|
||||||
const { addLoading, addError, accessKey, secretKey } = this.state;
|
const { addLoading, addError, accessKey, secretKey, selectedGroups, loadingGroups, groupsList } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<DialogTitle id="alert-dialog-title">
|
<DialogTitle id="alert-dialog-title">
|
||||||
Create User
|
{selectedUser !== null ? 'Edit User' : 'Add User'}
|
||||||
</DialogTitle>
|
</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<form
|
<form
|
||||||
@@ -149,13 +162,6 @@ class AddUserContent extends React.Component<
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Grid container>
|
<Grid container>
|
||||||
<Grid item xs={12}>
|
|
||||||
{selectedUser !== null ? (
|
|
||||||
<Title>Edit User</Title>
|
|
||||||
) : (
|
|
||||||
<Title>Add User</Title>
|
|
||||||
)}
|
|
||||||
</Grid>
|
|
||||||
{addError !== "" && (
|
{addError !== "" && (
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Typography
|
<Typography
|
||||||
@@ -167,33 +173,58 @@ class AddUserContent extends React.Component<
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
<Grid item xs={12}>
|
|
||||||
<TextField
|
{selectedUser !== null ? (
|
||||||
id="standard-basic"
|
<React.Fragment>
|
||||||
fullWidth
|
<span className={classes.strongText}>Access Key:</span>
|
||||||
label="Access Key"
|
<span className={classes.keyName}>{` ${accessKey}`}</span>
|
||||||
value={accessKey}
|
</React.Fragment>
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
) : (
|
||||||
this.setState({ accessKey: e.target.value });
|
<React.Fragment>
|
||||||
}}
|
<Grid item xs={12}>
|
||||||
/>
|
<TextField
|
||||||
</Grid>
|
id="standard-basic"
|
||||||
<Grid item xs={12}>
|
fullWidth
|
||||||
<TextField
|
label="Access Key"
|
||||||
id="standard-multiline-static"
|
value={accessKey}
|
||||||
label={selectedUser !== null ? 'New Secret Key': 'Secret Key'}
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
type="password"
|
this.setState({ accessKey: e.target.value });
|
||||||
fullWidth
|
}}
|
||||||
value={secretKey}
|
/>
|
||||||
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
</Grid>
|
||||||
this.setState({ secretKey: e.target.value });
|
<Grid item xs={12}>
|
||||||
}}
|
<TextField
|
||||||
autoComplete="current-password"
|
id="standard-multiline-static"
|
||||||
/>
|
label={selectedUser !== null ? 'New Secret Key': 'Secret Key'}
|
||||||
</Grid>
|
type="password"
|
||||||
|
fullWidth
|
||||||
|
value={secretKey}
|
||||||
|
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
this.setState({ secretKey: e.target.value });
|
||||||
|
}}
|
||||||
|
autoComplete="current-password"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<br />
|
<br />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<GroupsSelectors
|
||||||
|
selectedGroups={selectedGroups}
|
||||||
|
setSelectedGroups={
|
||||||
|
(elements: string[]) => {
|
||||||
|
this.setState({
|
||||||
|
selectedGroups: elements
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loading={loadingGroups}
|
||||||
|
records={groupsList}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
<Grid item xs={12}>
|
<Grid item xs={12}>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ class DeleteUser extends React.Component<
|
|||||||
}
|
}
|
||||||
this.setState({ deleteLoading: true }, () => {
|
this.setState({ deleteLoading: true }, () => {
|
||||||
api
|
api
|
||||||
.invoke("DELETE", `/api/v1/users/${selectedUser.id}`, {
|
.invoke("DELETE", `/api/v1/users/${selectedUser.accessKey}`, {
|
||||||
id: selectedUser.id
|
id: selectedUser.id
|
||||||
})
|
})
|
||||||
.then((res: UsersList) => {
|
.then((res: UsersList) => {
|
||||||
@@ -114,7 +114,7 @@ class DeleteUser extends React.Component<
|
|||||||
<DialogContent>
|
<DialogContent>
|
||||||
{deleteLoading && <LinearProgress />}
|
{deleteLoading && <LinearProgress />}
|
||||||
<DialogContentText id="alert-dialog-description">
|
<DialogContentText id="alert-dialog-description">
|
||||||
Are you sure you want to delete user{" "}<b>{selectedUser.name}</b>?
|
Are you sure you want to delete user{" "}<b>{selectedUser.accessKey}</b>?
|
||||||
{deleteError !== "" && (
|
{deleteError !== "" && (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<br />
|
<br />
|
||||||
|
|||||||
188
portal-ui/src/screens/Console/Users/GroupsSelectors.tsx
Normal file
188
portal-ui/src/screens/Console/Users/GroupsSelectors.tsx
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// This file is part of MinIO Kubernetes Cloud
|
||||||
|
// 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 { createStyles, Theme, withStyles } from "@material-ui/core/styles";
|
||||||
|
import { LinearProgress } from "@material-ui/core";
|
||||||
|
import Table from "@material-ui/core/Table";
|
||||||
|
import TableBody from "@material-ui/core/TableBody";
|
||||||
|
import TableCell from "@material-ui/core/TableCell";
|
||||||
|
import TableHead from "@material-ui/core/TableHead";
|
||||||
|
import TableRow from "@material-ui/core/TableRow";
|
||||||
|
import Paper from "@material-ui/core/Paper";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import Title from "../../../common/Title";
|
||||||
|
import InputAdornment from "@material-ui/core/InputAdornment";
|
||||||
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
import Checkbox from "@material-ui/core/Checkbox";
|
||||||
|
|
||||||
|
interface IGroupsProps {
|
||||||
|
classes: any;
|
||||||
|
selectedGroups: string[];
|
||||||
|
setSelectedGroups: any;
|
||||||
|
records: any[];
|
||||||
|
loading: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = (theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
seeMore: {
|
||||||
|
marginTop: theme.spacing(3)
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
// padding: theme.spacing(2),
|
||||||
|
display: "flex",
|
||||||
|
overflow: "auto",
|
||||||
|
flexDirection: "column"
|
||||||
|
},
|
||||||
|
addSideBar: {
|
||||||
|
width: "320px",
|
||||||
|
padding: "20px"
|
||||||
|
},
|
||||||
|
errorBlock: {
|
||||||
|
color: "red"
|
||||||
|
},
|
||||||
|
tableToolbar: {
|
||||||
|
paddingLeft: theme.spacing(2),
|
||||||
|
paddingRight: theme.spacing(0)
|
||||||
|
},
|
||||||
|
wrapCell: {
|
||||||
|
maxWidth: "200px",
|
||||||
|
whiteSpace: "normal",
|
||||||
|
wordWrap: "break-word"
|
||||||
|
},
|
||||||
|
minTableHeader: {
|
||||||
|
color: "#393939",
|
||||||
|
"& tr": {
|
||||||
|
"& th": {
|
||||||
|
fontWeight:'bold'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
actionsTray: {
|
||||||
|
textAlign: "left",
|
||||||
|
"& button": {
|
||||||
|
marginLeft: 10,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterField: {
|
||||||
|
background: "#FFFFFF",
|
||||||
|
padding: 12,
|
||||||
|
borderRadius: 5,
|
||||||
|
boxShadow: "0px 3px 6px #00000012",
|
||||||
|
width: '100%'
|
||||||
|
},
|
||||||
|
noFound: {
|
||||||
|
textAlign: "center",
|
||||||
|
padding: "10px 0",
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const GroupsSelectors = ({
|
||||||
|
classes,
|
||||||
|
selectedGroups,
|
||||||
|
setSelectedGroups,
|
||||||
|
records,
|
||||||
|
loading
|
||||||
|
}: IGroupsProps) => {
|
||||||
|
|
||||||
|
if(!records) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectionChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const targetD = e.target;
|
||||||
|
const value = targetD.value;
|
||||||
|
const checked = targetD.checked;
|
||||||
|
|
||||||
|
let elements : string[] = [...selectedGroups]; // We clone the selectedGroups array
|
||||||
|
|
||||||
|
if(checked) { // If the user has checked this field we need to push this to selectedGroupsList
|
||||||
|
elements.push(value);
|
||||||
|
} else { // User has unchecked this field, we need to remove it from the list
|
||||||
|
elements = elements.filter(element => element !== value);
|
||||||
|
}
|
||||||
|
setSelectedGroups(elements);
|
||||||
|
|
||||||
|
return elements;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Title>Groups</Title>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
{loading && <LinearProgress />}
|
||||||
|
{records != null && records.length > 0 ? (
|
||||||
|
<React.Fragment>
|
||||||
|
<Grid item xs={12} className={classes.actionsTray}>
|
||||||
|
<TextField
|
||||||
|
placeholder="Filter Groups"
|
||||||
|
className={classes.filterField}
|
||||||
|
id="search-resource"
|
||||||
|
label=""
|
||||||
|
InputProps={{
|
||||||
|
disableUnderline: true,
|
||||||
|
startAdornment: (
|
||||||
|
<InputAdornment position="start">
|
||||||
|
<SearchIcon />
|
||||||
|
</InputAdornment>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Table size="medium">
|
||||||
|
<TableHead className={classes.minTableHeader}>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell>Select</TableCell>
|
||||||
|
<TableCell>Group</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{records.map(row => (
|
||||||
|
<TableRow key={`group-${row.groupName}`}>
|
||||||
|
<TableCell padding="checkbox">
|
||||||
|
<Checkbox
|
||||||
|
value={row.groupName}
|
||||||
|
color="primary"
|
||||||
|
inputProps={{
|
||||||
|
'aria-label': 'secondary checkbox'
|
||||||
|
}}
|
||||||
|
onChange={ selectionChanged }
|
||||||
|
checked={selectedGroups.includes(row.groupName)}
|
||||||
|
/>
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className={classes.wrapCell}>
|
||||||
|
{row.groupName}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</React.Fragment>
|
||||||
|
) : (
|
||||||
|
<div className={classes.noFound}>No Groups Available</div>
|
||||||
|
)}
|
||||||
|
</Paper>
|
||||||
|
</Grid>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default withStyles(styles)(GroupsSelectors);
|
||||||
|
|
||||||
|
|
||||||
@@ -255,7 +255,8 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
|||||||
startIcon={<CreateIcon />}
|
startIcon={<CreateIcon />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
addScreenOpen: true
|
addScreenOpen: true,
|
||||||
|
selectedUser: null,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -295,6 +296,10 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
|||||||
<IconButton
|
<IconButton
|
||||||
aria-label="view"
|
aria-label="view"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
addScreenOpen: true,
|
||||||
|
selectedUser: row,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ViewIcon />
|
<ViewIcon />
|
||||||
@@ -302,7 +307,10 @@ class Users extends React.Component<IUsersProps, IUsersState> {
|
|||||||
<IconButton
|
<IconButton
|
||||||
aria-label="delete"
|
aria-label="delete"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
deleteOpen: true,
|
||||||
|
selectedUser: row,
|
||||||
|
});
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteIcon />
|
<DeleteIcon />
|
||||||
|
|||||||
Reference in New Issue
Block a user