Compare commits

...

10 Commits

13 changed files with 222 additions and 131 deletions
+7 -1
View File
@@ -84,4 +84,10 @@ REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
#Family
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
+7 -1
View File
@@ -52,4 +52,10 @@ REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
+6
View File
@@ -59,3 +59,9 @@ REACT_APP_MAX_CREDIT_BANK_ACCOUNT=4
REACT_APP_MAX_FAMILY_MEMBERS=8
REACT_APP_SHOW_OFFER_GROUP_JOB=0
#UPLOAD PROFILE PICTURE
REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE=0
#GOOGLE RECAPTCHA SITEKEY
REACT_APP_GOOGLE_RECAPTCHA_SITEKEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
+1
View File
@@ -21,6 +21,7 @@
"react-chartjs-2": "^4.1.0",
"react-countup": "^6.2.0",
"react-dom": "^18.2.0",
"react-google-recaptcha": "^3.1.0",
"react-qr-code": "^2.0.11",
"react-redux": "^8.0.5",
"react-router-dom": "^6.0.2",
@@ -7,6 +7,8 @@ import AuthLayout from "../AuthLayout";
import EmailValidator from "../../../lib/EmailValidator";
import ForgetPwdResponse from "../ForgetPwdResponse";
import ReCAPTCHA from "react-google-recaptcha";
export default function ForgotPassword() {
const [checked, setValue] = useState(false);
const [resetLoading, setResetLoading] = useState(false);
@@ -22,9 +24,18 @@ export default function ForgotPassword() {
setMail(e?.target.value);
};
const humanChecker = () => {
setValue(!checked);
};
// const humanChecker = () => {
// setValue(!checked);
// };
function humanChecker(value) {
// console.log("Captcha value:", value);
if(value){
setValue(true)
}else{
setValue(false)
}
}
const resetHandler = async () => {
if (email == "") {
@@ -113,10 +124,16 @@ export default function ForgotPassword() {
/>
</div>
{/* hCaptha clone for the time being */}
<div className="mb-10">
<div className="mb-10 flex justify-center w-full">
<ReCAPTCHA
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
onChange={humanChecker}
/>
</div>
{/* <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">
@@ -148,7 +165,7 @@ export default function ForgotPassword() {
<div className="h-full relative inline-block w-16"></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}
+21 -6
View File
@@ -7,7 +7,7 @@ import React, {
useState,
} from "react";
import { useReactToPrint } from "react-to-print";
import profile from "../../assets/images/profile-info-profile.png";
import profile from "../../assets/images/icons/family.svg";
import localImgLoad from "../../lib/localImgLoad";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner";
@@ -82,14 +82,29 @@ export default function FamilyManageTabs({
profileImgInput.current.click();
};
// Function to handle changes in the profile image input
/**
* Handles the change event of the profile image input field.
* Checks if the selected file exceeds the maximum file size limit and displays an alert if it does.
* If the file is within the size limit, it reads the file using the FileReader API and sets the profile image state with the result.
*/
const profileImgChangeHandler = (e) => {
if (e.target.value !== "") {
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
const file = e.target.files[0];
if (file && file.size > MAX_FILE_SIZE) {
alert("File size exceeds the limit.");
return;
}
if (file) {
const imgReader = new FileReader();
imgReader.onload = (event) => {
setProfileImg(event.target.result);
imgReader.onload = () => {
const imageDataUrl = imgReader.result;
// Set the profile image
setProfileImg(imageDataUrl);
};
imgReader.readAsDataURL(e.target.files[0]);
imgReader.readAsDataURL(file);
}
};
+10 -5
View File
@@ -1,26 +1,31 @@
import React from "react";
import localImgLoad from "../../../lib/localImgLoad";
export default function ProfileInfo({
profileImg,
profileImgInput,
profileImgChangHandler,
profileImgChangeHandler,
browseProfileImg,
accountDetails,
}) {
// console.log(accountDetails.banner)
return (
<div className="flex flex-col items-center gap-4">
<div className="flex justify-center">
<div className="w-full relative">
<img
src={localImgLoad(`images/icons/${accountDetails.banner}`)}
alt=""
src={
profileImg
? profileImg
: localImgLoad(`images/icons/${accountDetails.banner}`)
}
alt="profile"
className="sm:w-[180px] sm:h-[180px] w-[120px] h-[120px] rounded-full overflow-hidden object-cover"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
accept="image/*"
onChange={profileImgChangeHandler}
type="file"
className="hidden"
/>
+44 -52
View File
@@ -12,8 +12,7 @@ function NairaWithdraw({
state,
setShowConfirmNairaWithdraw,
}) {
let MaxNoOfBanks = process.env.REACT_APP_MAX_CREDIT_BANK_ACCOUNT // HOLDS THE VALUE OF THE MAX NUMBER OF BANKS USER CAN ADD
let MaxNoOfBanks = process.env.REACT_APP_MAX_CREDIT_BANK_ACCOUNT; // HOLDS THE VALUE OF THE MAX NUMBER OF BANKS USER CAN ADD
const apiCall = new usersService();
const [tab, setTab] = useState("previous");
let [requestStatus, setRequestStatus] = useState(false);
@@ -25,7 +24,9 @@ function NairaWithdraw({
recipientID: state?.previousAccount?.recipientID || "",
},
newAccount: {
country: state?.newAccount?.amount || "",
country: wallet.walletCountry
? wallet.walletCountry[0][0]
: wallet.country,
bank: state?.newAccount?.amount || "",
accountNumber: state?.newAccount?.amount || "",
accountType: state?.newAccount?.amount || "",
@@ -67,10 +68,14 @@ function NairaWithdraw({
// Handling card change
const handleBankOptions = (event) => {
const { value } = event.target;
let bankCountry = wallet.walletCountry
? wallet.walletCountry[0][0]
: wallet.country
setBankName((prev) => ({ loading: true, data: [] }));
apiCall
.getCountryBank({ country: value })
.getCountryBank({
country: bankCountry
})
.then((res) => {
if (res.data.internal_return < 0) {
setBankName((prev) => ({ loading: false, data: [] }));
@@ -140,6 +145,7 @@ function NairaWithdraw({
};
useEffect(() => {
handleBankOptions()
getCountry(); // TO LOAD LIST COUNTRY
getAccountTypes(); // TO LOAD LIST ACCOUNT TYPES
}, []);
@@ -226,7 +232,7 @@ function NairaWithdraw({
setShowConfirmNairaWithdraw({ show: true, state: stateData });
}, 1000);
}
if (tab === "new") {
const { accountNumber, accountType, bank, city, country, state } =
values?.newAccount;
@@ -281,6 +287,8 @@ function NairaWithdraw({
getRecipients();
}, []);
console.log("Testing Wallet Country", wallet?.walletCountry[0][0]);
return (
<ModalCom action={action} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper lw-[90%] md:w-[768px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
@@ -424,7 +432,11 @@ function NairaWithdraw({
<label
onClick={() => setTab("new")}
htmlFor="new"
className={`cursor-pointer flex items-center gap-1 ${recipients.data.length >= MaxNoOfBanks ? 'pointer-events-none':''}`}
className={`cursor-pointer flex items-center gap-1 ${
recipients.data.length >= MaxNoOfBanks
? "pointer-events-none"
: ""
}`}
>
<input
id="new"
@@ -436,7 +448,12 @@ function NairaWithdraw({
tab == "new" ? "" : ""
} tracking-wide transition duration-200`}
/>
New Account{" "} {recipients.data.length >= MaxNoOfBanks && <span className="text-[14px] text-red-500">Max Reached</span>}
New Account{" "}
{recipients.data.length >= MaxNoOfBanks && (
<span className="text-[14px] text-red-500">
Max Reached
</span>
)}
</label>
</div>
</div>
@@ -523,12 +540,12 @@ function NairaWithdraw({
</>
)}
{tab == "new" && (
recipients.loading ?
{tab == "new" &&
(recipients.loading ? (
<div className="mt-3 flex flex-col w-full h-[188px] justify-center items-center">
<LoadingSpinner size='10' color='sky-blue' />
<LoadingSpinner size="10" color="sky-blue" />
</div>
:recipients.data.length < MaxNoOfBanks ?
) : recipients.data.length < MaxNoOfBanks ? (
<div className="w-full mt-3 rounded-md bg-slate-100">
<div className="relative fields w-full flex flex-col p-4">
<div className="flex flex-[2] min-h-[52px]">
@@ -541,7 +558,12 @@ function NairaWithdraw({
Country{" "}
<span className="text-red-500">*</span>
</label>
<select
<div className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding tracking-[1.5px] flex items-center pointer-events-none">
<span className="text-slate-500 text-lg italic">
{wallet.walletCountry[0][1]}
</span>
</div>
{/* <select
className="w-full text-base p-2 text-dark-gray dark:text-white border border-slate-300 outline-0 flex-[0.6] rounded-full h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding tracking-[1.5px]"
name="newAccount.country"
value={props.values.newAccount?.country}
@@ -552,7 +574,7 @@ function NairaWithdraw({
>
{allCountries.loading ? (
<option
className="text-slate-500 text-lg"
className="text-slate-500 text-lg"
value=""
>
Loading...
@@ -585,13 +607,7 @@ function NairaWithdraw({
No Options Found!
</option>
)}
</select>
{/* {props.errors.country &&
props.touched.country && (
<p className="text-sm text-red-500">
{props.errors.country}
</p>
)} */}
</select> */}
</div>
{/* bank name */}
@@ -646,11 +662,6 @@ function NairaWithdraw({
</option>
)}
</select>
{/* {props.errors.bank && props.touched.bank && (
<p className="text-sm text-red-500">
{props.errors.bank}
</p>
)} */}
</div>
</div>
@@ -671,16 +682,12 @@ function NairaWithdraw({
name="newAccount.accountNumber"
placeholder="Account No"
maxLength={10}
value={props.values.newAccount?.accountNumber}
value={
props.values.newAccount?.accountNumber
}
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{/* {props.errors.accountNumber &&
props.touched.accountNumber && (
<p className="text-sm text-red-500">
{props.errors.accountNumber}
</p>
)} */}
</div>
{/* Account Type */}
@@ -732,12 +739,6 @@ function NairaWithdraw({
</option>
)}
</select>
{/* {props.errors.accountType &&
props.touched.accountType && (
<p className="text-sm text-red-500">
{props.errors.accountType}
</p>
)} */}
</div>
</div>
@@ -748,7 +749,8 @@ function NairaWithdraw({
htmlFor="newAccount.state"
className="input-label text-[#181c32] dark:text-white text-base font-semibold inline-flex flex-[0.4]"
>
State <span className="text-red-500">*</span>
State{" "}
<span className="text-red-500">*</span>
</label>
<InputCom
fieldClass="px-6 tracking-[1.5px]"
@@ -760,11 +762,6 @@ function NairaWithdraw({
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{/* {props.errors.state && props.touched.state && (
<p className="text-sm text-red-500">
{props.errors.state}
</p>
)} */}
</div>
{/* city */}
@@ -785,20 +782,15 @@ function NairaWithdraw({
inputHandler={props.handleChange}
blurHandler={props.handleBlur}
/>
{/* {props.errors.city && props.touched.city && (
<p className="text-sm text-red-500">
{props.errors.city}
</p>
)} */}
</div>
</div>
{/* end of inputs for new accounts */}
</div>
</div>
:
) : (
<div className="mt-3 flex w-full h-[188px] justify-center items-center"></div>
)}
))}
</div>
<div className="transfer-fund-btn flex justify-end items-center gap-2 py-4">
+38 -2
View File
@@ -5,7 +5,6 @@ import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
const WalletBox = lazy(() => import("./WalletBox"));
const WalletRoutes = () => {
const apiCall = new usersService();
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
@@ -16,6 +15,12 @@ const WalletRoutes = () => {
data: [],
});
const [allCountries, setAllCountries] = useState({
// STATE TO HOLD LIST OF COUNTRIES
loading: true,
data: [],
});
const getPaymentHistory = () => {
apiCall
.getPaymentHx()
@@ -31,14 +36,45 @@ const WalletRoutes = () => {
});
};
// FUNCTION TO GET COUNTRIES
const getCountry = () => {
apiCall
.getSignupCountryData()
.then((res) => {
if (res.data.internal_return < 0) {
setAllCountries((prev) => ({ loading: false, data: [] }));
return;
}
setAllCountries((prev) => ({
loading: false,
data: res.data.signup_country,
}));
})
.catch((error) => {
setAllCountries((prev) => ({ loading: false, data: [] }));
});
};
useEffect(() => {
getCountry();
getPaymentHistory();
}, [walletTable]);
console.log(
"Testing all country: ",
allCountries,
"Testing wallet: ",
walletDetails
);
return (
<Layout>
<Suspense fallback={<LoadingSpinner size="16" color="sky-blue" />}>
<WalletBox wallet={walletDetails} payment={paymentHistory} />
<WalletBox
wallet={walletDetails}
payment={paymentHistory}
countries={allCountries.data}
/>
</Suspense>
</Layout>
);
+2 -2
View File
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from "react";
import React, { useEffect, useState } from "react";
import usersService from "../../services/UsersService";
import ConfirmNairaWithdraw from "./Popup/ConfirmNairaWithdraw";
import NairaWithdraw from "./Popup/NairaWithdraw";
@@ -8,7 +8,7 @@ function WalletAction({ walletItem, payment, openPopUp }) {
show: false,
state: {},
}); // DETERMINES WHEN NAIRA WITHDRAWAL POPS UP
const [showConfirmNairaWithdraw, setShowConfirmNairaWithdraw] = useState({
show: false,
state: {},
+2 -2
View File
@@ -4,7 +4,7 @@ import WalletItemCard from "./WalletItemCard";
/**
* Renders a list of wallet items or a loading spinner depending on the state of the `wallet` object.
*/
export default function WalletBox({ wallet, payment }) {
export default function WalletBox({ wallet, payment, countries }) {
const { loading, data } = wallet;
return (
@@ -18,7 +18,7 @@ export default function WalletBox({ wallet, payment }) {
) : (
data.length > 0 && data.map((item) => (
<div key={item.wallet_uid} className="lg:w-full h-full mb-10 lg:mb-0">
<WalletItemCard walletItem={item} payment={payment} />
<WalletItemCard walletItem={item} payment={payment} countries={countries} />
</div>
))
)}
+7 -2
View File
@@ -10,7 +10,7 @@ import WalletAction from "./WalletAction";
/**
* Renders a card displaying information about a wallet item.
*/
export default function WalletItemCard({ walletItem, payment }) {
export default function WalletItemCard({ walletItem, payment, countries }) {
const { userDetails } = useSelector((state) => state.userDetails);
const accountType = userDetails?.account_type === "FAMILY";
const dispatch = useDispatch();
@@ -35,10 +35,15 @@ export default function WalletItemCard({ walletItem, payment }) {
dispatch(tableReload({ type: "WALLETTABLE" }));
};
const currentWalletCurrency = countries
.map((country) => country)
.filter((country) => country[0] === walletItem.country);
const image = walletItem.code
? `${walletItem.code.toLowerCase()}.svg`
: "default.png";
return (
<>
<div
@@ -88,7 +93,7 @@ export default function WalletItemCard({ walletItem, payment }) {
{!accountType && (
<WalletAction
walletItem={walletItem}
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
payment={payment}
openPopUp={openPopUp}
/>
@@ -361,63 +361,65 @@ export default function PersonalInfoTab({
{/* inputs ends here */}
</div>
</div>
<div className="w-[232px] mb-10">
<div className="update-profile w-full mb-9">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white flex items-center mb-2">
Update Profile
<span className="ml-1">
<Icons name="block-question" />
</span>
</h1>
<p className="text-base text-thin-light-gray mb-5">
Profile of at least Size
<span className="ml-1 text-dark-gray dark:text-white">
300x300
</span>
. Gifs work too.
<span className="ml-1 text-dark-gray dark:text-white">
Max 5mb
</span>
.
</p>
<div className="flex justify-center">
<div className="w-full relative">
<img
src={profileImg}
alt="profile"
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-contain object-center"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
type="file"
className="hidden"
/>
<div
onClick={browseProfileImg}
className="w-[32px] h-[32px] absolute bottom-7 sm:right-10 right-[105px] hover:bg-pink bg-dark-gray rounded-full cursor-pointer"
>
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{process.env.REACT_APP_SHOW_UPLOAD_PROFILE_PICTURE != 0 &&
<div className="w-[232px] mb-10">
<div className="update-profile w-full mb-9">
<h1 className="text-xl tracking-wide font-bold text-dark-gray dark:text-white flex items-center mb-2">
Update Profile
<span className="ml-1">
<Icons name="block-question" />
</span>
</h1>
<p className="text-base text-thin-light-gray mb-5">
Profile of at least Size
<span className="ml-1 text-dark-gray dark:text-white">
300x300
</span>
. Gifs work too.
<span className="ml-1 text-dark-gray dark:text-white">
Max 5mb
</span>
.
</p>
<div className="flex justify-center">
<div className="w-full relative">
<img
src={profileImg}
alt="profile"
className="sm:w-[198px] sm:h-[198px] w-[120px] h-[120px] rounded-full overflow-hidden object-contain object-center"
/>
<input
ref={profileImgInput}
onChange={(e) => profileImgChangHandler(e)}
type="file"
className="hidden"
/>
<div
onClick={browseProfileImg}
className="w-[32px] h-[32px] absolute bottom-7 sm:right-10 right-[105px] hover:bg-pink bg-dark-gray rounded-full cursor-pointer"
>
<path
d="M16.5147 11.5C17.7284 12.7137 18.9234 13.9087 20.1296 15.115C19.9798 15.2611 19.8187 15.4109 19.6651 15.5683C17.4699 17.7635 15.271 19.9587 13.0758 22.1539C12.9334 22.2962 12.7948 22.4386 12.6524 22.5735C12.6187 22.6034 12.5663 22.6296 12.5213 22.6296C11.3788 22.6334 10.2362 22.6297 9.09365 22.6334C9.01498 22.6334 9 22.6034 9 22.536C9 21.4009 9 20.2621 9.00375 19.1271C9.00375 19.0746 9.02997 19.0109 9.06368 18.9772C10.4123 17.6249 11.7609 16.2763 13.1095 14.9277C14.2295 13.8076 15.3459 12.6913 16.466 11.5712C16.4884 11.5487 16.4997 11.5187 16.5147 11.5Z"
fill="white"
/>
<path
d="M20.9499 14.2904C19.7436 13.0842 18.5449 11.8854 17.3499 10.6904C17.5634 10.4694 17.7844 10.2446 18.0054 10.0199C18.2639 9.76139 18.5261 9.50291 18.7884 9.24443C19.118 8.91852 19.5713 8.91852 19.8972 9.24443C20.7251 10.0611 21.5492 10.8815 22.3771 11.6981C22.6993 12.0165 22.7105 12.4698 22.3996 12.792C21.9238 13.2865 21.4443 13.7772 20.9686 14.2717C20.9648 14.2792 20.9536 14.2867 20.9499 14.2904Z"
fill="white"
/>
</svg>
<svg
width="32"
height="32"
viewBox="0 0 32 32"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M16.5147 11.5C17.7284 12.7137 18.9234 13.9087 20.1296 15.115C19.9798 15.2611 19.8187 15.4109 19.6651 15.5683C17.4699 17.7635 15.271 19.9587 13.0758 22.1539C12.9334 22.2962 12.7948 22.4386 12.6524 22.5735C12.6187 22.6034 12.5663 22.6296 12.5213 22.6296C11.3788 22.6334 10.2362 22.6297 9.09365 22.6334C9.01498 22.6334 9 22.6034 9 22.536C9 21.4009 9 20.2621 9.00375 19.1271C9.00375 19.0746 9.02997 19.0109 9.06368 18.9772C10.4123 17.6249 11.7609 16.2763 13.1095 14.9277C14.2295 13.8076 15.3459 12.6913 16.466 11.5712C16.4884 11.5487 16.4997 11.5187 16.5147 11.5Z"
fill="white"
/>
<path
d="M20.9499 14.2904C19.7436 13.0842 18.5449 11.8854 17.3499 10.6904C17.5634 10.4694 17.7844 10.2446 18.0054 10.0199C18.2639 9.76139 18.5261 9.50291 18.7884 9.24443C19.118 8.91852 19.5713 8.91852 19.8972 9.24443C20.7251 10.0611 21.5492 10.8815 22.3771 11.6981C22.6993 12.0165 22.7105 12.4698 22.3996 12.792C21.9238 13.2865 21.4443 13.7772 20.9686 14.2717C20.9648 14.2792 20.9536 14.2867 20.9499 14.2904Z"
fill="white"
/>
</svg>
</div>
</div>
</div>
</div>
</div>
</div>
}
</div>
<div className="content-footer w-full">