diff --git a/.env b/.env
index f58ba9f..9cf6e84 100644
--- a/.env
+++ b/.env
@@ -23,6 +23,8 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
+REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
+
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
diff --git a/.env.development b/.env.development
index f58ba9f..9cf6e84 100644
--- a/.env.development
+++ b/.env.development
@@ -23,6 +23,8 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
+REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
+
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
diff --git a/.env.poduction b/.env.poduction
index 2dc4744..e20588e 100644
--- a/.env.poduction
+++ b/.env.poduction
@@ -23,6 +23,8 @@ REACT_APP_SESSION_EXPIRE_CHECKER=60000
REACT_APP_LOGIN_ERROR_TIMEOUT=7000
REACT_APP_SIGNUP_ERROR_TIMEOUT=7000
+REACT_APP_FLUTTERWAVE_APIKEY=FLWPUBK_TEST-54c90141b028789d671067bd72f781a9-X
+
# Had to change the error time to 3sec cause it took too long
REACT_APP_RESET_START_ERROR_TIMEOUT=3000
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..9b0b067
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,15 @@
+# Set the default behavior, in case people don't have core.autocrlf set.
+* text=auto
+
+# Explicitly declare text files you want to always be normalized and converted
+# to native line endings on checkout.
+*.jsx text
+*.js text
+*.css text
+
+# Declare files that will always have CRLF line endings on checkout.
+*.md text eol=crlf
+
+# Denote all files that are truly binary and should not be modified.
+*.sh binary
+
diff --git a/package.json b/package.json
index e37d41a..330a6a0 100644
--- a/package.json
+++ b/package.json
@@ -14,16 +14,16 @@
"cors": "^2.8.5",
"faker": "^6.6.6",
"formik": "^2.2.9",
- "react": "^18.0.0",
"react-chartjs-2": "^4.1.0",
"react-countup": "^6.2.0",
- "react-dom": "^18.0.0",
- "react-lottie": "^1.2.3",
- "react-redux": "^8.0.2",
"react-router-dom": "^6.0.2",
- "react-scripts": "5.0.0",
"react-slick": "^0.29.0",
"react-toastify": "^9.0.1",
+ "flutterwave-react-v3": "^1.3.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "react-redux": "^8.0.5",
+ "react-scripts": "5.0.1",
"redux": "^4.2.0",
"slick-carousel": "^1.8.1",
"web-vitals": "^1.0.1",
@@ -53,4 +53,4 @@
"last 1 safari version"
]
}
-}
+}
\ No newline at end of file
diff --git a/run.sh b/run.sh
index d1fcf3f..35de5f6 100755
--- a/run.sh
+++ b/run.sh
@@ -6,12 +6,15 @@ export NODE_ENV="${NODE_ENV:-development}"
if [ $NODE_ENV == "development" ]; then
# this runs webpack-dev-server with hot reloading
+ echo "Development build"
npm install --legacy-peer-deps
npm start
else
# build the app and serve it via nginx
+ echo "Production build"
npm install --legacy-peer-deps
npm run build
nginx -g 'daemon off;' -c /usr/src/app/nginx.conf
nginx -c /usr/src/app/nginx.conf
-fi
\ No newline at end of file
+fi
+
diff --git a/src/components/AuthPages/Login/index.jsx b/src/components/AuthPages/Login/index.jsx
index 7d18634..bcb294c 100644
--- a/src/components/AuthPages/Login/index.jsx
+++ b/src/components/AuthPages/Login/index.jsx
@@ -9,7 +9,13 @@ import usersService from "../../../services/UsersService";
import InputCom from "../../Helpers/Inputs/InputCom";
import AuthLayout from "../AuthLayout";
+import { useDispatch, useSelector } from "react-redux";
+import {updateUserDetails} from '../../../store/UserDetails'
+
export default function Login() {
+
+ const dispatch = useDispatch()
+
const [checked, setValue] = useState(false);
const [loginLoading, setLoginLoading] = useState(false);
@@ -51,21 +57,13 @@ export default function Login() {
//debugger;
// if (email === "support@mermsemr.com") {
if (loginResult.data.status > 0 && loginResult.data.internal_return == 100 && loginResult.data.session != '') { // just for a start
- localStorage.setItem("email", `${loginResult.data.email}`);
localStorage.setItem("member_id", `${loginResult.data.member_id}`);
localStorage.setItem("uid", `${loginResult.data.uid}`);
localStorage.setItem("session_token", `${loginResult.data.session}`);
- localStorage.setItem("added", `${loginResult.data.added}`);
- localStorage.setItem("city", `${loginResult.data.city}`);
- localStorage.setItem("country", `${loginResult.data.country}`);
- localStorage.setItem("firstname", `${loginResult.data.firstname}`);
- localStorage.setItem("last_login", `${loginResult.data.last_login}`);
- localStorage.setItem("lastname", `${loginResult.data.lastname}`);
- localStorage.setItem("state", `${loginResult.data.state}`);
- localStorage.setItem("zip_code", `${loginResult.data.zip_code}`);
localStorage.setItem("session", `${loginResult.data.session}`);
setLoginLoading(true);
// userApi.getUserReminders(); //testing
+ dispatch(updateUserDetails(loginResult.data))
setTimeout(() => {
navigate("/", { replace: true });
setLoginLoading(false);
diff --git a/src/components/AuthPages/VerifyLink/index.jsx b/src/components/AuthPages/VerifyLink/index.jsx
index 8d5d8cb..1b28c87 100644
--- a/src/components/AuthPages/VerifyLink/index.jsx
+++ b/src/components/AuthPages/VerifyLink/index.jsx
@@ -1,4 +1,4 @@
-import { useState, useLayoutEffect, useCallback } from "react";
+import { useState, useEffect, useCallback } from "react";
import { useLocation, Link, useNavigate } from "react-router-dom";
import AuthLayout from "../AuthLayout";
import InputCom from "../../Helpers/Inputs/InputCom";
@@ -28,34 +28,6 @@ export default function VerifyLink() {
setPassword(e.target.value);
};
- // for verifying the incoming verification link and render the correct component
- const verifyEmail = useCallback(async (code) => {
- try {
- const verifyRes = await userApi.verifyEmail(code);
- if (verifyRes.status === 200) {
- let { data } = verifyRes;
-
- if (
- data &&
- data.internal_return >= 0 &&
- data.status_text === "Link Verfied"
- ) {
- setPageLoader(false);
- } else {
- setPageLoader(false);
- setLinkSuccess(false);
- }
- }
- } catch (error) {
- setPageLoader(false);
- setLinkSuccess(false);
- throw new Error(error);
- }
- }, []);
-
- // delay verify requests by 10000ms
- const debouncedEmail = debounce(verifyEmail, 1000);
-
// if verification is okay. set a complete signup form
const completeSignup = async () => {
if (email === "" && password === "") {
@@ -85,14 +57,6 @@ export default function VerifyLink() {
localStorage.setItem("email", `${data?.email}`);
localStorage.setItem("member_id", `${data?.member_id}`);
localStorage.setItem("session_token", `${data?.session}`);
- localStorage.setItem("added", `${data?.added}`);
- localStorage.setItem("city", `${data?.city}`);
- localStorage.setItem("country", `${data?.country}`);
- localStorage.setItem("firstname", `${data?.firstname}`);
- localStorage.setItem("last_login", `${data?.last_login}`);
- localStorage.setItem("lastname", `${data?.lastname}`);
- localStorage.setItem("state", `${data?.state}`);
- localStorage.setItem("zip_code", `${data?.zip_code}`);
localStorage.setItem("session", `${data?.session}`);
navigate("/", { replace: true });
@@ -118,9 +82,37 @@ export default function VerifyLink() {
}
};
- useLayoutEffect(() => {
+ // for verifying the incoming verification link and render the correct component
+ const verifyEmail = useCallback(async (code) => {
+ try {
+ const verifyRes = await userApi.verifyEmail(code);
+ if (verifyRes.status === 200) {
+ let { data } = verifyRes;
+
+ if (
+ data &&
+ data.internal_return >= 0 &&
+ data.status_text === "Link Verfied"
+ ) {
+ setPageLoader(false);
+ } else {
+ setPageLoader(false);
+ setLinkSuccess(false);
+ }
+ }
+ } catch (error) {
+ setPageLoader(false);
+ setLinkSuccess(false);
+ throw new Error(error);
+ }
+ }, []);
+
+ // delay verify requests by 10000ms
+ const debouncedEmail = debounce(verifyEmail, 1000);
+
+ useEffect(() => {
debouncedEmail(token);
- });
+ }, []);
return (
<>
diff --git a/src/components/Home/Hero.jsx b/src/components/Home/Hero.jsx
index 06f0859..c4e04b0 100644
--- a/src/components/Home/Hero.jsx
+++ b/src/components/Home/Hero.jsx
@@ -9,6 +9,7 @@ import slider3 from "../../assets/images/slider-3.jpg";
import CountDown from "../Helpers/CountDown";
import SliderCom from "../Helpers/SliderCom";
import HomeSliders from "./HomeSliders";
+import { useSelector } from "react-redux";
export default function Hero({ className }) {
const settings = {
@@ -20,6 +21,8 @@ export default function Hero({ className }) {
};
const sildeData =null;
const [addFavorite, setValue] = useState(false);
+
+ const {userDetails} = useSelector((state) => state?.userDetails)
const favoriteHandler = () => {
if (!addFavorite) {
setValue(true);
@@ -29,6 +32,11 @@ export default function Hero({ className }) {
toast.warn("Remove to Favorite List");
}
};
+
+ let loginDate = userDetails?.last_login.split(' ')[0]
+ let {firstname, lastname, email, profile_pic} = userDetails
+ let userEmail = email.split('@')[0]
+
return (
- Last Login : 10-10-2026
+ Last Login : {loginDate}
{/* user */}
-

+
- Brokln Simons
+ {`${firstname} ${lastname}`}
-
@broklinslam_75
+
@{userEmail}
{/* countdown */}
diff --git a/src/components/Home/index.jsx b/src/components/Home/index.jsx
index d11733a..89c4872 100644
--- a/src/components/Home/index.jsx
+++ b/src/components/Home/index.jsx
@@ -10,6 +10,8 @@ import UpdateTable from "./UpdateTable";
import HomeTaskDisplay from "./HomeTaskDisplay";
import UsersService from "../../services/UsersService";
import usersService from "../../services/UsersService";
+import { useSelector } from "react-redux";
+
export default function Home() {
const trending = datas.datas;
@@ -18,10 +20,12 @@ export default function Home() {
const userApi = new usersService();
const homeData = userApi.getHomeDate();
+ const {userDetails} = useSelector((state) => state?.userDetails)
+
return (
-
+
{/*
*/}
{
- setInputs(value)
+ setInput(value)
}
//FUNCTION TO HANDLE SUBMIT
- const handleSubmit = (e) => {
- e.preventDefault();
+ const handleSubmit = () => {
+ setInputError('')
+ if(!input || input == '0'){
+ setInputError('Please Enter Amount')
+ return
+ }
- //valid inputs before submitting. Just for texting remove later
+ if(isNaN(input)){
+ setInputError('Amount must be a Number')
+ return
+ }
+
+ const stateData = {amount: Number(input)}
+ navigate('confirm-add-fund', {state: stateData})
- setInputs('')
+ setInput('')
}
return (
@@ -30,7 +45,7 @@ function AddFund({payment}) {
+ {inputError &&
{inputError}
}
diff --git a/src/components/MyWallet/ConfirmAddFund.jsx b/src/components/MyWallet/ConfirmAddFund.jsx
new file mode 100644
index 0000000..8f45f30
--- /dev/null
+++ b/src/components/MyWallet/ConfirmAddFund.jsx
@@ -0,0 +1,131 @@
+import React, {useState, useEffect} from 'react'
+import RecentActivityTable from './WalletComponent/RecentActivityTable'
+import LoadingSpinner from '../Spinners/LoadingSpinner'
+import InputCom from '../Helpers/Inputs/InputCom'
+import {toast} from 'react-toastify'
+import { useLocation, useNavigate } from 'react-router-dom'
+import { useSelector } from 'react-redux'
+
+import usersService from '../../services/UsersService'
+
+import { FlutterWaveButton, closePaymentModal } from 'flutterwave-react-v3'
+
+
+function ConfirmAddFund({payment}) {
+
+ let {userDetails} = useSelector(state => state.userDetails) // TO GET LOGGEDIN USER DETAILS
+
+ let [pageLoading, setPageLoading] = useState(true)
+
+ let [requestStatus, setRequestStatus] = useState({message: '', loading: false, status: false}) // STATE FOR API REQUEST
+
+ const apiURL = new usersService()
+ const navigate = useNavigate()
+
+ let {state} = useLocation()
+
+
+ //FUNCTION TO HANDLE SUBMIT
+ const onSuccessPayment = () => {
+ setRequestStatus({message: '', loading: true, status: false})
+ let reqData = {amount: state?.account, currency: 'NGN'}
+ apiURL.startTopUp(reqData).then((res)=>{
+ if(res.data.internal_return < 0){
+ setRequestStatus({message: 'Could not finish transaction', loading: false, status: false})
+ toast.success('Opps! something went wrong')
+ }
+ // do something
+ setRequestStatus({message: 'Topup successful', loading: false, status: true})
+ toast.success('Account Topup was sucessful')
+ setTimeout(()=>{
+ navigate('/my-wallet', {replace: true})
+ }, 1000)
+ }).catch(err => {
+ // do something
+ setRequestStatus({message: 'Opps! An Error Occured', loading: false, status: false})
+ toast.success('Opps! something went wrong')
+ })
+ }
+
+ const config = {
+ public_key: process.env.REACT_APP_FLUTTERWAVE_APIKEY,
+ tx_ref: Date.now(),
+ amount: state?.amount,
+ currency: 'NGN',
+ payment_options: 'card,mobilemoney,ussd',
+ customer: {
+ email: `${userDetails.email}`,
+ phone_number: userDetails.phone,
+ name: `${userDetails.lastname} ${userDetails.firstname}`
+ },
+ customizations: {
+ title: 'WrenchBoard',
+ description: 'Topup Payment',
+ logo: 'https://st2.depositphotos.com/4403291/7418/v/450/depositphotos_74189661-stock-illustration-online-shop-log.jpg',
+ },
+ };
+
+ const fwConfig = {
+ ...config,
+ text: 'Proceed',
+ callback: (response) => {
+ onSuccessPayment()
+ closePaymentModal() // this will close the modal programmatically
+ },
+ onClose: () => {},
+ };
+
+ useEffect(()=>{
+ // what happens if not state redirect user
+ if(!state){
+ navigate('../add-fund',{replace: true})
+ }else{
+ setPageLoading(false)
+ }
+ },[])
+
+ return (
+
+ {pageLoading ?
+
+ :
+
+
+
Confirm Add Fund To Account
+
+
+
+
+
+
+
+
+
+ }
+
+
+
+
Recent Activity
+
Activity Report
+ {payment.loading ?
+
+ :
+
+ }
+
+
+
+ )
+}
+
+export default ConfirmAddFund
\ No newline at end of file
diff --git a/src/components/MyWallet/ConfirmTransfer.jsx b/src/components/MyWallet/ConfirmTransfer.jsx
index 1ce2289..a0ef9bd 100644
--- a/src/components/MyWallet/ConfirmTransfer.jsx
+++ b/src/components/MyWallet/ConfirmTransfer.jsx
@@ -13,10 +13,7 @@ function ConfirmTransfer({payment, wallet}) {
const navigate = useNavigate()
let {state} = useLocation()
- // what happens if not state redirect user
- if(!state){
- navigate('../transfer-fund',{replace: true})
- }
+
let [requestStatus, setRequestStatus] = useState({message: '', loading: false, status: false})
let [pageLoading, setPageLoading] = useState(true)
@@ -45,7 +42,12 @@ function ConfirmTransfer({payment, wallet}) {
}
useEffect(()=>{
- setPageLoading(false)
+ // what happens if not state redirect user
+ if(!state){
+ navigate('../transfer-fund',{replace: true})
+ }else{
+ setPageLoading(false)
+ }
},[])
return (
diff --git a/src/components/MyWallet/TransferFund.jsx b/src/components/MyWallet/TransferFund.jsx
index bfc0b49..0e32309 100644
--- a/src/components/MyWallet/TransferFund.jsx
+++ b/src/components/MyWallet/TransferFund.jsx
@@ -125,8 +125,8 @@ function TransferFund({payment, wallet}) {
inputHandler={props.handleChange}
blurHandler={(e)=>{
getSendMoneyFee(e)
- props.handleBlur
}}
+ // props.handleBlur
// onMouseLeave={(e)=>{getSendMoneyFee(e)}}
/>
{(props.errors.amount && props.touched.amount) &&
{props.errors.amount}
}
diff --git a/src/components/MyWallet/Wallet.jsx b/src/components/MyWallet/Wallet.jsx
index 6f40c91..9b9cc16 100644
--- a/src/components/MyWallet/Wallet.jsx
+++ b/src/components/MyWallet/Wallet.jsx
@@ -9,6 +9,7 @@ import TransferFund from './TransferFund'
import AddFund from './AddFund'
import AddRecipient from './AddRecipient'
import ConfirmTransfer from './ConfirmTransfer'
+import ConfirmAddFund from './ConfirmAddFund'
function Wallet() {
return (
@@ -112,6 +113,7 @@ const WalletRoutes = () => {
}>
} />
+ } />
} />
} />
} />
diff --git a/src/components/Partials/Header.jsx b/src/components/Partials/Header.jsx
index 893d542..ef85dd6 100644
--- a/src/components/Partials/Header.jsx
+++ b/src/components/Partials/Header.jsx
@@ -15,6 +15,7 @@ import usersService from "../../services/UsersService";
import siteLogo from '../../assets/images/wrenchboard.png'
import Flag from '../../assets/images/united-states.svg'
+import { useSelector } from "react-redux";
export default function Header({ logoutModalHandler, sidebarHandler }) {
@@ -24,6 +25,8 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
const [moneyPopup, setPopup] = useToggle(false);
const [toggleNotification, setToggleNotification] = useToggle(false)
const darkMode = useContext(DarkModeContext);
+ const {userDetails} = useSelector((state) => state?.userDetails)
+
const [myWalletList, setMyWalletList] = useState([]);
const api = new usersService();
@@ -87,6 +90,11 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
const setNotification = ()=> {
setToggleNotification.toggle()
}
+
+ // User Profile
+ let {firstname, lastname, email, profile_pic} = userDetails
+ let userEmail = email.split('@')[0]
+
return (
<>
@@ -463,17 +471,17 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
{/* profile-image */}
- Brokln Simons
+ {`${firstname} ${lastname}`}
- @broklinslam_75
+ @{userEmail}
diff --git a/src/components/Partials/Layout.jsx b/src/components/Partials/Layout.jsx
index 26e8f5b..3c666d6 100644
--- a/src/components/Partials/Layout.jsx
+++ b/src/components/Partials/Layout.jsx
@@ -22,9 +22,9 @@ export default function Layout({ children }) {
const logOut = () => {
localStorage.removeItem("email");
localStorage.clear();
- toast.success("Come Back Soon", {
- icon: `🙂`,
- });
+ // toast.success("Come Back Soon", {
+ // icon: `🙂`,
+ // });
navigate("/login", { replace: true });
};
diff --git a/src/components/Referral/ReferralDisplay.jsx b/src/components/Referral/ReferralDisplay.jsx
index 2b514e0..620b5e1 100644
--- a/src/components/Referral/ReferralDisplay.jsx
+++ b/src/components/Referral/ReferralDisplay.jsx
@@ -2,6 +2,33 @@ import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { toast } from "react-toastify";
import usersService from '../../services/UsersService';
+import InputCom from '../Helpers/Inputs/InputCom';
+import LoadingSpinner from '../Spinners/LoadingSpinner';
+
+import {Formik, Form} from 'formik'
+import * as Yup from 'yup'
+
+const validationSchema = Yup.object().shape({
+ email: Yup.string()
+ .email('Wrong email format')
+ .min(3, 'Minimum 3 characters')
+ .max(50, 'Maximum 50 characters')
+ .required('Email is required'),
+ firstname: Yup.string()
+ .min(3, 'Minimum 3 characters')
+ .max(25, 'Maximum 25 characters')
+ .required('Firstname is required'),
+ lastname: Yup.string()
+ .min(3, 'Minimum 3 characters')
+ .max(25, 'Maximum 25 characters')
+ .required('Lastname is required'),
+ })
+
+ const initialValues = {
+ firstname: '',
+ lastname: '',
+ email: ''
+ }
function ReferralDisplay() {
const apiCall = new usersService() // GET API CALL
@@ -16,7 +43,7 @@ function ReferralDisplay() {
data: []
})
- let [error, setError] = useState({message: '', loading: false}) // for displaying error message on the page
+ let [error, setError] = useState({message: '', loading: false, status: false}) // for displaying error message on the page
//function to call referral history API
const allReferrals = () => {
@@ -38,49 +65,28 @@ function ReferralDisplay() {
const sendReferralMsg = (postData) => {
apiCall.sendReferralMsg(postData).then((res)=>{
if(res.data.internal_return < 0){
- setError({message:'Email already referred', loading: false})
+ setError({message:'Email already referred', loading: false, status: false})
return
}else{
- setInputs({ firstname: '', lastname: '', email: '',})
toast.success("Message Sent");
- setError({message:'', loading: false})
+ setError({message:'', loading: false, status: true})
setRefHistoryReload(prev => !prev)
}
}).catch((error)=>{
- setError({message:'Opps! an error occured, try again later', loading: false})
+ setError({message:'Opps! an error occured, try again later', loading: false, status: false})
})
}
- //STATE FOR CONTROLLED INPUTS
- let [inputs, setInputs] = useState({
- firstname: '',
- lastname: '',
- email: ''
- })
-
- // FUNCTION TO HANDLE INPUT CHANGE
- const handleChange = ({target:{name, value}}) => {
- setInputs(prev => ({...prev, [name]:value}))
- }
-
//FUNCTION TO HANDLE SUBMIT
- const handleSubmit = (e) => {
- e.preventDefault();
- setError({message: '', loading: true})
- let {firstname, lastname, email} = inputs
- if(!firstname || !lastname || !email){
- setError({message: 'Please fill all fields', loading: false})
- return
- }
+ const handleSubmit = (values, helpers) => {
+ setError({message: '', loading: true, status: false})
var postData = {
uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: 11032,
- ref_firstname: firstname,
- ref_lastname: lastname,
- ref_email: email
+ ...values
};
sendReferralMsg(postData) // FUNCTION TO SEND REFERRAL MESSAGE
@@ -95,56 +101,62 @@ function ReferralDisplay() {
@@ -153,14 +165,7 @@ function ReferralDisplay() {
Referral List
{referralList.loading ?
(
-
+
)
:
(
diff --git a/src/components/Spinners/LoadingSpinner.jsx b/src/components/Spinners/LoadingSpinner.jsx
index ed48666..13f172f 100644
--- a/src/components/Spinners/LoadingSpinner.jsx
+++ b/src/components/Spinners/LoadingSpinner.jsx
@@ -1,8 +1,8 @@
import React from 'react'
-function LoadingSpinner({size, color}) {
+function LoadingSpinner({size, color, height}) {
return (
-
+