Skip to content

Commit 3b0b475

Browse files
committed
Merge branch 'feature/edit-user-profile' into dev
2 parents c26dfea + 86be45b commit 3b0b475

File tree

13 files changed

+384
-4
lines changed

13 files changed

+384
-4
lines changed

src/common/api/user.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ export default (apiEngine) => ({
88
logout: () => apiEngine.get('/api/users/logout'),
99
show: () => apiEngine.get('/api/users/me'),
1010
update: (user) => apiEngine.put('/api/users/me', { data: user }),
11+
updateAvatarURL: (form) => apiEngine.put('/api/users/me/avatarURL', {
12+
data: form,
13+
}),
14+
updatePassword: (form) => apiEngine.put('/api/users/me/password', {
15+
data: form,
16+
}),
1117
uploadAvatar: (avatar) =>
1218
apiEngine.post('/api/users/me/avatar', { files: { avatar } }),
1319
});

src/common/components/forms/AvatarForm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ class AvatarForm extends Component {
132132
})
133133
.then((downloadURL) => {
134134
return userAPI(getState().apiEngine)
135-
.update({
135+
.updateAvatarURL({
136136
avatarURL: downloadURL,
137137
})
138138
.catch((err) => {
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import React, { Component } from 'react';
2+
import { connect } from 'react-redux';
3+
import { Field, reduxForm, SubmissionError } from 'redux-form';
4+
import Alert from 'react-bootstrap/lib/Alert';
5+
import Button from 'react-bootstrap/lib/Button';
6+
import userAPI from '../../../api/user';
7+
import { pushErrors } from '../../../actions/errorActions';
8+
import { Form, FormField, FormFooter } from '../../utils/BsForm';
9+
10+
export const validate = (values) => {
11+
const errors = {};
12+
13+
if (
14+
values.newPasswordConfirm &&
15+
values.newPassword !== values.newPasswordConfirm
16+
) {
17+
errors.newPassword = errors.newPasswordConfirm = 'Password Not Matched';
18+
}
19+
20+
if (values.oldPassword === values.newPassword) {
21+
errors.newPassword = 'Cannot be same as old password';
22+
}
23+
24+
if (!values.oldPassword) {
25+
errors.oldPassword = 'Required';
26+
}
27+
28+
if (!values.newPassword) {
29+
errors.newPassword = 'Required';
30+
}
31+
32+
if (!values.newPasswordConfirm) {
33+
errors.newPasswordConfirm = 'Required';
34+
}
35+
36+
return errors;
37+
};
38+
39+
class ChangePasswordForm extends Component {
40+
constructor(props) {
41+
super(props);
42+
this.handleSubmit = this._handleSubmit.bind(this);
43+
}
44+
45+
_handleSubmit(formData) {
46+
let { dispatch, apiEngine, initialize } = this.props;
47+
48+
return userAPI(apiEngine)
49+
.updatePassword(formData)
50+
.catch((err) => {
51+
dispatch(pushErrors(err));
52+
throw err;
53+
})
54+
.then((json) => {
55+
if (json.isAuth) {
56+
initialize({
57+
oldPassword: '',
58+
newPassword: '',
59+
newPasswordConfirm: '',
60+
});
61+
} else {
62+
throw new SubmissionError({
63+
oldPassword: 'Wrong old password',
64+
_error: 'Change password failed',
65+
});
66+
}
67+
});
68+
}
69+
70+
render() {
71+
const {
72+
handleSubmit,
73+
submitSucceeded,
74+
submitFailed,
75+
error,
76+
pristine,
77+
submitting,
78+
invalid,
79+
} = this.props;
80+
81+
return (
82+
<Form horizontal onSubmit={handleSubmit(this.handleSubmit)}>
83+
{submitSucceeded && (<Alert bsStyle="success">Password Changed</Alert>)}
84+
{submitFailed && error && (<Alert bsStyle="danger">{error}</Alert>)}
85+
<Field
86+
label="Old Password"
87+
name="oldPassword"
88+
component={FormField}
89+
type="password"
90+
placeholder="Old Password"
91+
/>
92+
<Field
93+
label="New Password"
94+
name="newPassword"
95+
component={FormField}
96+
type="password"
97+
placeholder="New Password"
98+
/>
99+
<Field
100+
label="New Password Confirm"
101+
name="newPasswordConfirm"
102+
component={FormField}
103+
type="password"
104+
placeholder="New Password Confirm"
105+
/>
106+
<FormFooter>
107+
<Button type="submit" disabled={pristine || submitting || invalid}>
108+
Change
109+
{submitting && (
110+
<i className="fa fa-spinner fa-spin" aria-hidden="true" />
111+
)}
112+
</Button>
113+
</FormFooter>
114+
</Form>
115+
);
116+
}
117+
};
118+
119+
export default reduxForm({
120+
form: 'userChangePassword',
121+
validate,
122+
})(connect(state => ({
123+
apiEngine: state.apiEngine,
124+
}))(ChangePasswordForm));
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import React, { Component } from 'react';
2+
import { connect } from 'react-redux';
3+
import { Field, reduxForm } from 'redux-form';
4+
import Alert from 'react-bootstrap/lib/Alert';
5+
import Button from 'react-bootstrap/lib/Button';
6+
import userAPI from '../../../api/user';
7+
import { pushErrors } from '../../../actions/errorActions';
8+
import { setCookies } from '../../../actions/cookieActions';
9+
import { Form, FormField, FormFooter } from '../../utils/BsForm';
10+
11+
export const validate = (values) => {
12+
const errors = {};
13+
14+
if (!values.name) {
15+
errors.name = 'Required';
16+
}
17+
18+
return errors;
19+
};
20+
21+
class EditForm extends Component {
22+
constructor(props) {
23+
super(props);
24+
this.init = this._init.bind(this);
25+
this.handleSubmit = this._handleSubmit.bind(this);
26+
}
27+
28+
_init(user) {
29+
let { initialize } = this.props;
30+
31+
initialize({
32+
name: user.name,
33+
});
34+
}
35+
36+
componentDidMount() {
37+
let { dispatch, apiEngine } = this.props;
38+
39+
userAPI(apiEngine)
40+
.show()
41+
.catch((err) => {
42+
dispatch(pushErrors(err));
43+
throw err;
44+
})
45+
.then((json) => {
46+
this.init(json.user);
47+
});
48+
}
49+
50+
_handleSubmit(formData) {
51+
let { dispatch, apiEngine } = this.props;
52+
53+
return userAPI(apiEngine)
54+
.update(formData)
55+
.catch((err) => {
56+
dispatch(pushErrors(err));
57+
throw err;
58+
})
59+
.then((json) => {
60+
this.init(json.user);
61+
dispatch(setCookies({
62+
user: json.user,
63+
}));
64+
});
65+
}
66+
67+
render() {
68+
const {
69+
handleSubmit,
70+
submitSucceeded,
71+
submitFailed,
72+
error,
73+
pristine,
74+
submitting,
75+
invalid,
76+
} = this.props;
77+
78+
return (
79+
<Form horizontal onSubmit={handleSubmit(this.handleSubmit)}>
80+
{submitSucceeded && (<Alert bsStyle="success">Profile Saved</Alert>)}
81+
{submitFailed && error && (<Alert bsStyle="danger">{error}</Alert>)}
82+
<Field
83+
label="Name"
84+
name="name"
85+
component={FormField}
86+
type="text"
87+
placeholder="Name"
88+
/>
89+
<FormFooter>
90+
<Button type="submit" disabled={pristine || submitting || invalid}>
91+
Save
92+
{submitting && (
93+
<i className="fa fa-spinner fa-spin" aria-hidden="true" />
94+
)}
95+
</Button>
96+
</FormFooter>
97+
</Form>
98+
);
99+
}
100+
};
101+
102+
export default reduxForm({
103+
form: 'userEdit',
104+
validate,
105+
})(connect(state => ({
106+
apiEngine: state.apiEngine,
107+
}))(EditForm));
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { Link } from 'react-router';
3+
import PageHeader from 'react-bootstrap/lib/PageHeader';
4+
import Row from 'react-bootstrap/lib/Row';
5+
import Col from 'react-bootstrap/lib/Col';
6+
import Button from 'react-bootstrap/lib/Button';
7+
import PageLayout from '../../layouts/PageLayout';
8+
import EditForm from '../../forms/user/EditForm';
9+
import ChangePasswordForm from '../../forms/user/ChangePasswordForm';
10+
11+
let EditPage = () => {
12+
return (
13+
<PageLayout>
14+
<Row>
15+
<Col md={12}>
16+
<Link to="/user/me">
17+
<Button>Finish</Button>
18+
</Link>
19+
</Col>
20+
</Row>
21+
<hr />
22+
<Row>
23+
<Col md={6}>
24+
<PageHeader>Edit Profile</PageHeader>
25+
<EditForm />
26+
</Col>
27+
<Col md={6}>
28+
<PageHeader>Change Password</PageHeader>
29+
<ChangePasswordForm />
30+
</Col>
31+
</Row>
32+
</PageLayout>
33+
);
34+
};
35+
36+
export default EditPage;

src/common/components/pages/user/ShowPage.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import React, { Component } from 'react';
2+
import { Link } from 'react-router';
23
import PageHeader from 'react-bootstrap/lib/PageHeader';
4+
import Row from 'react-bootstrap/lib/Row';
5+
import Col from 'react-bootstrap/lib/Col';
6+
import Button from 'react-bootstrap/lib/Button';
37
import userAPI from '../../../api/user';
48
import { pushErrors } from '../../../actions/errorActions';
59
import Head from '../../widgets/Head';
@@ -39,6 +43,14 @@ class ShowPage extends Component {
3943
'https://www.gstatic.com/firebasejs/live/3.0/firebase.js',
4044
]}
4145
/>
46+
<Row>
47+
<Col md={12}>
48+
<Link to="/user/me/edit">
49+
<Button bsStyle="primary">Edit My Profile</Button>
50+
</Link>
51+
</Col>
52+
</Row>
53+
<hr />
4254
<PageHeader>My Profile</PageHeader>
4355
<dl className="dl-horizontal">
4456
<dt>_id</dt>

src/common/constants/ErrorCodes.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export default {
1111
LOCALE_NOT_SUPPORTED: 'LOCALE_NOT_SUPPORTED',
1212
ODM_VALIDATION: 'ODM_VALIDATION',
1313
INVALID_RECAPTCHA: 'INVALID_RECAPTCHA',
14+
INVALID_DATA: 'INVALID_DATA',
1415
AUTHORIZATION_FAIL: 'AUTHORIZATION_FAIL',
1516
SEND_EMAIL_FAIL: 'SEND_EMAIL_FAIL',
1617
};

src/common/constants/Errors.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ export default {
2525
title: 'Invalid Recaptcha',
2626
detail: 'The value of recaptcha is invalid.',
2727
},
28+
[ErrorCodes.INVALID_DATA]: {
29+
code: ErrorCodes.INVALID_DATA,
30+
status: 400,
31+
title: 'Invalid Data',
32+
detail: 'You are sending invalid data.',
33+
},
2834
[ErrorCodes.STATE_PRE_FETCHING_FAIL]: {
2935
code: ErrorCodes.STATE_PRE_FETCHING_FAIL,
3036
status: 500,

src/common/routes/user/edit.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default (store) => ({
2+
path: 'me/edit',
3+
getComponent(nextState, cb) {
4+
require.ensure([], (require) => {
5+
cb(null, require('../../components/pages/user/EditPage').default);
6+
});
7+
},
8+
});

src/common/routes/user/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default (store) => ({
66
require('./register').default(store),
77
require('./verification').default(store),
88
require('./login').default(store),
9+
require('./edit').default(store),
910
require('./logout').default(store),
1011
require('./me').default(store),
1112
]);

0 commit comments

Comments
 (0)