Compare commits

...

7 Commits

13 changed files with 219 additions and 163 deletions
+96 -47
View File
@@ -1,6 +1,6 @@
import { Field, Form, Formik } from "formik"; import { Field, Form, Formik } from "formik";
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useDispatch } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup"; import * as Yup from "yup";
import usersService from "../../services/UsersService"; import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads"; import { tableReload } from "../../store/TableReloads";
@@ -43,7 +43,7 @@ const validationSchema = Yup.object().shape({
function AddJob({ popUpHandler, categories }) { function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService(); const ApiCall = new usersService();
const { walletDetails } = useSelector((state) => state.walletDetails);
let dispatch = useDispatch(); let dispatch = useDispatch();
let [currency, setCurrency] = useState({ let [currency, setCurrency] = useState({
@@ -90,9 +90,15 @@ function AddJob({ popUpHandler, categories }) {
}); });
}; };
// FUNCTION TO HANDLE ADD JOB FORM const getWalletDetail = (country) => {
const handleAddJob = (values, helpers) => { const walletChecker = walletDetails.result_list.find(
let reqData = { (item) => item.country === country
);
return walletChecker ? walletChecker.amount : 0;
};
const handleAddJob = async (values, helpers) => {
const reqData = {
country: values?.country, country: values?.country,
price: Number(values.price) * 100, price: Number(values.price) * 100,
title: values?.title, title: values?.title,
@@ -102,20 +108,34 @@ function AddJob({ popUpHandler, categories }) {
category: values.category?.join("@"), category: values.category?.join("@"),
}; };
const walletAmount = getWalletDetail(reqData.country);
if (reqData.price > walletAmount) {
setRequestStatus({
loading: false,
status: false,
message: "Insufficient Balance",
});
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 1500);
return;
}
setRequestStatus({ loading: true, status: false, message: "" }); setRequestStatus({ loading: true, status: false, message: "" });
ApiCall.jobManagerCreateJob(reqData)
.then((res) => { try {
if (res.data.internal_return < 1) { const res = await ApiCall.jobManagerCreateJob(reqData);
setRequestStatus({ if (res.data.internal_return < 1) {
loading: false, setRequestStatus({
status: false, loading: false,
message: "Could not complete your request at the moment", status: false,
}); message: "Could not complete your request at the moment",
setTimeout(() => { });
popUpHandler(); setTimeout(() => {
}, 1500); popUpHandler();
return; }, 1500);
} } else {
setRequestStatus({ setRequestStatus({
loading: false, loading: false,
status: true, status: true,
@@ -125,19 +145,18 @@ function AddJob({ popUpHandler, categories }) {
dispatch(tableReload({ type: "JOBTABLE" })); dispatch(tableReload({ type: "JOBTABLE" }));
popUpHandler(); popUpHandler();
}, 1000); }, 1000);
}) }
.catch((err) => { } catch (err) {
setRequestStatus({ setRequestStatus({
loading: false, loading: false,
status: false, status: false,
message: "Opps! something went wrong. Try Again", message: "Oops! Something went wrong. Try Again",
});
})
.finally(() => {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}); });
} finally {
setTimeout(() => {
setRequestStatus({ loading: false, status: false, message: "" });
}, 5000);
}
}; };
useEffect(() => { useEffect(() => {
@@ -164,7 +183,11 @@ function AddJob({ popUpHandler, categories }) {
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1" className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
> >
Currency Currency
{props.errors.country && props.touched.country && <span className="text-[12px] text-red-500">{props.errors.country}</span>} {props.errors.country && props.touched.country && (
<span className="text-[12px] text-red-500">
{props.errors.country}
</span>
)}
</label> </label>
<select <select
id="country" id="country"
@@ -214,7 +237,11 @@ function AddJob({ popUpHandler, categories }) {
value={props.values.price} value={props.values.price}
inputHandler={props.handleChange} inputHandler={props.handleChange}
blurHandler={props.handleBlur} blurHandler={props.handleBlur}
error={props.errors.price && props.touched.price && props.errors.price} error={
props.errors.price &&
props.touched.price &&
props.errors.price
}
/> />
</div> </div>
</div> </div>
@@ -231,7 +258,11 @@ function AddJob({ popUpHandler, categories }) {
value={props.values.title} value={props.values.title}
inputHandler={props.handleChange} inputHandler={props.handleChange}
blurHandler={props.handleBlur} blurHandler={props.handleBlur}
error={props.errors.title && props.touched.title && props.errors.title} error={
props.errors.title &&
props.touched.title &&
props.errors.title
}
/> />
</div> </div>
@@ -247,7 +278,11 @@ function AddJob({ popUpHandler, categories }) {
value={props.values.description} value={props.values.description}
inputHandler={props.handleChange} inputHandler={props.handleChange}
blurHandler={props.handleBlur} blurHandler={props.handleBlur}
error={props.errors.description && props.touched.description && props.errors.description} error={
props.errors.description &&
props.touched.description &&
props.errors.description
}
/> />
</div> </div>
@@ -259,7 +294,12 @@ function AddJob({ popUpHandler, categories }) {
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1' className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
> >
Job Delivery Details Job Delivery Details
{props.errors.job_detail && props.touched.job_detail && <span className="text-[12px] text-red-500">{props.errors.job_detail}</span>} {props.errors.job_detail &&
props.touched.job_detail && (
<span className="text-[12px] text-red-500">
{props.errors.job_detail}
</span>
)}
</label> </label>
<textarea <textarea
id="Job Delivery Details" id="Job Delivery Details"
@@ -286,19 +326,28 @@ function AddJob({ popUpHandler, categories }) {
role="group" role="group"
aria-labelledby="checked-group" aria-labelledby="checked-group"
> >
{Object?.entries(categories).map(([key, value]) => ( {categories ? (
<label <>
key={key} {Object?.entries(categories).map(([key, value]) => (
className="flex gap-1 w-full items-center" <label
> key={key}
<Field className="flex gap-1 w-full items-center"
type="checkbox" >
name="category" <Field
value={key} type="checkbox"
/> name="category"
<span className="text-[13.975px]">{value}</span> value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
</>
) : (
<label className="flex gap-1 w-full items-center">
<Field type="checkbox" name="category" />
<span className="text-[13.975px]">null</span>
</label> </label>
))} )}
<span className="h-5 text-sm italic text-[#cf3917]"> <span className="h-5 text-sm italic text-[#cf3917]">
{props.errors.category && {props.errors.category &&
props.touched.category && props.touched.category &&
@@ -1,26 +1,26 @@
import React, { useEffect } from 'react'; import React, { useEffect } from "react";
import { useLocation, useNavigate } from 'react-router-dom';
import usersService from '../../../services/UsersService';
import {updateUserDetails} from "../../../store/UserDetails";
import { useDispatch } from "react-redux"; import { useDispatch } from "react-redux";
import { useLocation, useNavigate } from "react-router-dom";
import usersService from "../../../services/UsersService";
import { updateUserDetails } from "../../../store/UserDetails";
import AuthLayout from "../AuthLayout"; import AuthLayout from "../AuthLayout";
function Redirect() { function Redirect() {
const location = useLocation(); const location = useLocation();
const navigate = useNavigate(); const navigate = useNavigate();
const userApi = new usersService(); const userApi = new usersService();
const dispatch = useDispatch() const dispatch = useDispatch();
const queryParams = new URLSearchParams(location?.search); const queryParams = new URLSearchParams(location?.search);
const codeResponse = queryParams.get("code"); const codeResponse = queryParams.get("code");
useEffect(()=>{ useEffect(() => {
if(!codeResponse){ if (!codeResponse) {
navigate('/login', {state: {error: true}}) navigate("/login", { state: { error: true } });
return return;
} }
console.log(codeResponse); console.log(codeResponse);
/* /*
POST /token HTTP/1.1 POST /token HTTP/1.1
Host: oauth2.googleapis.com Host: oauth2.googleapis.com
Content-Type: application/x-www-form-urlencoded Content-Type: application/x-www-form-urlencoded
@@ -31,34 +31,40 @@ function Redirect() {
redirect_uri=https%3A//oauth2.example.com/code& redirect_uri=https%3A//oauth2.example.com/code&
grant_type=authorization_code grant_type=authorization_code
*/ */
var reqData = { var reqData = {
auth_type: "GOOGLE", auth_type: "GOOGLE",
code: codeResponse, code: codeResponse,
redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL, redirect_uri: process.env.REACT_APP_GOOGLE_REDIRECT_URL,
}; };
userApi userApi
.authStart(reqData) .authStart(reqData)
.then((res) => { .then((res) => {
if (res.status == 200 && res.data.internal_return >= 0 && res.data.member_id && res.data.uid && res.data.session) { if (
localStorage.setItem("member_id", `${res.data.member_id}`); res.status == 200 &&
localStorage.setItem("uid", `${res.data.uid}`); res.data.internal_return >= 0 &&
localStorage.setItem("session_token", `${res.data.session}`); res.data.member_id &&
dispatch(updateUserDetails({...res.data})); res.data.uid &&
navigate('/', {replace: true}) res.data.session
return ) {
} localStorage.setItem("member_id", `${res.data.member_id}`);
navigate('/login', {state: {error: true}}) localStorage.setItem("uid", `${res.data.uid}`);
}) localStorage.setItem("session_token", `${res.data.session}`);
.catch((error) => { dispatch(updateUserDetails({ ...res.data }));
navigate('/login', {state: {error: true}}) navigate("/", { replace: true });
console.log(error); return;
}); }
},[]) navigate("/login", { state: { error: true } });
})
.catch((error) => {
navigate("/login", { state: { error: true } });
console.log(error);
});
}, []);
return ( return (
<AuthLayout> <AuthLayout>
<div className='min-h-[70vh]'>Redirecting ... </div> <div className="min-h-[70vh]">Redirecting ... </div>
</AuthLayout> </AuthLayout>
) );
} }
export default Redirect export default Redirect;
+2 -1
View File
@@ -26,7 +26,8 @@ const CouponPopup = ({ popUpHandler, data }) => {
setStatusMsg({ error: "An error occurred" }); setStatusMsg({ error: "An error occurred" });
else setStatusMsg({ success: res.data?.status_text }); else setStatusMsg({ success: res.data?.status_text });
dispatch(tableReload({ type: "COUPONTABLE" })); // dispatch(tableReload({ type: "COUPONTABLE" }));
dispatch(tableReload({ type: "WALLETTABLE" }));
setTimeout(() => { setTimeout(() => {
popUpHandler(); popUpHandler();
setLoader(false); setLoader(false);
+2 -2
View File
@@ -7,7 +7,7 @@ import { useSelector } from "react-redux";
export default function MyCoupons() { export default function MyCoupons() {
const apiCall = useMemo(() => new usersService(), []); const apiCall = useMemo(() => new usersService(), []);
const {couponTable} = useSelector(state => state.tableReload) const {couponTable, walletTable} = useSelector(state => state.tableReload)
let [couponHistory, setCouponHistory] = useState({ let [couponHistory, setCouponHistory] = useState({
// FOR COUPON HISTORY // FOR COUPON HISTORY
loading: true, loading: true,
@@ -38,7 +38,7 @@ export default function MyCoupons() {
useEffect(() => { useEffect(() => {
getCouponHistory(); getCouponHistory();
}, [couponTable]); }, [couponTable, walletTable]);
return ( return (
<> <>
+7 -39
View File
@@ -1,45 +1,21 @@
import React, { import React, { Suspense, lazy, useEffect, useState } from "react";
Suspense,
lazy,
useCallback,
useEffect,
useMemo,
useState,
} from "react";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import usersService from "../../services/UsersService"; import usersService from "../../services/UsersService";
import Layout from "../Partials/Layout"; import Layout from "../Partials/Layout";
import LoadingSpinner from "../Spinners/LoadingSpinner"; import LoadingSpinner from "../Spinners/LoadingSpinner";
const WalletBox = lazy(() => import("./WalletBox")); const WalletBox = lazy(() => import("./WalletBox"));
const WalletRoutes = () => { const WalletRoutes = () => {
const apiCall = new usersService(); const apiCall = new usersService();
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
const { walletTable } = useSelector((state) => state.tableReload); const { walletTable } = useSelector((state) => state.tableReload);
const [walletList, setWalletList] = useState({
loading: true,
data: [],
reload: false,
});
const [paymentHistory, setPaymentHistory] = useState({ const [paymentHistory, setPaymentHistory] = useState({
loading: true, loading: true,
data: [], data: [],
}); });
const getWalletList = () => {
apiCall
.getUserWallets()
.then((res) => {
if (res.data.internal_return < 0) {
setWalletList({ loading: false, data: [] });
} else {
setWalletList({ loading: false, data: res.data?.result_list });
}
})
.catch(() => {
setWalletList({ loading: false, data: [] });
});
}
const getPaymentHistory = () => { const getPaymentHistory = () => {
apiCall apiCall
.getPaymentHx() .getPaymentHx()
@@ -53,24 +29,16 @@ const WalletRoutes = () => {
.catch(() => { .catch(() => {
setPaymentHistory({ loading: false, data: [] }); setPaymentHistory({ loading: false, data: [] });
}); });
} };
useEffect(() => { useEffect(() => {
// const fetchData = async () => { getPaymentHistory();
// await Promise.all([getWalletList(), getPaymentHistory()]);
// };
// if (walletList.loading) {
// fetchData();
// }
getWalletList()
getPaymentHistory()
}, [walletTable]); }, [walletTable]);
return ( return (
<Layout> <Layout>
<Suspense fallback={<LoadingSpinner size="16" color="sky-blue" />}> <Suspense fallback={<LoadingSpinner size="16" color="sky-blue" />}>
<WalletBox wallet={walletList} payment={paymentHistory} /> <WalletBox wallet={walletDetails} payment={paymentHistory} />
</Suspense> </Suspense>
</Layout> </Layout>
); );
+1 -1
View File
@@ -13,7 +13,7 @@ export default function WalletBox({ wallet, payment }) {
</div> </div>
) : wallet.data.length ? ( ) : wallet.data.length ? (
wallet.data.map((item, index) => ( wallet.data.map((item, index) => (
<div className="lg:w-full h-full mb-10 lg:mb-0"> <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} />
</div> </div>
)) ))
+2 -2
View File
@@ -41,8 +41,8 @@ export default function WalletHeader(props) {
<div className="content px-7 pb-7"> <div className="content px-7 pb-7">
<ul> <ul>
{props.myWalletList && {props.myWalletList &&
props.myWalletList?.result_list?.length > 0 && props.myWalletList?.length > 0 &&
props.myWalletList.result_list.map((value, index) => props.myWalletList.map((value, index) =>
{ {
let image = value.code ? `${value.code.toLocaleLowerCase()}.svg` : 'default.png' let image = value.code ? `${value.code.toLocaleLowerCase()}.svg` : 'default.png'
return( return(
+3 -15
View File
@@ -15,6 +15,7 @@ import WalletHeader from "../MyWallet/WalletHeader";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import Flag from "../../assets/images/united-states.svg"; import Flag from "../../assets/images/united-states.svg";
import siteLogo from "../../assets/images/wrenchboard-logo-text.png"; import siteLogo from "../../assets/images/wrenchboard-logo-text.png";
// import { updateWalletDetails } from "../../store/walletDetails";
import TimeDifference from "../Helpers/TimeDifference"; import TimeDifference from "../Helpers/TimeDifference";
const DEFAULT_PROFILE_IMAGE = require("../../assets/images/profile.jpg"); const DEFAULT_PROFILE_IMAGE = require("../../assets/images/profile.jpg");
@@ -33,19 +34,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
const navigate = useNavigate(); const navigate = useNavigate();
const { notifications } = useSelector((state) => state?.notifications); // NOTIFICATION STORE const { notifications } = useSelector((state) => state?.notifications); // NOTIFICATION STORE
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
const getMyWalletList = async () => {
try {
const res = await api.getUserWallets(null);
console.log("wallet - > ", res.data);
setMyWalletList(res.data);
} catch (error) {
console.log("Error getting mode");
}
};
useEffect(() => {
getMyWalletList();
}, [walletTable]);
const handlerBalance = () => { const handlerBalance = () => {
setbalanceValue.toggle(); setbalanceValue.toggle();
@@ -104,7 +93,6 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
let userEmail = email?.split("@")[0]; let userEmail = email?.split("@")[0];
const userProfileImage = profile_pic_url || DEFAULT_PROFILE_IMAGE; const userProfileImage = profile_pic_url || DEFAULT_PROFILE_IMAGE;
return ( return (
<> <>
<div className="header-wrapper backdrop-blur-sm bg-[#efedfe5e]/60 dark:bg-transparent w-full h-full flex items-center xl:px-0 md:px-10 px-5"> <div className="header-wrapper backdrop-blur-sm bg-[#efedfe5e]/60 dark:bg-transparent w-full h-full flex items-center xl:px-0 md:px-10 px-5">
@@ -241,7 +229,7 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
{/*<div className="lg:hidden block"></div>*/} {/*<div className="lg:hidden block"></div>*/}
<WalletHeader <WalletHeader
myWalletList={myWalletList} myWalletList={walletDetails.data}
handlerBalance={handlerBalance} handlerBalance={handlerBalance}
balanceDropdown={balanceDropdown} balanceDropdown={balanceDropdown}
addMoneyHandler={addMoneyHandler} addMoneyHandler={addMoneyHandler}
+12 -7
View File
@@ -158,13 +158,18 @@ export default function Resources(props) {
return ( return (
<ul className="lg:flex lg:space-x-14 space-x-8"> <ul className="lg:flex lg:space-x-14 space-x-8">
{tabCategories?.length > 0 && {tabCategories?.length > 0 &&
tabCategories?.map((tabValue, idx) => ( tabCategories?.map((tabValue, idx) => {
<TabItem if(tabValue.enabled){
key={tabValue.id} return (
tabValue={tabValue} <TabItem
isActive={tab === tabValue.name || (idx === 0 && tab === "")} key={tabValue.id}
/> tabValue={tabValue}
))} isActive={tab === tabValue.name || (idx === 0 && tab === "")}
/>
)
}
}
)}
</ul> </ul>
); );
}; };
+17 -1
View File
@@ -10,6 +10,7 @@ import { updateUserDetails } from "../store/UserDetails";
import { updateJobs } from "../store/jobLists"; import { updateJobs } from "../store/jobLists";
import { updateNotifications } from "../store/notifications"; import { updateNotifications } from "../store/notifications";
import { updateUserJobList } from "../store/userJobList"; import { updateUserJobList } from "../store/userJobList";
import { updateWalletDetails } from "../store/walletDetails";
const AuthRoute = ({ redirectPath = "/login", children }) => { const AuthRoute = ({ redirectPath = "/login", children }) => {
const apiCall = useMemo(() => new usersService(), []); const apiCall = useMemo(() => new usersService(), []);
@@ -19,7 +20,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
const [loadProfileDetails, setLoadProfileDetails] = useState([]); const [loadProfileDetails, setLoadProfileDetails] = useState([]);
const navigate = useNavigate(); const navigate = useNavigate();
const { jobListTable } = useSelector((state) => state.tableReload); const { jobListTable, walletTable } = useSelector((state) => state.tableReload);
const { const {
userDetails: { username, uid, session }, userDetails: { username, uid, session },
@@ -177,6 +178,21 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
getMyJobList(); getMyJobList();
}, [jobListTable]); }, [jobListTable]);
useEffect(() => {
const getMyWalletList = async () => {
dispatch(updateWalletDetails({ loading: true, data:[] }));
try {
const res = await apiCall.getUserWallets();
console.log("wallet - >", res.data);
dispatch(updateWalletDetails({ loading: false, data:res.data?.result_list }));
} catch (error) {
dispatch(updateWalletDetails({ loading: false, data:[] }));
console.log("Error getting mode");
}
};
getMyWalletList();
}, [walletTable]);
useEffect(() => { useEffect(() => {
// Getting market data // Getting market data
const getMarketActiveJobList = async () => { const getMarketActiveJobList = async () => {
+4 -4
View File
@@ -1,15 +1,15 @@
import { createSlice } from "@reduxjs/toolkit"; import { createSlice } from "@reduxjs/toolkit";
const initialState = { const initialState = {
userDetails: {} userDetails: {},
}; };
export const userSlice = createSlice({ export const userSlice = createSlice({
name: "userDetails", name: "userDetails",
initialState, initialState,
reducers: { reducers: {
updateUserDetails: (state,action) => { updateUserDetails: (state, action) => {
state.userDetails = {...action.payload} state.userDetails = { ...action.payload };
}, },
}, },
}); });
@@ -17,4 +17,4 @@ export const userSlice = createSlice({
// Action creators are generated for each case reducer function // Action creators are generated for each case reducer function
export const { updateUserDetails } = userSlice.actions; export const { updateUserDetails } = userSlice.actions;
export default userSlice.reducer; export default userSlice.reducer;
+2
View File
@@ -7,6 +7,7 @@ import userDetailReducer from "./UserDetails";
import jobReducer from "./jobLists"; import jobReducer from "./jobLists";
import notificationsReducer from "./notifications"; import notificationsReducer from "./notifications";
import userJobListReducer from "./userJobList"; import userJobListReducer from "./userJobList";
import walletDetails from "./walletDetails";
export default configureStore({ export default configureStore({
reducer: { reducer: {
@@ -17,5 +18,6 @@ export default configureStore({
userJobList: userJobListReducer, userJobList: userJobListReducer,
commonHeadBanner: commonHeadBannerReducer, commonHeadBanner: commonHeadBannerReducer,
notifications: notificationsReducer, notifications: notificationsReducer,
walletDetails: walletDetails
}, },
}); });
+21
View File
@@ -0,0 +1,21 @@
import { createSlice } from "@reduxjs/toolkit";
const initialState = {
walletDetails: {
loading: true,
data: []
},
};
export const walletSlice = createSlice({
name: "walletDetails",
initialState,
reducers: {
updateWalletDetails: (state, action) => {
state.walletDetails = { ...action.payload };
},
},
});
export const { updateWalletDetails } = walletSlice.actions;
export default walletSlice.reducer;