Skip to content

Commit

Permalink
Merge branch 'feature/edit-user-profile' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
gocreating committed Oct 28, 2016
2 parents c26dfea + 86be45b commit 3b0b475
Show file tree
Hide file tree
Showing 13 changed files with 384 additions and 4 deletions.
6 changes: 6 additions & 0 deletions src/common/api/user.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ export default (apiEngine) => ({
logout: () => apiEngine.get('/api/users/logout'),
show: () => apiEngine.get('/api/users/me'),
update: (user) => apiEngine.put('/api/users/me', { data: user }),
updateAvatarURL: (form) => apiEngine.put('/api/users/me/avatarURL', {
data: form,
}),
updatePassword: (form) => apiEngine.put('/api/users/me/password', {
data: form,
}),
uploadAvatar: (avatar) =>
apiEngine.post('/api/users/me/avatar', { files: { avatar } }),
});
2 changes: 1 addition & 1 deletion src/common/components/forms/AvatarForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class AvatarForm extends Component {
})
.then((downloadURL) => {
return userAPI(getState().apiEngine)
.update({
.updateAvatarURL({
avatarURL: downloadURL,
})
.catch((err) => {
Expand Down
124 changes: 124 additions & 0 deletions src/common/components/forms/user/ChangePasswordForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm, SubmissionError } from 'redux-form';
import Alert from 'react-bootstrap/lib/Alert';
import Button from 'react-bootstrap/lib/Button';
import userAPI from '../../../api/user';
import { pushErrors } from '../../../actions/errorActions';
import { Form, FormField, FormFooter } from '../../utils/BsForm';

export const validate = (values) => {
const errors = {};

if (
values.newPasswordConfirm &&
values.newPassword !== values.newPasswordConfirm
) {
errors.newPassword = errors.newPasswordConfirm = 'Password Not Matched';
}

if (values.oldPassword === values.newPassword) {
errors.newPassword = 'Cannot be same as old password';
}

if (!values.oldPassword) {
errors.oldPassword = 'Required';
}

if (!values.newPassword) {
errors.newPassword = 'Required';
}

if (!values.newPasswordConfirm) {
errors.newPasswordConfirm = 'Required';
}

return errors;
};

class ChangePasswordForm extends Component {
constructor(props) {
super(props);
this.handleSubmit = this._handleSubmit.bind(this);
}

_handleSubmit(formData) {
let { dispatch, apiEngine, initialize } = this.props;

return userAPI(apiEngine)
.updatePassword(formData)
.catch((err) => {
dispatch(pushErrors(err));
throw err;
})
.then((json) => {
if (json.isAuth) {
initialize({
oldPassword: '',
newPassword: '',
newPasswordConfirm: '',
});
} else {
throw new SubmissionError({
oldPassword: 'Wrong old password',
_error: 'Change password failed',
});
}
});
}

render() {
const {
handleSubmit,
submitSucceeded,
submitFailed,
error,
pristine,
submitting,
invalid,
} = this.props;

return (
<Form horizontal onSubmit={handleSubmit(this.handleSubmit)}>
{submitSucceeded && (<Alert bsStyle="success">Password Changed</Alert>)}
{submitFailed && error && (<Alert bsStyle="danger">{error}</Alert>)}
<Field
label="Old Password"
name="oldPassword"
component={FormField}
type="password"
placeholder="Old Password"
/>
<Field
label="New Password"
name="newPassword"
component={FormField}
type="password"
placeholder="New Password"
/>
<Field
label="New Password Confirm"
name="newPasswordConfirm"
component={FormField}
type="password"
placeholder="New Password Confirm"
/>
<FormFooter>
<Button type="submit" disabled={pristine || submitting || invalid}>
Change
{submitting && (
<i className="fa fa-spinner fa-spin" aria-hidden="true" />
)}
</Button>
</FormFooter>
</Form>
);
}
};

export default reduxForm({
form: 'userChangePassword',
validate,
})(connect(state => ({
apiEngine: state.apiEngine,
}))(ChangePasswordForm));
107 changes: 107 additions & 0 deletions src/common/components/forms/user/EditForm.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Field, reduxForm } from 'redux-form';
import Alert from 'react-bootstrap/lib/Alert';
import Button from 'react-bootstrap/lib/Button';
import userAPI from '../../../api/user';
import { pushErrors } from '../../../actions/errorActions';
import { setCookies } from '../../../actions/cookieActions';
import { Form, FormField, FormFooter } from '../../utils/BsForm';

export const validate = (values) => {
const errors = {};

if (!values.name) {
errors.name = 'Required';
}

return errors;
};

class EditForm extends Component {
constructor(props) {
super(props);
this.init = this._init.bind(this);
this.handleSubmit = this._handleSubmit.bind(this);
}

_init(user) {
let { initialize } = this.props;

initialize({
name: user.name,
});
}

componentDidMount() {
let { dispatch, apiEngine } = this.props;

userAPI(apiEngine)
.show()
.catch((err) => {
dispatch(pushErrors(err));
throw err;
})
.then((json) => {
this.init(json.user);
});
}

_handleSubmit(formData) {
let { dispatch, apiEngine } = this.props;

return userAPI(apiEngine)
.update(formData)
.catch((err) => {
dispatch(pushErrors(err));
throw err;
})
.then((json) => {
this.init(json.user);
dispatch(setCookies({
user: json.user,
}));
});
}

render() {
const {
handleSubmit,
submitSucceeded,
submitFailed,
error,
pristine,
submitting,
invalid,
} = this.props;

return (
<Form horizontal onSubmit={handleSubmit(this.handleSubmit)}>
{submitSucceeded && (<Alert bsStyle="success">Profile Saved</Alert>)}
{submitFailed && error && (<Alert bsStyle="danger">{error}</Alert>)}
<Field
label="Name"
name="name"
component={FormField}
type="text"
placeholder="Name"
/>
<FormFooter>
<Button type="submit" disabled={pristine || submitting || invalid}>
Save
{submitting && (
<i className="fa fa-spinner fa-spin" aria-hidden="true" />
)}
</Button>
</FormFooter>
</Form>
);
}
};

export default reduxForm({
form: 'userEdit',
validate,
})(connect(state => ({
apiEngine: state.apiEngine,
}))(EditForm));
36 changes: 36 additions & 0 deletions src/common/components/pages/user/EditPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import { Link } from 'react-router';
import PageHeader from 'react-bootstrap/lib/PageHeader';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button';
import PageLayout from '../../layouts/PageLayout';
import EditForm from '../../forms/user/EditForm';
import ChangePasswordForm from '../../forms/user/ChangePasswordForm';

let EditPage = () => {
return (
<PageLayout>
<Row>
<Col md={12}>
<Link to="/user/me">
<Button>Finish</Button>
</Link>
</Col>
</Row>
<hr />
<Row>
<Col md={6}>
<PageHeader>Edit Profile</PageHeader>
<EditForm />
</Col>
<Col md={6}>
<PageHeader>Change Password</PageHeader>
<ChangePasswordForm />
</Col>
</Row>
</PageLayout>
);
};

export default EditPage;
12 changes: 12 additions & 0 deletions src/common/components/pages/user/ShowPage.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import React, { Component } from 'react';
import { Link } from 'react-router';
import PageHeader from 'react-bootstrap/lib/PageHeader';
import Row from 'react-bootstrap/lib/Row';
import Col from 'react-bootstrap/lib/Col';
import Button from 'react-bootstrap/lib/Button';
import userAPI from '../../../api/user';
import { pushErrors } from '../../../actions/errorActions';
import Head from '../../widgets/Head';
Expand Down Expand Up @@ -39,6 +43,14 @@ class ShowPage extends Component {
'https://www.gstatic.com/firebasejs/live/3.0/firebase.js',
]}
/>
<Row>
<Col md={12}>
<Link to="/user/me/edit">
<Button bsStyle="primary">Edit My Profile</Button>
</Link>
</Col>
</Row>
<hr />
<PageHeader>My Profile</PageHeader>
<dl className="dl-horizontal">
<dt>_id</dt>
Expand Down
1 change: 1 addition & 0 deletions src/common/constants/ErrorCodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default {
LOCALE_NOT_SUPPORTED: 'LOCALE_NOT_SUPPORTED',
ODM_VALIDATION: 'ODM_VALIDATION',
INVALID_RECAPTCHA: 'INVALID_RECAPTCHA',
INVALID_DATA: 'INVALID_DATA',
AUTHORIZATION_FAIL: 'AUTHORIZATION_FAIL',
SEND_EMAIL_FAIL: 'SEND_EMAIL_FAIL',
};
6 changes: 6 additions & 0 deletions src/common/constants/Errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ export default {
title: 'Invalid Recaptcha',
detail: 'The value of recaptcha is invalid.',
},
[ErrorCodes.INVALID_DATA]: {
code: ErrorCodes.INVALID_DATA,
status: 400,
title: 'Invalid Data',
detail: 'You are sending invalid data.',
},
[ErrorCodes.STATE_PRE_FETCHING_FAIL]: {
code: ErrorCodes.STATE_PRE_FETCHING_FAIL,
status: 500,
Expand Down
8 changes: 8 additions & 0 deletions src/common/routes/user/edit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export default (store) => ({
path: 'me/edit',
getComponent(nextState, cb) {
require.ensure([], (require) => {
cb(null, require('../../components/pages/user/EditPage').default);
});
},
});
1 change: 1 addition & 0 deletions src/common/routes/user/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export default (store) => ({
require('./register').default(store),
require('./verification').default(store),
require('./login').default(store),
require('./edit').default(store),
require('./logout').default(store),
require('./me').default(store),
]);
Expand Down
Loading

0 comments on commit 3b0b475

Please sign in to comment.