From 68491d6f34fef6cd0eeae38e2d7c18d07b8455c3 Mon Sep 17 00:00:00 2001 From: Larbi Gharib <larbi.gharib@savoirfairelinux.com> Date: Thu, 27 Aug 2020 17:25:25 -0400 Subject: [PATCH] Password change Change-Id: I4269f85309799454df2c319a9db2b35f5db08982 --- jams-react-client/src/auth.js | 78 ++++++---- .../src/components/FormikField/FormikField.js | 3 + .../src/components/Navbars/Navbar.js | 2 +- .../src/components/Snackbar/Snackbar.js | 2 +- .../components/Snackbar/SnackbarContent.js | 2 +- jams-react-client/src/globalUrls.js | 2 + jams-react-client/src/layouts/SignIn.js | 2 +- jams-react-client/src/layouts/SignUp.js | 2 +- .../src/views/Blueprints/Blueprints.js | 4 +- .../src/views/Contacts/Contacts.js | 2 +- .../src/views/Dashboard/Dashboard.js | 6 +- jams-react-client/src/views/Groups/Groups.js | 13 +- .../src/views/Notifications/Notifications.js | 4 +- .../src/views/RTLPage/RTLPage.js | 6 +- .../src/views/UpgradeToPro/UpgradeToPro.js | 2 +- .../views/UserProfile/DisplayUserProfile.js | 147 +++++++++++++++++- .../UserProfile/EditCreateUserProfile.js | 9 +- 17 files changed, 223 insertions(+), 63 deletions(-) diff --git a/jams-react-client/src/auth.js b/jams-react-client/src/auth.js index 72b476a2..ffaf9639 100644 --- a/jams-react-client/src/auth.js +++ b/jams-react-client/src/auth.js @@ -11,12 +11,11 @@ import { class Auth { constructor() { - this.authenticated = false - this.admin = false - this.installed = false - this.uri = '' - this.adminScope = true - this.username = '' + this.authenticated = false; + this.admin = false; + this.installed = false; + this.uri = ''; + this.username = ''; } setJWT(access_token) { @@ -24,19 +23,37 @@ class Auth { window.localStorage.setItem('access_token', access_token); } + setScope(scope) { + window.localStorage.removeItem('scope'); + window.localStorage.setItem('scope', scope); + } + + setLocalDirectory(localDirectory) { + window.localStorage.removeItem('localDirectory'); + window.localStorage.setItem('localDirectory', localDirectory); + } + deleteJWT(){ window.localStorage.removeItem('access_token'); } + setUsername(username) { + window.localStorage.removeItem('username'); + window.localStorage.setItem('username', username); + } + + deleteUserhame() { + window.localStorage.removeItem('username'); + } + login(jsonData, cb) { this.deleteJWT() axios(configApiCall(api_path_post_auth_login, "POST", jsonData, null)).then((response) => { if(response.status == 200){ - this.setJWT(response.data['access_token']) - this.adminScope = JSON.parse(atob(response.data['access_token'].split('.')[1])).scope == "ADMIN" ? true : false; - if(!this.adminScope) - this.username= jsonData.username - this.authenticated = true + this.setJWT(response.data['access_token']); + this.setScope(response.data["scope"] == "ADMIN" ? true : false); + this.setUsername(jsonData.username); + this.authenticated = true; } cb() }).catch((error) => { @@ -47,8 +64,7 @@ class Auth { logout(cb) { this.deleteJWT() this.authenticated = false - this.username='' - this.adminScope = true + this.deleteUserhame() cb() } @@ -57,22 +73,22 @@ class Auth { } isLocalDirectory() { - return this.localDirectory + return window.localStorage.getItem('localDirectory') === 'true' ? true : false; } checkDirectoryType(cb){ axios(configApiCall(api_path_get_directories, "GET", null, null)).then((response) => { if (response.data.length == 1) { - this.localDirectory = true + this.setLocalDirectory(true); } else if (response.data.length == 2){ - this.localDirectory = false + this.setLocalDirectory(false); }else{ - console.log("Error getting on checkDirectoryType: Size of directory types is " + response.data.length) + console.log("Error getting on checkDirectoryType: Size of directory types is " + response.data.length); } cb() }).catch((error) => { - console.log("Error getting on checkDirectoryType: " + error) + console.log("Error getting on checkDirectoryType: " + error); cb() }); } @@ -80,15 +96,15 @@ class Auth { checkAdminAccountStatus(cb) { axios(configApiCall(api_path_post_install_admin, "GET", null, null)).then((response) => { if (response['headers']['showlogin'] == "true") { - this.admin = true + this.admin = true; } cb() }).catch((error) => { if(error.response.status == 404){ - this.admin = true + this.admin = true; } else{ - console.log("Error during API request on checkAdminAccountStatus: " + error) + console.log("Error during API request on checkAdminAccountStatus: " + error); } cb() }); @@ -97,11 +113,11 @@ class Auth { isServerInstalled(cb) { axios(configApiCall(api_path_get_server_status, "GET", null, null)).then((response) => { if (response.data['installed'] == 'true') { - this.installed = true - console.log("Server is insalled") + this.installed = true; + console.log("Server is insalled"); } else { - this.installed = false - console.log("Server is not insalled") + this.installed = false; + console.log("Server is not insalled"); } cb() }).catch((error) => { @@ -116,8 +132,8 @@ class Auth { cb() }else{ axios(configApiCall(api_path_get_install_lastKnownStep, 'GET', null, null)).then((response) => { - this.uri = response.data['uri'] - this.authenticated = true + this.uri = response.data['uri']; + this.authenticated = true; cb() }).catch((error) => { console.log("Error during API request on checkLastKnowStep: " + error); @@ -127,19 +143,19 @@ class Auth { } hasAdmin() { - return this.admin + return this.admin; } hasAdminScope(){ - return this.adminScope + return window.localStorage.getItem('scope') === 'true' ? true : false; } getUsername(){ - return this.username + return window.localStorage.getItem('username'); } isInstalled() { - return this.installed + return this.installed; } } diff --git a/jams-react-client/src/components/FormikField/FormikField.js b/jams-react-client/src/components/FormikField/FormikField.js index a1762a67..3dc5394c 100644 --- a/jams-react-client/src/components/FormikField/FormikField.js +++ b/jams-react-client/src/components/FormikField/FormikField.js @@ -18,6 +18,7 @@ class FormikField extends React.Component { name={this.props.name} as={Input} startAdornment={this.props.startAdornment} + endAdornment={this.props.endAdornment} label={this.props.label} fullWidth type={this.props.type} @@ -29,6 +30,8 @@ class FormikField extends React.Component { } FormikField.propTypes = { + startAdornment: false, + endAdornment: false, placeholder: PropTypes.string, required: false, name: PropTypes.string.isRequired, diff --git a/jams-react-client/src/components/Navbars/Navbar.js b/jams-react-client/src/components/Navbars/Navbar.js index 5636b793..2b6a4356 100755 --- a/jams-react-client/src/components/Navbars/Navbar.js +++ b/jams-react-client/src/components/Navbars/Navbar.js @@ -48,7 +48,7 @@ export default function Header(props) { </Hidden> */} <Hidden mdUp implementation="css"> <IconButton - color="inherit" + color="info" aria-label="open drawer" onClick={props.handleDrawerToggle} > diff --git a/jams-react-client/src/components/Snackbar/Snackbar.js b/jams-react-client/src/components/Snackbar/Snackbar.js index 14527a79..4b4e6ebf 100644 --- a/jams-react-client/src/components/Snackbar/Snackbar.js +++ b/jams-react-client/src/components/Snackbar/Snackbar.js @@ -25,7 +25,7 @@ export default function Snackbar(props) { className={classes.iconButton} key="close" aria-label="Close" - color="inherit" + color="info" onClick={() => props.closeNotification()} > <Close className={classes.close} /> diff --git a/jams-react-client/src/components/Snackbar/SnackbarContent.js b/jams-react-client/src/components/Snackbar/SnackbarContent.js index 120178b6..f0002739 100644 --- a/jams-react-client/src/components/Snackbar/SnackbarContent.js +++ b/jams-react-client/src/components/Snackbar/SnackbarContent.js @@ -25,7 +25,7 @@ export default function SnackbarContent(props) { className={classes.iconButton} key="close" aria-label="Close" - color="inherit" + color="info" > <Close className={classes.close} /> </IconButton> diff --git a/jams-react-client/src/globalUrls.js b/jams-react-client/src/globalUrls.js index e3caeb49..ec31b557 100644 --- a/jams-react-client/src/globalUrls.js +++ b/jams-react-client/src/globalUrls.js @@ -31,6 +31,7 @@ const api_path_get_needs_update = '/api/admin/update'; const api_path_get_start_update = '/api/admin/update'; const api_path_post_create_group = '/api/admin/group'; const api_path_post_create_user = '/api/admin/user'; +const api_path_put_update_user = '/api/admin/user'; const api_path_get_auth_user = '/api/auth/user'; const api_path_get_admin_user = '/api/admin/user'; const api_path_post_update_user = '/api/auth/user'; @@ -75,6 +76,7 @@ module.exports = { api_path_get_needs_update, api_path_get_start_update, api_path_post_create_user, + api_path_put_update_user, api_path_get_auth_user, api_path_get_admin_user, api_path_post_update_user, diff --git a/jams-react-client/src/layouts/SignIn.js b/jams-react-client/src/layouts/SignIn.js index d2b00a44..d9d9447a 100644 --- a/jams-react-client/src/layouts/SignIn.js +++ b/jams-react-client/src/layouts/SignIn.js @@ -25,7 +25,7 @@ function Copyright() { return ( <Typography variant="body2" color="textSecondary" align="center"> {'Copyright © '} - <Link color="inherit" href="https://material-ui.com/"> + <Link color="info" href="https://material-ui.com/"> Your Website </Link>{' '} {new Date().getFullYear()} diff --git a/jams-react-client/src/layouts/SignUp.js b/jams-react-client/src/layouts/SignUp.js index fae10c16..c311d4bd 100644 --- a/jams-react-client/src/layouts/SignUp.js +++ b/jams-react-client/src/layouts/SignUp.js @@ -25,7 +25,7 @@ function Copyright() { return ( <Typography variant="body2" color="textSecondary" align="center"> {'Copyright © '} - <Link color="inherit" href="https://savoirfairelinux.com/"> + <Link color="info" href="https://savoirfairelinux.com/"> JAMS by Savoir-faire Linux </Link>{' '} {new Date().getFullYear()} diff --git a/jams-react-client/src/views/Blueprints/Blueprints.js b/jams-react-client/src/views/Blueprints/Blueprints.js index b963eaca..13e72936 100644 --- a/jams-react-client/src/views/Blueprints/Blueprints.js +++ b/jams-react-client/src/views/Blueprints/Blueprints.js @@ -220,7 +220,7 @@ export default function Blueprints() { </DialogContentText> </DialogContent> <DialogActions> - <Button onClick={handleClose} color="danger"> + <Button onClick={handleClose} color="info"> Cancel </Button> <Button onClick={handleCreateBlueprint} color="primary" autoFocus> @@ -244,7 +244,7 @@ export default function Blueprints() { <Button onClick={() => setOpenRemoveDialog(false)} color="primary"> Cancel </Button> - <Button onClick={removeBlueprint} color="danger" autoFocus> + <Button onClick={removeBlueprint} color="info" autoFocus> Remove </Button> </DialogActions> diff --git a/jams-react-client/src/views/Contacts/Contacts.js b/jams-react-client/src/views/Contacts/Contacts.js index 80075e5f..500e1b50 100644 --- a/jams-react-client/src/views/Contacts/Contacts.js +++ b/jams-react-client/src/views/Contacts/Contacts.js @@ -182,7 +182,7 @@ export default function Users(props) { <Button onClick={() => setOpen(false)} color="primary"> Cancel </Button> - <Button onClick={removeContact} color="danger" autoFocus> + <Button onClick={removeContact} color="info" autoFocus> Remove </Button> </DialogActions> diff --git a/jams-react-client/src/views/Dashboard/Dashboard.js b/jams-react-client/src/views/Dashboard/Dashboard.js index a737dfe7..0678d244 100755 --- a/jams-react-client/src/views/Dashboard/Dashboard.js +++ b/jams-react-client/src/views/Dashboard/Dashboard.js @@ -88,8 +88,8 @@ export default function Dashboard() { </GridItem> <GridItem xs={12} sm={6} md={3}> <Card> - <CardHeader color="danger" stats icon> - <CardIcon color="danger"> + <CardHeader color="info" stats icon> + <CardIcon color="info"> <Icon>info_outline</Icon> </CardIcon> <p className={classes.cardCategory}>Fixed Issues</p> @@ -174,7 +174,7 @@ export default function Dashboard() { </GridItem> <GridItem xs={12} sm={12} md={4}> <Card chart> - <CardHeader color="danger"> + <CardHeader color="info"> <ChartistGraph className="ct-chart" data={completedTasksChart.data} diff --git a/jams-react-client/src/views/Groups/Groups.js b/jams-react-client/src/views/Groups/Groups.js index 84df70fb..9399fa7c 100644 --- a/jams-react-client/src/views/Groups/Groups.js +++ b/jams-react-client/src/views/Groups/Groups.js @@ -222,7 +222,14 @@ export default function Groups() { setSelectedBlueprint(getBlueprintsOptions()[e.target.value]) } - if(selectedGroup && auth.hasAdminScope()){ + if (!auth.hasAdminScope()) { + return ( + <div> + <h4>You are not allowed to access this section. Please contact your administrator to get administrator privileges.</h4> + </div> + ) + } + else if(selectedGroup && auth.hasAdminScope()){ return( <div> <EditGroup @@ -282,7 +289,7 @@ export default function Groups() { </DialogContentText> </DialogContent> <DialogActions> - <Button onClick={handleCloseCreate} color="danger"> + <Button onClick={handleCloseCreate} color="info"> Cancel </Button> <Button onClick={handleCreateGroup} color="primary" autoFocus> @@ -306,7 +313,7 @@ export default function Groups() { <Button onClick={() => setOpenRemoveDialog(false)} color="primary"> Cancel </Button> - <Button onClick={removeGroup} color="danger" autoFocus> + <Button onClick={removeGroup} color="info" autoFocus> Remove </Button> </DialogActions> diff --git a/jams-react-client/src/views/Notifications/Notifications.js b/jams-react-client/src/views/Notifications/Notifications.js index 7674258c..b10db293 100755 --- a/jams-react-client/src/views/Notifications/Notifications.js +++ b/jams-react-client/src/views/Notifications/Notifications.js @@ -195,10 +195,10 @@ export default function Notifications() { /> <SnackbarContent message={ - 'DANGER - This is a regular notification made with color="danger"' + 'DANGER - This is a regular notification made with color="info"' } close - color="danger" + color="info" /> <SnackbarContent message={ diff --git a/jams-react-client/src/views/RTLPage/RTLPage.js b/jams-react-client/src/views/RTLPage/RTLPage.js index 47934d68..eca4168e 100644 --- a/jams-react-client/src/views/RTLPage/RTLPage.js +++ b/jams-react-client/src/views/RTLPage/RTLPage.js @@ -108,8 +108,8 @@ export default function RTLPage() { </GridItem> <GridItem xs={12} sm={6} md={3}> <Card> - <CardHeader color="danger" stats icon> - <CardIcon color="danger"> + <CardHeader color="info" stats icon> + <CardIcon color="info"> <Icon>info_outline</Icon> </CardIcon> <p className={classes.cardCategory}>مشکلات ØÙ„ شده</p> @@ -194,7 +194,7 @@ export default function RTLPage() { </GridItem> <GridItem xs={12} sm={12} md={4}> <Card chart> - <CardHeader color="danger"> + <CardHeader color="info"> <ChartistGraph className="ct-chart" data={completedTasksChart.data} diff --git a/jams-react-client/src/views/UpgradeToPro/UpgradeToPro.js b/jams-react-client/src/views/UpgradeToPro/UpgradeToPro.js index ffee3263..fd6d2af0 100644 --- a/jams-react-client/src/views/UpgradeToPro/UpgradeToPro.js +++ b/jams-react-client/src/views/UpgradeToPro/UpgradeToPro.js @@ -194,7 +194,7 @@ export default function UpgradeToPro() { <td className={classes.center}> <Button round - color="danger" + color="info" href="https://www.creative-tim.com/product/material-dashboard-pro-react?ref=mdr-upgrade-live" > Upgrade to Pro diff --git a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js index 5251d5f9..dd850c4b 100644 --- a/jams-react-client/src/views/UserProfile/DisplayUserProfile.js +++ b/jams-react-client/src/views/UserProfile/DisplayUserProfile.js @@ -40,7 +40,11 @@ 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_auth_user, api_path_get_user_directory_search, api_path_delete_admin_user_revoke} 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, + api_path_put_update_user} from "globalUrls"; import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; @@ -48,6 +52,17 @@ import { hexToRgb, blackColor } from "assets/jss/material-dashboard-react.js"; import axios from "axios"; import PersonIcon from "../../../node_modules/@material-ui/icons/Person"; +import Input from '@material-ui/core/Input'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import Visibility from '@material-ui/icons/Visibility'; +import VisibilityOff from '@material-ui/icons/VisibilityOff'; + +import VpnKeyIcon from '@material-ui/icons/VpnKey'; +import IconButton from '@material-ui/core/IconButton'; + +import {Formik} from 'formik'; +import FormikField from 'components/FormikField/FormikField'; +import * as Yup from 'yup'; const styles = (theme)=> ( { ...dashboardStyle, @@ -111,9 +126,7 @@ const styles = (theme)=> ( { // [theme.breakpoints.up('sm')]: { // flexDirection: 'row', // alignItems: 'center', - // }, - // }, - // sliderContainer: { + // },passwordSchematainer: { // display: 'flex', // flex: '1', // alignItems: 'center', @@ -143,6 +156,11 @@ export default function DisplayUserProfile(props) { const [userStatus, setUserStatus] = React.useState(false) const [open, setOpen] = React.useState(false); const [revokedUser, setRevokedUser] = React.useState(""); + const [changePasswordOpen, setChangePasswordOpen] = React.useState(false) + const [currentPassword, setCurrentPassword] = React.useState(""); + const [visibilityValues, setVisibilityValues] = React.useState({ + showPassword: false + }); const searchData = { "queryString":props.username @@ -158,13 +176,14 @@ export default function DisplayUserProfile(props) { axios(configApiCall(api_path_get_admin_user, 'GET', userData, null)).then((response)=>{ const result = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); setUserStatus(result.revoked) + setCurrentPassword(result.password) } ).catch((error) =>{ console.log(error); }); } else{ - axios(configApiCall(api_path_get_auth_user, 'GET', null, null)).then((response)=>{ + axios(configApiCall(api_path_get_auth_user, 'GET', userData, null)).then((response)=>{ const result = JSON.parse(response.data.replace(/\s+/g, ' ').trim()); setUserStatus(result.revoked) } @@ -212,6 +231,34 @@ export default function DisplayUserProfile(props) { setOpen(false); }; + const handleClosechangePassword = () => { + setChangePasswordOpen(false); + } + + const changePassword = (values) => { + axios(configApiCall(api_path_put_update_user+'?username='+props.username+'&password='+values.password, 'PUT', null, null)).then(()=>{ + setChangePasswordOpen(false); + setCurrentPassword(values.password); + }).catch((error)=> { + console.log("Updating user " + props.username + ' password failed with error: ' + error); + }); + setChangePasswordOpen(false); + } + + const passwordSchema = Yup.object().shape({ + password: Yup.string().required('Password is required'), + passwordConfirmation: Yup.string() + .oneOf([Yup.ref('password'), null], 'Passwords must match') + }); + + const handleClickShowPassword = () => { + setVisibilityValues({ ...visibilityValues, showPassword: !visibilityValues.showPassword }); + }; + + const handleMouseDownPassword = (event) => { + event.preventDefault(); + }; + return ( <div> <Dialog @@ -230,11 +277,66 @@ export default function DisplayUserProfile(props) { <Button onClick={handleClose} color="primary"> Cancel </Button> - <Button onClick={revokeUser} color="danger" autoFocus> + <Button onClick={revokeUser} color="info" autoFocus> Revoke </Button> </DialogActions> </Dialog> + <Dialog + open={changePasswordOpen} + onClose={handleClosechangePassword} + aria-labelledby="alert-dialog-title" + aria-describedby="alert-dialog-description" + > + <DialogTitle id="change-password-dialog">{"Change password"}</DialogTitle> + <Formik initialValues={{ + password: "", + passwordConfirmation: "" + }} + validationSchema={passwordSchema} + onSubmit={changePassword} + > + {({ isValid, dirty, handleSubmit}) => ( + <form onSubmit={handleSubmit}> + <DialogContent> + <DialogContentText id="change-password-description"> + Password change for <strong>{props.username}</strong> + </DialogContentText> + <FormikField + name="password" + label="Password" + placeholder="Password" + type="password" + startAdornment={ + <InputAdornment position="start"> + <VpnKeyIcon /> + </InputAdornment> + } + /> + <FormikField + name="passwordConfirmation" + label="Confirm password" + placeholder="Confirm password" + type="password" + startAdornment={ + <InputAdornment position="start"> + <VpnKeyIcon /> + </InputAdornment> + } + /> + </DialogContent> + <DialogActions> + <Button onClick={handleClosechangePassword} color="primary"> + Cancel + </Button> + <Button type="submit" disable={!isValid && !dirty} color="info" autoFocus> + Update password + </Button> + </DialogActions> + </form> + )} + </Formik> + </Dialog> {users.map(user => <GridContainer> <Grid item xs={12} sm={12} md={8}> @@ -341,6 +443,34 @@ export default function DisplayUserProfile(props) { primary={user.faxNumber ? user.faxNumber : 'no fax number'} /> </ListItem> + {auth.isLocalDirectory() && auth.hasAdminScope() && <ListItem> + <ListItemAvatar> + <Avatar> + <VpnKeyIcon/> + </Avatar> + </ListItemAvatar> + <ListItemText + children={ + <Input + disabled + id="current-password" + type={visibilityValues.showPassword ? 'text' : 'password'} + value={currentPassword} + endAdornment={ + <InputAdornment position="end"> + <IconButton + aria-label="toggle password visibility" + onClick={handleClickShowPassword} + onMouseDown={handleMouseDownPassword} + > + {visibilityValues.showPassword ? <Visibility /> : <VisibilityOff />} + </IconButton> + </InputAdornment> + } + /> + } + /> + </ListItem>} </List> </Grid> </Grid> @@ -350,8 +480,11 @@ export default function DisplayUserProfile(props) { {auth.isLocalDirectory() && <Button color="info" onClick={() => props.setDisplayUser(false)}> <EditIcon /> Edit Profile </Button>} + {auth.isLocalDirectory() && auth.hasAdminScope() && <Button color="info" onClick={() => {setChangePasswordOpen(true);}}> + <VpnKeyIcon /> Change password + </Button>} {auth.hasAdminScope() && (userStatus === "Active" || userStatus === '') && - <Button color="danger" onClick={() => handleClickOpen(user.username)} > + <Button color="info" onClick={() => handleClickOpen(user.username)} > <DeleteIcon fontSize="small"/> Revoke user </Button>} </CardFooter> diff --git a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js index 2d5d6ab7..8b030e82 100644 --- a/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js +++ b/jams-react-client/src/views/UserProfile/EditCreateUserProfile.js @@ -62,9 +62,8 @@ import { import dashboardStyle from "assets/jss/material-dashboard-react/views/dashboardStyle.js"; import FormikField from 'components/FormikField/FormikField'; -import {Formik, ErrorMessage, Field} from "formik"; +import {Formik} from "formik"; import * as Yup from 'yup'; -require('yup-phone'); const fileUpload = require('fuctbase64'); @@ -408,7 +407,7 @@ export default function EditCreateUserProfile(props) { /> </div> </div> - <Button onClick={handleClose} color="danger"> + <Button onClick={handleClose} color="info"> Cancel </Button> <Button onClick={cropProfilePicture} color="success" autoFocus> @@ -439,7 +438,7 @@ export default function EditCreateUserProfile(props) { <Grid item xs={12} sm={12} md={12}> <input accept="image/*" className={classes.input} id="icon-button-file" type="file" onChange={handleProfilePictureChange}/> <label htmlFor="icon-button-file" > - <IconButton color="inherit" aria-label="upload picture" component="span" > + <IconButton color="info" aria-label="upload picture" component="span" > <PhotoCamera /> </IconButton> Change profile image </label> @@ -529,7 +528,7 @@ export default function EditCreateUserProfile(props) { </div> </CardBody> <CardFooter className={classes.alignRight}> - {!createUser && <Button color="danger" onClick={handleCancelUpdate}>Cancel</Button>} + {!createUser && <Button color="info" onClick={handleCancelUpdate}>Cancel</Button>} {createUser ? <Button type="submit" disabled={!isValid || !dirty} color="info">Create Profile</Button> : <Button type="submit" disabled={!isValid || !dirty} color="info">Save Profile</Button>} </CardFooter> </Card> -- GitLab