| @@ -22,6 +22,7 @@ export const TextField = React.forwardRef((props, ref) => { | |||
| > | |||
| <TextFieldStyled | |||
| inputRef={ref} | |||
| inputProps={props.inputProps} | |||
| placeholder={props.placeholder} | |||
| width={props.width} | |||
| height={props.height} | |||
| @@ -91,6 +92,7 @@ TextField.propTypes = { | |||
| onFocus: PropTypes.func, | |||
| onBlur: PropTypes.func, | |||
| focused: PropTypes.bool, | |||
| inputProps: PropTypes.any, | |||
| InputProps: PropTypes.shape({ | |||
| startAdornment: PropTypes.node, | |||
| endAdornment: PropTypes.node, | |||
| @@ -64,6 +64,8 @@ export default { | |||
| signUp: "Registrujte se.", | |||
| usernameRequired: "Username je obavezan!", | |||
| passwordRequired: "Lozinka je obavezna!", | |||
| passwordConfirmRequired: "Potvrdite lozinku!", | |||
| passwordConfirmIncorrect: "Lozinke se ne podudaraju!", | |||
| forgotYourPassword: "Zaboravili ste lozinku?", | |||
| forgotPasswordEmail: "Email", | |||
| useDifferentEmail: "Iskoristite drugačiju lozinku.", | |||
| @@ -142,6 +144,7 @@ export default { | |||
| passwordLabel: "Nova Lozinka", | |||
| passwordConfirmLabel: "Potvrdite Lozinku", | |||
| buttonText: "Postavi lozinku", | |||
| resetSuccess: "Uspešno ste promenili lozinku!" | |||
| }, | |||
| filters: { | |||
| title: "Filteri", | |||
| @@ -30,7 +30,7 @@ const FirstPartOfRegistration = (props) => { | |||
| const formik = useFormik({ | |||
| initialValues: { | |||
| mail: props.informations?.mail ?? "", | |||
| password: props.informations?.password ?? "", | |||
| registerPassword: props.informations?.registerPassword ?? "", | |||
| }, | |||
| validationSchema: firstPartValidation, | |||
| onSubmit: props.handleSubmit, | |||
| @@ -45,9 +45,9 @@ const FirstPartOfRegistration = (props) => { | |||
| formik.setFieldValue("mail", ""); | |||
| formik.setFieldTouched("mail", true); | |||
| } | |||
| if (formik.errors.password) { | |||
| formik.setFieldValue("password", ""); | |||
| formik.setFieldTouched("password", true); | |||
| if (formik.errors.registerPassword) { | |||
| formik.setFieldValue("registerPassword", ""); | |||
| formik.setFieldTouched("registerPassword", true); | |||
| } | |||
| } else { | |||
| formik.handleSubmit(event); | |||
| @@ -79,16 +79,22 @@ const FirstPartOfRegistration = (props) => { | |||
| /> | |||
| <TextField | |||
| name="password" | |||
| name="registerPassword" | |||
| inputProps={{ autocomplete: "new-password" }} | |||
| placeholder={t("common.labelPassword")} | |||
| margin="normal" | |||
| type={showPassword ? "text" : "password"} | |||
| value={formik.values.password} | |||
| value={formik.values.registerPassword} | |||
| onChange={(value) => | |||
| formik.setFieldValue("password", value.target.value) | |||
| formik.setFieldValue("registerPassword", value.target.value) | |||
| } | |||
| error={ | |||
| formik.touched.registerPassword && | |||
| Boolean(formik.errors.registerPassword) | |||
| } | |||
| helperText={ | |||
| formik.touched.registerPassword && formik.errors.registerPassword | |||
| } | |||
| error={formik.touched.password && Boolean(formik.errors.password)} | |||
| helperText={formik.touched.password && formik.errors.password} | |||
| fullWidth | |||
| InputProps={{ | |||
| endAdornment: ( | |||
| @@ -99,10 +105,14 @@ const FirstPartOfRegistration = (props) => { | |||
| }} | |||
| /> | |||
| {console.log(formik)} | |||
| {formik.errors.mail && formik.touched.mail ? ( | |||
| <ErrorMessage>{formik.errors.mail}</ErrorMessage> | |||
| ) : formik.errors.password && formik.touched.password ? ( | |||
| <ErrorMessage>{formik.errors.password}</ErrorMessage> | |||
| ) : formik.errors.registerPassword && formik.touched.registerPassword ? ( | |||
| <ErrorMessage>{formik.errors.registerPassword}</ErrorMessage> | |||
| ) : ( | |||
| props.error && <ErrorMessage>{props.errorMessage}</ErrorMessage> | |||
| )} | |||
| @@ -116,7 +126,7 @@ const FirstPartOfRegistration = (props) => { | |||
| textcolor="white" | |||
| disabled={ | |||
| formik.values.mail.length === 0 || | |||
| formik.values.password.length === 0 || | |||
| formik.values.registerPassword.length === 0 || | |||
| formik.values.mail === props.error | |||
| } | |||
| > | |||
| @@ -0,0 +1,25 @@ | |||
| import React from "react"; | |||
| import PropTypes from "prop-types"; | |||
| import { ErrorText } from "./ErrorMessage.styled"; | |||
| const ErrorMessage = (props) => { | |||
| const formik = props.formik; | |||
| return ( | |||
| <> | |||
| {formik.errors.password?.length > 0 && formik.touched.password ? ( | |||
| <ErrorText>{formik.errors.password}</ErrorText> | |||
| ) : ( | |||
| formik.errors.passwordConfirm?.length > 0 && | |||
| formik.touched.passwordConfirm && ( | |||
| <ErrorText>{formik.errors.passwordConfirm}</ErrorText> | |||
| ) | |||
| )} | |||
| </> | |||
| ); | |||
| }; | |||
| ErrorMessage.propTypes = { | |||
| formik: PropTypes.any, | |||
| }; | |||
| export default ErrorMessage; | |||
| @@ -0,0 +1,11 @@ | |||
| import { Typography } from "@mui/material"; | |||
| import styled from "styled-components"; | |||
| import selectedTheme from "../../../themes"; | |||
| export const ErrorText = styled(Typography)` | |||
| color: red; | |||
| font-family: ${selectedTheme.fonts.textFont}; | |||
| position: relative; | |||
| top: -7px; | |||
| font-size: 14px; | |||
| ` | |||
| @@ -1,7 +1,6 @@ | |||
| import React, { useEffect, useState } from "react"; | |||
| import { useFormik } from "formik"; | |||
| import { useTranslation } from "react-i18next"; | |||
| import * as Yup from "yup"; | |||
| import { ReactComponent as Logo } from "../../assets/images/svg/logo-vertical.svg"; | |||
| import { | |||
| ResetPasswordPageContainer, | |||
| @@ -23,6 +22,8 @@ import { ReactComponent as VisibilityOn } from "../../assets/images/svg/eye-stri | |||
| import { ReactComponent as VisibilityOff } from "../../assets/images/svg/eye.svg"; | |||
| import { IconButton } from "../../components/Buttons/IconButton/IconButton"; | |||
| import jwt from "jsonwebtoken"; | |||
| import resetPasswordValidation from "../../validations/resetPasswordValidation"; | |||
| import ErrorMessage from "./ErrorMessage/ErrorMessage"; | |||
| const ResetPasswordPage = () => { | |||
| const history = useHistory(); | |||
| @@ -35,7 +36,7 @@ const ResetPasswordPage = () => { | |||
| const routeMatch = useRouteMatch(); | |||
| useEffect(() => { | |||
| const tokenFromParams = routeMatch.params.token | |||
| const tokenFromParams = routeMatch.params.token; | |||
| setToken(tokenFromParams); | |||
| const data = jwt.decode(tokenFromParams); | |||
| if (!data || new Date() > new Date(data?.exp * 1000)) { | |||
| @@ -76,10 +77,7 @@ const ResetPasswordPage = () => { | |||
| password: "", | |||
| passwordConfirm: "", | |||
| }, | |||
| validationSchema: Yup.object().shape({ | |||
| password: Yup.string().required().min(8), | |||
| passwordConfirm: Yup.string().oneOf([Yup.ref("password"), null]), | |||
| }), | |||
| validationSchema: resetPasswordValidation, | |||
| onSubmit: handleSubmit, | |||
| validateOnBlur: true, | |||
| enableReinitialize: true, | |||
| @@ -142,6 +140,8 @@ const ResetPasswordPage = () => { | |||
| }} | |||
| /> | |||
| <ErrorMessage formik={formik} /> | |||
| <PrimaryButton | |||
| type="submit" | |||
| variant="contained" | |||
| @@ -1,7 +1,9 @@ | |||
| import { all, takeLatest, call, put } from "@redux-saga/core/effects"; | |||
| import i18n from "../../i18n"; | |||
| import { forgotPasswordRequest, resetPasswordRequest } from "../../request/forgotPasswordRequest"; | |||
| import { FORGOT_PASSWORD, RESET_PASSWORD } from "../actions/user/userActionConstants"; | |||
| import { forgotPasswordError, forgotPasswordSuccess } from "../actions/user/userActions"; | |||
| import { makeToastMessage } from "../utils/makeToastMessage"; | |||
| function* forgotPassword({payload}) { | |||
| try { | |||
| @@ -32,6 +34,7 @@ function* resetPassword({payload}) { | |||
| if (payload.handleResponseSuccess) { | |||
| yield call(payload.handleResponseSuccess); | |||
| } | |||
| makeToastMessage(i18n.t("resetPassword.resetSuccess")) | |||
| } | |||
| } | |||
| catch(e) { | |||
| @@ -2,7 +2,9 @@ import * as Yup from "yup"; | |||
| import i18n from "../i18n"; | |||
| export default Yup.object().shape({ | |||
| email: Yup.string().email(i18n.t("login.emailFormat")).required(i18n.t("login.emailRequired")), | |||
| email: Yup.string() | |||
| .email(i18n.t("login.emailFormat")) | |||
| .required(i18n.t("login.emailRequired")), | |||
| password: Yup.string() | |||
| .required(i18n.t("login.passwordRequired")) | |||
| .min(8, i18n.t("login.passwordLength")), | |||
| @@ -6,7 +6,7 @@ export default Yup.object().shape({ | |||
| mail: Yup.string() | |||
| .email(i18n.t("forgotPassword.emailFormat")) | |||
| .required(i18n.t("login.usernameRequired")), | |||
| password: Yup.string() | |||
| registerPassword: Yup.string() | |||
| .required(i18n.t("login.passwordRequired")) | |||
| .min(8, i18n.t("login.passwordLength")) | |||
| .minLowercase(1, i18n.t("password.strongPassword")) | |||
| @@ -1,6 +1,20 @@ | |||
| import * as Yup from "yup"; | |||
| import YupPassword from "yup-password"; | |||
| import i18n from "../i18n"; | |||
| YupPassword(Yup); | |||
| export default Yup.object().shape({ | |||
| password: Yup.string().required().min(8), | |||
| passwordConfirm: Yup.string().oneOf([Yup.ref("password"), null]), | |||
| password: Yup.string() | |||
| .required(i18n.t("login.passwordRequired")) | |||
| .min(8, i18n.t("login.passwordLength")) | |||
| .minLowercase(1, i18n.t("password.strongPassword")) | |||
| .minUppercase(1, i18n.t("password.strongPassword")) | |||
| .minSymbols(1, i18n.t("password.strongPassword")) | |||
| .minNumbers(1, i18n.t("password.strongPassword")), | |||
| passwordConfirm: Yup.string() | |||
| .oneOf( | |||
| [Yup.ref("password"), null], | |||
| i18n.t("login.passwordConfirmIncorrect") | |||
| ) | |||
| .required(i18n.t("login.passwordConfirmRequired")), | |||
| }); | |||