frontend/app/Users/UserList.jsx (223 lines of code) (raw):
import React from 'react';
import axios from 'axios';
import {createStyles, Paper, Snackbar, withStyles, Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle} from "@material-ui/core";
import {baseStyles} from "../BaseStyles";
import AdminContainer from "../admin/AdminContainer";
import {makeUserListColumns} from "./UserListContent";
import {DataGrid} from "@material-ui/data-grid";
import MuiAlert from "@material-ui/lab/Alert";
import ErrorViewComponent from "../common/ErrorViewComponent";
import {Helmet} from "react-helmet";
const styles = (theme)=> Object.assign(createStyles({
tableContainer: {
height: "90vh"
}
}), baseStyles);
class UserList extends React.Component {
constructor(props){
super(props);
this.state = {
usersList: [],
loading: false,
saveCompleted: false,
showingAlert: false,
collectionsList: [],
lastError: null,
knownDepartments: [],
open: false,
userToDelete: ""
};
this.handleClose = this.handleClose.bind(this);
this.boolFieldChanged = this.boolFieldChanged.bind(this);
this.stringFieldChanged = this.stringFieldChanged.bind(this);
this.loadUsers = this.loadUsers.bind(this);
this.userCollectionsUpdated = this.userCollectionsUpdated.bind(this);
this.quotaChanged = this.quotaChanged.bind(this);
this.deleteClicked = this.deleteClicked.bind(this);
this.handleCloseDialogue = this.handleCloseDialogue.bind(this);
this.handleDelete = this.handleDelete.bind(this);
}
/**
* updates the specific user profile in our state
* @param newEntry
*/
updateEntry(newEntry){
this.setState({usersList: this.state.usersList
.filter(entry=>entry.userEmail!==newEntry.userEmail)
.concat(newEntry)
.sort(UserList.sortFunc)
.map((user,idx)=>Object.assign({id: idx}, user))
})
}
boolFieldChanged(entry, fieldName, currentValue) {
this.stringFieldChanged(entry, fieldName, currentValue ? "false" : "true")
}
stringFieldChanged(entry, fieldName, newString){
const updateRq = {
user: entry.userEmail,
fieldName: fieldName,
stringValue: newString,
operation: "OP_OVERWRITE"
};
axios.put("/api/user/update",updateRq).then(response=>{
this.updateEntry(response.data.entry);
this.setState({saveCompleted: true, showingAlert: true});
}).catch(err=>{
console.error(err);
this.setState({lastError: err, showingAlert: true});
})
}
performUpdate(updateRq){
axios.put("/api/user/update", updateRq).then(response=>{
this.updateEntry(response.data.entry);
this.setState({saveCompleted: true, showingAlert: true});
}).catch(err=>{
console.error(err);
this.setState({showingAlert: true, lastError: err});
});
}
userCollectionsUpdated(entry, newValue){
const updateRq = {
user: entry.userEmail,
fieldName: "VISIBLE_COLLECTIONS",
listValue: newValue,
operation: "OP_OVERWRITE"
};
this.performUpdate(updateRq);
}
quotaChanged(entry, quotaField, newValue){
const newValueString = (newValue / 1048576).toString();
const updateRq = {
user: entry.userEmail,
fieldName: quotaField,
stringValue: newValueString,
operation: "OP_OVERWRITE"
};
this.performUpdate(updateRq);
}
loadCollectionsList(){
console.log("loadCollectionsList");
return new Promise((resolve, reject)=>
this.setState({loading: true}, ()=>axios.get("/api/scanTarget")
.then(result=>{
this.setState({collectionsList: result.data.entries.map(entry=>entry.bucketName), loading: false}, ()=>resolve())
}).catch(err=>{
this.setState({lastError: err, loading: false});
window.setTimeout(this.loadCollectionsList, 3000); //retry after 3 seconds
})
)
);
}
static sortFunc(a,b){
return a.userEmail.localeCompare(b.userEmail);
}
loadUsers(){
return new Promise((resolve, reject)=>
this.setState({loading: true}, ()=>axios.get("/api/user")
.then(result=>{
this.setState({
usersList: result.data.entries
.sort(UserList.sortFunc)
.map((user,idx)=>Object.assign({id: idx}, user)), //give each UserProfile an id parameter for MUI
//dedupe the list of known departments by using Set
knownDepartments: [...new Set(result.data.entries.map(entry=>entry.department).filter(dept=>dept!==null))],
loading: false}, ()=>resolve());
}).catch(err=>{
this.setState({lastError: err, loading: false});
window.setTimeout(this.loadUsers, 3000)
})
)
);
}
componentDidMount(){
this.loadCollectionsList().then(this.loadUsers);
}
handleClose() {
this.setState({showingAlert: false},()=>this.setState({saveCompleted: false, lastError: null}));
}
deleteClicked(entry){
this.setState({open: true, userToDelete: entry.userEmail});
}
handleCloseDialogue() {
this.setState({open: false});
};
handleDelete() {
this.setState({open: false});
const deleteRq = {
user: this.state.userToDelete
};
axios.put("/api/user/delete",deleteRq).then(response=>{
}).then(res => {
this.loadUsers();
}).catch(err=>{
console.error(err);
})
};
render(){
const columns = makeUserListColumns(
this.state.knownDepartments,
this.state.collectionsList,
this.stringFieldChanged,
this.boolFieldChanged,
this.userCollectionsUpdated,
this.quotaChanged,
this.deleteClicked
);
const targetRowHeight = (this.state.collectionsList && this.state.collectionsList.length>0) ?
this.state.collectionsList.length * 40 : 52;
return <>
<Helmet>
<title>Users - ArchiveHunter</title>
</Helmet>
<Snackbar open={this.state.showingAlert}
autoHideDuration={8000}
onClose={this.handleClose}>
<>
{
this.state.lastError ?
<MuiAlert elevation={6} severity="error">
{ErrorViewComponent.formatError(this.state.lastError)}
</MuiAlert> : null
}
{
this.state.saveCompleted ?
<MuiAlert elevation={6} severity="success">
Saved
</MuiAlert> : null
}
</>
</Snackbar>
<AdminContainer {...this.props}>
<Paper elevation={3} className={this.props.classes.tableContainer}>
<DataGrid columns={columns}
rows={this.state.usersList}
rowHeight={targetRowHeight}
rowsPerPageOptions={[10,20,50,100]}
/>
</Paper>
</AdminContainer>
<Dialog
open={this.state.open}
onClose={this.handleCloseDialogue}
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
<DialogTitle id="alert-dialog-title">
{"Delete User?"}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-description">
Are you sure you want to delete the user {this.state.userToDelete}?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={this.handleCloseDialogue}>No</Button>
<Button onClick={this.handleDelete} autoFocus>
Yes
</Button>
</DialogActions>
</Dialog>
</>
}
}
export default withStyles(styles)(UserList);