Skip to content

Commit

Permalink
feat: add edit profile to change nickname and password
Browse files Browse the repository at this point in the history
  • Loading branch information
dvaJi committed Apr 30, 2021
1 parent def795e commit ec8b18f
Show file tree
Hide file tree
Showing 13 changed files with 236 additions and 11 deletions.
4 changes: 3 additions & 1 deletion admin/shared/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,7 @@
"confirm_password": "Confirm password",
"change_password": "Change Password",
"reset_password": "Reset Password",
"error_passwords_not_identical": "Passwords are not identical"
"error_passwords_not_identical": "Passwords are not identical",
"edit_profile": "Edit Profile",
"username": "Username"
}
4 changes: 3 additions & 1 deletion admin/shared/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -196,5 +196,7 @@
"confirm_password": "Confirmar contraseña",
"change_password": "Cambiar Contraseña",
"reset_password": "Restablecer la contraseña",
"error_passwords_not_identical": "Las contraseñas no son idénticas"
"error_passwords_not_identical": "Las contraseñas no son idénticas",
"edit_profile": "Editar Perfil",
"username": "Nombre de usuario"
}
8 changes: 8 additions & 0 deletions admin/src/Routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ const UsersList = Loadable({
modules: ['users']
});

const EditProfile = Loadable({
loader: () =>
import(/* webpackChunkName: "editprofile" */ './user/EditProfile'),
loading: () => null,
modules: ['editprofile']
});

const RegistryList = Loadable({
loader: () =>
import(/* webpackChunkName: "registries" */ './registry/RegistryList'),
Expand Down Expand Up @@ -256,6 +263,7 @@ export default (
exact
component={withTracker(UsersList)}
/>
<RoutePrivate path="/me/edit" exact component={withTracker(EditProfile)} />
<RoutePrivate path="/registry" exact component={RegistryList} />
<Route path="/401" component={withTracker(Unauthorized)} />
<Route component={NotFound} />
Expand Down
6 changes: 3 additions & 3 deletions admin/src/auth/containers/LoginContainer.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { useLocation, useHistory, Link } from 'react-router-dom';
import { useLocation, useHistory } from 'react-router-dom';
import { useIntl } from 'react-intl';
import { useMutation } from '@apollo/client';
import { Alert, Button, Form, FormGroup, Label, Input } from 'reactstrap';
Expand Down Expand Up @@ -104,14 +104,14 @@ function Login() {
onChange={onChange}
/>
</FormGroup>
<div className="mb-4">
{/* <div className="mb-4">
<Link to="/auth/reset_password">
{f({
id: 'forgot_password',
defaultMessage: 'Forgot your password?'
})}
</Link>
</div>
</div> */}
<Button type="submit" size="lg" block disabled={isLoading}>
{f({ id: 'login', defaultMessage: 'Login' })}
</Button>
Expand Down
5 changes: 4 additions & 1 deletion admin/src/layout/header/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
import { useLocation, useHistory } from 'react-router-dom';
import { useLocation, useHistory, Link } from 'react-router-dom';

import { FormattedMessage, useIntl } from 'react-intl';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
Expand Down Expand Up @@ -101,6 +101,9 @@ function AdminNav() {
</UserLogged>
<UserLoggedMenu>
<DropdownItem header>{user.name}</DropdownItem>
<DropdownItem>
<Link to="/me/edit">Edit Profile</Link>
</DropdownItem>
<DropdownItem
onClick={() => {
history.push('/auth/logout');
Expand Down
137 changes: 137 additions & 0 deletions admin/src/user/EditProfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import React, { useEffect, useState } from 'react';
import { useHistory } from 'react-router';
import { useIntl } from 'react-intl';
import { Alert, Container, Form, FormGroup } from 'reactstrap';
import { useMutation } from '@apollo/client';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';

import { Button, ButtonLink, Card, Input, Label } from 'common/ui';

import { UPDATE_PROFILE } from './mutation';
import { useGlobalState } from 'state';

function Profile() {
const [newUser, setNewUser] = useState({});
const [error, setError] = useState(null);
const [user] = useGlobalState('user');
const history = useHistory();
const [updateProfile] = useMutation(UPDATE_PROFILE);
const { formatMessage: f } = useIntl();

useEffect(() => {
if (!newUser.id) {
setNewUser(user);
}
}, [user, newUser.id]);

const handleOnChange = event => {
let post = { ...newUser };
post[event.target.name] = event.target.value;

setNewUser(post);
};

const onSubmit = async event => {
event.preventDefault();

console.warn(newUser);
if (newUser.password !== newUser.confirm_password) {
setError(
f({
id: 'error_passwords_not_identical',
defaultMessage: 'Passwords are not identical'
})
);
} else {
setError(null);
try {
await updateProfile({
variables: {
username: newUser.username,
password: newUser.password
}
});
history.push('/');
} catch (err) {
alert(err);
}
}
};

return (
<Container>
<div style={{ marginTop: '1rem' }}>
<ButtonLink to={'/dashboard'}>
<FontAwesomeIcon icon="arrow-left" />{' '}
{f({ id: 'go_back', defaultMessage: 'Go back' })}
</ButtonLink>
</div>
<Card>
<h4>{f({ id: 'edit_profile', defaultMessage: 'Edit Profile' })}</h4>
{error && (
<Alert id="error_alert" color="danger">
{error}
</Alert>
)}
<Form onSubmit={onSubmit}>
<FormGroup>
<Label htmlFor="username">
{f({ id: 'username', defaultMessage: 'Username' })}
</Label>
<Input
id="username"
type="text"
placeholder={f({
id: 'username',
defaultMessage: 'Username'
})}
name="username"
value={newUser.username}
onChange={handleOnChange}
/>
</FormGroup>
<FormGroup>
<Label htmlFor="password">
{f({ id: 'password', defaultMessage: 'Password' })}
</Label>
<Input
id="password"
type="password"
placeholder={f({
id: 'password',
defaultMessage: 'Password'
})}
name="password"
value={newUser.password}
onChange={handleOnChange}
/>
</FormGroup>
<FormGroup>
<Label htmlFor="confirm_password">
{f({
id: 'confirm_password',
defaultMessage: 'Confirm password'
})}
</Label>
<Input
id="confirm_password"
type="password"
placeholder={f({
id: 'confirm_password',
defaultMessage: 'Confirm password'
})}
name="confirm_password"
value={newUser.confirm_password}
onChange={handleOnChange}
/>
</FormGroup>
<Button type="submit" size="lg" block>
{f({ id: 'save', defaultMessage: 'Save' })}
</Button>
</Form>
</Card>
</Container>
);
}

export default Profile;
8 changes: 8 additions & 0 deletions admin/src/user/mutation.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,11 @@ export const CHANGE_ROLE = gql`
}
}
`;

export const UPDATE_PROFILE = gql`
mutation ChangeUserRole($username: String, $password: String) {
userUpdateProfile(username: $username, password: $password) {
message
}
}
`;
4 changes: 3 additions & 1 deletion api/shared/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,7 @@
"confirm_password": "Confirm password",
"change_password": "Change Password",
"reset_password": "Reset Password",
"error_passwords_not_identical": "Passwords are not identical"
"error_passwords_not_identical": "Passwords are not identical",
"edit_profile": "Edit Profile",
"username": "Username"
}
4 changes: 3 additions & 1 deletion api/shared/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,7 @@
"confirm_password": "Confirmar contraseña",
"change_password": "Cambiar Contraseña",
"reset_password": "Restablecer la contraseña",
"error_passwords_not_identical": "Las contraseñas no son idénticas"
"error_passwords_not_identical": "Las contraseñas no son idénticas",
"edit_profile": "Editar Perfil",
"username": "Nombre de usuario"
}
12 changes: 11 additions & 1 deletion api/src/modules/user/mutations.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
activate,
changePassword,
recoverPassword,
changeRole
changeRole,
updateProfile
} from './resolvers';

// Auth
Expand Down Expand Up @@ -143,3 +144,12 @@ export const userChangeRole = {
},
resolve: changeRole
};

export const userUpdateProfile = {
type: MessageType,
args: {
username: { name: 'username', type: GraphQLString },
password: { name: 'password', type: GraphQLString }
},
resolve: updateProfile
};
47 changes: 47 additions & 0 deletions api/src/modules/user/resolvers.js
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,53 @@ export async function getGenders() {
return {};
}

export async function updateProfile(_, newValues, { auth }) {
if (auth && auth.user && auth.user.id) {
const user = await models.User.findOne({ where: { id: auth.user.id } });
if (!user) {
throw new Error(`User does not exists`);
}

const userDetails = user.get();

const passwordEquals = await bcrypt.compare(
newValues.password,
userDetails.password
);
let passwordHashed = '';
if (newValues.password) {
passwordHashed = await bcrypt.hash(
newValues.password,
serverConfig.saltRounds
);
}

const newUserInfo = Object.keys(newValues).reduce((obj, key) => {
if (newValues[key]) {
if (!passwordEquals && key === 'password') {
obj[key] = passwordHashed;
} else {
obj[key] = newValues[key];
}
}

return obj;
}, {});

await models.User.update(
{
...newUserInfo
},
{ where: { id: userDetails.id } }
);
await revokeToken({ auth, userId: userDetails.id });

return { id: userDetails.id };
} else {
throw new Error('Operation denied.');
}
}

// Ban user
export async function ban(_, { id, reason }, { auth }) {
if (await hasPermission('update', auth, 'users')) {
Expand Down
4 changes: 3 additions & 1 deletion shared/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,7 @@
"confirm_password": "Confirm password",
"change_password": "Change Password",
"reset_password": "Reset Password",
"error_passwords_not_identical": "Passwords are not identical"
"error_passwords_not_identical": "Passwords are not identical",
"edit_profile": "Edit Profile",
"username": "Username"
}
4 changes: 3 additions & 1 deletion shared/lang/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -195,5 +195,7 @@
"confirm_password": "Confirmar contraseña",
"change_password": "Cambiar Contraseña",
"reset_password": "Restablecer la contraseña",
"error_passwords_not_identical": "Las contraseñas no son idénticas"
"error_passwords_not_identical": "Las contraseñas no son idénticas",
"edit_profile": "Editar Perfil",
"username": "Nombre de usuario"
}

0 comments on commit ec8b18f

Please sign in to comment.