From 68d1ef7f9758e8cb6c85e8510c0da05ff84419f3 Mon Sep 17 00:00:00 2001 From: Larbi Gharib <larbi.gharib@savoirfairelinux.com> Date: Tue, 28 Jul 2020 10:25:38 -0400 Subject: [PATCH] Users search bar user display Change-Id: I5910dcfdace9c0a429c968af0d164b656ddd0878 --- jams-react-client/src/api.js | 2 +- .../components/cardStyle.js | 6 - .../src/components/CaSetup/CaSetup.js | 1 - .../src/components/Devices/Devices.js | 13 +- .../IdentityManagement/IdentityManagement.js | 1 - jams-react-client/src/layouts/Admin.js | 4 +- .../views/UserProfile/DisplayUserProfile.js | 254 +++++++++++++----- .../UserProfile/EditCreateUserProfile.js | 116 +++++--- jams-react-client/src/views/Users/Users.js | 138 +++++----- 9 files changed, 331 insertions(+), 204 deletions(-) diff --git a/jams-react-client/src/api.js b/jams-react-client/src/api.js index 9cebbd30..86819e69 100644 --- a/jams-react-client/src/api.js +++ b/jams-react-client/src/api.js @@ -47,7 +47,7 @@ import { api_path_get_needs_update, api_path_get_start_update, api_path_post_create_user, - api_path_get_user, + api_path_get_auth_user, api_path_post_update_user, api_path_get_exists_user, api_path_get_user_directory_search, diff --git a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js index 28778131..9c8ad53a 100644 --- a/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js +++ b/jams-react-client/src/assets/jss/material-dashboard-react/components/cardStyle.js @@ -27,12 +27,6 @@ const cardStyle = { "& li" :{ display: 'flex', alignItems: 'center', - "& svg": { - marginRight: "10px" - }, - "& img": { - marginRight: "10px" - } } }, "&:hover": { diff --git a/jams-react-client/src/components/CaSetup/CaSetup.js b/jams-react-client/src/components/CaSetup/CaSetup.js index cdb6bc19..89980f29 100644 --- a/jams-react-client/src/components/CaSetup/CaSetup.js +++ b/jams-react-client/src/components/CaSetup/CaSetup.js @@ -135,7 +135,6 @@ export default function CaSetup(props) { axios(configApiCall(api_path_post_install_ca, "POST", jsonData, null)).then((response)=>{ handleInstallCA(response); - console.log(response); }).catch((error)=>{ props.setError(error); console.log('Error installing CA Setup: ' + error ); diff --git a/jams-react-client/src/components/Devices/Devices.js b/jams-react-client/src/components/Devices/Devices.js index 2c0aa556..2bdab736 100755 --- a/jams-react-client/src/components/Devices/Devices.js +++ b/jams-react-client/src/components/Devices/Devices.js @@ -32,18 +32,17 @@ export default function Devices(props) { auth.checkDirectoryType(() => { if(auth.hasAdminScope()){ axios(configApiCall(api_path_get_admin_devices, 'GET', userData, null)).then((response)=>{ - var resultSet = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); - setDevices(resultSet) - } - ).catch((error) =>{ + var resultSet = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); + setDevices(resultSet) + }).catch((error) =>{ console.log(error); }); } else{ axios(configApiCall(api_path_get_auth_devices, 'GET', null, null)).then((response)=>{ - setDevices(response.data) - } - ).catch((error) =>{ + var resultSet = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); + setDevices(resultSet) + }).catch((error) =>{ console.log(error); }); } diff --git a/jams-react-client/src/components/IdentityManagement/IdentityManagement.js b/jams-react-client/src/components/IdentityManagement/IdentityManagement.js index 61da28f4..68ec43e0 100644 --- a/jams-react-client/src/components/IdentityManagement/IdentityManagement.js +++ b/jams-react-client/src/components/IdentityManagement/IdentityManagement.js @@ -203,7 +203,6 @@ export default function IdentityManagement(props) { } axios(configApiCall(api_path_post_install_auth,'POST',data,null)).then((response)=>{ - console.log(response) callbackIdentityManagement() }).catch(()=>{ props.setErrorMessage('The information provided appears to be incorrect, the connection to the directory has failed. Please check the information and credentials provided and try again.') diff --git a/jams-react-client/src/layouts/Admin.js b/jams-react-client/src/layouts/Admin.js index bc63075e..f1f29631 100644 --- a/jams-react-client/src/layouts/Admin.js +++ b/jams-react-client/src/layouts/Admin.js @@ -128,14 +128,14 @@ export default function Admin({ ...rest }) { <div className={classes.map}>{switchRoutes}</div> )} {getRoute() ? <Footer /> : null} - <FixedPlugin + {/* <FixedPlugin handleImageClick={handleImageClick} handleColorClick={handleColorClick} bgColor={color} bgImage={image} handleFixedClick={handleFixedClick} fixedClasses={fixedClasses} - /> + /> */} </div> </div> ); diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js index 3f2d2bcf..b162854f 100644 --- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js +++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js @@ -12,6 +12,14 @@ import CardBody from "components/Card/CardBody.js"; import CardFooter from "components/Card/CardFooter.js"; import noProfilePicture from "assets/img/faces/no-profile-picture.png"; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import IconButton from '@material-ui/core/IconButton'; +import EditIcon from '@material-ui/icons/Edit'; +import DeleteIcon from '@material-ui/icons/Delete'; import Grid from '@material-ui/core/Grid'; import BusinessCenterOutlinedIcon from '@material-ui/icons/BusinessCenterOutlined'; @@ -21,13 +29,22 @@ import SmartphoneOutlinedIcon from '@material-ui/icons/SmartphoneOutlined'; import LocalPrintshopOutlinedIcon from '@material-ui/icons/LocalPrintshopOutlined'; import PhoneForwardedOutlinedIcon from '@material-ui/icons/PhoneForwardedOutlined'; import PersonOutlinedIcon from '@material-ui/icons/PersonOutlined'; -import AccountBoxOutlinedIcon from '@material-ui/icons/AccountBoxOutlined'; -import AccountCircleIcon from '@material-ui/icons/AccountCircle'; +import PermIdentityOutlinedIcon from '@material-ui/icons/PermIdentityOutlined'; +import Avatar from '@material-ui/core/Avatar'; +import Chip from '@material-ui/core/Chip'; +import DoneIcon from '@material-ui/icons/Done'; + +import List from '@material-ui/core/List'; +import ListItem from '@material-ui/core/ListItem'; +import ListItemAvatar from '@material-ui/core/ListItemAvatar'; +import ListItemIcon from '@material-ui/core/ListItemIcon'; +import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'; +import ListItemText from '@material-ui/core/ListItemText'; import auth from "auth.js" import configApiCall from "api.js"; -import { api_path_get_admin_user, api_path_get_user, api_path_get_user_directory_search} from "globalUrls"; +import { api_path_get_admin_user, api_path_get_auth_user, api_path_get_user_directory_search, api_path_delete_admin_user_revoke} from "globalUrls"; import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; @@ -92,7 +109,8 @@ const styles = (theme)=> ( { maxHeight: '60vh', minWidth: '80vh', maxWidth: '80vh' - }, + } + // controls: { // padding: 16, // display: 'flex', @@ -130,7 +148,10 @@ export default function DisplayUserProfile(props) { const classes = useStyles(); const [users, setUsers] = React.useState([]) - const [userStatus, setUserStatus] = React.useState('') + const [userStatus, setUserStatus] = React.useState(false) + const [open, setOpen] = React.useState(false); + const [revokedUser, setRevokedUser] = React.useState(""); + const searchData = { "queryString":props.username }; @@ -138,20 +159,22 @@ export default function DisplayUserProfile(props) { const userData = { "username":props.username }; - const [value, setValue] = React.useState(0); + useEffect(() => { auth.checkDirectoryType(() => { if(auth.hasAdminScope()){ axios(configApiCall(api_path_get_admin_user, 'GET', userData, null)).then((response)=>{ - setStatus(response.data) + const result = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); + setUserStatus(result.revoked) } ).catch((error) =>{ console.log(error); }); } else{ - axios(configApiCall(api_path_get_user, 'GET', null, null)).then((response)=>{ - setStatus(response.data) + axios(configApiCall(api_path_get_auth_user, 'GET', null, null)).then((response)=>{ + const result = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); + setUserStatus(result.revoked) } ).catch((error) =>{ console.log(error); @@ -166,92 +189,179 @@ export default function DisplayUserProfile(props) { }); }, []); - function setStatus(data) { - if (data.revoked) - setUserStatus("Revoked"); - else - setUserStatus("Active"); + const getUserStatus = (user) => { + if(userStatus == false) { + return <Chip label="Active" avatar={<Avatar alt={user.username} src={user.profilePicture ? ('data:image/png;base64, ' + user.profilePicture) : noProfilePicture} />} color="primary" deleteIcon={<DoneIcon />}/> + }else{ + return <Chip label="Revoked" avatar={<Avatar alt={user.username} src={user.profilePicture ? ('data:image/png;base64, ' + user.profilePicture) : noProfilePicture} />} deleteIcon={<DoneIcon />}/> + } + } + + function revokeUser(){ + + const data = { + 'username': revokedUser + } + + axios(configApiCall(api_path_delete_admin_user_revoke, 'DELETE', data, null)).then(()=>{ + setUserStatus(true) + }).catch((error)=> { + console.log("Error revoking user: " + revokedUser + ' with error: ' + error); + }); + setOpen(false); } - console.log(userStatus) + + const handleClickOpen = (username) => { + setRevokedUser(username) + setOpen(true); + }; + + const handleClose = () => { + setOpen(false); + }; + return ( <div> + <Dialog + open={open} + onClose={handleClose} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="alert-dialog-title">{"Revoke user account"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description"> + Are you sure you want to revoke <strong>{revokedUser}</strong> ? + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={handleClose} color="primary"> + Cancel + </Button> + <Button onClick={revokeUser} color="danger" autoFocus> + Revoke + </Button> + </DialogActions> + </Dialog> {users.map(user => <GridContainer> <Grid item xs={12} sm={12} md={8}> <Card profile> <CardHeader color="info" stats icon> <CardIcon color="info"> - <AccountBoxOutlinedIcon /> + <PermIdentityOutlinedIcon /> </CardIcon> <p className={classes.cardCategory}>{"Profile information"}</p> <h3 className={classes.cardTitle}>{user.username}</h3> + {getUserStatus(user)} </CardHeader> <CardBody profile> <div className={classes.root}> - <GridContainer spacing={5}> - <Grid item xs={12} sm={12} md={6}> - <Grid container spacing={5} > - <Grid item align="center" xs={12} sm={12} md={12} > - <img src={user.profilePicture ? ('data:image/png;base64, ' + user.profilePicture) : noProfilePicture} className={classes.editProfilePicture} alt="..." /> - </Grid> - </Grid> + <Grid container spacing={2}> + <Grid item align="center" xs={12} sm={12} md={12}> + <img src={user.profilePicture ? ('data:image/png;base64, ' + user.profilePicture) : noProfilePicture} className={classes.editProfilePicture} alt="..." /> + <h4>{user.username ? user.username : 'no username'}</h4> </Grid> - <GridItem xs={12} sm={12} md={6}> - <Grid container spacing={5}> - <Grid item xs={12} sm={12} md={12}> - <FormControl className={classes.margin} fullWidth> - <span><AccountCircleIcon /> {user.username ? user.username : 'no username'}, {userStatus ? userStatus: "no user status"}</span> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={12}> - <FormControl className={classes.margin} fullWidth> - <span> <PersonIcon /> {user.firstName ? user.firstName : 'no firstname'}</span> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={12}> - <FormControl className={classes.margin} fullWidth> - <span><PersonOutlinedIcon/> {user.lastName ? user.lastName : 'no lastname'}</span> - </FormControl> - </Grid> - </Grid> - </GridItem> - <Grid item xs={12} sm={12} md={6}> - <FormControl className={classes.margin} fullWidth> - <span> <AlternateEmailOutlinedIcon/> {user.email ? user.email : 'no email'} </span> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={6}> - <FormControl className={classes.margin} fullWidth> - <span> <BusinessCenterOutlinedIcon/> {user.organization ? user.organization : 'no organization'} </span> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={6}> - <FormControl className={classes.margin} fullWidth> - <span> <PhoneInTalkOutlinedIcon/> {user.phoneNumber ? user.phoneNumber : 'no phone number'} </span> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={6}> - <FormControl className={classes.margin} fullWidth> - <span> <PhoneForwardedOutlinedIcon/> {user.phoneNumberExtension ? user.phoneNumberExtension : 'no Extension'} </span> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={6}> - <FormControl className={classes.margin} fullWidth> - <span> <SmartphoneOutlinedIcon/> {user.mobileNumber ? user.mobileNumber : 'no mobile number'} </span> - </FormControl> + <List dense={false}> + <ListItem> + <ListItemAvatar> + <Avatar> + <PersonIcon /> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.firstName ? user.firstName : 'no firstname'} + /> + </ListItem> + <ListItem> + <ListItemAvatar> + <Avatar> + <PersonOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.lastName ? user.lastName : 'no lastname'} + /> + </ListItem> + <ListItem> + <ListItemAvatar> + <Avatar> + <AlternateEmailOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.email ? user.email : 'no email'} + /> + </ListItem> + <ListItem> + <ListItemAvatar> + <Avatar> + <BusinessCenterOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.organization ? user.organization : 'no organization'} + /> + </ListItem> + </List> </Grid> + <Grid item xs={12} sm={12} md={6}> - <FormControl className={classes.margin} fullWidth> - <span> <LocalPrintshopOutlinedIcon/> {user.faxNumber ? user.faxNumber : 'no fax number'} </span> - </FormControl> + <List dense={false}> + <ListItem> + <ListItemAvatar> + <Avatar> + <PhoneInTalkOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.phoneNumber ? user.phoneNumber : 'no phone number'} + /> + </ListItem> + <ListItem> + <ListItemAvatar> + <Avatar> + <PhoneForwardedOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.phoneNumberExtension ? user.phoneNumberExtension : 'no Extension'} + /> + </ListItem> + <ListItem> + <ListItemAvatar> + <Avatar> + <SmartphoneOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.mobileNumber ? user.mobileNumber : 'no mobile number'} + /> + </ListItem> + <ListItem> + <ListItemAvatar> + <Avatar> + <LocalPrintshopOutlinedIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + primary={user.faxNumber ? user.faxNumber : 'no fax number'} + /> + </ListItem> + </List> </Grid> - </GridContainer> + </Grid> </div> </CardBody> <CardFooter> - <Button color="info" onClick={() => props.setDisplayUser(false)}>Edit Profile</Button> - {auth.hasAdminScope() && (userStatus == "Active" || userStatus == '') && <Button color="danger">Revoke Profile</Button>} + {auth.isLocalDirectory() && <Button color="info" onClick={() => props.setDisplayUser(false)}> + <EditIcon /> Edit Profile + </Button>} + {auth.hasAdminScope() && (userStatus == "Active" || userStatus == '') && + <Button color="danger" onClick={() => handleClickOpen(user.username)} > + <DeleteIcon fontSize="small"/> Revoke user + </Button>} </CardFooter> </Card> </Grid> diff --git a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js index 505fb948..af62ea4c 100644 --- a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js +++ b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js @@ -56,7 +56,8 @@ import { api_path_put_update_user_profile, api_path_post_create_user, api_path_post_create_user_profile, - api_path_get_user_directory_search + api_path_get_user_directory_search, + api_path_get_exists_user } from "../../globalUrls" import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; @@ -157,6 +158,7 @@ export default function EditCreateUserProfile(props) { const classes = useStyles(); const history = useHistory(); const [createUser, setCreateUser] = React.useState(props.createUser); + const [userExists, setUserExists] = React.useState(false) const [firstName, setFirstName] = React.useState("") const [lastName, setLastName] = React.useState("") @@ -170,6 +172,8 @@ export default function EditCreateUserProfile(props) { const [phoneNumber, setPhoneNumber] = React.useState("") const [extension, setExtension] = React.useState("") const [mobileNumber, setMobileNumber] = React.useState("") + const [openPassword, setOpenPassword] = React.useState(false) + const [temporaryPassword, setTemporaryPassword] = React.useState("") const [open, setOpen] = React.useState(false); const [crop, setCrop] = React.useState({ x: 0, y: 0 }) @@ -219,13 +223,10 @@ export default function EditCreateUserProfile(props) { } const handleUserProfileCreation = (data) => { - axios(configApiCall(api_path_post_create_user + "?username="+ data.username, 'POST', null, null)).then((response) => { - console.log("New user successfully created. Here is the one time password: " + response.data["password"]) - history.push('/admin'); + axios(configApiCall(api_path_post_create_user_profile, 'POST', data, null)).then(() => { + setOpenPassword(true) }).catch((error) => { - console.log("Failed to create new user. This is either because the username is already in use" + - " on the public nameserver, or another unknown error has occurred. " + - "Please choose another one."); + console.log("Error creating user profile: " + error) }) } @@ -243,14 +244,29 @@ export default function EditCreateUserProfile(props) { 'mobileNumber': mobileNumber } - axios(configApiCall(api_path_post_create_user_profile, 'POST', data, null)).then(() => { + axios(configApiCall(api_path_post_create_user + "?username="+ data.username, 'POST', null, null)).then((response) => { + setTemporaryPassword(response.data["password"]) handleUserProfileCreation(data) - }).catch((error) => { - console.log("Error updating user: " + error) + console.log("Failed to create new user. This is either because the username is already in use" + + " on the public nameserver, or another unknown error has occurred. " + + "Please choose another one."); }) } + const handleUserExists = (input) => { + const data = { + "username": input + } + axios(configApiCall(api_path_get_exists_user, 'GET', data, null)).then((response) =>{ + if(response.data == "[]"){ + setUserExists(true) + } + }).catch((error) => { + console.log("Error checking for existing users: " + error) + }) + } + const handleUserUpdate = () => { props.setDisplayUser(true) } @@ -287,7 +303,6 @@ export default function EditCreateUserProfile(props) { rotation ).then((value) => { setProfilePicturePreview(value) - console.log(value) }) } @@ -304,10 +319,31 @@ export default function EditCreateUserProfile(props) { setProfilePicture(profilePicturePreview.replace("data:image/jpeg;base64,", "")) setOpen(false) } - + + const handleClosePassword = () => { + setOpenPassword(false); + history.push('/admin'); + } return( <div> + <Dialog + open={openPassword} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="alert-dialog-title">{"Temporary password"}</DialogTitle> + <DialogContent> + <DialogContentText id="alert-dialog-description"> + New user successfully created. Here is the one time password: <strong>{temporaryPassword}</strong> ? + </DialogContentText> + </DialogContent> + <DialogActions> + <Button onClick={handleClosePassword} color="primary"> + Ok + </Button> + </DialogActions> + </Dialog> <Dialog open={open} onClose={handleClose} @@ -385,7 +421,7 @@ export default function EditCreateUserProfile(props) { <CardBody profile> <div className={classes.root}> <Grid container spacing={5}> - <Grid item xs={12} sm={12} md={6}> + <Grid item xs={12} sm={12} md={12}> <Grid container spacing={5} > <Grid item align="center" xs={12} sm={12} md={12} > <img src={profilePicturePreview} alt="..." className={classes.editProfilePicture} /> @@ -398,14 +434,10 @@ export default function EditCreateUserProfile(props) { </IconButton> Update profile image </label> </Grid> - </Grid> - </Grid> - <Grid item xs={12} sm={12} md={6}> - <Grid container spacing={5}> - <Grid item xs={12} sm={12} md={12}> {createUser && - <FormControl className={classes.margin} fullWidth> - <InputLabel htmlFor="username">Username</InputLabel> + <Grid item align="center" xs={12} sm={12} md={12}> + <FormControl className={classes.margin} size="medium" error={userExists}> + <InputLabel htmlFor="username">{userExists ? "Error username exists" : "Username"}</InputLabel> <Input id="username" placeholder={userName} @@ -414,27 +446,32 @@ export default function EditCreateUserProfile(props) { <AccountCircleIcon /> </InputAdornment> } - onChange={e => setUserName(e.target.value)} + onChange={e => { + setUserName(e.target.value); + handleUserExists(e.target.value) + }} /> </FormControl> - } </Grid> - <Grid item xs={12} sm={12} md={12}> - <FormControl className={classes.margin} fullWidth> - <InputLabel htmlFor="firstname">First Name</InputLabel> - <Input - id="firstname" - placeholder={firstName} - startAdornment={ - <InputAdornment position="start"> - <PersonIcon /> - </InputAdornment> - } - onChange={e => setFirstName(e.target.value)} - /> - </FormControl> - </Grid> - <Grid item xs={12} sm={12} md={12}> + } + </Grid> + </Grid> + <Grid item xs={12} sm={12} md={6}> + <FormControl className={classes.margin} fullWidth> + <InputLabel htmlFor="firstname">First Name</InputLabel> + <Input + id="firstname" + placeholder={firstName} + startAdornment={ + <InputAdornment position="start"> + <PersonIcon /> + </InputAdornment> + } + onChange={e => setFirstName(e.target.value)} + /> + </FormControl> + </Grid> + <Grid item xs={12} sm={12} md={6}> <FormControl className={classes.margin} fullWidth> <InputLabel htmlFor="lastname">Last Name</InputLabel> <Input @@ -448,10 +485,7 @@ export default function EditCreateUserProfile(props) { onChange={e => setLastName(e.target.value)} /> </FormControl> - </Grid> - </Grid> </Grid> - <Grid item xs={12} sm={12} md={6}> <FormControl className={classes.margin} fullWidth> <InputLabel htmlFor="email">Email</InputLabel> diff --git a/jams-react-client/src/views/Users/Users.js b/jams-react-client/src/views/Users/Users.js index 910798c3..d5d34538 100644 --- a/jams-react-client/src/views/Users/Users.js +++ b/jams-react-client/src/views/Users/Users.js @@ -1,4 +1,5 @@ import React, { useState, useEffect } from "react"; +import { useHistory } from 'react-router-dom'; import { Switch, Route, Redirect } from "react-router-dom"; // @material-ui/core components import { makeStyles } from "@material-ui/core/styles"; @@ -15,28 +16,18 @@ import CardBody from "components/Card/CardBody.js"; import CardFooter from "components/Card/CardFooter.js"; import UserProfile from "views/UserProfile/UserProfile.js" import Divider from '@material-ui/core/Divider'; -import Dialog from '@material-ui/core/Dialog'; -import DialogActions from '@material-ui/core/DialogActions'; -import DialogContent from '@material-ui/core/DialogContent'; -import DialogContentText from '@material-ui/core/DialogContentText'; -import DialogTitle from '@material-ui/core/DialogTitle'; import PersonIcon from '@material-ui/icons/Person'; import PermIdentityIcon from '@material-ui/icons/PermIdentity'; import PhoneOutlinedIcon from '@material-ui/icons/PhoneOutlined'; import BusinessOutlinedIcon from '@material-ui/icons/BusinessOutlined'; -import DeleteIcon from '@material-ui/icons/Delete'; import Search from "@material-ui/icons/Search"; -import IconButton from '@material-ui/core/IconButton'; import MailOutlineIcon from '@material-ui/icons/MailOutline'; import axios from "axios"; import configApiCall from "api.js"; import auth from 'auth.js' -import { - api_path_get_user_directory_search, - api_path_delete_admin_user_revoke - } from "globalUrls"; +import { api_path_get_user_directory_search } from "globalUrls"; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import KeyboardReturnIcon from '@material-ui/icons/KeyboardReturn'; @@ -45,6 +36,7 @@ import jami from "assets/img/faces/jami.png"; import noProfilePicture from "assets/img/faces/no-profile-picture.png"; import EditCreateUserProfile from "views/UserProfile/EditCreateUserProfile"; +import LinearProgress from '@material-ui/core/LinearProgress'; import headerLinksStyle from "assets/jss/material-dashboard-react/components/headerLinksStyle.js"; @@ -68,6 +60,12 @@ const styles = { }, deleteIcon : { float: "right" + }, + search : { + width: "90%" + }, + loading: { + width: '100%', } }; @@ -75,20 +73,42 @@ const useStyles = makeStyles(styles); export default function Users() { const classes = useStyles(); + const history = useHistory(); const [users, setUsers] = React.useState([]) - const [open, setOpen] = React.useState(false); - const [revokedUser, setRevokedUser] = React.useState(""); const [selectedUsername, setSelectedUsername] = React.useState("") const [createUser, setCreateUser] = React.useState(false) + const [searchValue, setSearchValue] = React.useState(null) + const [loading, setLoading] = React.useState(false) + const [progress, setProgress] = React.useState(0); useEffect(() => { - setRevokedUser('') + setLoading(true) + const timer = setInterval(() => { + setProgress((oldProgress) => { + if (oldProgress === 100) { + return 0; + } + const diff = Math.random() * 10; + return Math.min(oldProgress + diff, 100); + }); + }, 500); axios(configApiCall(api_path_get_user_directory_search, 'GET', {"queryString":"*"}, null)).then((response)=>{ - setUsers(response.data) - } - ).catch((error) =>{ + let orginalUsers = response.data + orginalUsers.map((user) => { + user.display = "" + }) + setUsers(orginalUsers) + setLoading(false) + }).catch((error) =>{ console.log(error); + if(error.response.status == 401){ + auth.authenticated = false + history.push('/') + } }); + return () => { + clearInterval(timer); + }; }, []); const [selectedProfile, setSelectedProfile] = useState(false); @@ -101,32 +121,10 @@ export default function Users() { function redirectToUsers(e) { e.preventDefault() setSelectedProfile(false); + history.push('/admin') } - function revokeUser(){ - - const data = { - 'username': revokedUser - } - - axios(configApiCall(api_path_delete_admin_user_revoke, 'DELETE', data, null)).then((result)=>{ - console.log("Revoked user: " + revokedUser); - }).catch((error)=> { - console.log("Error revoking user: " + revokedUser + ' with error: ' + error); - }); - setOpen(false); - } - - const handleClickOpen = (username) => { - setRevokedUser(username) - setOpen(true); - }; - - const handleClose = () => { - setOpen(false); - }; - - if (selectedProfile && !auth.hasAdminScope()) { + if (!auth.hasAdminScope()) { return ( <div> <UserProfile username={auth.getUsername()}/> @@ -149,27 +147,6 @@ export default function Users() { else { return ( <div> - <Dialog - open={open} - onClose={handleClose} - aria-labelledby="alert-dialog-title" - aria-describedby="alert-dialog-description" - > - <DialogTitle id="alert-dialog-title">{"Revoke user account"}</DialogTitle> - <DialogContent> - <DialogContentText id="alert-dialog-description"> - Are you sure you want to revoke <strong>{revokedUser}</strong> ? - </DialogContentText> - </DialogContent> - <DialogActions> - <Button onClick={handleClose} color="primary"> - Cancel - </Button> - <Button onClick={revokeUser} color="danger" autoFocus> - Revoke - </Button> - </DialogActions> - </Dialog> <GridContainer> <GridItem xs={12} sm={12} md={12}> {auth.isLocalDirectory() && @@ -178,23 +155,42 @@ export default function Users() { </Button> } <div className={classes.searchWrapper}> - <CustomInput + <CustomInput formControlProps={{ className: classes.margin + " " + classes.search }} inputProps={{ - placeholder: "Search users", + placeholder: "Search users using (username, name, phone, email, ...)", inputProps: { "aria-label": "Search users" - } + }, + onKeyUp: (e) => setSearchValue(e.target.value), }} /> <Search /> + <div className={classes.loading}> + {loading && <LinearProgress variant="determinate" value={progress} />} + </div> </div> </GridItem> { - users.map(user => - <GridItem xs={12} sm={12} md={2} key={user.username}> + users.filter((data)=>{ + if(searchValue == null) + return data + else if((data.username != null ) && data.username.toLowerCase().includes(searchValue.toLowerCase()) || + (data.firstName != null ) && data.firstName.toLowerCase().includes(searchValue.toLowerCase()) || + (data.lastName != null ) && data.lastName.toLowerCase().includes(searchValue.toLowerCase()) || + (data.organization != null ) && data.organization.toLowerCase().includes(searchValue.toLowerCase()) || + (data.email != null ) && data.email.toLowerCase().includes(searchValue.toLowerCase()) || + (data.phoneNumber != null ) && data.phoneNumber.toLowerCase().includes(searchValue.toLowerCase()) || + (data.phoneNumberExtension != null ) && data.phoneNumberExtension.toLowerCase().includes(searchValue.toLowerCase()) || + (data.mobileNumber != null ) && data.mobileNumber.toLowerCase().includes(searchValue.toLowerCase()) || + (data.faxNumber != null ) && data.faxNumber.toLowerCase().includes(searchValue.toLowerCase()) + ){ + return data + } + }).map(user => + <GridItem xs={12} sm={12} md={2} key={user.username} style={{display: user.display}}> <Card profile> <CardBody profile> @@ -205,14 +201,10 @@ export default function Users() { <h4 className={classes.cardTitle}>{user.firstName} {user.lastName}</h4> <ul> - <li><img src={jami} width="20" alt="Jami" /> {user.username}</li> - <li><BusinessOutlinedIcon fontSize='small' /> {user.organization ? user.organization : 'No organization'}</li> + <li><img src={jami} width="20" alt="Jami" style={{marginRight: "10px"}} /> {user.username}</li> + <li><BusinessOutlinedIcon fontSize='small' style={{marginRight: "10px"}} /> {user.organization ? user.organization : 'No organization'}</li> </ul> </a> - <Divider variant="middle" /> - <IconButton aria-label="delete" className={classes.deleteIcon} onClick={() => handleClickOpen(user.username)}> - <DeleteIcon fontSize="small" color="error" /> - </IconButton> </CardBody> </Card> -- GitLab