Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6a34ca6c18 | |||
| fc09771b8d | |||
| cab5ed0ece | |||
| 8d3dca96b4 | |||
| e71a25630d | |||
| cf79a15837 | |||
| 042e8c2c17 | |||
| 46d286e8f3 | |||
| c2bfcc81ea | |||
| 654ed3741c | |||
| 6cccd1c372 | |||
| 1e6be76449 | |||
| 476ca04f5f | |||
| ce41db474d | |||
| 7ace7e5b5d | |||
| 51e829f7b4 | |||
| 173b2adc66 | |||
| 31cb7a521d | |||
| ad4f68b252 | |||
| 10abf35d4f | |||
| ce767086fd | |||
| 0f44616767 | |||
| bce00b5c0e | |||
| 312cd54f87 | |||
| a0c3437eae | |||
| 711226f913 | |||
| f46c6232b0 | |||
| 172f0ccbce | |||
| a2047cc2de | |||
| 6ea52e6481 | |||
| a7bbcfdc1b | |||
| ff28be3e70 | |||
| e91b4a4424 | |||
| 9960033b72 | |||
| ef135f1a9b | |||
| 1df6380c4a | |||
| fc8cf551e5 | |||
| b1feb0438a | |||
| a89649f774 |
Binary file not shown.
|
After Width: | Height: | Size: 22 KiB |
@@ -0,0 +1,37 @@
|
||||
import React from 'react'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
|
||||
const ForgetPwdResponse = ({title, message, type}) => {
|
||||
const navigate = useNavigate()
|
||||
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
|
||||
@@ -4,6 +4,8 @@ import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
|
||||
import usersService from "../../../services/UsersService";
|
||||
import InputCom from "../../Helpers/Inputs/InputCom";
|
||||
import AuthLayout from "../AuthLayout";
|
||||
import EmailValidator from "../../../lib/EmailValidator";
|
||||
import ForgetPwdResponse from "../ForgetPwdResponse";
|
||||
|
||||
export default function ForgotPassword() {
|
||||
const [checked, setValue] = useState(false);
|
||||
@@ -11,7 +13,7 @@ export default function ForgotPassword() {
|
||||
// email
|
||||
const [email, setMail] = useState("");
|
||||
const [msgError, setMsgError] = useState("");
|
||||
const [msgSuccess, setMsgSuccess] = useState(false);
|
||||
const [msgSuccess, setMsgSuccess] = useState(null);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const userApi = new usersService();
|
||||
@@ -27,8 +29,22 @@ export default function ForgotPassword() {
|
||||
const resetHandler = async () => {
|
||||
if (email == "") {
|
||||
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");
|
||||
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) {
|
||||
@@ -41,8 +57,11 @@ export default function ForgotPassword() {
|
||||
setMail("");
|
||||
setValue(false);
|
||||
setResetLoading(false);
|
||||
}else{
|
||||
setMsgSuccess(false);
|
||||
}
|
||||
} catch (error) {
|
||||
setMsgSuccess(false);
|
||||
setResetLoading(false);
|
||||
setMail("");
|
||||
setMsgError("An error occurred");
|
||||
@@ -53,9 +72,6 @@ export default function ForgotPassword() {
|
||||
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
setMsgError(null);
|
||||
}, process.env.REACT_APP_RESET_START_ERROR_TIMEOUT);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -73,106 +89,102 @@ export default function ForgotPassword() {
|
||||
</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="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">
|
||||
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
|
||||
Forget Password
|
||||
</h1>
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
Enter your email to reset your password.
|
||||
</span>
|
||||
</div>
|
||||
<div className="input-area">
|
||||
<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"
|
||||
/>
|
||||
{msgSuccess == null ?
|
||||
<>
|
||||
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
|
||||
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
|
||||
Forget Password
|
||||
</h1>
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
Enter your email to reset your password.
|
||||
</span>
|
||||
</div>
|
||||
{/* hCaptha clone for the time being */}
|
||||
<div className="mb-10">
|
||||
<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 className="input-area">
|
||||
<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>
|
||||
{/* hCaptha clone for the time being */}
|
||||
<div className="mb-10">
|
||||
<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 className="h-full relative inline-block w-[170px]">
|
||||
<label className="relative table top-0 h-full">
|
||||
<label className="table-cell align-middle">
|
||||
<label
|
||||
className="text-800 text-sm"
|
||||
htmlFor="human-checkbox"
|
||||
>
|
||||
I am human
|
||||
<div className="h-full relative inline-block w-[170px]">
|
||||
<label className="relative table top-0 h-full">
|
||||
<label className="table-cell align-middle">
|
||||
<label
|
||||
className="text-800 text-sm"
|
||||
htmlFor="human-checkbox"
|
||||
>
|
||||
I am human
|
||||
</label>
|
||||
</label>
|
||||
</label>
|
||||
</label>
|
||||
</div>
|
||||
<div className="h-full relative inline-block w-16"></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>
|
||||
)}
|
||||
<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>
|
||||
{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>
|
||||
</>
|
||||
:
|
||||
<ForgetPwdResponse
|
||||
title={'Forget Password'}
|
||||
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'}
|
||||
type={msgSuccess}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Link, useLocation, useNavigate } from "react-router-dom";
|
||||
import WrenchBoard from "../../../assets/images/wrenchboard-logo-text.png";
|
||||
import usersService from "../../../services/UsersService";
|
||||
import InputCom from "../../Helpers/Inputs/InputCom";
|
||||
import AuthLayout from "../AuthLayout";
|
||||
import ForgetPwdResponse from "../ForgetPwdResponse";
|
||||
import PasswordValidator from "../../../lib/PasswordValidator";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
|
||||
const VerifyPassword = () => {
|
||||
const [password, setPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
const [requestStatus, setRequestStatus] = useState({loading: true, status:false, data: []})
|
||||
const [msgError, setMsgError] = useState("");
|
||||
const [linkLoader, setLinkLoader] = useState(false);
|
||||
const [linkSuccess, setLinkSuccess] = useState(true);
|
||||
const [linkSuccess, setLinkSuccess] = useState(null);
|
||||
const [showPassword, setShowPassword] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
@@ -23,11 +27,6 @@ const VerifyPassword = () => {
|
||||
setShowPassword(!showPassword);
|
||||
};
|
||||
|
||||
// little checker for the validity of the token
|
||||
if (token?.length != 64) {
|
||||
setLinkSuccess(false);
|
||||
}
|
||||
|
||||
// Password
|
||||
const handlePassword = (e) => {
|
||||
let { name, value } = e?.target;
|
||||
@@ -36,46 +35,60 @@ const VerifyPassword = () => {
|
||||
};
|
||||
|
||||
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 {
|
||||
if (password !== "" && confirmPassword !== "") {
|
||||
if (password === confirmPassword) {
|
||||
setLinkLoader(true);
|
||||
var reqData = {
|
||||
sessionid: "DUMMY-CANNOT_BE_EMPTY",
|
||||
reset_link: token,
|
||||
newpass: password,
|
||||
m_uid: requestStatus.data?.m_uid || '',
|
||||
reset_uid: requestStatus.data?.reset_uid || '',
|
||||
step: 300,
|
||||
action: 730,
|
||||
};
|
||||
|
||||
const res = await userApi?.CompleteResetPassword(reqData);
|
||||
|
||||
if (res.status === 200) {
|
||||
if (res.status == 200) {
|
||||
const { data } = res;
|
||||
|
||||
if (data?.status > 0 && data?.email) {
|
||||
setTimeout(() => {
|
||||
navigate("/login", { replace: true });
|
||||
setLinkLoader(false);
|
||||
}, 2000);
|
||||
} else if (data && data?.status == "Invalid Request") {
|
||||
setLinkLoader(false);
|
||||
setLinkSuccess(false);
|
||||
if (data?.internal_return >= 0) {
|
||||
// setTimeout(() => {
|
||||
// navigate("/login", { replace: true });
|
||||
// setLinkLoader(false);
|
||||
// }, 2000);
|
||||
setLinkSuccess(true);
|
||||
} else {
|
||||
setLinkLoader(false);
|
||||
setMsgError("An error occurred");
|
||||
setLinkSuccess(false);
|
||||
}
|
||||
} else {
|
||||
setLinkLoader(false);
|
||||
setLinkSuccess(false);
|
||||
}
|
||||
} else {
|
||||
setLinkLoader(false);
|
||||
setMsgError("Passwords does not match");
|
||||
}
|
||||
} else {
|
||||
setMsgError("Please fill in fields");
|
||||
}
|
||||
} catch (error) {
|
||||
setLinkLoader(false);
|
||||
setLinkSuccess(false);
|
||||
@@ -87,6 +100,31 @@ const VerifyPassword = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const verifyResetPwd = () => { // FUNCTION TO VERIFY RESET PASSWORD LINK
|
||||
setRequestStatus({loading: true, status:false, data: []})
|
||||
var reqData = {
|
||||
sessionid: "DUMMY-CANNOT_BE_EMPTY",
|
||||
reset_link: token,
|
||||
step: 200,
|
||||
action: 730,
|
||||
};
|
||||
userApi.CompleteResetPassword(reqData).then(res => {
|
||||
if(res.status != 200 || res.data.internal_return < 0){
|
||||
return setRequestStatus({loading: false, status:false, data: []})
|
||||
}
|
||||
setRequestStatus({loading: false, status:true, data: res.data})
|
||||
}).catch(error => {
|
||||
setRequestStatus({loading: false, status:false, data: []})
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
// little checker for the validity of the token
|
||||
if (token==null || token?.length != 64) {
|
||||
return setRequestStatus({loading: false, status:false, data: []});
|
||||
}
|
||||
verifyResetPwd()
|
||||
},[])
|
||||
return (
|
||||
<>
|
||||
<AuthLayout slogan="Welcome to WrenchBoard">
|
||||
@@ -101,39 +139,54 @@ const VerifyPassword = () => {
|
||||
</Link>
|
||||
</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">
|
||||
{requestStatus.loading ?
|
||||
<LoadingSpinner color='sky-blue' size='16' height='h-300px' />
|
||||
:
|
||||
!requestStatus.loading && requestStatus.status ?
|
||||
<div className="w-full">
|
||||
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
|
||||
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
|
||||
{linkSuccess ? "Password Reset" : "Invalid verification link"}
|
||||
</h1>
|
||||
{linkSuccess && (
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
Enter a new password to reset
|
||||
</span>
|
||||
)}
|
||||
{linkSuccess && (
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
We'll send an email to confirm reset
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{/* If the verification was a success */}
|
||||
{linkSuccess ? (
|
||||
<SuccessfulComponent
|
||||
password={password}
|
||||
confirmPassword={confirmPassword}
|
||||
handlePassword={handlePassword}
|
||||
onSubmit={completeReset}
|
||||
msgErr={msgError}
|
||||
loader={linkLoader}
|
||||
showPassword={showPassword}
|
||||
onClick={togglePasswordVisibility}
|
||||
navigateHandler={() => navigate("/login")}
|
||||
/>
|
||||
) : (
|
||||
<ErrorComponent onClick={() => navigate("/login")} />
|
||||
)}
|
||||
{linkSuccess == null ?
|
||||
<>
|
||||
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
|
||||
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
|
||||
Password Reset
|
||||
</h1>
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
Enter a new password to reset
|
||||
</span>
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
We'll send an email to confirm reset
|
||||
</span>
|
||||
</div>
|
||||
<SuccessfulComponent
|
||||
password={password}
|
||||
confirmPassword={confirmPassword}
|
||||
handlePassword={handlePassword}
|
||||
onSubmit={completeReset}
|
||||
msgErr={msgError}
|
||||
loader={linkLoader}
|
||||
showPassword={showPassword}
|
||||
onClick={togglePasswordVisibility}
|
||||
navigateHandler={() => navigate("/login")}
|
||||
/>
|
||||
</>
|
||||
:
|
||||
<ForgetPwdResponse
|
||||
title={linkSuccess? 'Password Reset Complete' : 'Password Reset Error'}
|
||||
message={linkSuccess? 'Password Reset Complete. You can login now with your new credentials' : 'Password Reset Error. Please get in touch with support for further support'
|
||||
}
|
||||
type={linkSuccess}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
|
||||
<ForgetPwdResponse
|
||||
title={'Forget Password'}
|
||||
message={'We are unable to continue to reset process. This error is usually due to expired links. Please start all over or contact us'}
|
||||
type={requestStatus.status}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</AuthLayout>
|
||||
@@ -159,7 +212,7 @@ const SuccessfulComponent = ({
|
||||
<div className="mb-5">
|
||||
<InputCom
|
||||
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
|
||||
value={password?.replace(/./g, "●")}
|
||||
value={password}
|
||||
inputHandler={handlePassword}
|
||||
placeholder="● ● ● ● ● ●"
|
||||
label="Password"
|
||||
@@ -172,7 +225,7 @@ const SuccessfulComponent = ({
|
||||
<div className="mb-5">
|
||||
<InputCom
|
||||
fieldClass="sm:px-6 px-2 tracking-[0.25em] text-2xl"
|
||||
value={confirmPassword?.replace(/./g, "●")}
|
||||
value={confirmPassword}
|
||||
inputHandler={handlePassword}
|
||||
placeholder="● ● ● ● ● ●"
|
||||
label="Confirm Password"
|
||||
@@ -209,25 +262,3 @@ const SuccessfulComponent = ({
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
const ErrorComponent = ({ onClick }) => (
|
||||
<div className="input-area">
|
||||
<div className="my-5">
|
||||
<p className="text-[14px] leading-[19px] text-center text-[#181c32]">
|
||||
This error occurs because you have already used this link or the link
|
||||
has broken/expired. Start with the reset process again. If it doesn't
|
||||
work, try to create the account from the start.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="signin-area flex justify-center mb-3.5">
|
||||
<button
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
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.81rem]`}
|
||||
>
|
||||
<span>Return Home</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import PendingJobsPopout from "../jobPopout/PendingJobsPopout";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
import localImgLoad from "../../lib/localImgLoad";
|
||||
|
||||
|
||||
export default function MyPendingJobTable({ MyJobList, className }) {
|
||||
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Link } from "react-router-dom";
|
||||
import Layout from "../Partials/Layout";
|
||||
import CommonHead from "../UserHeader/CommonHead";
|
||||
import MyPendingJobTable from "./MyPendingJobTable";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
|
||||
export default function MyPendingJobs(props) {
|
||||
const [selectTab, setValue] = useState("today");
|
||||
@@ -35,7 +36,13 @@ export default function MyPendingJobs(props) {
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
<MyPendingJobTable MyJobList={props.MyJobList} />
|
||||
{props.MyJobList.loading ?
|
||||
<div className="bg-white">
|
||||
<LoadingSpinner size='16' color='sky-blue' height='min-h-[300px]' />
|
||||
</div>
|
||||
:
|
||||
<MyPendingJobTable MyJobList={props.MyJobList.data} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -261,7 +261,7 @@ function AddFundDollars(props) {
|
||||
|
||||
{/* previous tab */}
|
||||
{tab === "previous" && (
|
||||
<div className="p-4 previous-details w-full min-h-[16rem] flex flex-col">
|
||||
<div className="p-4 previous-details w-full min-h-[16.5rem] flex flex-col">
|
||||
{payListCards.loading ? (
|
||||
<LoadingSpinner size="10" color="sky-blue" />
|
||||
) : payListCards?.data?.length ? (
|
||||
@@ -313,7 +313,7 @@ function AddFundDollars(props) {
|
||||
)}
|
||||
|
||||
{tab === "new" && (
|
||||
<div className="new-details w-full max-h-[23rem]">
|
||||
<div className="new-details w-full max-h-[22rem]">
|
||||
<div className="w-full flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
@@ -527,9 +527,9 @@ function AddFundDollars(props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="md:px-8 add-fund-btn flex justify-end items-center gap-3">
|
||||
<div className="add-fund-btn flex justify-end items-center gap-2 mt-4">
|
||||
<button
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center items-center border-gradient text-base rounded-full"
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
|
||||
onClick={handleClose}
|
||||
>
|
||||
Cancel
|
||||
@@ -558,9 +558,9 @@ function AddFundDollars(props) {
|
||||
</div>
|
||||
|
||||
{tab == "previous" && (
|
||||
<div className="md:py-8 px-[38px] add-fund-btn flex justify-end items-center gap-4 py-4">
|
||||
<div className="md:py-8 add-fund-btn flex justify-end items-center gap-2 py-4">
|
||||
<button
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center items-center border-gradient text-base rounded-full"
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
|
||||
onClick={props.onClose}
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -138,12 +138,12 @@ function AddFundPop({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{countryWallet == "NG" && <div className="h-[18rem]"></div>}
|
||||
{countryWallet == "NG" && <div className="h-[19rem]"></div>}
|
||||
|
||||
{countryWallet == "NG" && (
|
||||
<div className="md:p-8 p-4 add-fund-btn flex justify-end items-center py-4 gap-4">
|
||||
<button
|
||||
className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center items-center bg-[#f5a430] text-black text-base rounded-full"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
|
||||
function CompleteConfirmCredit({ onClose, confirmCredit }) {
|
||||
const { data } = confirmCredit
|
||||
const { data } = confirmCredit;
|
||||
return (
|
||||
<div className="logout-modal-body w-full flex flex-col items-center">
|
||||
<div className="content-wrapper w-full h-[32rem]">
|
||||
@@ -17,7 +17,8 @@ function CompleteConfirmCredit({ onClose, confirmCredit }) {
|
||||
>
|
||||
{/* Success Icon for now */}
|
||||
<div className="flex items-center w-full justify-center">
|
||||
{data?.result == "Charge success" ? (
|
||||
{data?.result == "Charge success" ||
|
||||
data?.status == "successful" ? (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="100"
|
||||
@@ -55,13 +56,15 @@ function CompleteConfirmCredit({ onClose, confirmCredit }) {
|
||||
|
||||
<div className={`flex items-center`}>
|
||||
<h1 className="text-xl font-semibold text-dark-gray dark:text-white tracking-tighter my-1">
|
||||
{data?.result == "Charge success"
|
||||
{data?.result == "Charge success" ||
|
||||
data?.status == "successful"
|
||||
? "Credit was Successful!"
|
||||
: "Credit was Unsuccessful"}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{data?.internal_return >= 0 ? (
|
||||
{data?.internal_return >= 0 &&
|
||||
data?.result !== "Charge failed" ? (
|
||||
<>
|
||||
<div className="flex items-center gap-8">
|
||||
<h1 className="text-xl font-bold text-dark-gray dark:text-white tracking-tighter my-1">
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { FlutterWaveButton, closePaymentModal } from "flutterwave-react-v3";
|
||||
import React, { useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { toast } from "react-toastify";
|
||||
import debounce from "../../../hooks/debounce";
|
||||
import usersService from "../../../services/UsersService";
|
||||
import { tableReload } from "../../../store/TableReloads";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
@@ -102,7 +102,6 @@ function ConfirmAddFund({
|
||||
|
||||
const { userDetails } = useSelector((state) => state?.userDetails);
|
||||
|
||||
const navigate = useNavigate();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const [requestStatus, setRequestStatus] = useState({
|
||||
@@ -112,7 +111,7 @@ function ConfirmAddFund({
|
||||
});
|
||||
|
||||
const config = {
|
||||
public_key: process.env.REACT_APP_FLUTTERWAVE_APIKEY,
|
||||
public_key: __confirmData?.flutterwave_key,
|
||||
tx_ref: __confirmData?.credit_reference,
|
||||
currency: "NGN",
|
||||
amount: Number(__confirmData.amount),
|
||||
@@ -133,10 +132,19 @@ function ConfirmAddFund({
|
||||
...config,
|
||||
text: "Proceed",
|
||||
callback: (response) => {
|
||||
onSuccessPayment();
|
||||
debouncedSuccessPayment(); //delays the call for 5 secs
|
||||
|
||||
setTimeout(() => {
|
||||
closePaymentModal();
|
||||
}, 2000);
|
||||
setConfirmCredit((prev) => ({
|
||||
...prev,
|
||||
show: {
|
||||
awaitConfirm: { loader: false, state: false },
|
||||
acceptConfirm: { loader: false, state: true },
|
||||
},
|
||||
data: response,
|
||||
}));
|
||||
}, 1500);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -153,28 +161,9 @@ function ConfirmAddFund({
|
||||
loading: false,
|
||||
status: false,
|
||||
});
|
||||
toast.success("Opps! something went wrong");
|
||||
} else {
|
||||
setRequestStatus({
|
||||
message: "Topup successful",
|
||||
loading: false,
|
||||
status: true,
|
||||
});
|
||||
|
||||
setConfirmCredit((prev) => ({
|
||||
...prev,
|
||||
show: {
|
||||
awaitConfirm: { loader: false, state: false },
|
||||
acceptConfirm: { loader: false, state: true },
|
||||
},
|
||||
data: res,
|
||||
}));
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
navigate("/my-wallet", { replace: true });
|
||||
// setTimeout(() => {
|
||||
// navigate("/my-wallet", { replace: true });
|
||||
// }, 1000);
|
||||
}
|
||||
|
||||
return dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
})
|
||||
.catch((err) => {
|
||||
setRequestStatus({
|
||||
@@ -182,10 +171,23 @@ function ConfirmAddFund({
|
||||
loading: false,
|
||||
status: false,
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
setConfirmCredit((prev) => ({
|
||||
...prev,
|
||||
show: {
|
||||
awaitConfirm: { loader: false, state: false },
|
||||
acceptConfirm: { loader: false, state: true },
|
||||
},
|
||||
data: err,
|
||||
}));
|
||||
}, 1500);
|
||||
toast.success("Opps! something went wrong");
|
||||
});
|
||||
};
|
||||
|
||||
const debouncedSuccessPayment = debounce(onSuccessPayment, 5000);
|
||||
|
||||
const handlePrevCard = async () => {
|
||||
const { amount, credit_reference, currency } = __confirmData;
|
||||
const { card_uid } = __confirmCardDetails;
|
||||
@@ -377,7 +379,7 @@ function ConfirmAddFund({
|
||||
<hr />
|
||||
<div className="md:p-8 p-4 add-fund-btn flex justify-end items-center py-4 gap-4">
|
||||
<button
|
||||
className="px-4 h-11 flex justify-center items-center border-gradient text-base rounded-full"
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
|
||||
onClick={getBack}
|
||||
>
|
||||
Back
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import usersService from "../../../services/UsersService";
|
||||
import { tableReload } from "../../../store/TableReloads";
|
||||
import InputCom from "../../Helpers/Inputs/InputCom";
|
||||
import ModalCom from "../../Helpers/ModalCom";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { tableReload } from "../../../store/TableReloads";
|
||||
|
||||
function ConfirmNairaWithdraw({
|
||||
payment,
|
||||
@@ -393,9 +393,9 @@ function ConfirmNairaWithdraw({
|
||||
<button
|
||||
type="button"
|
||||
onClick={action}
|
||||
className="border-gradient text-base tracking-wide px-4 py-2 rounded-full"
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center items-center bg-[#f5a430] text-black text-base rounded-full"
|
||||
>
|
||||
<span className="text-gradient">Cancel</span>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
|
||||
|
||||
@@ -25,7 +25,10 @@ const CreditPopup = ({ details, onClose, situation, walletItem }) => {
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
{confirmCredit?.show?.acceptConfirm?.state &&
|
||||
confirmCredit?.data?.internal_return < 0 ? (
|
||||
(confirmCredit?.data?.internal_return < 0
|
||||
// ||
|
||||
// confirmCredit?.data?.status !== "successful"
|
||||
) ? (
|
||||
"Credit Unsuccessful"
|
||||
) : (
|
||||
<>
|
||||
|
||||
@@ -171,7 +171,7 @@ function NairaWithdraw({
|
||||
setErrorMsgs({ amount: "amount required" });
|
||||
setTimeout(() => setErrorMsgs({ amount: "" }), 3000);
|
||||
return;
|
||||
}else if (Number(values.amount * 100) > Number(wallet?.transfer_limit)) {
|
||||
} else if (Number(values.amount * 100) > Number(wallet?.transfer_limit)) {
|
||||
setErrorMsgs({ amount: "Withdraw limit has been exceeded" });
|
||||
setTimeout(() => setErrorMsgs({ amount: "" }), 3000);
|
||||
return;
|
||||
@@ -448,7 +448,7 @@ function NairaWithdraw({
|
||||
{/* <label className="text-[#181c32] dark:text-white text-base font-semibold block flex-[0.2] mb-0 mt-3"></label> */}
|
||||
<div className="flex flex-col gap-3 flex-[0.8] items-center">
|
||||
<select
|
||||
className="sm:w-full w-48 text-base p-2 text-dark-gray dark:text-white rounded-md border border-slate-300 outline-0 flex-[0.8]"
|
||||
className="sm:w-full w-48 text-dark-gray border-slate-300 outline-0 flex-[0.8] my-3 rounded-full p-2 outline-none text-base dark:text-gray-100 bg-[#FAFAFA] dark:bg-[#11131F] border appearance-none"
|
||||
value={
|
||||
props.values.previousAccount?.recipientID
|
||||
}
|
||||
@@ -473,7 +473,7 @@ function NairaWithdraw({
|
||||
?.recipientID
|
||||
? errorMsgs.previousAccount
|
||||
.recipientID
|
||||
: "Select..."}
|
||||
: "Select an account"}
|
||||
</option>
|
||||
{recipients.data.map((item, index) => (
|
||||
<option
|
||||
@@ -517,7 +517,7 @@ function NairaWithdraw({
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[7.8rem]"></div>
|
||||
<div className="h-[6.1rem]"></div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -796,9 +796,9 @@ function NairaWithdraw({
|
||||
<button
|
||||
type="button"
|
||||
onClick={action}
|
||||
className="border-gradient text-base tracking-wide px-4 py-2 rounded-full"
|
||||
className="px-4 py-1 h-11 max-w-[100px] w-full flex justify-center bg-[#f5a430] text-black items-center text-base rounded-full"
|
||||
>
|
||||
<span className="text-gradient">Cancel</span>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
<button
|
||||
|
||||
@@ -4,7 +4,7 @@ import bank1 from "../../assets/images/bank-1.png";
|
||||
import bank2 from "../../assets/images/bank-2.png";
|
||||
import bank3 from "../../assets/images/bank-3.png";
|
||||
import bank4 from "../../assets/images/bank-4.png";
|
||||
import profileImg from "../../assets/images/profile-pic.jpg";
|
||||
import profileImg from "../../assets/images/profile.jpg";
|
||||
import useToggle from "../../hooks/useToggle";
|
||||
import usersService from "../../services/UsersService";
|
||||
import DarkModeContext from "../Contexts/DarkModeContext";
|
||||
@@ -15,10 +15,10 @@ import WalletHeader from "../MyWallet/WalletHeader";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import Flag from "../../assets/images/united-states.svg";
|
||||
import siteLogo from "../../assets/images/wrenchboard-logo-text.png";
|
||||
import formattedDate from "../../lib/fomattedDate";
|
||||
import { updateNotifications } from "../../store/notifications";
|
||||
import TimeDifference from "../Helpers/TimeDifference";
|
||||
|
||||
const DEFAULT_PROFILE_IMAGE = require("../../assets/images/profile.jpg");
|
||||
|
||||
export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
const [balanceDropdown, setbalanceValue] = useToggle(false);
|
||||
const [notificationDropdown, setNotificationValue] = useToggle(false);
|
||||
@@ -30,7 +30,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
const [myWalletList, setMyWalletList] = useState([]);
|
||||
const api = useMemo(() => new usersService(), []);
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate()
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { notifications } = useSelector((state) => state?.notifications); // NOTIFICATION STORE
|
||||
|
||||
@@ -100,10 +100,10 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
};
|
||||
|
||||
// User Profile
|
||||
let { firstname, lastname, email, profile_pic } = userDetails;
|
||||
let { firstname, lastname, email, profile_pic_url } = userDetails;
|
||||
let userEmail = email?.split("@")[0];
|
||||
const userProfileImage = profile_pic_url || DEFAULT_PROFILE_IMAGE;
|
||||
|
||||
// console.log("Notify: ", notifications?.data?.raw);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -255,9 +255,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
>
|
||||
<Icons name="notification" />
|
||||
<span className="absolute right-2 top-2 z-10 text-xs lg:w-5 lg:h-5 w-4 h-4 flex justify-center items-center rounded-full primary-gradient text-white cursor-default">
|
||||
{notifications?.loading
|
||||
? "●"
|
||||
: notifications?.data?.length}
|
||||
{notifications?.loading ? "●" : notifications?.data?.length}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
@@ -273,46 +271,45 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
|
||||
<div className="content px-7 pb-7">
|
||||
<ul>
|
||||
{notifications?.data?.length && notifications?.data?.map((item, idx) =>
|
||||
{
|
||||
if(idx < 5){
|
||||
return (
|
||||
<li
|
||||
className={`content-item ${
|
||||
idx == 4
|
||||
? "py-5 "
|
||||
: "py-4 border-b dark:border-[#5356fb29] border-light-purple hover:border-purple dark:hover:border-purple"
|
||||
}`}
|
||||
key={idx}
|
||||
>
|
||||
<div className="notifications flex space-x-4 items-center">
|
||||
<div className="icon max-w-[52px] max-h-[52px] w-full h-full rounded-full object-cover">
|
||||
<img
|
||||
src={require(`../../assets/images/notifications/${item?.icon}`)}
|
||||
alt="icon"
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="name">
|
||||
<p className="text-base text-dark-gray dark:text-white font-medium mb-2">
|
||||
{item?.title}
|
||||
{/* <span className="ml-1 font-bold">
|
||||
{notifications?.data?.length &&
|
||||
notifications?.data?.map((item, idx) => {
|
||||
if (idx < 5) {
|
||||
return (
|
||||
<li
|
||||
className={`content-item ${
|
||||
idx == 4
|
||||
? "py-5 "
|
||||
: "py-4 border-b dark:border-[#5356fb29] border-light-purple hover:border-purple dark:hover:border-purple"
|
||||
}`}
|
||||
key={idx}
|
||||
>
|
||||
<div className="notifications flex space-x-4 items-center">
|
||||
<div className="icon max-w-[52px] max-h-[52px] w-full h-full rounded-full object-cover">
|
||||
<img
|
||||
src={require(`../../assets/images/notifications/${item?.icon}`)}
|
||||
alt="icon"
|
||||
className="w-full h-full"
|
||||
/>
|
||||
</div>
|
||||
<div className="name">
|
||||
<p className="text-base text-dark-gray dark:text-white font-medium mb-2">
|
||||
{item?.title}
|
||||
{/* <span className="ml-1 font-bold">
|
||||
successfully done
|
||||
</span> */}
|
||||
</p>
|
||||
<p className="text-sm text-thin-light-gray font-medium">
|
||||
<TimeDifference
|
||||
time={item?.date}
|
||||
key={item?.uid}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
</p>
|
||||
<p className="text-sm text-thin-light-gray font-medium">
|
||||
<TimeDifference
|
||||
time={item?.date}
|
||||
key={item?.uid}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</ul>
|
||||
|
||||
<div className="add-money-btn flex justify-center items-center">
|
||||
@@ -351,9 +348,9 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
{/* profile-image */}
|
||||
<div className="lg:w-[62px] lg:h-[62px] w-[50px] h-[50px] rounded-full overflow-hidden">
|
||||
<img
|
||||
src={profile_pic != "" ? profile_pic : profileImg}
|
||||
src={userProfileImage}
|
||||
alt="profile"
|
||||
className="w-full h-full"
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
</div>
|
||||
<div className="lg:block hidden">
|
||||
@@ -375,7 +372,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
<img
|
||||
src={profileImg}
|
||||
alt="profile"
|
||||
className="w-full h-full"
|
||||
className="w-full h-full object-cover object-center"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -62,7 +62,7 @@ export default function Layout({ children }) {
|
||||
drawer
|
||||
? "2xl:w-[335px] w-[280px] 2xl:pl-20 pl-10 pr-6 "
|
||||
: "w-[70px]"
|
||||
} bg-white dark:bg-dark-white h-full overflow-y-scroll overflow-style-none fixed left-0 top-0 pt-8`}
|
||||
} bg-white dark:bg-dark-white h-full overflow-y-scroll overflow-style-none fixed left-0 top-0 pt-[30px]`}
|
||||
>
|
||||
<Sidebar
|
||||
logoutModalHandler={logoutModalHandler}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function Sidebar({
|
||||
{/* logo-area */}
|
||||
<div
|
||||
className={`w-full flex items-center transition-all duration-300 ease-in-out ${
|
||||
sidebar ? "justify-between mb-14" : "justify-center"
|
||||
sidebar ? "justify-start gap-3 mb-14" : "justify-center"
|
||||
}`}
|
||||
>
|
||||
<div className={`sidebar-logo ${sidebar ? "enter" : ""}`}>
|
||||
|
||||
@@ -68,6 +68,7 @@ export default function PersonalInfoTab({
|
||||
state: userDetails?.state,
|
||||
city: userDetails?.city,
|
||||
email: userDetails?.email,
|
||||
profile: userDetails?.profile_pic,
|
||||
};
|
||||
|
||||
let [profile, setProfile] = useState({
|
||||
@@ -86,6 +87,9 @@ export default function PersonalInfoTab({
|
||||
const handleUpdateUser = (values, helpers) => {
|
||||
setRequestState({ message: "", loading: true, status: false });
|
||||
|
||||
// there is no profile accommodation on the payload
|
||||
delete values?.profile;
|
||||
|
||||
apiCall
|
||||
.updateProfile(values)
|
||||
.then((res) => {
|
||||
@@ -336,8 +340,8 @@ export default function PersonalInfoTab({
|
||||
<div className="w-full relative">
|
||||
<img
|
||||
src={profileImg}
|
||||
alt=""
|
||||
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
|
||||
alt="profile"
|
||||
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-contain object-center"
|
||||
/>
|
||||
<input
|
||||
ref={profileImgInput}
|
||||
@@ -384,7 +388,8 @@ export default function PersonalInfoTab({
|
||||
)}
|
||||
<div className="w-full h-[120px] border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
|
||||
<div className="flex items-center space-x-4 mr-9">
|
||||
<Link
|
||||
|
||||
{/* <Link
|
||||
to="/"
|
||||
className="text-18 text-light-red tracking-wide "
|
||||
>
|
||||
@@ -392,7 +397,7 @@ export default function PersonalInfoTab({
|
||||
{" "}
|
||||
Cancel
|
||||
</span>
|
||||
</Link>
|
||||
</Link> */}
|
||||
|
||||
{requestStatus.loading ? (
|
||||
<LoadingSpinner size="8" color="sky-blue" />
|
||||
|
||||
@@ -5,8 +5,9 @@ import React, {
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import cover from "../../assets/images/profile-info-cover.png";
|
||||
import profile from "../../assets/images/profile-info-profile.png";
|
||||
import profile from "../../assets/images/profile.jpg";
|
||||
import usersService from "../../services/UsersService";
|
||||
import Icons from "../Helpers/Icons";
|
||||
import Layout from "../Partials/Layout";
|
||||
@@ -23,15 +24,19 @@ import {
|
||||
import RecipientAccountTab from "./Tabs/RecipientAccountTab";
|
||||
|
||||
export default function Settings({ faq }) {
|
||||
const [profileImg, setProfileImg] = useState(profile);
|
||||
const { userDetails } = useSelector((state) => state?.userDetails);
|
||||
const [profileImg, setProfileImg] = useState(
|
||||
userDetails?.profile_pic_url ? userDetails.profile_pic_url : profile
|
||||
);
|
||||
const [coverImg, setCoverImg] = useState(cover);
|
||||
const [reloadCardList, setReloadCardList] = useState(false) // STATE TO DETERMINE WHEN CARD LIST RELOADS. EG: WHEN USER DELETES A CARD
|
||||
|
||||
const [reloadCardList, setReloadCardList] = useState(false); // STATE TO DETERMINE WHEN CARD LIST RELOADS. EG: WHEN USER DELETES A CARD
|
||||
|
||||
// profile img
|
||||
const profileImgInput = useRef(null);
|
||||
const browseProfileImg = () => {
|
||||
profileImgInput.current.click();
|
||||
};
|
||||
|
||||
const profileImgChangHandler = (e) => {
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import React, { useCallback, useMemo, useState } from "react";
|
||||
import Detail from "./popoutcomponent/Detail";
|
||||
import ModalCom from "../Helpers/ModalCom";
|
||||
import usersService from "../../services/UsersService";
|
||||
import React, { useMemo, useState } from "react";
|
||||
import { toast } from "react-toastify";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import usersService from "../../services/UsersService";
|
||||
import ModalCom from "../Helpers/ModalCom";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import Detail from "./popoutcomponent/Detail";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import { useDispatch } from "react-redux";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
|
||||
const showSuccessToast = (message) => {
|
||||
toast.success(message, {
|
||||
@@ -14,48 +18,85 @@ const showSuccessToast = (message) => {
|
||||
};
|
||||
|
||||
function PendingJobsPopout({ details, onClose, situation }) {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [pendingJobLoader, setPendingJobLoader] = useState({
|
||||
extend: false,
|
||||
offer: false,
|
||||
});
|
||||
const apiCall = useMemo(() => new usersService(), []);
|
||||
|
||||
const handlePendingJobsBtn = useCallback(
|
||||
async ({ target: { name } }) => {
|
||||
let [requestMessage, setRequestMessage] = useState({status: false, message: ''})
|
||||
|
||||
const apiCall = new usersService();
|
||||
|
||||
const handlePendingJobsBtn = ({ target: { name } }) => {
|
||||
let { job_uid, offer_code } = details;
|
||||
|
||||
let reqData;
|
||||
|
||||
let pendingData = { job_uid, offer_code };
|
||||
if(name=='extend'){ // RUNS THIS TO EXTEND JOB EXPIRY BY ONE WEEK
|
||||
reqData = { ...pendingData };
|
||||
setPendingJobLoader({ extend: true });
|
||||
apiCall.pendingJobExtend(reqData).then(res => {
|
||||
setRequestMessage({status: true, message: res.data.status})
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ extend: false, offer: false });
|
||||
setRequestMessage({status: false, message: ''})
|
||||
onClose();
|
||||
dispatch(tableReload({ type: "PENDINGTABLE" }));
|
||||
}, 4000);
|
||||
}).catch(error => {
|
||||
setRequestMessage({status: false, message:'Try Again'})
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ extend: false, offer: false });
|
||||
setRequestMessage({status: false, message: ''})
|
||||
}, 3000);
|
||||
})
|
||||
}else if(name=='offer'){ // RUNS THIS IF JOB IS SENT TO USER
|
||||
reqData = { ...pendingData };
|
||||
setPendingJobLoader({ offer: true });
|
||||
apiCall.pendingJobSendTome(reqData).then(res => {
|
||||
setRequestMessage({status: true, message: res.data.status})
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ extend: false, offer: false });
|
||||
setRequestMessage({status: false, message: ''})
|
||||
}, 4000);
|
||||
}).catch(error => {
|
||||
setRequestMessage('Try Again')
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ extend: false, offer: false });
|
||||
setRequestMessage({status: false, message: ''})
|
||||
}, 3000);
|
||||
})
|
||||
} else return
|
||||
// try {
|
||||
// if (name === "extend") {
|
||||
// setPendingJobLoader({ extend: true });
|
||||
// reqData = { ...pendingData };
|
||||
// // let { data } =
|
||||
// await apiCall.pendingJobExtend(reqData);
|
||||
// showSuccessToast("Job has been extended by a week!");
|
||||
// dispatch(tableReload({ type: "PENDINGTABLE" }));
|
||||
// } else if (name === "offer") {
|
||||
// setPendingJobLoader({ offer: true });
|
||||
// reqData = { ...pendingData };
|
||||
// // let { data } =
|
||||
// await apiCall.pendingJobSendTome(reqData);
|
||||
// showSuccessToast("Offer sent, check your email");
|
||||
// } else return;
|
||||
|
||||
try {
|
||||
if (name === "extend") {
|
||||
setPendingJobLoader({ extend: true });
|
||||
reqData = { ...pendingData };
|
||||
// let { data } =
|
||||
await apiCall.pendingJobExtend(reqData);
|
||||
// console.log("This is for extend", data);
|
||||
showSuccessToast("Job has been extended by a week!");
|
||||
} else if (name === "offer") {
|
||||
setPendingJobLoader({ offer: true });
|
||||
reqData = { ...pendingData };
|
||||
// let { data } =
|
||||
await apiCall.pendingJobSendTome(reqData);
|
||||
// console.log("This is for offer", data);
|
||||
showSuccessToast("Offer sent, check your email");
|
||||
} else return;
|
||||
|
||||
setTimeout(() => {
|
||||
setPendingJobLoader({ extend: false, offer: false });
|
||||
onClose();
|
||||
}, 2700);
|
||||
} catch (error) {
|
||||
setPendingJobLoader({ extend: false, offer: false });
|
||||
throw new Error(error);
|
||||
}
|
||||
},
|
||||
[onClose, apiCall, details]
|
||||
);
|
||||
// setTimeout(() => {
|
||||
// setPendingJobLoader({ extend: false, offer: false });
|
||||
// onClose();
|
||||
// }, 2700);
|
||||
// } catch (error) {
|
||||
// setPendingJobLoader({ extend: false, offer: false });
|
||||
// throw new Error(error);
|
||||
// }
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation} className="edit-popup">
|
||||
@@ -101,7 +142,8 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
</span>
|
||||
<div className="">
|
||||
<p className="text-sm">
|
||||
This Job have been sent to public view
|
||||
{/* This Job have been sent to public view */}
|
||||
This Job have been sent to {details.job_to}
|
||||
</p>
|
||||
<p className="text-sm text-slate-600">This Job will expire</p>
|
||||
</div>
|
||||
@@ -153,13 +195,13 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="my-2 md:flex">
|
||||
{/* <div className="my-2 md:flex">
|
||||
<Detail
|
||||
label="Public Link"
|
||||
value="https://work.wrenchboard.com/plb/viewjob/218B4BWB83"
|
||||
bg="bg-slate-200"
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
|
||||
{/* ACTION SECTION */}
|
||||
@@ -168,11 +210,11 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
|
||||
<div className="mb-3">
|
||||
<p className="px-2 py-1 text-sm bg-slate-100">
|
||||
Job sent to public view
|
||||
Job sent to {details.job_to}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="my-3">
|
||||
<div className="mt-3 mb-8">
|
||||
<button
|
||||
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
onClick={handlePendingJobsBtn}
|
||||
@@ -187,6 +229,11 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
{pendingJobLoader.extend && requestMessage.message &&
|
||||
<div className={`p-1 ${requestMessage.status ? 'bg-green-500':'bg-red-500'} text-white rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}>
|
||||
<p>{requestMessage.message}</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className="my-3">
|
||||
<button
|
||||
@@ -204,6 +251,12 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{pendingJobLoader.offer && requestMessage.message &&
|
||||
<div className={`p-1 ${requestMessage.status ? 'bg-green-500':'bg-red-500'} text-white rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}>
|
||||
<p>{requestMessage.message}</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
<div className="mt-10 md:mt-32 md:flex md:justify-center">
|
||||
<button
|
||||
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
|
||||
+1
-1
@@ -303,7 +303,7 @@ input[type="text"][dir="rtl"] {
|
||||
|
||||
.sidebar-logo.enter {
|
||||
transform: scale(1);
|
||||
width: 100%;
|
||||
width: 80%;
|
||||
}
|
||||
.sidebar-logo {
|
||||
transform: scale(0);
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -595,7 +595,7 @@ class usersService {
|
||||
resultTopUp(post) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
member_id: Number(localStorage.getItem("member_id")),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 11061,
|
||||
...post,
|
||||
|
||||
@@ -5,20 +5,25 @@ import { useSelector } from "react-redux";
|
||||
|
||||
export default function MyPendingJobsPage() {
|
||||
let { commonHeadBanner } = useSelector((state) => state.commonHeadBanner);
|
||||
const [MyJobList, setMyJobList] = useState([]);
|
||||
let { pendingListTable } = useSelector((state) => state.tableReload);
|
||||
|
||||
const [MyJobList, setMyJobList] = useState({loading: true, data: []});
|
||||
const api = new usersService();
|
||||
|
||||
const getMyJobList = async () => {
|
||||
setMyJobList({loading: true, data: []});
|
||||
try {
|
||||
const res = await api.getMyPendingJobList();
|
||||
setMyJobList(res.data);
|
||||
setMyJobList({loading: false, data: res.data});
|
||||
} catch (error) {
|
||||
setMyJobList({loading: false, data: []});
|
||||
console.log("Error getting mode");
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
getMyJobList();
|
||||
}, []);
|
||||
}, [pendingListTable]);
|
||||
|
||||
// debugger;
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user