-
- this.setState({
- password: e.target.value,
- })
- }
- required
- />
-
- Please enter the correct password.
+
+
+
Password
+
+
+ this.setState({
+ password: e.target.value,
+ })
+ }
+ required
+ />
+
+ Please enter the correct password.
+
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
+
+
+
+ this.setState({ isForgotPasswordOpen: true })}
+ >
+ Forgot Your Password?{' '}
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+ return (
+
+
+ this.setState({
+ username: newUsername,
+ })
+ }
+ closeForgotPassword={() =>
+ this.setState({
+ isForgotPasswordOpen: false,
+ })
+ }
+ />
);
diff --git a/src/app/components/Authentication/Register.tsx b/src/app/components/Authentication/Register.tsx
index 2a1f3186..6dd0ad0e 100644
--- a/src/app/components/Authentication/Register.tsx
+++ b/src/app/components/Authentication/Register.tsx
@@ -70,6 +70,7 @@ export class Register extends React.Component
{
+ return {
+ errorMessage: rootState.user.errorMessage,
+ };
+};
+
+const changePasswordContainer = connect(mapStateToProps, {
+ changePassword: UserActions.changeUserPassword,
+})(ChangePassword);
+
+export default changePasswordContainer;
diff --git a/src/app/containers/Authentication/Login.ts b/src/app/containers/Authentication/Login.ts
index 7f3eee77..7d900daf 100644
--- a/src/app/containers/Authentication/Login.ts
+++ b/src/app/containers/Authentication/Login.ts
@@ -15,6 +15,7 @@ const mapStateToProps = (rootState: RootState) => {
const loginContainer = connect(
mapStateToProps,
{
+ forgotPassword: UserActions.forgotPassword,
login: UserActions.login,
updateErrorMessage: UserActions.updateErrorMessage,
},
diff --git a/src/app/index.tsx b/src/app/index.tsx
index d70d20c7..49d531db 100644
--- a/src/app/index.tsx
+++ b/src/app/index.tsx
@@ -1,4 +1,5 @@
import ActivateUser from 'app/containers/Authentication/ActivateUser';
+import ChangePassword from 'app/containers/Authentication/ChangePassword';
import Login from 'app/containers/Authentication/Login';
import Register from 'app/containers/Authentication/Register';
import Dashboard from 'app/containers/Dashboard';
@@ -25,6 +26,7 @@ export const App = hot(module)(() => (
+
diff --git a/src/app/routes.ts b/src/app/routes.ts
index 7b0c8cb8..174ec139 100644
--- a/src/app/routes.ts
+++ b/src/app/routes.ts
@@ -7,4 +7,5 @@ export enum Routes {
GITHUB_OAUTH = '/login/github',
GOOGLE_OAUTH = '/login/google',
USER_ACTIVATION = '/user-activate',
+ CHANGE_PASSWORD = '/reset-password',
}
diff --git a/src/app/sagas/User.ts b/src/app/sagas/User.ts
index c3435890..78995d05 100644
--- a/src/app/sagas/User.ts
+++ b/src/app/sagas/User.ts
@@ -209,6 +209,19 @@ export function* editUserPassword(action: ActionType) {
+ try {
+ const res = yield call(UserFetch.changeUserPassword, action.payload);
+ yield put(UserActions.updateErrorMessage(res.error ? res.body.message : ''));
+
+ if (res.type !== resType.ERROR) {
+ window.location.assign('/login');
+ }
+ } catch (err) {
+ console.error(err);
+ }
+}
+
export function* checkEmailExists(action: ActionType) {
try {
const res = yield call(UserFetch.checkEmailExists, action.payload.email);
@@ -231,6 +244,29 @@ export function* checkUsernameExists(action: ActionType) {
+ try {
+ const res = yield call(UserFetch.userForgotPassword, action.payload.email);
+
+ if (res === 'Password Reset URL sent to the registered email!') {
+ yield put(
+ NotificationActions.success(`Password reset URL have been sent to ${action.payload.email}`),
+ );
+ }
+
+ // Call returns error if username already exists, else empty
+ const message =
+ res === 'Invalid email'
+ ? 'Email is not registered'
+ : res === 'Password Reset URL sent to the registered email!'
+ ? ' '
+ : res;
+ yield put(UserActions.updateErrorMessage(message));
+ } catch (err) {
+ console.error(err);
+ }
+}
+
export function* resetAppState(action: ActionType) {
try {
yield put(CodeActions.resetCodeState());
@@ -253,11 +289,13 @@ export function* userSagas() {
takeEvery(UserActions.Type.REGISTER, register),
takeEvery(UserActions.Type.EDIT_USER_PROFILE, editUserProfile),
takeEvery(UserActions.Type.EDIT_USER_PASSWORD, editUserPassword),
+ takeEvery(UserActions.Type.CHANGE_USER_PASSWORD, changeUserPassword),
takeEvery(UserActions.Type.LOGIN, login),
takeEvery(UserActions.Type.LOGOUT, logout),
takeEvery(UserActions.Type.CHECK_EMAIL_EXISTS, checkEmailExists),
takeEvery(UserActions.Type.CHECK_USERNAME_EXISTS, checkUsernameExists),
takeEvery(UserActions.Type.GET_USER_DETAILS, getUserDetails),
takeEvery(UserActions.Type.RESET_APP_STATE, resetAppState),
+ takeEvery(UserActions.Type.FORGOT_PASSWORD, forgotPassword),
]);
}
diff --git a/src/app/styles/Authentication.module.css b/src/app/styles/Authentication.module.css
index 37c00f98..6627a5fa 100755
--- a/src/app/styles/Authentication.module.css
+++ b/src/app/styles/Authentication.module.css
@@ -568,3 +568,11 @@
background: #fff;
padding: 0 10px;
}
+
+.forgot-your-password {
+ cursor: pointer;
+}
+
+.forgot-your-password:hover {
+ color: #4630eb !important;
+}
diff --git a/src/app/types/Authentication/ChangePassword.ts b/src/app/types/Authentication/ChangePassword.ts
new file mode 100644
index 00000000..15888467
--- /dev/null
+++ b/src/app/types/Authentication/ChangePassword.ts
@@ -0,0 +1,16 @@
+import { RouteComponentProps } from 'react-router-dom';
+
+export interface ChangePasswordState {
+ password: string;
+ repeatPassword: string;
+ passwordError: string;
+}
+export interface StateProps {
+ errorMessage: string;
+}
+
+export interface DispatchProps {
+ changePassword: (password: string, passwordResetToken: string, userId: number) => void;
+}
+
+export type changePasswordProps = StateProps & DispatchProps & RouteComponentProps;
diff --git a/src/app/types/Authentication/Login.ts b/src/app/types/Authentication/Login.ts
index 2cbf9e8e..9c020162 100644
--- a/src/app/types/Authentication/Login.ts
+++ b/src/app/types/Authentication/Login.ts
@@ -1,6 +1,7 @@
import { AuthType } from 'app/types/Authentication';
export interface State {
+ isForgotPasswordOpen: boolean;
username: string;
password: string;
}
@@ -15,8 +16,19 @@ export interface StateProps {
}
export interface DispatchProps {
+ forgotPassword: (email: string) => void;
login: (username: string, password: string) => void;
updateErrorMessage: (errorMessage: string) => void;
}
+export interface ForgotPasswordProps {
+ updateErrorMessage: (errorMessage: string) => void;
+ handleSelectPanel: (authType: AuthType) => void;
+ closeForgotPassword: () => void;
+ errorMessage: string;
+ username: string;
+ setUsername: (username: string) => void;
+ forgotPassword: (email: string) => void;
+}
+
export type Props = ElementOwnProps & StateProps & DispatchProps;
diff --git a/src/app/types/User.ts b/src/app/types/User.ts
index 99e8ab1a..ebb2c782 100644
--- a/src/app/types/User.ts
+++ b/src/app/types/User.ts
@@ -30,6 +30,12 @@ export interface EditUserPassword {
oldPassword?: string;
}
+export interface ChangeUserPassword {
+ newPassword: string;
+ passwordResetToken: string;
+ userId: number;
+}
+
export interface Login {
email: string;
password: string;
@@ -39,11 +45,15 @@ export interface ActivateUser {
authToken: string;
userId: number;
}
+export interface ForgotPassword {
+ email: string;
+}
const actions = {
ActivateUser: UserActions.activateUser,
editUserPassword: UserActions.editUserPassword,
editUserProfile: UserActions.editUserProfile,
+ forgotPassword: UserActions.forgotPassword,
getUserDetails: UserActions.getUserDetails,
login: UserActions.login,
logout: UserActions.logout,