Просмотр исходного кода

added forget password and reset password

pull/21/head
Safet Purkovic 3 лет назад
Родитель
Сommit
86c5714a2b

+ 6
- 0
src/AppRoutes.js Просмотреть файл

@@ -6,9 +6,11 @@ import {
HOME_PAGE,
ADS_PAGE,
FORGOT_PASSWORD_PAGE,
FORGOT_PASSWORD_CONFIRMATION_PAGE,
NOT_FOUND_PAGE,
ERROR_PAGE,
BASE_PAGE,
RESET_PASSWORD_PAGE
} from "./constants/pages";

// import LoginPage from './pages/LoginPage/LoginPage';
@@ -21,6 +23,8 @@ import ErrorPage from "./pages/ErrorPages/ErrorPage";
// import ForgotPasswordPage from './pages/ForgotPasswordPage/ForgotPasswordPage';
import ForgotPasswordPage from "./pages/ForgotPasswordPage/ForgotPasswordPageMUI";
import PrivateRoute from "./components/Router/PrivateRoute";
import ForgotPasswordConfirmationPage from "./pages/ForgotPasswordPage/ForgotPasswordConfirmationPageMUI";
import ResetPasswordPage from "./pages/ForgotPasswordPage/ResetPasswordPageMUI";

const AppRoutes = () => (
<Switch>
@@ -29,6 +33,8 @@ const AppRoutes = () => (
<Route path={NOT_FOUND_PAGE} component={NotFoundPage} />
<Route path={ERROR_PAGE} component={ErrorPage} />
<Route path={FORGOT_PASSWORD_PAGE} component={ForgotPasswordPage} />
<Route path={FORGOT_PASSWORD_CONFIRMATION_PAGE} component={ForgotPasswordConfirmationPage} />
<Route path={RESET_PASSWORD_PAGE} component={ResetPasswordPage} />
<PrivateRoute exact path={HOME_PAGE} component={HomePage} />
<PrivateRoute exact path={ADS_PAGE} component={AdsPage} />
<Redirect from="*" to={NOT_FOUND_PAGE} />

+ 3
- 1
src/constants/pages.js Просмотреть файл

@@ -4,4 +4,6 @@ export const FORGOT_PASSWORD_PAGE = '/forgot-password';
export const HOME_PAGE = '/home';
export const ADS_PAGE = '/ads';
export const ERROR_PAGE = '/error-page';
export const NOT_FOUND_PAGE = '/not-found';
export const NOT_FOUND_PAGE = '/not-found';
export const FORGOT_PASSWORD_CONFIRMATION_PAGE = '/forgot-password-confirmation';
export const RESET_PASSWORD_PAGE = '/reset-password';

+ 5
- 0
src/i18n/resources/rs.js Просмотреть файл

@@ -15,6 +15,7 @@ export default {
// continue: 'Continue',
labelUsername: 'Korisničko ime',
labelPassword: 'Šifra',
labelConfirmPassword: 'Ponovljena šifra',
or: 'ili',
// next: 'Next',
// nextPage: 'Next page',
@@ -56,6 +57,10 @@ export default {
usernameRequired: 'Potrebno je uneti korisničko ime.',
passwordRequired: 'Potrebno je uneti šifru.',
forgotYourPassword: 'Zaboravio/la si šifru?',
forgotYourPasswordHelpText: 'Samo unesi e-mail adresu svog HR Center profila.',
forgotYourPasswordButton: 'POŠALJI',
forgotYourPasswordBackLink: 'Nazad na Login',
forgotYourPasswordConfimation: 'Proveri email adresu da bi resetovao šifru.',
// _useDifferentEmail: 'Use different email address or username',
// get useDifferentEmail() {
// return this._useDifferentEmail;

+ 66
- 0
src/pages/ForgotPasswordPage/ForgotPasswordConfirmationPageMUI.js Просмотреть файл

@@ -0,0 +1,66 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import HrLogo from "../../assets/images/hrcenter.png";
import {
Box,
Container,
Typography,
Link,
Grid,
} from '@mui/material';
import Backdrop from '../../components/MUI/BackdropComponent';
import { LOGIN_PAGE } from '../../constants/pages';
import { NavLink } from 'react-router-dom';



const ForgotPasswordConfirmationPage = () => {
const { t } = useTranslation();

return (
<Container
component="main"
maxWidth="xl"
className="c-login-container"
fullwidth="true">
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<Box
sx={{
marginTop: 2,
width: 350,
height: 684,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<img src={HrLogo} className="login-logo" />
<Typography variant="h5" sx={{ m: 2, mt: 3 }}>
{t("login.forgotYourPassword")}
</Typography>
<Typography variant="p">
{t("login.forgotYourPasswordConfimation")}
</Typography>
<Box
component="form"
sx={{ position: 'relative', mt: 1, p: 1 }}
>
<Backdrop position="absolute" isLoading={false} />
<Grid container justifyContent="center">
<Link
to={LOGIN_PAGE}
component={NavLink}
variant="body2"
underline="hover"
>
{t('login.forgotYourPasswordBackLink')}
</Link>
</Grid>
</Box>
</Box>
</Container>
);
};

export default ForgotPasswordConfirmationPage;

+ 56
- 12
src/pages/ForgotPasswordPage/ForgotPasswordPageMUI.js Просмотреть файл

@@ -1,7 +1,10 @@
import React from 'react';
import PropTypes from "prop-types";
import { useFormik } from 'formik';
import { useTranslation } from 'react-i18next';
import { useDispatch } from "react-redux";
import * as Yup from 'yup';
import HrLogo from "../../assets/images/hrcenter.png";
import i18next from 'i18next';
import {
Box,
@@ -13,8 +16,10 @@ import {
Grid,
} from '@mui/material';
import Backdrop from '../../components/MUI/BackdropComponent';
import { LOGIN_PAGE } from '../../constants/pages';
import { FORGOT_PASSWORD_CONFIRMATION_PAGE, LOGIN_PAGE } from '../../constants/pages';
import { NavLink } from 'react-router-dom';
import { forgetPassword } from '../../store/actions/login/loginActions';


const forgotPasswordValidationSchema = Yup.object().shape({
email: Yup.string()
@@ -22,11 +27,27 @@ const forgotPasswordValidationSchema = Yup.object().shape({
.email(i18next.t('forgotPassword.emailFormat')),
});

const ForgotPasswordPage = () => {
const ForgotPasswordPage = ({ history }) => {
const dispatch = useDispatch();
const { t } = useTranslation();

const handleSubmit = (values) => {
console.log('Values', values);
const email = values.email;
dispatch(
forgetPassword({
email,
handleApiResponseSuccess,
})
);
};

const handleApiResponseSuccess = () => {
history.push({
pathname: FORGOT_PASSWORD_CONFIRMATION_PAGE,
state: {
from: history.location.pathname,
},
});
};

const formik = useFormik({
@@ -40,17 +61,29 @@ const ForgotPasswordPage = () => {
});

return (
<Container component="main" maxWidth="md">
<Container
component="main"
maxWidth="xl"
className="c-login-container"
fullwidth="true">
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<Box
sx={{
marginTop: 32,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: 2,
width: 350,
height: 684,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Typography component="h1" variant="h5">
{t('forgotPassword.title')}
<img src={HrLogo} className="login-logo" />
<Typography variant="h5" sx={{ m: 2, mt: 3 }}>
{t("login.forgotYourPassword")}
</Typography>
<Typography variant="p">
{t("login.forgotYourPasswordHelpText")}
</Typography>
<Box
component="form"
@@ -74,8 +107,9 @@ const ForgotPasswordPage = () => {
variant="contained"
sx={{ mt: 3, mb: 2 }}
fullWidth
className="c-btn c-btn--primary"
>
{t('forgotPassword.label')}
{t('login.forgotYourPasswordButton')}
</Button>
<Grid container justifyContent="center">
<Link
@@ -84,7 +118,7 @@ const ForgotPasswordPage = () => {
variant="body2"
underline="hover"
>
{t('common.back')}
{t('login.forgotYourPasswordBackLink')}
</Link>
</Grid>
</Box>
@@ -93,4 +127,14 @@ const ForgotPasswordPage = () => {
);
};

ForgotPasswordPage.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
}

export default ForgotPasswordPage;

+ 210
- 0
src/pages/ForgotPasswordPage/ResetPasswordPageMUI.js Просмотреть файл

@@ -0,0 +1,210 @@
import React, { useState } from 'react';
import PropTypes from "prop-types";
import { useFormik } from 'formik';
import { useTranslation } from 'react-i18next';
import { useDispatch } from "react-redux";
// import * as Yup from 'yup';
// import i18next from 'i18next';
import HrLogo from "../../assets/images/hrcenter.png";
import {
Box,
Container,
Typography,
Button,
InputAdornment,
IconButton,
TextField,
Link,
Grid,
} from '@mui/material';
import { Visibility, VisibilityOff } from "@mui/icons-material";
import Backdrop from '../../components/MUI/BackdropComponent';
import { LOGIN_PAGE } from '../../constants/pages';
import { NavLink } from 'react-router-dom';
import { resetPassword } from '../../store/actions/login/loginActions';

// const resetPasswordValidationSchema = Yup.object().shape({
// email: Yup.string()
// .required(i18next.t('forgotPassword.emailRequired'))
// .email(i18next.t('forgotPassword.emailFormat')),
// });
function getQueryVariable(variable) {
var query = window.location.search.substring(1);
// console.log(query)//"app=article&act=news_content&aid=160990"
var vars = query.split("&");
// console.log(vars) //[ 'app=article', 'act=news_content', 'aid=160990' ]
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split("=");
// console.log(pair)//[ 'app', 'article' ][ 'act', 'news_content' ][ 'aid', '160990' ]
if (pair[0] == variable) { return pair[1]; }
}
return (false);
}
const ResetPasswordPage = ({ history }) => {
const dispatch = useDispatch();
const { t } = useTranslation();

const [showPassword, setShowPassword] = useState(false);
const [showConfirmPassword, setShowConfirmPassword] = useState(false);
const handleClickShowPassword = () => setShowPassword(!showPassword);
const handleMouseDownPassword = () => setShowPassword(!showPassword);

const handleClickShowConfirmPassword = () => setShowConfirmPassword(!showConfirmPassword);
const handleMouseDownConfirmPassword = () => setShowConfirmPassword(!showConfirmPassword);


const handleSubmit = (values) => {
const password = values.password;
const confirmPassword = values.confirmPassword;
console.log(password);
console.log(confirmPassword);
if (password === confirmPassword)
{
const code = getQueryVariable('token'),
email = getQueryVariable('email');
dispatch(
resetPassword({
code,
email,
password,
handleApiResponseSuccess,
})
);
}
};

const handleApiResponseSuccess = () => {
history.push({
pathname: LOGIN_PAGE,
state: {
from: history.location.pathname,
},
});
};

const formik = useFormik({
initialValues: {
password: '',
confirmPassword: '',
},
// validationSchema: resetPasswordValidationSchema,
onSubmit: handleSubmit,
validateOnBlur: true,
enableReinitialize: true,
});

return (
<Container
component="main"
maxWidth="xl"
className="c-login-container"
fullwidth="true">
<div className="l-t-rectangle"></div>
<div className="r-b-rectangle"></div>
<Box
sx={{
marginTop: 2,
width: 350,
height: 684,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<img src={HrLogo} className="login-logo" />
<Typography variant="h5" sx={{ m: 2, mt: 3 }}>
{t("login.forgotYourPassword")}
</Typography>
<Typography variant="p">
{t("login.forgotYourPasswordHelpText")}
</Typography>
<Box
component="form"
onSubmit={formik.handleSubmit}
sx={{ position: 'relative', mt: 1, p: 1 }}
>
<Backdrop position="absolute" isLoading={false} />
<TextField
className="rounded-input"
name="password"
label={t("common.labelPassword")}
margin="normal"
type={showPassword ? "text" : "password"}
value={formik.values.password}
onChange={formik.handleChange}
error={formik.touched.password && Boolean(formik.errors.password)}
helperText={formik.touched.password && formik.errors.password}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
>
{showPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
),
}}
/>
<TextField
className="rounded-input"
name="confirmPassword"
label={t("common.labelConfirmPassword")}
margin="normal"
type={showConfirmPassword ? "text" : "password"}
value={formik.values.confirmPassword}
onChange={formik.handleChange}
error={formik.touched.confirmPassword && Boolean(formik.errors.confirmPassword)}
helperText={formik.touched.confirmPassword && formik.errors.confirmPassword}
fullWidth
InputProps={{
endAdornment: (
<InputAdornment position="end">
<IconButton
onClick={handleClickShowConfirmPassword}
onMouseDown={handleMouseDownConfirmPassword}
>
{showConfirmPassword ? <Visibility /> : <VisibilityOff />}
</IconButton>
</InputAdornment>
),
}}
/>
<Button
type="submit"
variant="contained"
sx={{ mt: 3, mb: 2 }}
fullWidth
className="c-btn c-btn--primary"
>
{t('login.forgotYourPasswordButton')}
</Button>
<Grid container justifyContent="center">
<Link
to={LOGIN_PAGE}
component={NavLink}
variant="body2"
underline="hover"
>
{t('login.forgotYourPasswordBackLink')}
</Link>
</Grid>
</Box>
</Box>
</Container>
);
};

ResetPasswordPage.propTypes = {
history: PropTypes.shape({
replace: PropTypes.func,
push: PropTypes.func,
location: PropTypes.shape({
pathname: PropTypes.string,
}),
}),
}

export default ResetPasswordPage;

+ 2
- 0
src/request/apiEndpoints.js Просмотреть файл

@@ -6,5 +6,7 @@ export default {
googleLogin: base + "/users/authenticateGoogle",
refreshToken: base + "/users/refresh",
logout: base + "/users/logout?userId={userId}",
forgetPassword: base + "/users/ForgotPassword",
resetPassword: base + "/users/RessetPassword",
},
};

+ 6
- 0
src/request/loginRequest.js Просмотреть файл

@@ -9,6 +9,12 @@ export const getUsernames = (emailorusername) =>
export const attemptLogin = (payload) =>
postRequest(apiEndpoints.authentications.login, payload);

export const forgetPasswordEmailSend = (payload) =>
getRequest(apiEndpoints.authentications.forgetPassword, payload)

export const sendResetPassword = (payload) =>
postRequest(apiEndpoints.authentications.resetPassword, payload);

export const attemptGoogleLogin = (payload) =>
postRequest(apiEndpoints.authentications.googleLogin, payload);


+ 10
- 0
src/store/actions/login/loginActionConstants.js Просмотреть файл

@@ -37,3 +37,13 @@ const GENERATE_TOKEN_SCOPE = 'GENERATE_TOKEN';
export const GENERATE_TOKEN = createSubmitType(GENERATE_TOKEN_SCOPE);
export const GENERATE_TOKEN_SUCCESS = createSuccessType(GENERATE_TOKEN_SCOPE);
export const GENERATE_TOKEN_ERROR = createErrorType(GENERATE_TOKEN_SCOPE);

const FORGOT_PASSWORD_SCOPE = 'FORGOT_PASSWORD';
export const FORGOT_PASSWORD = createSubmitType(FORGOT_PASSWORD_SCOPE);
export const FORGOT_PASSWORD_SUCCESS = createSuccessType(FORGOT_PASSWORD_SCOPE);
export const FORGOT_PASSWORD_ERROR = createErrorType(FORGOT_PASSWORD_SCOPE);

const RESET_PASSWORD_SCOPE = 'RESET_PASSWORD';
export const RESET_PASSWORD = createSubmitType(RESET_PASSWORD_SCOPE);
export const RESET_PASSWORD_SUCCESS = createSuccessType(RESET_PASSWORD_SCOPE);
export const RESET_PASSWORD_ERROR = createErrorType(RESET_PASSWORD_SCOPE);

+ 39
- 0
src/store/actions/login/loginActions.js Просмотреть файл

@@ -14,6 +14,12 @@ import {
GENERATE_TOKEN,
GENERATE_TOKEN_SUCCESS,
GENERATE_TOKEN_ERROR,
FORGOT_PASSWORD,
FORGOT_PASSWORD_SUCCESS,
FORGOT_PASSWORD_ERROR,
RESET_PASSWORD,
RESET_PASSWORD_SUCCESS,
RESET_PASSWORD_ERROR,
} from './loginActionConstants';


@@ -87,3 +93,36 @@ export const generateTokenError = (payload) => ({
type: GENERATE_TOKEN_ERROR,
payload,
});


export const forgetPassword = (payload) => ({
type: FORGOT_PASSWORD,
payload,
});

export const forgetPasswordSuccess = (payload) => ({
type: FORGOT_PASSWORD_SUCCESS,
payload,
});

export const forgetPasswordError = (payload) => ({
type: FORGOT_PASSWORD_ERROR,
payload,
});

export const resetPassword = (payload) => ({
type: RESET_PASSWORD,
payload,
});

export const resetPasswordSuccess = (payload) => ({
type: RESET_PASSWORD_SUCCESS,
payload,
});

export const resetPasswordError = (payload) => ({
type: RESET_PASSWORD_ERROR,
payload,
});



+ 46
- 0
src/store/saga/loginSaga.js Просмотреть файл

@@ -8,6 +8,8 @@ import {
LOGOUT_USER,
REFRESH_TOKEN,
GENERATE_TOKEN,
FORGOT_PASSWORD,
RESET_PASSWORD
} from '../actions/login/loginActionConstants';
import {
attemptGoogleLogin,
@@ -15,10 +17,13 @@ import {
logoutUserRequest,
refreshTokenRequest,
generateTokenRequest,
forgetPasswordEmailSend,
sendResetPassword
} from '../../request/loginRequest';
import {
fetchUserError,
fetchUserSuccess,
forgetPasswordSuccess,
updateUserToken,
} from '../actions/login/loginActions';
import { LOGIN_PAGE,BASE_PAGE } from '../../constants/pages';
@@ -67,6 +72,45 @@ function* fetchUser({ payload }) {
}
}
}

function* forgetPassword({ payload }) {
try {
const { data } = yield call(forgetPasswordEmailSend, payload);
yield put(forgetPasswordSuccess(data));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (e) {
if (e.response && e.response.data) {
if (payload.handleApiResponseFailed) {
yield call(payload.handleApiResponseFailed);
}
const errorMessage = yield call(rejectErrorCodeHelper, e);
yield put(fetchUserError(errorMessage));
}
}
}

function* resetPassword({ payload }) {
try {
console.log(payload)
const { data } = yield call(sendResetPassword, payload);
console.log(data);
yield put(forgetPasswordSuccess(data));
if (payload.handleApiResponseSuccess) {
yield call(payload.handleApiResponseSuccess);
}
} catch (e) {
if (e.response && e.response.data) {
if (payload.handleApiResponseFailed) {
yield call(payload.handleApiResponseFailed);
}
const errorMessage = yield call(rejectErrorCodeHelper, e);
yield put(fetchUserError(errorMessage));
}
}
}

function* fetchGoogleUser({ payload }) {
try {
const { data } = yield call(attemptGoogleLogin, payload);
@@ -190,6 +234,8 @@ export function* generateToken({ payload }) {
export default function* loginSaga() {
yield all([
takeLatest(LOGIN_USER_FETCH, fetchUser),
takeLatest(FORGOT_PASSWORD, forgetPassword),
takeLatest(RESET_PASSWORD, resetPassword),
takeLatest(LOGIN_GOOGLE_USER_FETCH, fetchGoogleUser),
takeLatest(AUTHENTICATE_USER, authenticateUser),
takeLatest(LOGOUT_USER, logoutUser),

Загрузка…
Отмена
Сохранить