password reset first phase done

This commit was merged in pull request #378.
This commit is contained in:
victorAnumudu
2023-08-05 05:41:46 +01:00
parent 8d3dca96b4
commit cab5ed0ece
5 changed files with 253 additions and 149 deletions
@@ -0,0 +1,35 @@
import React from 'react'
const ForgetPwdResponse = ({title, message, type}) => {
return (
<>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className={`${type ? 'text-black' : 'text-red-500'}text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]`}>
{title}
</h1>
</div>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
{/* <img src='' /> */}
<p className='rounded-full border w-[50px] h-[50px] flex items-center justify-center'>Icon</p>
</div>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<p className={`${type ? 'text-sky-blue' : 'text-red-500'} font-semibold dark:text-white mb-3 leading-[27.3px] text-[18px]`}>
{message}
</p>
</div>
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-2">
<button
type="button"
onClick={() => navigate("/login")}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
<span>Home</span>
</button>
</div>
</div>
</>
)
}
export default ForgetPwdResponse
+121 -95
View File
@@ -4,6 +4,8 @@ import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService"; import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom"; import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout"; import AuthLayout from "../AuthLayout";
import EmailValidator from "../../../lib/EmailValidator";
import ForgetPwdResponse from "../ForgetPwdResponse";
export default function ForgotPassword() { export default function ForgotPassword() {
const [checked, setValue] = useState(false); const [checked, setValue] = useState(false);
@@ -11,7 +13,7 @@ export default function ForgotPassword() {
// email // email
const [email, setMail] = useState(""); const [email, setMail] = useState("");
const [msgError, setMsgError] = useState(""); const [msgError, setMsgError] = useState("");
const [msgSuccess, setMsgSuccess] = useState(false); const [msgSuccess, setMsgSuccess] = useState(null);
const navigate = useNavigate(); const navigate = useNavigate();
const userApi = new usersService(); const userApi = new usersService();
@@ -27,8 +29,22 @@ export default function ForgotPassword() {
const resetHandler = async () => { const resetHandler = async () => {
if (email == "") { if (email == "") {
setMsgError("An email is required"); setMsgError("An email is required");
} else if (!checked) { return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if (!checked) {
setMsgError("Check if you are human"); setMsgError("Check if you are human");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(!EmailValidator(email)){ // CHECKS IF EMAIL IS VALID
setMsgError("Invalid Email");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
} }
if (email !== "" && checked) { if (email !== "" && checked) {
@@ -41,8 +57,11 @@ export default function ForgotPassword() {
setMail(""); setMail("");
setValue(false); setValue(false);
setResetLoading(false); setResetLoading(false);
}else{
setMsgSuccess(false);
} }
} catch (error) { } catch (error) {
setMsgSuccess(false);
setResetLoading(false); setResetLoading(false);
setMail(""); setMail("");
setMsgError("An error occurred"); setMsgError("An error occurred");
@@ -53,9 +72,6 @@ export default function ForgotPassword() {
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT); }, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
} }
} }
setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}; };
return ( return (
@@ -73,106 +89,116 @@ export default function ForgotPassword() {
</div> </div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[500px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5"> <div className="content-wrapper login shadow-md w-full lg:max-w-[500px] mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="flex flex-col justify-center w-full h-full px-5"> <div className="flex flex-col justify-center w-full h-full px-5">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> {msgSuccess == null ?
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]"> <>
Forget Password <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
</h1> <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]"> Forget Password
Enter your email to reset your password. </h1>
</span> <span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
</div> Enter your email to reset your password.
<div className="input-area"> </span>
<div className="input-item mb-10">
<InputCom
fieldClass="px-6"
placeholder="Your Username/Email"
label="Email"
name="email"
type="email"
value={email}
inputHandler={handleEmail}
iconName="message"
/>
</div> </div>
{/* hCaptha clone for the time being */} <div className="input-area">
<div className="mb-10"> <div className="input-item mb-10">
<div className="w-[303px] h-[78px] mx-auto overflow-hidden"> <InputCom
<div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer"> fieldClass="px-6"
{/* Checkbox */} placeholder="Your Username/Email"
<div className="h-full relative inline-block"> label="Email"
<div className="relative table top-0 h-full"> name="email"
<div className="table-cell align-middle"> type="email"
<div className="relative w-[30px] h-[30px] mx-[15px]"> value={email}
<input inputHandler={handleEmail}
type="checkbox" iconName="message"
name="human-checkbox" />
id="human-checkbox" </div>
className="w-[28px] h-[28px] border-[1px] rounded border-gray-400 checked:bg-white" {/* hCaptha clone for the time being */}
checked={checked} <div className="mb-10">
onChange={humanChecker} <div className="w-[303px] h-[78px] mx-auto overflow-hidden">
/> <div className="w-[300px] h-[74px] bg-white bottom-[1px] rounded border-gray-100 overflow-hidden cursor-pointer">
{/* Checkbox */}
<div className="h-full relative inline-block">
<div className="relative table top-0 h-full">
<div className="table-cell align-middle">
<div className="relative w-[30px] h-[30px] mx-[15px]">
<input
type="checkbox"
name="human-checkbox"
id="human-checkbox"
className="w-[28px] h-[28px] border-[1px] rounded border-gray-400 checked:bg-white"
checked={checked}
onChange={humanChecker}
/>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> <div className="h-full relative inline-block w-[170px]">
<div className="h-full relative inline-block w-[170px]"> <label className="relative table top-0 h-full">
<label className="relative table top-0 h-full"> <label className="table-cell align-middle">
<label className="table-cell align-middle"> <label
<label className="text-800 text-sm"
className="text-800 text-sm" htmlFor="human-checkbox"
htmlFor="human-checkbox" >
> I am human
I am human </label>
</label> </label>
</label> </label>
</label> </div>
<div className="h-full relative inline-block w-16"></div>
</div> </div>
<div className="h-full relative inline-block w-16"></div> </div>
</div>
{msgError && (
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
{msgError}
</div>
)}
{/* {msgSuccess && (
<div className="relative p-4 text-[#44228c] bg-[#e3d7fb] border-[#d5c4f9] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]">
If we find your email, you will receive a link to reset your
password. Please use or{" "}
<Link
to="/contact"
className="text-[#4687ba] hover:text-[#009ef7]"
>
contact form
</Link>{" "}
if you did not get our message after few minutes.
</div>
)} */}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-2">
<button
type="button"
onClick={resetHandler}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Send Code</span>
)}
</button>
<button
type="button"
onClick={() => navigate("/login")}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
</div> </div>
</div> </div>
</div> </div>
{msgError && ( </>
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]"> :
{msgError} <ForgetPwdResponse
</div> title={msgSuccess? 'Password Reset Complete' : 'Password Reset Error'}
)} message={msgSuccess? `Check your email for the link to continue password reset. Note the reset link will expire short time` : 'We are unable to continue with your request. Please try another username or contact us for help'}
{msgSuccess && ( type={msgSuccess}
<div className="relative p-4 text-[#44228c] bg-[#e3d7fb] border-[#d5c4f9] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]"> />
If we find your email, you will receive a link to reset your }
password. Please use or{" "}
<Link
to="/contact"
className="text-[#4687ba] hover:text-[#009ef7]"
>
contact form
</Link>{" "}
if you did not get our message after few minutes.
</div>
)}
<div className="signin-area mb-3.5">
<div className="flex justify-center items-center gap-2">
<button
type="button"
onClick={resetHandler}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-white flex justify-center bg-[#4687ba] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.81rem]`}
>
{resetLoading ? (
<div className="signup btn-loader"></div>
) : (
<span>Send Code</span>
)}
</button>
<button
type="button"
onClick={() => navigate("/login")}
className={`rounded-[0.475rem] mb-6 text-[15px] font-semibold text-[#009ef7] hover:text-white flex justify-center bg-[#f1faff] hover:bg-[#009ef7] transition-all duration-300 items-center py-[0.8875rem] px-[1.8125rem] `}
>
Cancel
</button>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -1,16 +1,18 @@
import { useState } from "react"; import { useEffect, useState } from "react";
import { Link, useLocation, useNavigate } from "react-router-dom"; import { Link, useLocation, useNavigate } from "react-router-dom";
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png"; import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
import usersService from "../../../services/UsersService"; import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom"; import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout"; import AuthLayout from "../AuthLayout";
import ForgetPwdResponse from "../ForgetPwdResponse";
import PasswordValidator from "../../../lib/PasswordValidator";
const VerifyPassword = () => { const VerifyPassword = () => {
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [confirmPassword, setConfirmPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState("");
const [msgError, setMsgError] = useState(""); const [msgError, setMsgError] = useState("");
const [linkLoader, setLinkLoader] = useState(false); const [linkLoader, setLinkLoader] = useState(false);
const [linkSuccess, setLinkSuccess] = useState(true); const [linkSuccess, setLinkSuccess] = useState(null);
const [showPassword, setShowPassword] = useState(false); const [showPassword, setShowPassword] = useState(false);
const navigate = useNavigate(); const navigate = useNavigate();
const location = useLocation(); const location = useLocation();
@@ -23,11 +25,6 @@ const VerifyPassword = () => {
setShowPassword(!showPassword); setShowPassword(!showPassword);
}; };
// little checker for the validity of the token
if (token?.length != 64) {
setLinkSuccess(false);
}
// Password // Password
const handlePassword = (e) => { const handlePassword = (e) => {
let { name, value } = e?.target; let { name, value } = e?.target;
@@ -36,9 +33,31 @@ const VerifyPassword = () => {
}; };
const completeReset = async () => { const completeReset = async () => {
if(!password || !confirmPassword){ // CHECKS IF PASSWORD IS EMPTY
setMsgError("Please fill in fields");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(password != confirmPassword){ // CHECKS IF PASSWORD EQUALS CONFIRM PASSWORD
setMsgError("Passwords does not match");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(password.length < 6){ // CHECKS IF PASSWORD LENGTH IS UPTO 6 CHARACTERS
setMsgError("Password must be upto six characters");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
if(!PasswordValidator(password)){ // CHECKS IF PASSWORD IS VALID
setMsgError("Invalid Password: eg: Password1@");
return setTimeout(() => {
setMsgError(null);
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
}
try { try {
if (password !== "" && confirmPassword !== "") {
if (password === confirmPassword) {
setLinkLoader(true); setLinkLoader(true);
var reqData = { var reqData = {
sessionid: "DUMMY-CANNOT_BE_EMPTY", sessionid: "DUMMY-CANNOT_BE_EMPTY",
@@ -47,35 +66,30 @@ const VerifyPassword = () => {
step: 300, step: 300,
action: 730, action: 730,
}; };
const res = await userApi?.CompleteResetPassword(reqData); const res = await userApi?.CompleteResetPassword(reqData);
if (res.status === 200) { if (res.status === 200) {
const { data } = res; const { data } = res;
console.log('RESPONSE', res)
if (data?.status > 0 && data?.email) { if (data?.status > 0 && data?.email) {
setTimeout(() => { // setTimeout(() => {
navigate("/login", { replace: true }); // navigate("/login", { replace: true });
setLinkLoader(false); // setLinkLoader(false);
}, 2000); // }, 2000);
setLinkSuccess(true);
} else if (data && data?.status == "Invalid Request") { } else if (data && data?.status == "Invalid Request") {
setLinkLoader(false); setLinkLoader(false);
setLinkSuccess(false); setLinkSuccess(false);
} else { } else {
setLinkLoader(false); setLinkLoader(false);
setMsgError("An error occurred"); setMsgError("An error occurred");
setLinkSuccess(false);
} }
} else { } else {
setLinkLoader(false); setLinkLoader(false);
setLinkSuccess(false); setLinkSuccess(false);
} }
} else {
setLinkLoader(false);
setMsgError("Passwords does not match");
}
} else {
setMsgError("Please fill in fields");
}
} catch (error) { } catch (error) {
setLinkLoader(false); setLinkLoader(false);
setLinkSuccess(false); setLinkSuccess(false);
@@ -87,6 +101,12 @@ const VerifyPassword = () => {
} }
}; };
useEffect(()=>{
// little checker for the validity of the token
if (token==null || token?.length != 64) {
setLinkSuccess(false);
}
},[])
return ( return (
<> <>
<AuthLayout slogan="Welcome to WrenchBoard"> <AuthLayout slogan="Welcome to WrenchBoard">
@@ -102,37 +122,42 @@ const VerifyPassword = () => {
</div> </div>
<div className="content-wrapper login shadow-md w-full lg:max-w-[500px] mx-auto flex justify-center items-center dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5"> <div className="content-wrapper login shadow-md w-full lg:max-w-[500px] mx-auto flex justify-center items-center dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5">
<div className="w-full"> <div className="w-full">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> {linkSuccess == null ?
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]"> <>
{linkSuccess ? "Password Reset" : "Invalid verification link"} <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
</h1> <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
{linkSuccess && ( Password Reset
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]"> </h1>
Enter a new password to reset <span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
</span> Enter a new password to reset
)} </span>
{linkSuccess && ( <span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]"> We'll send an email to confirm reset
We'll send an email to confirm reset </span>
</span> </div>
)} <SuccessfulComponent
</div> password={password}
{/* If the verification was a success */} confirmPassword={confirmPassword}
{linkSuccess ? ( handlePassword={handlePassword}
<SuccessfulComponent onSubmit={completeReset}
password={password} msgErr={msgError}
confirmPassword={confirmPassword} loader={linkLoader}
handlePassword={handlePassword} showPassword={showPassword}
onSubmit={completeReset} onClick={togglePasswordVisibility}
msgErr={msgError} navigateHandler={() => navigate("/login")}
loader={linkLoader} />
showPassword={showPassword} </>
onClick={togglePasswordVisibility} :
navigateHandler={() => navigate("/login")} <ForgetPwdResponse
/> title={linkSuccess? 'Password Reset Complete' : (linkSuccess==false && token==null) ? 'Forget Password' : 'Password Reset Error'}
) : ( message={linkSuccess?
<ErrorComponent onClick={() => navigate("/login")} /> 'Password Reset Complete. You can login now with your new credentials' :
)} (linkSuccess==false && token==null) ? 'We are unable to continue to reset process. This error is usually due to expired links. Please start all over or contact us' :
'Password Reset Error. Please get in touch with support for further support'
}
type={linkSuccess}
/>
}
</div> </div>
</div> </div>
</div> </div>
@@ -159,7 +184,7 @@ const SuccessfulComponent = ({
<div className="mb-5"> <div className="mb-5">
<InputCom <InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl" fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={password?.replace(/./g, "●")} value={password}
inputHandler={handlePassword} inputHandler={handlePassword}
placeholder="● ● ● ● ● ●" placeholder="● ● ● ● ● ●"
label="Password" label="Password"
@@ -172,7 +197,7 @@ const SuccessfulComponent = ({
<div className="mb-5"> <div className="mb-5">
<InputCom <InputCom
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl" fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
value={confirmPassword?.replace(/./g, "●")} value={confirmPassword}
inputHandler={handlePassword} inputHandler={handlePassword}
placeholder="● ● ● ● ● ●" placeholder="● ● ● ● ● ●"
label="Confirm Password" label="Confirm Password"
+9
View File
@@ -0,0 +1,9 @@
export default function EmailValidator(email) {
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (regEx.test(email) == false) {
return false
}else{
return true
}
}
+9
View File
@@ -0,0 +1,9 @@
export default function PasswordValidator(password) {
const regEx = /^[a-zA-Z0-9!@#$%^&*()_+{}\[\]:;<>,.?~\\/\-='"`|]+$/;
if (/[!@#$%^&*()_+{}\[\]:;<>,.?~\\/\-='"`|]/.test(password) == false || /[0-9]/.test(password)==false || /[A-Z]/.test(password)==false || /[a-z]/.test(password)==false) {
return false
}else{
return true
}
}