Skip to content

Commit bdebc53

Browse files
committed
Add ChangePassword component
1 parent 8286d87 commit bdebc53

File tree

10 files changed

+192
-1
lines changed

10 files changed

+192
-1
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"predeploy": "npm run build",
99
"test": "jest -i",
10-
"start": "webpack-dev-server --mode development --hot --progress --color --port 3000",
10+
"start": "webpack-dev-server --mode development --hot --progress --color --port 3000 --host 0.0.0.0",
1111
"build": "webpack -p --progress --colors",
1212
"lint": "prettier --write \"src/**/*.{ts,tsx,css}\" \"__tests__/**/*.{ts,tsx,css}\" && tslint --project .",
1313
"lint:fix": "tslint --project . --fix"

src/app/actions/User.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export namespace UserActions {
99
GET_USER_DETAILS = 'GET_USER_DETAILS',
1010
EDIT_USER_PROFILE = 'EDIT_USER_PROFILE',
1111
EDIT_USER_PASSWORD = 'EDIT_USER_PASSWORD',
12+
CHANGE_USER_PASSWORD = 'CHANGE_USER_PASSWORD',
1213
UPDATE_ERROR_MESSAGE = 'UPDATE_ERROR_MESSAGE',
1314
UPDATE_USER_DETAILS = 'UPDATE_USER_DETAILS',
1415
CHECK_EMAIL_EXISTS = 'CHECK_EMAIL_EXISTS',
@@ -63,6 +64,9 @@ export namespace UserActions {
6364
export const checkUsernameExists = (username: string) =>
6465
action(Type.CHECK_USERNAME_EXISTS, { username });
6566

67+
export const changeUserPassword = (changePasswordDetails: UserInterfaces.ChangeUserPassword) =>
68+
action(Type.CHANGE_USER_PASSWORD, changePasswordDetails);
69+
6670
export const toggleUserProfileModal = (isUserProfileModalOpen: boolean) =>
6771
action(Type.TOGGLE_USER_PROFILE_MODAL, { isUserProfileModalOpen });
6872

src/app/apiFetch/User.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,29 @@ export const userEditPassword = (body: UserInterfaces.EditUserPassword) => {
112112
});
113113
};
114114

115+
export const changeUserPassword = (body: UserInterfaces.ChangeUserPassword) => {
116+
return fetch(`${API_BASE_URL}user/password`, {
117+
body: JSON.stringify(body),
118+
headers: {
119+
Accept: 'application/json',
120+
'Content-Type': 'application/json',
121+
},
122+
})
123+
.then((response) => {
124+
console.log('api fetch response');
125+
console.log(response);
126+
return response.json();
127+
})
128+
.then((data) => {
129+
console.log('api fetch data');
130+
console.log(data);
131+
return data;
132+
})
133+
.catch((error) => {
134+
console.error(error);
135+
});
136+
};
137+
115138
export const userGetDetails = () => {
116139
return fetch(`${API_BASE_URL}user`, {
117140
credentials: 'include',
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import * as authStyles from 'app/styles/Authentication.module.css';
2+
import * as registerStyles from 'app/styles/Register.module.css';
3+
import { changePasswordProps } from 'app/types/Authentication/ChangePassword';
4+
import classnames from 'classnames';
5+
import * as React from 'react';
6+
7+
// tslint:disable-next-line: variable-name
8+
export const ChangePassword: React.FunctionComponent<changePasswordProps> = (
9+
props: changePasswordProps,
10+
) => {
11+
let passwordResetToken: string;
12+
const [password, setPassword] = React.useState('');
13+
const [repeatPassword, setRepeatPassword] = React.useState('');
14+
const [passwordError, setpasswordError] = React.useState('');
15+
const submitPassword = (e: React.MouseEvent) => {
16+
e.preventDefault();
17+
if (password !== '') {
18+
if (password.length >= 5) {
19+
if (password === repeatPassword) {
20+
setpasswordError('');
21+
props.changePassword({
22+
passwordResetToken,
23+
newPassword: password,
24+
});
25+
} else {
26+
setpasswordError('Password and confirm passwords have different values');
27+
}
28+
} else {
29+
setpasswordError('Password should have minimum 5 letters');
30+
}
31+
} else {
32+
setpasswordError('Password cannot be empty');
33+
}
34+
};
35+
React.useEffect(() => {
36+
// get string from url
37+
passwordResetToken = 'token from url';
38+
});
39+
return (
40+
<div className={classnames(authStyles.registerRoot)}>
41+
<div className={classnames(authStyles.registerMessage)}>
42+
<h1 className={classnames(authStyles['register-h1'])}> Reset Password </h1>
43+
<p> Enter your new password </p>
44+
</div>
45+
<div className={classnames('col-sm-12', authStyles.form)}>
46+
<form
47+
className={classnames('registerForm d-flex flex-wrap', authStyles['main-register-form'])}
48+
noValidate
49+
>
50+
<div className={classnames(authStyles['stage-div'], authStyles['stage-form'])}>
51+
<div className={classnames(authStyles['login-label'])}> New Password </div>
52+
<div className={classnames(registerStyles['input-group'])}>
53+
<input
54+
type="password"
55+
className={classnames('form-control', authStyles['register-input'])}
56+
id="registerValidationPassword"
57+
aria-describedby="inputGroupPrepend"
58+
pattern=".{5,}"
59+
value={password}
60+
onChange={(e) => setPassword(e.target.value)}
61+
required
62+
/>
63+
<div className={classnames('invalid-feedback', authStyles['register-error'])}>
64+
Password should have minimum 5 characters.
65+
</div>
66+
</div>
67+
<div className={classnames(authStyles['login-label'])}> Confirm Password </div>
68+
<div className={classnames(registerStyles['input-group'])}>
69+
<input
70+
type="password"
71+
className={classnames('form-control', authStyles['register-input'])}
72+
id="registerValidationrepeatPassword"
73+
aria-describedby="inputGroupPrepend"
74+
pattern=".{5,}"
75+
value={repeatPassword}
76+
onChange={(e) => setRepeatPassword(e.target.value)}
77+
required
78+
/>
79+
</div>
80+
81+
<div
82+
className={
83+
passwordError !== ''
84+
? classnames('form-row', authStyles['register-error-active'])
85+
: classnames('form-row', authStyles['register-error-inactive'])
86+
}
87+
>
88+
<div
89+
className={classnames(
90+
'col text-center mt -0 mb-2 errorMessage',
91+
registerStyles.errorMessage,
92+
)}
93+
>
94+
{passwordError}
95+
{'\n'}
96+
{props.errorMessage}
97+
</div>
98+
</div>
99+
<div
100+
className={classnames(registerStyles['input-group'], 'd-flex justify-content-center')}
101+
>
102+
<button
103+
className={classnames(authStyles['register-button'])}
104+
onClick={(e) => submitPassword(e)}
105+
>
106+
Confirm
107+
</button>
108+
</div>
109+
</div>
110+
</form>
111+
</div>
112+
</div>
113+
);
114+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { UserActions } from 'app/actions';
2+
import { ChangePassword } from 'app/components/Authentication/ChangePassword';
3+
import { RootState } from 'app/reducers';
4+
import { DispatchProps, StateProps } from 'app/types/Authentication/ChangePassword';
5+
import { connect } from 'react-redux';
6+
7+
const mapStateToProps = (rootState: RootState) => {
8+
return {
9+
errorMessage: rootState.user.errorMessage,
10+
};
11+
};
12+
13+
const changePasswordContainer = connect<StateProps, DispatchProps, {}>(mapStateToProps, {
14+
changePassword: UserActions.changeUserPassword,
15+
})(ChangePassword);
16+
17+
export default changePasswordContainer;

src/app/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import ChangePassword from 'app/containers/Authentication/ChangePassword';
12
import Login from 'app/containers/Authentication/Login';
23
import Register from 'app/containers/Authentication/Register';
34
import Dashboard from 'app/containers/Dashboard';
@@ -23,6 +24,7 @@ export const App = hot(module)(() => (
2324
<Route exact path={Routes.REGISTER} component={Register} />
2425
<Route exact path={Routes.LEADERBOARD} component={Leaderboard} />
2526
<Route exact path={Routes.USER_PROFILE_MODEL} component={UserProfileModal} />
27+
<Route path={Routes.CHANGE_PASSWORD} component={ChangePassword} />
2628
</Switch>
2729
<Sugar background="#484848" color="white" />
2830
</BrowserRouter>

src/app/routes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,5 @@ export enum Routes {
66
USER_PROFILE_MODEL = '/profile',
77
GITHUB_OAUTH = '/login/github',
88
GOOGLE_OAUTH = '/login/google',
9+
CHANGE_PASSWORD = '/forgot-password',
910
}

src/app/sagas/User.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import * as UserFetch from 'app/apiFetch/User';
1414
import { checkAuthentication } from 'app/sagas/utils';
1515
import { avatarName } from 'app/types/Authentication/Register';
1616
import { resType } from 'app/types/sagas';
17+
import { push } from 'react-router-redux';
1718
import { all, call, put, takeEvery } from 'redux-saga/effects';
1819
import { ActionType } from 'typesafe-actions';
1920

@@ -189,6 +190,18 @@ export function* editUserPassword(action: ActionType<typeof UserActions.editUser
189190
}
190191
}
191192

193+
export function* changeUserPassword(action: ActionType<typeof UserActions.changeUserPassword>) {
194+
try {
195+
const res = yield call(UserFetch.changeUserPassword, action.payload);
196+
197+
if (res.status === 200 || res.status === 201) {
198+
yield put(push('/login'));
199+
} else yield put(UserActions.updateErrorMessage(res.message));
200+
} catch (err) {
201+
console.error(err);
202+
}
203+
}
204+
192205
export function* checkEmailExists(action: ActionType<typeof UserActions.checkEmailExists>) {
193206
try {
194207
const res = yield call(UserFetch.checkEmailExists, action.payload.email);
@@ -232,6 +245,7 @@ export function* userSagas() {
232245
takeEvery(UserActions.Type.REGISTER, register),
233246
takeEvery(UserActions.Type.EDIT_USER_PROFILE, editUserProfile),
234247
takeEvery(UserActions.Type.EDIT_USER_PASSWORD, editUserPassword),
248+
takeEvery(UserActions.Type.CHANGE_USER_PASSWORD, changeUserPassword),
235249
takeEvery(UserActions.Type.LOGIN, login),
236250
takeEvery(UserActions.Type.LOGOUT, logout),
237251
takeEvery(UserActions.Type.CHECK_EMAIL_EXISTS, checkEmailExists),
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { ChangeUserPassword } from 'app/types/User';
2+
3+
export interface StateProps {
4+
errorMessage: string;
5+
}
6+
7+
export interface DispatchProps {
8+
changePassword: (changePasswordDetails: ChangeUserPassword) => void;
9+
}
10+
11+
export type changePasswordProps = StateProps & DispatchProps;

src/app/types/User.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ export interface EditUserPassword {
3030
oldPassword?: string;
3131
}
3232

33+
export interface ChangeUserPassword {
34+
newPassword: string;
35+
passwordResetToken: string;
36+
}
37+
3338
export interface Login {
3439
email: string;
3540
password: string;

0 commit comments

Comments
 (0)