Compare commits
98 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 393b4e7b37 | |||
| b2411285bc | |||
| 091faec9bd | |||
| 15fc2e6c9b | |||
| 3ef68adf7e | |||
| 71f987cbff | |||
| e8c3dca731 | |||
| 85d15ac2f1 | |||
| 236dfeae2c | |||
| 734acd9dd5 | |||
| 3f9534b968 | |||
| 13d69efbaf | |||
| 151aa4ba0c | |||
| 4985256c3a | |||
| 5c552fe006 | |||
| b14272f040 | |||
| 5e4887c01c | |||
| f6e55718bb | |||
| 98ddc5aaae | |||
| 53d8b0f411 | |||
| b3d33f87cf | |||
| 5922064c42 | |||
| eae8dc43ec | |||
| 34e6322126 | |||
| b3db82000b | |||
| fa565437f0 | |||
| 59f407ba26 | |||
| e7afc12334 | |||
| 5f81c0b847 | |||
| 0756747143 | |||
| 99eb1591a2 | |||
| 85b9aab229 | |||
| 67a0a34288 | |||
| 814e4c9693 | |||
| e74eb38caf | |||
| 67db1a72bf | |||
| 41e46d8030 | |||
| 8707411dda | |||
| 327a4a42ae | |||
| 455f4001f7 | |||
| 775bcd5005 | |||
| 492bda021f | |||
| eacfae19f0 | |||
| 376cf44a9c | |||
| 1aab0c2910 | |||
| 1f5fb8c7c3 | |||
| a303b24b53 | |||
| 322927045c | |||
| 3cf1dc7166 | |||
| 8ed331e909 | |||
| 2428923f73 | |||
| 139f87c919 | |||
| c24fbedf2c | |||
| 1a817e723e | |||
| c023911cd4 | |||
| 907dc298ab | |||
| 8095006386 | |||
| d9d28791da | |||
| a98d967ba4 | |||
| 59a5e84ee2 | |||
| 35ed1d48a3 | |||
| f9d385dfec | |||
| 033af03221 | |||
| b3afb94030 | |||
| 0b1aa4e7d9 | |||
| 18c4f31322 | |||
| 97e3344953 | |||
| 9565ca0d35 | |||
| 51cc4edc1d | |||
| d12a1fd579 | |||
| 21f1173e66 | |||
| cf5ae81918 | |||
| 235bb7d667 | |||
| 18a319a437 | |||
| d3e2cc6744 | |||
| a08d8feb7d | |||
| 7cb5cc558b | |||
| 36025ade56 | |||
| b80c21358a | |||
| f5ad68f7bb | |||
| 27cba1ac68 | |||
| dbb21d8a08 | |||
| a4597599b7 | |||
| 894f986574 | |||
| e42a358da6 | |||
| 23c2e840c2 | |||
| 7e7176ae5c | |||
| 306fa1300d | |||
| 7b3178bd1a | |||
| 5e94ddee48 | |||
| 32171ae3ce | |||
| 2c99fedd8c | |||
| 7e926cc7bc | |||
| 587bcaa411 | |||
| 756628ee1a | |||
| c77d8e5693 | |||
| 7e9d734e6f | |||
| 0c1db6b4b5 |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -1,16 +1,22 @@
|
||||
import Toaster from "./components/Helpers/Toaster";
|
||||
import Routers from "./Routers";
|
||||
import Default from "./components/Partials/Default";
|
||||
import { Navigate, useLocation } from "react-router-dom";
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<Default>
|
||||
<>
|
||||
<Routers />
|
||||
<Toaster />
|
||||
</>
|
||||
</Default>
|
||||
);
|
||||
const {pathname} = useLocation()
|
||||
return (
|
||||
<Default>
|
||||
<>
|
||||
{pathname.startsWith('/@') ?
|
||||
<Navigate to="/app" replace={true} />
|
||||
:
|
||||
<Routers />
|
||||
}
|
||||
<Toaster />
|
||||
</>
|
||||
</Default>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -50,6 +50,9 @@ import FamilyMarketPage from "./views/FamilyMarketPage";
|
||||
import FacebookRedirect from "./views/FacebookRedirect";
|
||||
import AppleRedirectPage from "./views/AppleRedirectPage";
|
||||
import LndPage from "./views/LndPage";
|
||||
import FamilySettingsPage from "./views/FamilySettingsPage";
|
||||
import AppDownloadPage from "./views/AppDownloadPage";
|
||||
import JobGroupsPage from "./views/JobGroupsPage";
|
||||
|
||||
export default function Routers() {
|
||||
return (
|
||||
@@ -78,6 +81,7 @@ export default function Routers() {
|
||||
<Route path="/complereset" element={<VerifyPasswordPages />} />
|
||||
<Route exact path="/outmessage" element={<VerifyYouPages />} />
|
||||
<Route exact path="/lnd/*" element={<LndPage />} />
|
||||
<Route exact path="/app" element={<AppDownloadPage />} />
|
||||
|
||||
{/* private route */}
|
||||
<Route element={<AuthRoute />}>
|
||||
@@ -98,6 +102,7 @@ export default function Routers() {
|
||||
<Route exact path="/market-place" element={<MarketPlacePage />} />
|
||||
<Route exact path="/market" element={<MarketPlacePage />} />
|
||||
<Route exact path="/familymarket" element={<FamilyMarketPage />} />
|
||||
<Route exact path="/familysettings" element={<FamilySettingsPage />} />
|
||||
<Route exact path="/notification" element={<Notification />} />
|
||||
<Route exact path="/mytask" element={<MyTaskPage />} />
|
||||
<Route exact path="/myjobs" element={<MyJobsPage />} />
|
||||
@@ -130,6 +135,7 @@ export default function Routers() {
|
||||
<Route exact path="/user-profile" element={<UserProfilePage />} />
|
||||
<Route exact path="/settings" element={<SettingsPage />} />
|
||||
<Route exact path="/referral" element={<ReferralPage />} />
|
||||
<Route exact path="/job-groups" element={<JobGroupsPage />} />
|
||||
</Route>
|
||||
<Route path="*" element={<FourZeroFour />} />
|
||||
</Routes>
|
||||
|
||||
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" id="DownloadArrow"><g fill="#4687ba" class="color9a7baa svgShape"><path d="M30.179 3.525V1.857c0-1.551-1.784-2.415-3.011-1.449L24 2.907 20.824.402c-1.216-.959-3.002-.093-3.002 1.456v1.668a1 1 0 0 0 1 1h10.357c.552-.001 1-.449 1-1.001zM30.179 25.172v-2.524a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357a1 1 0 0 0 1-1zM30.179 10.74V8.217a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357c.552-.001 1-.448 1-1.001zM30.179 17.956v-2.524a1 1 0 0 0-1-1H18.822a1 1 0 0 0-1 1v2.524a1 1 0 0 0 1 1h10.357a1 1 0 0 0 1-1zM25.748 47.029l9.336-15.018c.852-1.371-.133-3.145-1.748-3.145H14.664c-1.614 0-2.6 1.774-1.748 3.145l9.336 15.018a2.058 2.058 0 0 0 3.496 0z" fill="#000000" class="color000000 svgShape"></path></g></svg>
|
||||
|
After Width: | Height: | Size: 799 B |
@@ -1,74 +1,28 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import * as Yup from "yup";
|
||||
import usersService from "../../services/UsersService";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import {
|
||||
validationSchema as VS,
|
||||
useDispatch,
|
||||
useSelector,
|
||||
usersService,
|
||||
initialValues as IV,
|
||||
initialReqState,
|
||||
useState,
|
||||
tableReload,
|
||||
Formik,
|
||||
InputCom,
|
||||
Field,
|
||||
Form,
|
||||
LoadingSpinner,
|
||||
} from "./settings";
|
||||
|
||||
const validationSchema = Yup.object().shape({
|
||||
country: Yup.string()
|
||||
.min(1, "Minimum 3 characters")
|
||||
.max(25, "Maximum 25 characters")
|
||||
.required("Currency is required"),
|
||||
price: Yup.string()
|
||||
.typeError("Invalid number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.test("no-e", "Invalid number", (value) => {
|
||||
if (value && /\d+e/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.required("Price is required"),
|
||||
title: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(149, "Maximum 149 characters")
|
||||
.required("Title is required"),
|
||||
description: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(299, "Maximum 299 characters")
|
||||
.required("Description is required"),
|
||||
job_detail: Yup.string()
|
||||
.min(3, "Minimum 3 characters")
|
||||
.max(499, "Maximum 499 characters")
|
||||
.required("Details is required"),
|
||||
timeline_days: Yup.number()
|
||||
.typeError("you must specify a number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.required("Timeline is required"),
|
||||
category: Yup.array().min(1, "Select at least one checkbox"),
|
||||
});
|
||||
const validationSchema = VS;
|
||||
|
||||
function AddJob({ popUpHandler, categories }) {
|
||||
const ApiCall = new usersService();
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
|
||||
let dispatch = useDispatch();
|
||||
|
||||
let initialValues = {
|
||||
// initial values for formik
|
||||
country: "",
|
||||
price: "",
|
||||
title: "",
|
||||
description: "",
|
||||
job_detail: "",
|
||||
timeline_days: "",
|
||||
category: [],
|
||||
};
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // Holds state when submit button is pressed
|
||||
|
||||
const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
const walletChecker = walletDetails?.data.find(
|
||||
(item) => item.country === country
|
||||
);
|
||||
return walletChecker ? walletChecker.amount : 0;
|
||||
};
|
||||
const [requestStatus, setRequestStatus] = useState(initialReqState); // Holds state when submit button is pressed
|
||||
|
||||
const handleAddJob = async (values, helpers) => {
|
||||
const reqData = {
|
||||
@@ -81,21 +35,6 @@ function AddJob({ popUpHandler, categories }) {
|
||||
category: values.category?.join("@"),
|
||||
};
|
||||
|
||||
const walletAmount = getWalletDetail(reqData.country); // GETTING USER BALANCE BASED ON COUNTRY SELECTED
|
||||
|
||||
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: "" });
|
||||
|
||||
try {
|
||||
@@ -136,7 +75,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
return (
|
||||
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
initialValues={IV}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleAddJob}
|
||||
>
|
||||
@@ -167,7 +106,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
onChange={props.handleChange}
|
||||
onBlur={props.handleBlur}
|
||||
>
|
||||
{walletDetails.loading ? (
|
||||
{walletDetails?.loading ? (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Loading...
|
||||
</option>
|
||||
@@ -258,7 +197,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
<div className="sm:w-[60%] w-full">
|
||||
<label
|
||||
htmlFor="Job Delivery Details"
|
||||
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
|
||||
>
|
||||
Job Delivery Details
|
||||
{props.errors.job_detail &&
|
||||
@@ -400,13 +339,12 @@ function AddJob({ popUpHandler, categories }) {
|
||||
</span>
|
||||
</button>
|
||||
|
||||
{requestStatus.loading ? (
|
||||
{requestStatus?.loading ? (
|
||||
<LoadingSpinner size="8" color="sky-blue" />
|
||||
) : (
|
||||
<button
|
||||
type="submit"
|
||||
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
|
||||
>
|
||||
Add Job
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { Field, Form, Formik } from "formik";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import * as Yup from "yup";
|
||||
import usersService from "../../services/UsersService";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
|
||||
// Initialize state for request values
|
||||
const initialReqState = {
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
};
|
||||
|
||||
// For form initial values
|
||||
const initialValues = {
|
||||
// initial values for formik
|
||||
country: "",
|
||||
price: "",
|
||||
title: "",
|
||||
description: "",
|
||||
job_detail: "",
|
||||
timeline_days: "",
|
||||
category: [],
|
||||
};
|
||||
|
||||
// const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
// const walletChecker = walletDetails?.data.find(
|
||||
// (item) => item.country === country
|
||||
// );
|
||||
// return walletChecker ? walletChecker.amount : 0;
|
||||
// };
|
||||
|
||||
// To get the validation schema
|
||||
const validationSchema = Yup.object().shape({
|
||||
country: Yup.string()
|
||||
.min(1, "Minimum 3 characters")
|
||||
.max(25, "Maximum 25 characters")
|
||||
.required("Currency is required"),
|
||||
price: Yup.string()
|
||||
.typeError("Invalid number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.test("no-e", "Invalid number", (value) => {
|
||||
if (value && /\d+e/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
})
|
||||
.required("Price is required"),
|
||||
title: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(149, "Maximum 149 characters")
|
||||
.required("Title is required"),
|
||||
description: Yup.string()
|
||||
.min(5, "Minimum 5 characters")
|
||||
.max(299, "Maximum 299 characters")
|
||||
.required("Description is required"),
|
||||
job_detail: Yup.string()
|
||||
.min(3, "Minimum 3 characters")
|
||||
.max(499, "Maximum 499 characters")
|
||||
.required("Details is required"),
|
||||
timeline_days: Yup.number()
|
||||
.typeError("you must specify a number")
|
||||
.min(1, "Price must be greater than 0")
|
||||
.required("Timeline is required"),
|
||||
category: Yup.array().min(1, "Select category"),
|
||||
});
|
||||
|
||||
const getWalletDetail = (countryParams, walletDetails) => {
|
||||
// A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
|
||||
const walletChecker = walletDetails?.data.find(
|
||||
(item) => item.country === countryParams
|
||||
);
|
||||
return walletChecker
|
||||
? {
|
||||
description: walletChecker.description,
|
||||
country: walletChecker.country,
|
||||
}
|
||||
: "";
|
||||
};
|
||||
|
||||
export {
|
||||
Field,
|
||||
Form,
|
||||
Formik,
|
||||
useState,
|
||||
useEffect,
|
||||
useDispatch,
|
||||
useSelector,
|
||||
usersService,
|
||||
InputCom,
|
||||
LoadingSpinner,
|
||||
initialReqState,
|
||||
initialValues,
|
||||
validationSchema,
|
||||
getWalletDetail,
|
||||
tableReload
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
export default function AppDownload() {
|
||||
return (
|
||||
<div>
|
||||
<div className='h-screen flex justify-center items-center'>App Download Content Comes here</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -17,14 +17,16 @@ import { updateUserDetails } from "../../../store/UserDetails";
|
||||
import ReCAPTCHA from "react-google-recaptcha";
|
||||
|
||||
export default function Login() {
|
||||
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
const queryParams = new URLSearchParams(location?.search);
|
||||
const sessionExpired = queryParams.get("sessionExpired")
|
||||
// const sessionExpired = queryParams.get("sessionExpired");
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const { state } = useLocation();
|
||||
|
||||
const [validCaptcha, setValidCaptcha] = useState({show: false, valid:''}); // FOR CAPTCHA
|
||||
const [sessionExpired, setSessionExpired] = useState(queryParams.get("sessionExpired"))
|
||||
|
||||
const [validCaptcha, setValidCaptcha] = useState({ show: false, valid: "" }); // FOR CAPTCHA
|
||||
|
||||
let [loginType, setLoginType] = useState("");
|
||||
|
||||
@@ -115,7 +117,8 @@ export default function Login() {
|
||||
}, Number(process.env.REACT_APP_LOGIN_ERROR_TIMEOUT));
|
||||
return;
|
||||
}
|
||||
if(name == "full" && !validCaptcha.valid && validCaptcha.show){ // RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
|
||||
if (name == "full" && !validCaptcha.valid && validCaptcha.show) {
|
||||
// RUNS AND DISPLAYS CAPTCHA, IF ERROR OCCURED DURING LOGIN FOR FULL LOGIN ALONE
|
||||
setMsgError("Please Verify Captcha");
|
||||
setLoginLoading(false);
|
||||
setTimeout(() => {
|
||||
@@ -136,12 +139,15 @@ export default function Login() {
|
||||
// setMsgError("Wrong, email/password");
|
||||
setLoginError(true);
|
||||
setLoginLoading(false);
|
||||
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
|
||||
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
|
||||
return;
|
||||
}
|
||||
localStorage.setItem("member_id", `${res.data.member_id}`);
|
||||
localStorage.setItem("uid", `${res.data.uid}`);
|
||||
localStorage.setItem("session_token", `${res.data.session}`);
|
||||
if (name === "family") {
|
||||
sessionStorage.setItem("family_uid", res.data?.family_uid);
|
||||
}
|
||||
// localStorage.setItem("session", `${res.data.session}`);
|
||||
dispatch(updateUserDetails({ ...res.data }));
|
||||
setTimeout(() => {
|
||||
@@ -152,7 +158,7 @@ export default function Login() {
|
||||
.catch((error) => {
|
||||
setMsgError("Unable to login, try again");
|
||||
setLoginLoading(false);
|
||||
setValidCaptcha(prev => ({...prev, show:true})) // DISPLAYS CAPTCHA IF ERROR
|
||||
setValidCaptcha((prev) => ({ ...prev, show: true })); // DISPLAYS CAPTCHA IF ERROR
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
@@ -163,11 +169,12 @@ export default function Login() {
|
||||
});
|
||||
};
|
||||
|
||||
function captchaChecker(value) { // FUNCTION TO VALIDATE CAPTCHA
|
||||
if(value){
|
||||
setValidCaptcha({show: true, valid:value})
|
||||
}else{
|
||||
setValidCaptcha({show: true, valid:''})
|
||||
function captchaChecker(value) {
|
||||
// FUNCTION TO VALIDATE CAPTCHA
|
||||
if (value) {
|
||||
setValidCaptcha({ show: true, valid: value });
|
||||
} else {
|
||||
setValidCaptcha({ show: true, valid: "" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,6 +237,20 @@ export default function Login() {
|
||||
setPassword("");
|
||||
}, [loginType]);
|
||||
|
||||
|
||||
// EFFECT TO CLEAR SESSION EXPIRY IF IT EXISTS AFTER SOME SECONDS
|
||||
useEffect(()=>{
|
||||
let timer;
|
||||
if(sessionExpired == "true"){
|
||||
timer = setTimeout(()=>{
|
||||
setSessionExpired(false)
|
||||
},5000)
|
||||
}
|
||||
return () => {
|
||||
clearTimeout(timer)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<AuthLayout slogan="Welcome to WrenchBoard">
|
||||
@@ -245,31 +266,32 @@ export default function Login() {
|
||||
</div>
|
||||
<div className="content-wrapper login shadow-md w-full lg:max-w-[530px] 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="w-full">
|
||||
|
||||
{/* HIDES THIS IF USER SESSION HAS EXPIRED */}
|
||||
{sessionExpired != 'true' &&
|
||||
<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]">
|
||||
{sessionExpired != "true" && (
|
||||
<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]">
|
||||
Sign In to WrenchBoard
|
||||
</h1> */}
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
New Here?{" "}
|
||||
<Link
|
||||
to="/signup"
|
||||
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
|
||||
>
|
||||
Create an Account
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
<span className="text-gray-400 font-medium text-[16.25px] leading-[24.375px]">
|
||||
New Here?{" "}
|
||||
<Link
|
||||
to="/signup"
|
||||
className="font-semibold text-[#4687ba] hover:text-[#009ef7] transition"
|
||||
>
|
||||
Create an Account
|
||||
</Link>
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* SHOWS THIS IF USER SESSION HAS EXPIRED */}
|
||||
{sessionExpired == 'true' &&
|
||||
<div className="w-full p-1 mb-7">
|
||||
<p className="text-red-500 text-base text-center">Your session expired and will need to login again</p>
|
||||
</div>
|
||||
}
|
||||
{sessionExpired == "true" && (
|
||||
<div className="w-full p-1 mb-7">
|
||||
<p className="text-red-500 text-base text-center">
|
||||
Your session expired and will need to login again
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* switch login component */}
|
||||
<div className="ml-7 flex justify-start items-center gap-3">
|
||||
@@ -335,14 +357,16 @@ export default function Login() {
|
||||
</div>
|
||||
|
||||
{/* hCaptha clone for the time being */}
|
||||
{validCaptcha.show &&
|
||||
<div className="mb-5 flex justify-center w-full">
|
||||
<ReCAPTCHA
|
||||
sitekey={process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY}
|
||||
onChange={captchaChecker}
|
||||
{validCaptcha.show && (
|
||||
<div className="mb-5 flex justify-center w-full">
|
||||
<ReCAPTCHA
|
||||
sitekey={
|
||||
process.env.REACT_APP_GOOGLE_RECAPTCHA_SITEKEY
|
||||
}
|
||||
onChange={captchaChecker}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{loginError && (
|
||||
<div className="relative p-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-thin leading-[19.5px] text-[13px]">
|
||||
@@ -499,7 +523,8 @@ export default function Login() {
|
||||
|
||||
{loginType == "full" && (
|
||||
<div className="pt-5 text-[#181c32] text-center font-semibold text-[13.975px] leading-[20.9625px]">
|
||||
This site is protected by a Captcha. Our Privacy Policy and Terms of Service apply.
|
||||
This site is protected by a Captcha. Our Privacy Policy and
|
||||
Terms of Service apply.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -47,21 +47,20 @@ export default function SignUp() {
|
||||
|
||||
// Get Country Api
|
||||
const getCountryList = useCallback(async () => {
|
||||
const res = await userApi.getSignupCountryData();
|
||||
|
||||
|
||||
try {
|
||||
if (res.status === 200) {
|
||||
const { signup_country } = await res.data;
|
||||
// setCountries(signup_country);
|
||||
const res = await userApi.getSignupCountryData();
|
||||
if (res.status === 200 && res.data.internal_return >= 0) {
|
||||
const { result_list } = await res.data;
|
||||
if(country){ // IF LINK/PATHNAME HAS CNT QUERY VALUE
|
||||
let cnt = signup_country.filter(item => item[0]==country) // test to see country passed in query param exist from list of countries supplied by API
|
||||
let cnt = result_list.filter(item => item.code == country) // test to see country passed in query param exist from list of countries supplied by API
|
||||
if(!cnt.length){ // IF CNT EMPTY, SET FORMDATA COUNTRY BACK TO EMPTY STRING: RE: THIS IS BCOS WE INITAIL SET COUNTRY VALUE IN FORMDATA, IF COUNTRY PARAM IS PRESENT IN LINK
|
||||
setFormData(prev => ({...prev, country: ''}))
|
||||
return setCountries({loading: false, data: signup_country});
|
||||
return setCountries({loading: false, data: result_list});
|
||||
}
|
||||
return setCountries({loading: false, data: cnt});
|
||||
}
|
||||
setCountries({loading: false, data:signup_country});
|
||||
setCountries({loading: false, data:result_list});
|
||||
} else if (res.data.result !== 100) {
|
||||
setCountries({loading: false, data:[]});
|
||||
}
|
||||
@@ -375,17 +374,17 @@ const SelectOption = ({
|
||||
{data?.data?.length > 1 ?
|
||||
<>
|
||||
<option value={""}>Select your Country</option>
|
||||
{data?.data?.map((item, idx) => (
|
||||
<option value={item[0]} key={idx}>
|
||||
{item[1]}
|
||||
{data?.data?.map((item) => (
|
||||
<option value={item.code} key={item.uid}>
|
||||
{item.country}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
:
|
||||
data?.data?.length == 1 ?
|
||||
data?.data?.map((item, idx) => (
|
||||
<option value={item[0]} key={idx}>
|
||||
{item[1]}
|
||||
data?.data?.map((item) => (
|
||||
<option value={item.code} key={item.uid}>
|
||||
{item.country}
|
||||
</option>
|
||||
))
|
||||
:
|
||||
|
||||
@@ -47,7 +47,7 @@ export default function BlogItem(props) {
|
||||
</h1>
|
||||
</div>
|
||||
<div className="notification-wrapper w-full bg-white p-8 rounded-2xl">
|
||||
{blogdata.loading ?
|
||||
{blogdata?.loading ?
|
||||
<LoadingSpinner size='8' color='sky-blue' height='h-[100px]' />
|
||||
:
|
||||
blogdata?.data?.blogdata && blogdata.data?.blogdata.length ?
|
||||
|
||||
@@ -5,7 +5,7 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
import CountDown from "../Helpers/CountDown";
|
||||
import Icons from "../Helpers/Icons";
|
||||
|
||||
export default function FamilyActiveJobsCard({ datas, hidden = false }) {
|
||||
export default function FamilyActiveJobsCard({ datas, hidden = false, image_server }) {
|
||||
|
||||
let { pathname } = useLocation();
|
||||
|
||||
@@ -22,7 +22,8 @@ export default function FamilyActiveJobsCard({ datas, hidden = false }) {
|
||||
}
|
||||
};
|
||||
//debugger;
|
||||
const bannerName = datas.banner == null ?'default.jpg':datas.banner;
|
||||
// const bannerName = datas.banner == null ?'default.jpg':datas.banner;
|
||||
let image = `${image_server}${localStorage.getItem('session_token')}/job/${datas.origin_job_uid}`
|
||||
return (
|
||||
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
|
||||
<div className="content">
|
||||
@@ -31,10 +32,13 @@ export default function FamilyActiveJobsCard({ datas, hidden = false }) {
|
||||
{/* thumbnail image */}
|
||||
<div
|
||||
className="thumbnail w-full h-full rounded-xl overflow-hidden px-4 pt-4"
|
||||
// style={{
|
||||
// background: `url(${localImgLoad(
|
||||
// `images/taskbanners/${bannerName}`
|
||||
// )}) center / contain no-repeat`,
|
||||
// }}
|
||||
style={{
|
||||
background: `url(${localImgLoad(
|
||||
`images/taskbanners/${bannerName}`
|
||||
)}) center / contain no-repeat`,
|
||||
background: `url(${image}) center / contain no-repeat`,
|
||||
}}
|
||||
>
|
||||
{/* <div className="product-options flex justify-between relative">*/}
|
||||
|
||||
@@ -14,11 +14,7 @@ export default function OfferCard({
|
||||
datas?.currency
|
||||
);
|
||||
|
||||
let image = localStorage.getItem("session_token")
|
||||
? `${image_server}${localStorage.getItem("session_token")}/job/${
|
||||
datas.job_uid
|
||||
}`
|
||||
: "";
|
||||
let image = `${image_server}${localStorage.getItem("session_token")}/job/${datas.job_uid}`
|
||||
|
||||
return (
|
||||
<div className="card-style-one flex flex-col justify-between w-full h-[387px] bg-white dark:bg-dark-white p-3 pb rounded-2xl">
|
||||
|
||||
@@ -5,6 +5,8 @@ import { PriceFormatter } from "../../Helpers/PriceFormatter";
|
||||
import LoadingSpinner from "../../Spinners/LoadingSpinner";
|
||||
import Detail from "../../jobPopout/popoutcomponent/Detail";
|
||||
import { NewTasks } from "./forms";
|
||||
import { tableReload } from "../../../store/TableReloads";
|
||||
import { useDispatch } from "react-redux";
|
||||
|
||||
const AssignTaskPopout = React.memo(
|
||||
({
|
||||
@@ -19,6 +21,8 @@ const AssignTaskPopout = React.memo(
|
||||
}) => {
|
||||
const apiCall = new usersService();
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
@@ -90,11 +94,11 @@ const AssignTaskPopout = React.memo(
|
||||
const requiredFields = {
|
||||
banner,
|
||||
// category,
|
||||
country,
|
||||
currency:country,
|
||||
description,
|
||||
job_detail,
|
||||
'job detail':job_detail,
|
||||
price,
|
||||
timeline_days,
|
||||
timeline:timeline_days,
|
||||
title,
|
||||
};
|
||||
|
||||
@@ -104,7 +108,7 @@ const AssignTaskPopout = React.memo(
|
||||
setRequestStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: `${field} is empty`,
|
||||
message: `${field[0].toUpperCase()+field.slice(1).toLowerCase()} is empty`,
|
||||
});
|
||||
return setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
@@ -145,6 +149,7 @@ const AssignTaskPopout = React.memo(
|
||||
message: "action successful",
|
||||
});
|
||||
setUpdatePage(prev => !prev) // Updates family task page by calling the useeffect hook
|
||||
dispatch(tableReload({ type: "WALLETTABLE" })); // RELOADS USER WALLET
|
||||
setTimeout(() => {
|
||||
setRequestStatus({ loading: false, status: false, message: "" });
|
||||
action(); // FUNCTION THAT CLOSES THE MODAL BOX
|
||||
@@ -201,7 +206,7 @@ const AssignTaskPopout = React.memo(
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{familyTask.loading ? (
|
||||
{familyTask?.loading ? (
|
||||
<div className="h-[100px] w-full flex justify-center items-center">
|
||||
<LoadingSpinner color="sky-blue" size="16" />
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@ export default function NewTasks({ formState, setFormState }) {
|
||||
onChange={handleInputChange}
|
||||
// onBlur={props.handleBlur}
|
||||
>
|
||||
{currency.loading ? (
|
||||
{currency?.loading ? (
|
||||
<option className="text-slate-500 text-[13.975px]" value="">
|
||||
Loading...
|
||||
</option>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const AddFamily = () => {
|
||||
return (
|
||||
<div>Add Family</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default AddFamily
|
||||
@@ -0,0 +1,153 @@
|
||||
import React, { useMemo, useRef, useState } from "react";
|
||||
import usersService from "../../../../services/UsersService";
|
||||
|
||||
const FamilyBanner = ({ imageServer }) => {
|
||||
const uploadedImage = `${imageServer}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/familybanner/${localStorage.getItem("uid")}`;
|
||||
|
||||
const familyBannerRef = useRef(null);
|
||||
const jobApi = useMemo(() => new usersService(), []);
|
||||
|
||||
const [familyBannerImage, setFamilyBannerImage] = useState(uploadedImage);
|
||||
const [uploadStatus, setUploadStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
});
|
||||
|
||||
/**
|
||||
* Handles the change event of the family 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 familyBannerHandler = (e) => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp"]; // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0]; //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
if (!acceptedFormat.includes(fileFormat)) {
|
||||
//CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat
|
||||
.slice(0, -1)
|
||||
.join(", ")} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({ loading: false, status: false, message: msg });
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (uploadedFile.size > 5 * 1048576) {
|
||||
// CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "File must not exceed 5MB",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = {
|
||||
// PAYLOAD FOR API CALL
|
||||
job_uid: localStorage.getItem("uid"),
|
||||
file_name: uploadedFile?.name.slice(0, 19),
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: "FILE",
|
||||
action: 11303,
|
||||
};
|
||||
setUploadStatus({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: "Loading...",
|
||||
});
|
||||
jobApi
|
||||
.sendFiles(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
return setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Something went wrong, try again",
|
||||
});
|
||||
}
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Uploaded successfully",
|
||||
});
|
||||
setFamilyBannerImage(event.target.result);
|
||||
})
|
||||
.catch((error) => {
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Network error, try again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="w-full lg:h-[400px] rounded-2xl">
|
||||
<img
|
||||
src={familyBannerImage}
|
||||
alt="familyBanner"
|
||||
className="w-full h-full object-cover rounded-2xl"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex justify-between w-full">
|
||||
<div>
|
||||
{uploadStatus.message && !uploadStatus.loading && (
|
||||
<p
|
||||
className={`text-center ${
|
||||
uploadStatus.status ? "text-green-500" : "text-red-500"
|
||||
}`}
|
||||
>
|
||||
{uploadStatus.message}
|
||||
</p>
|
||||
)}
|
||||
{uploadStatus.loading && (
|
||||
<p className="text-center">{uploadStatus.message}</p>
|
||||
)}
|
||||
</div>
|
||||
<div>
|
||||
<input
|
||||
ref={familyBannerRef}
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={familyBannerHandler}
|
||||
/>
|
||||
<button
|
||||
className="btn-gradient w-[153px] h-[46px] rounded-full text-white lg:flex hidden justify-center items-center"
|
||||
onClick={() => familyBannerRef.current.click()}
|
||||
>
|
||||
Change Banner
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default FamilyBanner;
|
||||
@@ -0,0 +1,9 @@
|
||||
import React from 'react'
|
||||
|
||||
const Relatives = () => {
|
||||
return (
|
||||
<div>Relatives</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Relatives
|
||||
@@ -0,0 +1,5 @@
|
||||
import AddFamily from "./AddFamily";
|
||||
import FamilyBanner from "./FamilyBanner";
|
||||
import Relatives from "./Relatives";
|
||||
|
||||
export {AddFamily, FamilyBanner, Relatives}
|
||||
@@ -0,0 +1,103 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useLocation } from "react-router-dom";
|
||||
import Icons from "../../Helpers/Icons";
|
||||
import Layout from "../../Partials/Layout";
|
||||
import { AddFamily, FamilyBanner, Relatives } from "./Tabs";
|
||||
|
||||
const FamilySettings = () => {
|
||||
let {state} = useLocation()
|
||||
const tabs = [
|
||||
{
|
||||
id: 1,
|
||||
name: "add_family",
|
||||
title: "Add Family",
|
||||
iconName: "new-family",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: "relatives",
|
||||
title: "Relatives",
|
||||
iconName: "people-hover",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: "family_banner",
|
||||
title: "Family Banner",
|
||||
iconName: "people-hover",
|
||||
},
|
||||
];
|
||||
|
||||
const [tab, setTab] = useState(() => {
|
||||
// Retrieve the active tab from local storage or use the default tab
|
||||
return localStorage.getItem("activeTab") || tabs[0].name;
|
||||
});
|
||||
|
||||
const tabHandler = (value) => {
|
||||
setTab(value);
|
||||
};
|
||||
|
||||
// Update local storage when the tab changes
|
||||
useEffect(() => {
|
||||
localStorage.setItem("activeTab", tab);
|
||||
}, [tab]);
|
||||
|
||||
const tabComponents = {
|
||||
add_family: <AddFamily />,
|
||||
relatives: <Relatives />,
|
||||
family_banner: <FamilyBanner imageServer={state.imageServer} />,
|
||||
};
|
||||
|
||||
const defaultTabComponent = Array(tabComponents)[0].add_family;
|
||||
|
||||
const selectedComponent = tabComponents[tab] || defaultTabComponent;
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div className="notification-page w-full mb-10">
|
||||
<div className="notification-wrapper w-full">
|
||||
{/* heading */}
|
||||
<div className="sm:flex justify-between items-center mb-6">
|
||||
<div className="mb-5 sm:mb-0">
|
||||
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
|
||||
<span className={``}>Family Settings</span>
|
||||
</h1>
|
||||
</div>
|
||||
<Link to="/acc-family">Go Back</Link>
|
||||
</div>
|
||||
{/* Something Here */}
|
||||
{/* <form className="logout-modal-body w-full flex flex-col items-center px-10 py-8 gap-4"></form> */}
|
||||
<div className="w-full bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow h-full ">
|
||||
<div className="update-table w-full h-full p-4 bg-white dark:bg-dark-white overflow-y-auto rounded-2xl section-shadow min-h-[520px] lg:flex lg:px-10 px-4 justify-between">
|
||||
<div className="content-tab-items lg:w-[230px] w-full mr-2">
|
||||
<ul className="overflow-hidden mb-10 lg:mb-0 py-8">
|
||||
{tabs.map(({ name, id, title, iconName }) => (
|
||||
<li
|
||||
onClick={() => tabHandler(name)}
|
||||
key={id}
|
||||
className={`flex lg:space-x-4 space-x-2 hover:text-purple transition-all duration-300 ease-in-out items-center cursor-pointer lg:mb-11 mb-2 mr-6 lg:mr-0 float-left lg:float-none overflow-hidden ${
|
||||
tab === name ? "text-purple" : " text-thin-light-gray"
|
||||
}`}
|
||||
>
|
||||
<div>
|
||||
<Icons name={iconName} />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-18 tracking-wide">{title}</p>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-[1px] bg-[#E3E4FE] dark:bg-[#a7a9b533] mr-10"></div>
|
||||
<div className="flex-1">
|
||||
<div className="tab-item">{selectedComponent}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
};
|
||||
|
||||
export default FamilySettings;
|
||||
@@ -77,7 +77,7 @@ export default function FamilyTable({
|
||||
>
|
||||
<td className="py-4">
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center flex-[0.1]">
|
||||
<div className="w-[60px] h-[60px] rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
// src={profile_picture}
|
||||
src={image || profile_picture || localImgLoad(`images/icons/${banner}`)}
|
||||
@@ -87,7 +87,7 @@ export default function FamilyTable({
|
||||
</div>
|
||||
<div className="flex flex-col flex-[0.9]">
|
||||
<h1 className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
|
||||
{`${firstname} ${lastname} (${age})`}
|
||||
{`${firstname} ${lastname} (${age < 10 ? `0${age}` : age})`}
|
||||
</h1>
|
||||
<span className="text-sm text-thin-light-gray">
|
||||
Added: <span className="text-purple ml-1">{addedDate}</span>
|
||||
@@ -149,7 +149,7 @@ export default function FamilyTable({
|
||||
className="w-[0.7rem]"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
d="M.366 19.708c.405.39 1.06.39 1.464 0l8.563-8.264a1.95 1.95 0 0 0 0-2.827L1.768.292A1.063 1.063 0 0 0 .314.282a.976.976 0 0 0-.011 1.425l7.894 7.617a.975.975 0 0 1 0 1.414L.366 18.295a.974.974 0 0 0 0 1.413"
|
||||
// fill=""
|
||||
className="color000000 svgShape fill-[#fff]"
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, {
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import usersService from "../../services/UsersService";
|
||||
import InputCom from "../Helpers/Inputs/InputCom";
|
||||
import ModalCom from "../Helpers/ModalCom";
|
||||
@@ -13,7 +14,6 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import FamilyTable from "./FamilyTable";
|
||||
|
||||
export default function FamilyAcc() {
|
||||
const [selectTab, setValue] = useState("today");
|
||||
// State to store the selected year and month
|
||||
const [selectedYear, setSelectedYear] = useState("");
|
||||
const [selectedMonth, setSelectedMonth] = useState("");
|
||||
@@ -33,10 +33,6 @@ export default function FamilyAcc() {
|
||||
setPopUp((prev) => !prev);
|
||||
};
|
||||
|
||||
const filterHandler = (value) => {
|
||||
setValue(value);
|
||||
};
|
||||
|
||||
// Handle year selection
|
||||
const handleYearChange = (e) => {
|
||||
setSelectedYear(e.target.value);
|
||||
@@ -116,7 +112,7 @@ export default function FamilyAcc() {
|
||||
const { data } = res;
|
||||
if (data?.internal_return >= 0 && data?.status === "OK") {
|
||||
const { result_list, session_image_server } = data;
|
||||
setFamilyList({result_list, session_image_server});
|
||||
setFamilyList({ result_list, session_image_server });
|
||||
setLoader(false);
|
||||
} else {
|
||||
return;
|
||||
@@ -146,12 +142,9 @@ export default function FamilyAcc() {
|
||||
<div className="sm:flex justify-between items-center mb-6">
|
||||
<div className="mb-5 sm:mb-0">
|
||||
<h1 className="text-26 font-bold inline-flex gap-3 text-dark-gray dark:text-white items-center">
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
Family Accounts
|
||||
</span>
|
||||
{familyList.length < process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
|
||||
<span className={``}>Family Accounts</span>
|
||||
{familyList?.result_list?.length <
|
||||
process.env.REACT_APP_MAX_FAMILY_MEMBERS &&
|
||||
!loader && (
|
||||
<button
|
||||
onClick={popUpHandler}
|
||||
@@ -163,12 +156,31 @@ export default function FamilyAcc() {
|
||||
)}
|
||||
</h1>
|
||||
</div>
|
||||
<div className="slider-btns flex space-x-4">
|
||||
<div
|
||||
onClick={() => filterHandler("today")}
|
||||
className="relative"
|
||||
></div>
|
||||
</div>
|
||||
<Link
|
||||
to={`/familysettings`}
|
||||
state={{ imageServer: familyList?.session_image_server }}
|
||||
className="slider-btns flex space-x-4 w-12 h-12 rounded-md shadow-sm justify-center items-center cursor-pointer dark:bg-[linear-gradient(134.38deg,#f539f8_0%,#c342f9_43.55%,#5356fb_104.51%)]"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth={1.5}
|
||||
stroke="currentColor"
|
||||
className="w-6 h-6 dark:stroke-white"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z"
|
||||
/>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
|
||||
/>
|
||||
</svg>
|
||||
</Link>
|
||||
</div>
|
||||
<Suspense fallback={<LoadingSpinner color="sky-blue" size="16" />}>
|
||||
<FamilyTable
|
||||
|
||||
@@ -478,13 +478,13 @@ export default function Icons({ name }) {
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
strokeWidth="1.5"
|
||||
stroke="currentColor"
|
||||
className="w-4 h-4"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M8.25 4.5l7.5 7.5-7.5 7.5"
|
||||
/>
|
||||
</svg>
|
||||
|
||||
@@ -16,7 +16,7 @@ export default function ModalCom({ action, children, situation, isOpen, classNam
|
||||
onClick={action || isOpen}
|
||||
className="fixed top-0 left-0 w-full lg:h-[100vh] h-full bg-black bg-opacity-40 backdrop-filter backdrop-blur-sm z-50"
|
||||
></div>
|
||||
<div className={`children-element fixed lg:h-100vh h-full z-[99999999999999] w-full lg:w-auto ${className}`}>
|
||||
<div className={`fixed lg:h-100vh h-full z-[99999999999999] w-full lg:w-auto inset-0 flex flex-col justify-center items-center ${className}`}>
|
||||
{children && children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
import React, {useEffect, useState} from 'react'
|
||||
import Image from '../../assets/images/taskbanners/default.jpg'
|
||||
|
||||
import usersService from '../../services/UsersService';
|
||||
import { handlePagingFunc } from '../../components/Pagination/HandlePagination';
|
||||
import PaginatedList from '../../components/Pagination/PaginatedList';
|
||||
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner';
|
||||
|
||||
import { AmountTo2DP } from '../Helpers/PriceFormatter';
|
||||
|
||||
function JobsCompleted() {
|
||||
|
||||
const apiCall = new usersService()
|
||||
|
||||
let [familyRewardHistory, setFamilyRewardHistory] = useState({ // FOR PURCHASE HISTORY
|
||||
loading: true,
|
||||
data: [],
|
||||
error: false
|
||||
})
|
||||
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const indexOfFirstItem = Number(currentPage);
|
||||
const indexOfLastItem = Number(indexOfFirstItem)+Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
const currentReward = familyRewardHistory?.data?.slice(indexOfFirstItem, indexOfLastItem);
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e,setCurrentPage)
|
||||
}
|
||||
|
||||
|
||||
//FUNCTION TO GET FAMILY REWARD HISTORY
|
||||
const getJobCompletedHistory = ()=>{
|
||||
// apiCall.getFamilyRewardHx().then((res)=>{
|
||||
// if(res.data.internal_return < 0){ // success but no data
|
||||
// setFamilyRewardHistory(prev => ({...prev, loading: false}))
|
||||
// return
|
||||
// }
|
||||
// setFamilyRewardHistory(prev => ({...prev, loading: false, data: res.data.result_list}))
|
||||
// }).catch((error)=>{
|
||||
// setFamilyRewardHistory(prev => ({...prev, loading: false, error: true}))
|
||||
// })
|
||||
setTimeout(()=>{
|
||||
setFamilyRewardHistory(prev => ({...prev, loading: false, error:true}))
|
||||
},3000)
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
getJobCompletedHistory()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div className='flex flex-col justify-between min-h-[500px]'>
|
||||
{familyRewardHistory.loading ?
|
||||
<LoadingSpinner size='16' color='sky-blue' height='h-[500px]' />
|
||||
: familyRewardHistory.data.length ?
|
||||
<table className="wallet-activity w-full table-auto border-collapse text-left">
|
||||
<thead className='border-b-2'>
|
||||
<tr className='text-slate-600'>
|
||||
<th className="p-2"></th>
|
||||
<th className="p-2">Amount</th>
|
||||
<th className="p-2">Date</th>
|
||||
<th className="p-2">Confirmation</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{currentReward.map((item, index) => {
|
||||
let date = new Date(item.added).toLocaleDateString()
|
||||
return (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td className="p-2">
|
||||
<div className='flex items-center gap-2'>
|
||||
<img src={item.icon} className='min-w-[60px] max-w-[60px] min-h-[60px] max-h-[60px] rounded-full bg-slate-500' alt='Reward Logo' />
|
||||
<div className='flex flex-col'>
|
||||
<h1 className='text-lg font-bold'>Reward to {item.rec_firstname} {item.rec_lastname}</h1>
|
||||
<p className='text-sm'>{item.description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-2">{AmountTo2DP(item.amount*0.01)} {item.currency}</td>
|
||||
<td className="p-2">{date}</td>
|
||||
<td className="p-2">{item.confirmation}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
</table>
|
||||
:familyRewardHistory.error ?
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>Opps! an error occurred. Please try again!</span>
|
||||
</div>
|
||||
:
|
||||
<div className="p-2 text-slate-500 flex flex-col grow justify-center items-center">
|
||||
<span>No Completed Job History Found!</span>
|
||||
</div>
|
||||
}
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList onClick={handlePagination} prev={currentPage == 0 ? true : false} next={currentPage+Number(process.env.REACT_APP_ITEM_PER_PAGE) >= familyRewardHistory?.data?.length ? true : false} data={familyRewardHistory?.data} start={indexOfFirstItem} stop={indexOfLastItem} />
|
||||
{/* END OF PAGINATION BUTTON */}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default JobsCompleted
|
||||
@@ -11,6 +11,7 @@ import PurchasesTable from "../MyWallet/WalletComponent/PurchasesTable";
|
||||
import RecentActivityTable from "../MyWallet/WalletComponent/RecentActivityTable";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import RewardsTable from "./RewardsTable";
|
||||
import JobsCompleted from "./JobsCompleted";
|
||||
|
||||
export default function History() {
|
||||
|
||||
@@ -249,6 +250,15 @@ export default function History() {
|
||||
>
|
||||
Rewards
|
||||
</button>
|
||||
<button
|
||||
name="jobs_completed"
|
||||
onClick={(e) => setTab(e.target.name)}
|
||||
className={`px-4 py-1 rounded-t-2xl ${
|
||||
tab == "jobs_completed" ? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white" : "bg-white text-[#000] border-t-[2px]"
|
||||
}`}
|
||||
>
|
||||
Jobs Completed
|
||||
</button>
|
||||
</div>
|
||||
{/* END OF switch button */}
|
||||
<div className="history-tables w-full">
|
||||
@@ -286,6 +296,14 @@ export default function History() {
|
||||
</div>
|
||||
}
|
||||
{/* END OF REWARD SECTION */}
|
||||
|
||||
{/* JOBS COMPLETED SECTION */}
|
||||
{tab == 'jobs_completed' &&
|
||||
<div className="wallet w-full border-t">
|
||||
<JobsCompleted />
|
||||
</div>
|
||||
}
|
||||
{/* END OF JOBS COMPLETED SECTION */}
|
||||
</div>
|
||||
</div>
|
||||
{/*<HistoryTable />*/}
|
||||
|
||||
@@ -4,7 +4,7 @@ import Icons from "../Helpers/Icons";
|
||||
import SliderCom from "../Helpers/SliderCom";
|
||||
import FamilyActiveJobsCard from "../Cards/FamilyActiveJobsCard";
|
||||
|
||||
export default function FamilyActiveLSlde({ className, trending }) {
|
||||
export default function FamilyActiveLSlde({ className, trending, image_server }) {
|
||||
const settings = {
|
||||
arrows: false,
|
||||
slidesToShow: 3,
|
||||
@@ -98,7 +98,7 @@ export default function FamilyActiveLSlde({ className, trending }) {
|
||||
{trending &&
|
||||
trending.length > 0 &&
|
||||
trending.map((item) => (
|
||||
<FamilyActiveJobsCard key={item.id} datas={item} />
|
||||
<FamilyActiveJobsCard key={item.id} datas={item} image_server={image_server} />
|
||||
))}
|
||||
</SliderCom>
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function FamilyDash({ familyOffers, MyActiveJobList }) {
|
||||
/>
|
||||
)}
|
||||
{trending && trending.length > 0 && (
|
||||
<FamilyActiveLSlde trending={trending} className="mb-10" />
|
||||
<FamilyActiveLSlde trending={trending} className="mb-10" image_server={familyOffers?.session_image_server} />
|
||||
)}
|
||||
|
||||
{/*<TopSellerTopBuyerSliderSection className="mb-10" />*/}
|
||||
|
||||
@@ -34,6 +34,7 @@ export default function FullAccountDash(props) {
|
||||
<MyJobTable
|
||||
ActiveJobList={props.MyActiveJobList}
|
||||
Account={userDetails}
|
||||
imageServer={props.offersList?.data?.session_image_server}
|
||||
/>
|
||||
</>
|
||||
) : !props.offersList?.loading && !props.MyActiveJobList?.loading ? (
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import ModalCom from '../Helpers/ModalCom'
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
||||
import InputCom from '../Helpers/Inputs/InputCom/index'
|
||||
|
||||
import usersService from '../../services/UsersService'
|
||||
|
||||
export default function AddGroup({action, situation , setUpdateList}) {
|
||||
|
||||
const api = new usersService()
|
||||
|
||||
const [name, setName] = useState('')
|
||||
|
||||
const handleChange = ({target:{name, value}}) =>{
|
||||
setName(value)
|
||||
}
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
|
||||
|
||||
const addGroup = () => {
|
||||
setRequestStatus({loading:true, status:false, message:''})
|
||||
if(name == ''){
|
||||
setRequestStatus({loading:false, status:false, message:'Please enter a group name'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
},3000)
|
||||
}
|
||||
if(name.length < 6){
|
||||
setRequestStatus({loading:false, status:false, message:'Group name must be up to six characters'})
|
||||
return setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
},3000)
|
||||
}
|
||||
api.jobGroupAdd({group_name:name, action:13025}).then(response => {
|
||||
let {status, data} = response
|
||||
if(status != 200 || data.internal_return < 0){
|
||||
setRequestStatus({loading:false, status:false, message:'Unable to add Group'})
|
||||
return
|
||||
}
|
||||
setRequestStatus({loading:false, status:true, message:'Group Added'})
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
// close modal
|
||||
action()
|
||||
// reload group page
|
||||
setUpdateList(prev => !prev)
|
||||
},3000)
|
||||
}).catch(error => {
|
||||
setRequestStatus({loading:false, status:false, message:'Unable to add Group, try again later'})
|
||||
}).finally(()=>{
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({loading:false, status:false, message:''})
|
||||
},3000)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalCom
|
||||
action={action}
|
||||
situation={situation}
|
||||
>
|
||||
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Add Group
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
onClick={action}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
|
||||
<div className="mb-6 w-full">
|
||||
<div className="input-item">
|
||||
<InputCom
|
||||
labelClass="tracking-wider"
|
||||
fieldClass="sm:px-6 px-2"
|
||||
value={name}
|
||||
placeholder="Group Name"
|
||||
label="Group Name"
|
||||
name="name"
|
||||
type="text"
|
||||
inputHandler={handleChange}
|
||||
// iconName="message"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex justify-between items-center gap-4">
|
||||
<button
|
||||
onClick={action}
|
||||
type="button"
|
||||
className="text-base text-light-red tracking-wide "
|
||||
>
|
||||
<span className="border-b dark:border-[#5356fb29] border-light-red">Cancel</span>
|
||||
</button>
|
||||
{requestStatus.loading ? (
|
||||
<LoadingSpinner size="8" color="sky-blue" />
|
||||
) : (
|
||||
<button
|
||||
onClick={() => addGroup()}
|
||||
type="button"
|
||||
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
Add Group
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
<div
|
||||
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
</div>
|
||||
</div>
|
||||
</ModalCom>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import ModalCom from '../Helpers/ModalCom'
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
||||
|
||||
export default function DeleteGroup({action, situation, details}) {
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
laoding: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
|
||||
|
||||
const deleteGroup = () => {
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalCom
|
||||
action={action}
|
||||
situation={situation}
|
||||
>
|
||||
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Delete Group
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
onClick={action}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
|
||||
<div className="what-icon mb-6 cursor-pointer">
|
||||
<svg
|
||||
width="136"
|
||||
height="136"
|
||||
viewBox="0 0 136 136"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="68" cy="68" r="68" fill="#4687ba" />
|
||||
<path
|
||||
d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M67.485 100.21C66.1617 99.9268 64.9041 99.5091 63.803 98.6787C61.3804 96.8484 60.2877 93.7699 61.0386 90.7888C61.7726 87.8747 64.2138 85.6703 67.2089 85.2157C71.273 84.6 75.2024 87.3681 75.8135 91.277C76.4937 95.6153 73.8202 99.3782 69.544 100.103C69.4429 100.12 69.3487 100.172 69.2527 100.209C68.6635 100.21 68.0742 100.21 67.485 100.21Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<p className="text-xl text-center tracking-wide text-dark-gray dark:text-white">
|
||||
Are you sure, you want to delete <br /> <span>'{details?.group_name}'</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex space-x-2.5">
|
||||
<button
|
||||
onClick={action}
|
||||
type="button"
|
||||
className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full"
|
||||
>
|
||||
<span className="text-gradient">Cancel</span>
|
||||
</button>
|
||||
{requestStatus.laoding ? (
|
||||
<LoadingSpinner size="8" color="sky-blue" />
|
||||
) : (
|
||||
<button
|
||||
onClick={() => deleteGroup()}
|
||||
type="button"
|
||||
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full"
|
||||
>
|
||||
Confirm Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
<div
|
||||
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
</div>
|
||||
</div>
|
||||
</ModalCom>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import ModalCom from '../Helpers/ModalCom'
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
||||
|
||||
export default function DeleteMember({action, situation, details}) {
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
laoding: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
|
||||
|
||||
const deleteMember = () => {
|
||||
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalCom
|
||||
action={action}
|
||||
situation={situation}
|
||||
>
|
||||
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Delete Member
|
||||
</h1>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[#374557] dark:text-red-500"
|
||||
onClick={action}
|
||||
>
|
||||
<svg
|
||||
width="36"
|
||||
height="36"
|
||||
viewBox="0 0 36 36"
|
||||
fill="none"
|
||||
className="fill-current"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M36 16.16C36 17.4399 36 18.7199 36 20.0001C35.7911 20.0709 35.8636 20.2554 35.8385 20.4001C34.5321 27.9453 30.246 32.9248 22.9603 35.2822C21.9006 35.6251 20.7753 35.7657 19.6802 35.9997C18.4003 35.9997 17.1204 35.9997 15.8401 35.9997C15.5896 35.7086 15.2189 35.7732 14.9034 35.7093C7.77231 34.2621 3.08728 30.0725 0.769671 23.187C0.435002 22.1926 0.445997 21.1199 0 20.1599C0 18.7198 0 17.2798 0 15.8398C0.291376 15.6195 0.214408 15.2656 0.270759 14.9808C1.71321 7.69774 6.02611 2.99691 13.0428 0.700951C14.0118 0.383805 15.0509 0.386897 15.9999 0C17.2265 0 18.4532 0 19.6799 0C19.7156 0.124041 19.8125 0.136067 19.9225 0.146719C27.3 0.868973 33.5322 6.21922 35.3801 13.427C35.6121 14.3313 35.7945 15.2484 36 16.16ZM33.011 18.0787C33.0433 9.77105 26.3423 3.00309 18.077 2.9945C9.78479 2.98626 3.00344 9.658 2.98523 17.8426C2.96667 26.1633 9.58859 32.9601 17.7602 33.0079C26.197 33.0577 32.9787 26.4186 33.011 18.0787Z"
|
||||
fill=""
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
<path
|
||||
d="M15.9309 18.023C13.9329 16.037 12.007 14.1207 10.0787 12.2072C9.60071 11.733 9.26398 11.2162 9.51996 10.506C9.945 9.32677 11.1954 9.0811 12.1437 10.0174C13.9067 11.7585 15.6766 13.494 17.385 15.2879C17.9108 15.8401 18.1633 15.7487 18.6375 15.258C20.3586 13.4761 22.1199 11.7327 23.8822 9.99096C24.8175 9.06632 26.1095 9.33639 26.4967 10.517C26.7286 11.2241 26.3919 11.7413 25.9133 12.2178C24.1757 13.9472 22.4477 15.6855 20.7104 17.4148C20.5228 17.6018 20.2964 17.7495 20.0466 17.9485C22.0831 19.974 24.0372 21.8992 25.9689 23.8468C26.9262 24.8119 26.6489 26.1101 25.4336 26.4987C24.712 26.7292 24.2131 26.3441 23.7455 25.8757C21.9945 24.1227 20.2232 22.3892 18.5045 20.6049C18.0698 20.1534 17.8716 20.2269 17.4802 20.6282C15.732 22.4215 13.9493 24.1807 12.1777 25.951C11.7022 26.4262 11.193 26.7471 10.4738 26.4537C9.31345 25.9798 9.06881 24.8398 9.98589 23.8952C11.285 22.5576 12.6138 21.2484 13.9387 19.9355C14.5792 19.3005 15.2399 18.6852 15.9309 18.023Z"
|
||||
fill="#"
|
||||
fillOpacity="0.6"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="logout-modal-body w-full flex flex-col items-center px-10 py-8">
|
||||
<div className="what-icon mb-6 cursor-pointer">
|
||||
<svg
|
||||
width="136"
|
||||
height="136"
|
||||
viewBox="0 0 136 136"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle cx="68" cy="68" r="68" fill="#4687ba" />
|
||||
<path
|
||||
d="M69.8844 35.7891C71.1588 36.0357 72.4569 36.1967 73.7044 36.5423C81.5447 38.7098 87.2705 45.5378 87.9574 53.6156C88.5113 60.1147 86.3075 65.6006 81.5043 70.0195C79.8359 71.5545 78.0497 72.9604 76.3408 74.4534C76.127 74.6397 75.9654 75.0037 75.9604 75.2872C75.9284 77.2752 75.9435 79.2649 75.9435 81.2965C70.8895 81.2965 65.8758 81.2965 60.7915 81.2965C60.7915 81.0616 60.7915 80.8385 60.7915 80.6137C60.7915 76.5454 60.7999 72.4772 60.7797 68.4106C60.778 67.9392 60.9312 67.649 61.2831 67.3537C64.5643 64.5957 67.8271 61.8175 71.1033 59.0545C72.2616 58.0781 72.9215 56.8702 72.9081 55.3419C72.8878 52.916 70.8608 50.9146 68.423 50.8911C65.9701 50.8693 63.9145 52.8053 63.832 55.2328C63.8084 55.8988 63.8286 56.5665 63.8286 57.2695C58.7745 57.2695 53.7744 57.2695 48.6917 57.2695C48.6917 56.3149 48.6462 55.3385 48.6984 54.3655C49.222 44.699 56.7442 36.8745 66.4331 35.8914C66.5762 35.8763 66.7142 35.8243 66.854 35.7891C67.8641 35.7891 68.8742 35.7891 69.8844 35.7891Z"
|
||||
fill="white"
|
||||
/>
|
||||
<path
|
||||
d="M67.485 100.21C66.1617 99.9268 64.9041 99.5091 63.803 98.6787C61.3804 96.8484 60.2877 93.7699 61.0386 90.7888C61.7726 87.8747 64.2138 85.6703 67.2089 85.2157C71.273 84.6 75.2024 87.3681 75.8135 91.277C76.4937 95.6153 73.8202 99.3782 69.544 100.103C69.4429 100.12 69.3487 100.172 69.2527 100.209C68.6635 100.21 68.0742 100.21 67.485 100.21Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div className="mb-6">
|
||||
<p className="text-xl text-center tracking-wide text-dark-gray dark:text-white">
|
||||
Are you sure, you want to delete <br /> <span>'{details?.firstname} {details.lastname}'</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex space-x-2.5">
|
||||
<button
|
||||
onClick={action}
|
||||
type="button"
|
||||
className=" border-gradient text-18 tracking-wide px-4 py-3 rounded-full"
|
||||
>
|
||||
<span className="text-gradient">Cancel</span>
|
||||
</button>
|
||||
{requestStatus.laoding ? (
|
||||
<LoadingSpinner size="8" color="sky-blue" />
|
||||
) : (
|
||||
<button
|
||||
onClick={() => deleteMember()}
|
||||
type="button"
|
||||
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full"
|
||||
>
|
||||
Confirm Delete
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
|
||||
{requestStatus.message != "" &&
|
||||
(!requestStatus.status ? (
|
||||
<div
|
||||
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
) : (
|
||||
requestStatus.status && (
|
||||
<div
|
||||
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
||||
>
|
||||
{requestStatus.message}
|
||||
</div>
|
||||
)
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
</div>
|
||||
</div>
|
||||
</ModalCom>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import InputCom from '../../components/Helpers/Inputs/InputCom/index'
|
||||
import DeleteGroup from './DeleteGroup'
|
||||
import AddGroup from './AddGroup'
|
||||
|
||||
export default function GroupList({groupList, selectedGroup, changeSelectedGroup, setUpdateList}) {
|
||||
|
||||
const [deletePopout, setDeletePopout] = useState({
|
||||
status: false,
|
||||
data: {}
|
||||
})
|
||||
|
||||
const [addGroupPopout, setAddGroupPopout] = useState(false)
|
||||
|
||||
const handleAddGroup = () => {
|
||||
setAddGroupPopout(true)
|
||||
}
|
||||
|
||||
const handleDeleteGroup = (item) => {
|
||||
setDeletePopout({
|
||||
status: true,
|
||||
data: {...item}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='p-5 w-full lg:w-[400px] min-h-[300px] bg-sky-100 dark:bg-dark-gray rounded-2xl'>
|
||||
{/* <h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>Jobs Groups</h1> */}
|
||||
<div className='flex justify-end items-center'>
|
||||
<button onClick={handleAddGroup} className='py-2 px-4 flex justify-center items-center bg-sky-blue hover:bg-sky-600 text-base rounded-full text-white font-bold'>Add Group</button>
|
||||
</div>
|
||||
|
||||
{groupList && groupList.length < 1 ?
|
||||
<h1 className='my-5 text-lg tracking-wide text-slate-900 dark:text-slate-100'>No Group Found!</h1>
|
||||
:
|
||||
<div className='my-4 max-h-[400px] overflow-y-auto'>
|
||||
<div className='flex flex-col'>
|
||||
{groupList.map(item=> (
|
||||
<div key={item.group_uid} className='p-2 flex gap-2 items-center justify-between w-full'>
|
||||
<div className='flex gap-2 items-center'>
|
||||
<input type='radio' name='grouplist' value={item.group_id} checked={selectedGroup?.id == item?.group_id} onChange={changeSelectedGroup} className='w-[20px] h-[20px] outline-none' />
|
||||
<p className='text-sm lg:text-base text-slate-900 dark:text-slate-100'>{item.group_name}</p>
|
||||
</div>
|
||||
<button onClick={()=>{handleDeleteGroup(item)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5 flex justify-center items-center'>X</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
{deletePopout.status &&
|
||||
<DeleteGroup
|
||||
action={()=>setDeletePopout({status:false, data:{}})}
|
||||
situation={deletePopout.status}
|
||||
details={deletePopout.data}
|
||||
/>
|
||||
}
|
||||
|
||||
{addGroupPopout &&
|
||||
<AddGroup
|
||||
action={()=>setAddGroupPopout(false)}
|
||||
situation={addGroupPopout}
|
||||
setUpdateList={setUpdateList}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import React, { useState } from 'react'
|
||||
import { handlePagingFunc } from '../Pagination/HandlePagination';
|
||||
import PaginatedList from '../Pagination/PaginatedList';
|
||||
import DeleteMember from './DeleteMember';
|
||||
|
||||
export default function GroupMemberTable({selectedList}) {
|
||||
|
||||
// Handle Pagination
|
||||
const [currentPage, setCurrentPage] = useState(0);
|
||||
const indexOfFirstItem = Number(currentPage);
|
||||
const indexOfLastItem =Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
|
||||
|
||||
const currentSelectedList = selectedList?.slice(indexOfFirstItem, indexOfLastItem);
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e, setCurrentPage);
|
||||
};
|
||||
|
||||
const [deletePopout, setDeletePopout] = useState({
|
||||
status: false,
|
||||
data: {}
|
||||
})
|
||||
|
||||
const handleDeleteMember = (item) => {
|
||||
setDeletePopout({
|
||||
status: true,
|
||||
data: {...item}
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={`w-full p-8 bg-white dark:bg-dark-gray overflow-hidden rounded-2xl section-shadow`}>
|
||||
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[400px]">
|
||||
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
|
||||
<tbody>
|
||||
<>
|
||||
{selectedList && selectedList?.length > 0 ? (
|
||||
currentSelectedList.map((value, index) => (
|
||||
<tr key={value.uid} className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50">
|
||||
{/* <td className="p-1">{value?.firstname}</td>
|
||||
<td className="p-1">{value?.lastname}</td>
|
||||
<td className="p-1">{value?.email}</td>
|
||||
<td className="p-1 text-right">
|
||||
<button onClick={()=>{handleDeleteMember(value)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5'>X</button>
|
||||
</td> */}
|
||||
<td className='py-2'>
|
||||
<div className="flex flex-col">
|
||||
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
|
||||
{value.firstname && value.firstname} {value.lastname && value.lastname}
|
||||
</h1>
|
||||
<span className="text-sm text-thin-light-gray">
|
||||
{value.email && value.email}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className='py-2 text-right'>
|
||||
<button onClick={()=>{handleDeleteMember(value)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5'>X</button>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
) : (
|
||||
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
|
||||
<td className="p-2">No Members Found</td>
|
||||
</tr>
|
||||
)}
|
||||
</>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{/* PAGINATION BUTTON */}
|
||||
<PaginatedList
|
||||
onClick={handlePagination}
|
||||
prev={currentPage == 0 ? true : false}
|
||||
next={
|
||||
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
|
||||
selectedList?.length
|
||||
? true
|
||||
: false
|
||||
}
|
||||
data={selectedList}
|
||||
start={indexOfFirstItem}
|
||||
stop={indexOfLastItem}
|
||||
/>
|
||||
{/* END OF PAGINATION BUTTON */}
|
||||
</div>
|
||||
|
||||
{/* DELETE MEMBER POPOUT */}
|
||||
{deletePopout.status &&
|
||||
<DeleteMember
|
||||
action={()=>setDeletePopout({status:false, data:{}})}
|
||||
situation={deletePopout.status}
|
||||
details={deletePopout.data}
|
||||
/>
|
||||
}
|
||||
{/* END OF DELETE MEMBER POPOUT */}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,79 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Layout from '../Partials/Layout'
|
||||
|
||||
import GroupList from './GroupList'
|
||||
import MemberList from './MemberList'
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
||||
|
||||
import usersService from '../../services/UsersService'
|
||||
|
||||
export default function JobGroups() {
|
||||
|
||||
const userApi = new usersService();
|
||||
|
||||
const [updateList, setUpdateList] = useState(false)
|
||||
|
||||
|
||||
const [groupList, setGroupList] = useState({
|
||||
loading:true,
|
||||
groups: [],
|
||||
members: []
|
||||
})
|
||||
|
||||
const [selectedGroup, setSelectedGroup] = useState({id:'', name:'', data: []})
|
||||
|
||||
const changeSelectedGroup = (e) => {
|
||||
let groupID = e.target.value
|
||||
const activeMembers = groupList?.members?.filter(item => item.group_id == groupID)
|
||||
const activeGroup = groupList?.groups?.filter(item => item.group_id == groupID)
|
||||
setSelectedGroup({id: groupID, name:activeGroup[0]?.group_name, data:activeMembers})
|
||||
}
|
||||
|
||||
useEffect(()=>{
|
||||
setGroupList({loading: true, groups: [], members: []})
|
||||
userApi.jobGroupList({}).then(res => {
|
||||
const {status, data} = res
|
||||
if(status != 200 || data?.internal_return < 0){
|
||||
setGroupList({loading: false, groups: [], members: []})
|
||||
return
|
||||
}
|
||||
if(data.result_list.length < 0){
|
||||
setGroupList({loading: false, groups: [], members: []})
|
||||
return
|
||||
}
|
||||
setGroupList({loading: false, groups: data.result_list, members: data.result_list_member})
|
||||
if(selectedGroup.id == ''){
|
||||
let activeGroupId = data.result_list[0].group_id
|
||||
let activeGroup = data.result_list[0].group_name
|
||||
let activeMembers = data.result_list_member?.filter(item => item.group_id == activeGroupId)
|
||||
setSelectedGroup({id: activeGroupId, name:activeGroup, data:activeMembers})
|
||||
}else{
|
||||
let activeMembers = data.result_list_member?.filter(item => item.group_id == selectedGroup?.id)
|
||||
setSelectedGroup({id: selectedGroup?.id, name:selectedGroup?.name, data:activeMembers})
|
||||
}
|
||||
}).catch(error => {
|
||||
setGroupList({loading: false, groups: [], members: []})
|
||||
console.log(error)
|
||||
})
|
||||
},[updateList])
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<div>
|
||||
<h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>Jobs Groups</h1>
|
||||
</div>
|
||||
<div className='p-5 w-full min-h-[400px] flex flex-col lg:flex-row gap-3 lg:gap-6 rounded-lg shadow-md bg-white dark:bg-dark-white'>
|
||||
{groupList.loading ?
|
||||
<div className='w-full h-[400px] flex justify-center items-center'>
|
||||
<LoadingSpinner size='16' />
|
||||
</div>
|
||||
:
|
||||
<>
|
||||
<GroupList groupList={groupList?.groups} selectedGroup={selectedGroup} changeSelectedGroup={changeSelectedGroup} setUpdateList={setUpdateList} />
|
||||
<MemberList groupList={groupList?.groups} selectedGroup={selectedGroup} setUpdateList={setUpdateList} />
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
import React, { useState } from 'react'
|
||||
|
||||
import InputCom from '../../components/Helpers/Inputs/InputCom/index'
|
||||
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
||||
import GroupMemberTable from './GroupMemberTable'
|
||||
|
||||
import EmailValidator from '../../lib/EmailValidator'
|
||||
|
||||
import usersService from '../../services/UsersService'
|
||||
|
||||
export default function MemberList({groupList, selectedGroup, setUpdateList}) {
|
||||
|
||||
const api = new usersService()
|
||||
|
||||
const [fields, setFields] = useState({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
email: ''
|
||||
})
|
||||
|
||||
const handleFieldsChange = ({target:{name, value}}) => {
|
||||
setFields(prev => ({...prev, [name]:value}))
|
||||
// let error = requestState?.errors?.indexOf(name) //// checks if the input field was in error array and removes it when the input changes
|
||||
// if(error >= 0){
|
||||
// let oldErrorArr = requestState.errors
|
||||
// let newErrorArr = oldErrorArr.splice(error, 1)
|
||||
// setRequestState(prev => ({...prev, errors:oldErrorArr}))
|
||||
// }
|
||||
|
||||
if(value == ''){
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: [name]
|
||||
})
|
||||
}else{
|
||||
let error = requestState?.errors?.indexOf(name) //// checks if the input field was in error array and removes it when the input changes
|
||||
if(error >= 0){
|
||||
let oldErrorArr = requestState.errors
|
||||
let newErrorArr = oldErrorArr.splice(error, 1)
|
||||
setRequestState(prev => ({...prev, errors:oldErrorArr}))
|
||||
}
|
||||
}
|
||||
|
||||
if(name == 'email'){
|
||||
//checks if email is a valid email address
|
||||
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
|
||||
if (!EmailValidator(value)) {
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: ['email']
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const [requestState, setRequestState] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: ['email', 'firstname', 'lastname']
|
||||
})
|
||||
|
||||
const addMember = () => {
|
||||
// let errors = Object.keys(fields).filter((item) => { // CHECKS FOR EMPTY STRINGS
|
||||
// if(typeof item == 'string' && fields[item] === ''){
|
||||
// return item
|
||||
// }
|
||||
// })
|
||||
|
||||
// if(errors.length){
|
||||
// setRequestState({
|
||||
// loading: false,
|
||||
// status: false,
|
||||
// message: '',
|
||||
// data: [],
|
||||
// errors: [...errors]
|
||||
// })
|
||||
// return
|
||||
// }
|
||||
|
||||
//checks if email is a valid email address
|
||||
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
|
||||
if (!EmailValidator(fields.email)) {
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: 'Email is invalid',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
return setTimeout(()=>{
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
},3000)
|
||||
}
|
||||
|
||||
setRequestState({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
const requestData = {
|
||||
firstname:fields.firstname,lastname:fields.lastname, email:fields.email, group_id: selectedGroup?.id, action:13015
|
||||
}
|
||||
|
||||
api.groupMemberAdd(requestData).then(response => {
|
||||
let {status, data} = response
|
||||
if(status != 200 || data?.internal_return < 0){
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: 'Unable to add member',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
return
|
||||
}
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: 'Member added',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
|
||||
setTimeout(()=>{
|
||||
// trigger group page reload
|
||||
setUpdateList(prev => !prev)
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
}, 3000)
|
||||
|
||||
}).catch(error=>{
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: 'Something went wrong, try again',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
}).finally(()=>{
|
||||
setTimeout(()=>{
|
||||
setRequestState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: '',
|
||||
data: [],
|
||||
errors: []
|
||||
})
|
||||
setFields({
|
||||
firstname: '',
|
||||
lastname: '',
|
||||
email: ''
|
||||
})
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className='p-5 w-full min-h-[600px] overflow-y-auto'>
|
||||
{groupList && groupList.length < 1 ?
|
||||
<>
|
||||
<h1 className='my-5 text-lg lg:text-xl tracking-wide text-slate-900 dark:text-slate-100'>You Currently Do not have any Group, Please Add Group</h1>
|
||||
</>
|
||||
:
|
||||
<>
|
||||
<h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>{selectedGroup?.name}</h1>
|
||||
<div className='w-full flex flex-col-reverse lg:flex-col'>
|
||||
<div className='py-3 w-full'>
|
||||
<div className='relative grid grid-cols-1 sm:grid-cols-2 gap-2 place-content-center'>
|
||||
<div className="input-item">
|
||||
<InputCom
|
||||
labelClass="tracking-wider"
|
||||
fieldClass="sm:px-6 px-2"
|
||||
value={fields.firstname}
|
||||
inputHandler={handleFieldsChange}
|
||||
placeholder="First Name"
|
||||
// label="Firstname"
|
||||
name="firstname"
|
||||
type="text"
|
||||
// iconName="message"
|
||||
/>
|
||||
</div>
|
||||
<div className="input-item">
|
||||
<InputCom
|
||||
labelClass="tracking-wider"
|
||||
fieldClass="sm:px-6 px-2"
|
||||
value={fields.lastname}
|
||||
inputHandler={handleFieldsChange}
|
||||
placeholder="Last Name"
|
||||
// label="Lastname"
|
||||
name="lastname"
|
||||
type="text"
|
||||
// iconName="message"
|
||||
/>
|
||||
</div>
|
||||
<div className="input-item w-full sm:w-[150%]">
|
||||
<InputCom
|
||||
labelClass="tracking-wider"
|
||||
fieldClass="sm:px-6 px-2"
|
||||
value={fields.email}
|
||||
inputHandler={handleFieldsChange}
|
||||
placeholder="Email"
|
||||
// label="Email"
|
||||
name="email"
|
||||
type="email"
|
||||
// iconName="message"
|
||||
/>
|
||||
</div>
|
||||
<div className='flex justify-end items-end'>
|
||||
{requestState.loading ?
|
||||
<LoadingSpinner size='8' color='sky-blue' />
|
||||
:
|
||||
<button
|
||||
onClick={addMember}
|
||||
disabled={requestState.loading || requestState.status || requestState.errors.length}
|
||||
className={`py-2 px-4 h-[42px] flex justify-center items-center text-base rounded-full text-white font-bold transition-all duration-500 ${requestState.loading || requestState.status || requestState.errors.length ? 'bg-sky-blue/50' : 'bg-sky-blue hover:bg-sky-600'} `}
|
||||
>
|
||||
Add Member
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
|
||||
{!requestState.loading && requestState.message &&
|
||||
<p className={`text-lg absolute -bottom-7 left-0 ${requestState.status ? 'text-green-500' : 'text-red-500'}`}>{requestState.message}</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div className='my-2 flex flex-col min-h-[300px]'>
|
||||
{selectedGroup?.data?.length < 1 ?
|
||||
<h1 className='my-5 text-lg lg:text-xl tracking-wide text-slate-900 dark:text-slate-100'>No Member Found, Please Add</h1>
|
||||
:
|
||||
<GroupMemberTable selectedList={selectedGroup?.data} />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -30,21 +30,26 @@ export default function ActiveJobMessage({ activeJobMesList }) {
|
||||
{activeJobMesList?.data?.length ?
|
||||
(
|
||||
<tbody>
|
||||
{activeJobMesList.data.map((item, index) => (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td>
|
||||
<div className={`msg_box ${item.who}`}>
|
||||
<div className="msg_header">{item.msg_date} {item.msg_firstname}</div>
|
||||
{item.msg_type == 'FILE' ?
|
||||
<a href='' className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></a>
|
||||
:
|
||||
<span className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></span>
|
||||
}
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
{activeJobMesList.data.map((item, index) =>
|
||||
{
|
||||
let imageLink = `${activeJobMesList?.image}${localStorage.getItem('session_token')}/contracts/${item.msg_uid}`
|
||||
return (
|
||||
<tr key={index} className='text-slate-500'>
|
||||
<td>
|
||||
<div className={`msg_box ${item.who}`}>
|
||||
<div className="msg_header">{item.msg_date} {item.msg_firstname}</div>
|
||||
{item.msg_type == 'FILE' ?
|
||||
<a href={imageLink} target="_blank" className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></a>
|
||||
:
|
||||
<span className="p-2" dangerouslySetInnerHTML={{__html: item.message}}></span>
|
||||
}
|
||||
</div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
)}
|
||||
</tbody>
|
||||
)
|
||||
:
|
||||
|
||||
@@ -34,7 +34,7 @@ function PastDueJobAction({jobDetails}) {
|
||||
let reqData = { // API PAYLOADS
|
||||
contract: jobDetails.contract,
|
||||
contract_uid: jobDetails.contract_uid,
|
||||
job_action: 'REQUEST_CANCEL',
|
||||
job_action: 'CANCEL_CONTRACT',
|
||||
}
|
||||
|
||||
if(!checked){ // CHECKS IF CHECKBOX IS SELECTED
|
||||
|
||||
@@ -3,9 +3,12 @@ import ModalCom from '../../Helpers/ModalCom'
|
||||
import LoadingSpinner from '../../Spinners/LoadingSpinner'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import usersService from '../../../services/UsersService'
|
||||
import { useDispatch } from 'react-redux'
|
||||
import { tableReload } from '../../../store/TableReloads'
|
||||
|
||||
function ReviewJobAction({jobDetails}) {
|
||||
|
||||
const dispatch = useDispatch()
|
||||
const apiCall = new usersService()
|
||||
const navigate = useNavigate()
|
||||
|
||||
@@ -48,6 +51,7 @@ function ReviewJobAction({jobDetails}) {
|
||||
return
|
||||
}
|
||||
setReqStatus({loading:false, status: true, message: 'job completion accepted successfully'})
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
setTimeout(()=>{ // Sets popout to false and navigates user to /my-review-jobs after 3 seconds
|
||||
popUpHandler()
|
||||
navigate('/my-review-jobs', {replace: true})
|
||||
|
||||
@@ -21,7 +21,7 @@ export default function MyPastDueJobs(props) {
|
||||
<span
|
||||
className={`${selectTab === "today" ? "block" : "hidden"}`}
|
||||
>
|
||||
Pass Due Job(s)
|
||||
Past Due Job(s)
|
||||
</span>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
@@ -42,16 +42,16 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
|
||||
// Get Country Api
|
||||
const getCountryList = useCallback(async () => {
|
||||
const res = await userApi.getSignupCountryData();
|
||||
|
||||
|
||||
try {
|
||||
if (res.status === 200) {
|
||||
const res = await userApi.getSignupCountryData();
|
||||
if (res.status === 200 && res.data.internal_return >= 0) {
|
||||
const {
|
||||
data: { signup_country },
|
||||
data: { result_list },
|
||||
} = await res;
|
||||
let checkCountry = signup_country
|
||||
?.filter((item) => item[0] == country)
|
||||
?.map((item, idx) => item[1])
|
||||
let checkCountry = result_list
|
||||
?.filter((item) => item.code == country)
|
||||
?.map((item) => item.country)
|
||||
.join("");
|
||||
setCountries(checkCountry);
|
||||
}
|
||||
@@ -59,7 +59,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}, [userApi, country]);
|
||||
|
||||
console.log('MY COUNTRY', myCountry)
|
||||
useEffect(() => {
|
||||
getCountryList();
|
||||
}, [getCountryList]);
|
||||
@@ -220,7 +220,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
|
||||
contentBodyClasses="w-auto min-w-max"
|
||||
/>
|
||||
</div>
|
||||
{MyJobList.loading ? (
|
||||
{MyJobList?.loading ? (
|
||||
<LoadingSpinner size="16" color="sky-blue" />
|
||||
) : (
|
||||
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[520px]">
|
||||
|
||||
@@ -10,7 +10,7 @@ import localImgLoad from "../../lib/localImgLoad";
|
||||
const noTasksBg = require("../../assets/images/no-task-background.jpg");
|
||||
const noFamilyTasksBg = require("../../assets/images/family-no-task-background.jpg");
|
||||
|
||||
export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
export default function MyJobTable({ className, ActiveJobList, Account, imageServer }) {
|
||||
let navigate = useNavigate();
|
||||
let { pathname } = useLocation();
|
||||
|
||||
@@ -55,7 +55,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
{!ActiveJobList?.data.length && accountType && (
|
||||
<div className="absolute inset-0 bg-black opacity-30"></div>
|
||||
)}
|
||||
{ActiveJobList.loading ?
|
||||
{ActiveJobList?.loading ?
|
||||
<div className="w-full h-[520px] flex items-center justify-center">
|
||||
<LoadingSpinner size="16" color="sky-blue" />
|
||||
</div>
|
||||
@@ -71,6 +71,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
task?.currency_code,
|
||||
task?.currency
|
||||
);
|
||||
let image = `${imageServer}${localStorage.getItem('session_token')}/job/${task.origin_job_uid}`
|
||||
return (
|
||||
<div
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] w-full flex justify-between items-center hover:bg-gray-50"
|
||||
@@ -80,7 +81,7 @@ export default function MyJobTable({ className, ActiveJobList, Account }) {
|
||||
<div className="flex space-x-2 items-center">
|
||||
<div className="w-full min-w-[60px] max-w-[60px] flex-[0.1] h-[60px] rounded-full overflow-hidden flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/taskbanners/${task?.banner}`)}
|
||||
src={image}
|
||||
alt="data"
|
||||
className="w-full h-full"
|
||||
/>
|
||||
|
||||
@@ -122,7 +122,7 @@ export default function MyOffersTable({ className, MyActiveOffersList }) {
|
||||
MyActiveOffersList?.result_list?.length > 0 &&
|
||||
MyActiveOffersList.result_list.map((item) => {
|
||||
return (
|
||||
<OfferCard key={item.id} datas={item} setOfferPopout={setOfferPopout} />
|
||||
<OfferCard key={item.id} datas={item} setOfferPopout={setOfferPopout} image_server={MyActiveOffersList.session_image_server} />
|
||||
)
|
||||
})}
|
||||
</SliderCom>
|
||||
|
||||
@@ -94,7 +94,7 @@ export default function MyTasks({
|
||||
</button>
|
||||
}
|
||||
</div>
|
||||
<MyJobTable ActiveJobList={ActiveJobList} Account={userDetails} />
|
||||
<MyJobTable ActiveJobList={ActiveJobList} Account={userDetails} imageServer={MyActiveOffersList.session_image_server} />
|
||||
</div>
|
||||
</div>
|
||||
</Layout>
|
||||
|
||||
@@ -64,17 +64,21 @@ const initialValues = {
|
||||
};
|
||||
|
||||
function AddFundDollars(props) {
|
||||
let MaxNoOfCards = process.env.REACT_APP_MAX_CREDIT_CARDS // HOLDS THE VALUE OF THE MAX NUMBER OF CARDS USER CAN ADD
|
||||
|
||||
let MaxNoOfCards = process.env.REACT_APP_MAX_CREDIT_CARDS; // HOLDS THE VALUE OF THE MAX NUMBER OF CARDS USER CAN ADD
|
||||
|
||||
const apiCall = new usersService();
|
||||
let countryWallet = props.walletItem.country;
|
||||
const [tab, setTab] = useState("previous");
|
||||
const [selectedOption, setSelectedOption] = useState("previous");
|
||||
const { userDetails } = useSelector((state) => state?.userDetails);
|
||||
const [prevCardDetails, setPrevCardDetails] = useState({});
|
||||
const [payListCards, setPayListCards] = useState({ loading: true, data: [] });
|
||||
const [cardIcons, setCardIcons] = useState("atm-card");
|
||||
const [prevCardError, setPrevCardError] = useState("");
|
||||
|
||||
const handleOptionChange = (event) => {
|
||||
setSelectedOption(event.target.value);
|
||||
};
|
||||
|
||||
const { firstname, lastname } = userDetails;
|
||||
|
||||
// Handling Card Icons
|
||||
@@ -144,7 +148,12 @@ function AddFundDollars(props) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab === "previous") {
|
||||
let stateData = {
|
||||
amount: Number(props.input) * 100,
|
||||
currency: props.walletItem?.code,
|
||||
};
|
||||
|
||||
if (selectedOption === "previous") {
|
||||
// To check if card is empty
|
||||
if (Object.keys(prevCardDetails).length === 0) {
|
||||
setPrevCardError("No card selected!");
|
||||
@@ -158,10 +167,26 @@ function AddFundDollars(props) {
|
||||
show: { awaitConfirm: { loader: true } },
|
||||
}));
|
||||
|
||||
let stateData = {
|
||||
amount: Number(props.input) * 100,
|
||||
currency: props.walletItem?.code,
|
||||
};
|
||||
// Extracting card_uid from the previous card details
|
||||
const paymentCardValue = prevCardDetails["payment-card"];
|
||||
|
||||
if (paymentCardValue) {
|
||||
try {
|
||||
const paymentCardObject = JSON.parse(paymentCardValue);
|
||||
stateData = {
|
||||
...stateData,
|
||||
card_uid: paymentCardObject.card_uid,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error parsing JSON:", error);
|
||||
}
|
||||
} else {
|
||||
// For the new card details
|
||||
stateData = {
|
||||
...stateData,
|
||||
card_uid: "",
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
const res = await apiCall.getStartCredit(stateData);
|
||||
@@ -171,12 +196,11 @@ function AddFundDollars(props) {
|
||||
}
|
||||
|
||||
const _response = res.data;
|
||||
stateData.amount = Number(props.input);
|
||||
stateData.card =
|
||||
tab === "previous"
|
||||
selectedOption === "previous"
|
||||
? prevCardDetails["payment-card"]
|
||||
: { ...values, cvv: values.cvv };
|
||||
stateData.cardType = tab === "previous" ? "prev" : "new";
|
||||
stateData.cardType = selectedOption === "previous" ? "prev" : "new";
|
||||
stateData = { ...stateData, ..._response };
|
||||
|
||||
setTimeout(() => {
|
||||
@@ -224,36 +248,45 @@ function AddFundDollars(props) {
|
||||
</h1>
|
||||
<div className="my-1 flex items-center gap-2">
|
||||
<label
|
||||
onClick={() => setTab("previous")}
|
||||
htmlFor="previous"
|
||||
className="cursor-pointer flex items-center gap-1"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
id="previous"
|
||||
value="previous"
|
||||
name="card-option"
|
||||
checked={tab === "previous"}
|
||||
onChange={handleOptionChange}
|
||||
checked={selectedOption === "previous"}
|
||||
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border pointer-events-none w-7 h-7 ${
|
||||
tab == "previous" ? "" : ""
|
||||
selectedOption == "previous" ? "" : ""
|
||||
} tracking-wide transition duration-200`}
|
||||
/>
|
||||
Previous Cards
|
||||
</label>
|
||||
<label
|
||||
onClick={() => setTab("new")}
|
||||
htmlFor="new"
|
||||
className={`cursor-pointer flex items-center gap-1 ${payListCards.data.length >= MaxNoOfCards ? 'pointer-events-none':''}`}
|
||||
className={`cursor-pointer flex items-center gap-1 ${
|
||||
payListCards.data.length >= MaxNoOfCards
|
||||
? "pointer-events-none"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<input
|
||||
id="new"
|
||||
type="radio"
|
||||
name="card-option"
|
||||
checked={tab === "new"}
|
||||
value="new"
|
||||
onChange={handleOptionChange}
|
||||
checked={selectedOption === "new"}
|
||||
className={`p-2 text-lg font-bold text-slate-600 dark:text-white border pointer-events-none w-7 h-7 ${
|
||||
tab == "new" ? "" : ""
|
||||
selectedOption == "new" ? "" : ""
|
||||
} tracking-wide transition duration-200`}
|
||||
/>
|
||||
Add New Card {payListCards.data.length >= MaxNoOfCards && <span className="text-[14px] text-red-500">Max Reached</span>}
|
||||
Add New Card{" "}
|
||||
{payListCards.data.length >= MaxNoOfCards && (
|
||||
<span className="text-[14px] text-red-500">Max Reached</span>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
@@ -261,8 +294,8 @@ function AddFundDollars(props) {
|
||||
<hr />
|
||||
{/* END OF switch button */}
|
||||
|
||||
{/* previous tab */}
|
||||
{tab === "previous" && (
|
||||
{/* previous selectedOption */}
|
||||
{selectedOption === "previous" && (
|
||||
<div className="p-4 previous-details w-full min-h-[16.5rem] flex flex-col">
|
||||
{payListCards.loading ? (
|
||||
<LoadingSpinner size="10" color="sky-blue" />
|
||||
@@ -278,10 +311,11 @@ function AddFundDollars(props) {
|
||||
{currentPreviousCards.map((item, index) => (
|
||||
<option
|
||||
key={index}
|
||||
className={index !== 0 && "border-t-2"}
|
||||
className={index !== 0 ? "border-t-2" : undefined}
|
||||
value={JSON.stringify(item)}
|
||||
title={`${item.description} Card\nBank **************${item.digits}`}
|
||||
>
|
||||
<div className="my-2 flex items-center gap-5">
|
||||
{/* <div className="my-2 flex items-center gap-5">
|
||||
<div className="card-details">
|
||||
<h1 className="text-lg font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
{item.description} Card
|
||||
@@ -290,7 +324,8 @@ function AddFundDollars(props) {
|
||||
Bank **************{item.digits}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div> */}
|
||||
{item.description} Card - Bank **************{item.digits}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
@@ -300,7 +335,7 @@ function AddFundDollars(props) {
|
||||
No Previous Card Found!
|
||||
</p>
|
||||
<button
|
||||
onClick={() => setTab("new")}
|
||||
onClick={() => setSelectedOption("new")}
|
||||
type="button"
|
||||
className="my-5 px-2 py-1 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
@@ -314,13 +349,13 @@ function AddFundDollars(props) {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{tab === "new" && (
|
||||
{selectedOption === "new" && (
|
||||
<div className="new-details w-full max-h-[22rem]">
|
||||
{payListCards.loading ?
|
||||
{payListCards.loading ? (
|
||||
<div className="pt-10 flex w-full h-full justify-center items-center">
|
||||
<LoadingSpinner size='10' color='sky-blue' />
|
||||
<LoadingSpinner size="10" color="sky-blue" />
|
||||
</div>
|
||||
:payListCards.data.length < MaxNoOfCards ?
|
||||
) : payListCards.data.length < MaxNoOfCards ? (
|
||||
<div className="w-full flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
@@ -379,7 +414,8 @@ function AddFundDollars(props) {
|
||||
*
|
||||
</span>
|
||||
<span className="text-[12px] text-red-500 ml-1">
|
||||
{props.errors.expirationMonth && "**"}
|
||||
{props.errors.expirationMonth &&
|
||||
"**"}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -431,7 +467,8 @@ function AddFundDollars(props) {
|
||||
*
|
||||
</span>
|
||||
<span className="text-[12px] text-red-500 italic">
|
||||
{props.errors.expirationYear && "**"}
|
||||
{props.errors.expirationYear &&
|
||||
"**"}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
@@ -560,14 +597,12 @@ function AddFundDollars(props) {
|
||||
}}
|
||||
</Formik>
|
||||
</div>
|
||||
:
|
||||
null
|
||||
}
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{tab == "previous" && (
|
||||
{selectedOption == "previous" && (
|
||||
<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 bg-[#f5a430] text-black items-center text-base rounded-full"
|
||||
|
||||
@@ -71,6 +71,7 @@ function AddFundPop({
|
||||
// Prepare state data for API call
|
||||
let stateData = {
|
||||
amount: Number(input) * 100,
|
||||
card_uid: "", //added card_uid as empty string
|
||||
currency: walletItem?.code,
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ function ThePaymentText({ value, type }) {
|
||||
* @returns {JSX.Element} - The rendered component.
|
||||
*/
|
||||
function AmountSection({ currency, amount, country }) {
|
||||
const formattedAmount = amount?.toFixed(2);
|
||||
const formattedAmount = (+amount * 0.01)?.toFixed(2);
|
||||
const gapClassName = country === "US" ? "gap-14" : "gap-4";
|
||||
|
||||
return (
|
||||
@@ -87,7 +87,7 @@ function TransactionFeeSection({ currency, fee, country }) {
|
||||
*/
|
||||
function TotalSection({ currency, amount, fee, country }) {
|
||||
const total = Number(amount) + Number(fee);
|
||||
const formattedTotal = total?.toFixed(2);
|
||||
const formattedTotal = (total * 0.01)?.toFixed(2);
|
||||
|
||||
const gap = country === "US" ? "gap-[8rem]" : "gap-[6.3rem]";
|
||||
|
||||
@@ -130,7 +130,7 @@ function ConfirmAddFund({
|
||||
public_key: __confirmData?.flutterwave_key,
|
||||
tx_ref: __confirmData?.credit_reference,
|
||||
currency: "NGN",
|
||||
amount: Number(__confirmData.amount),
|
||||
amount: Number(__confirmData.amount) * 0.01,
|
||||
payment_options: "card,mobilemoney,ussd",
|
||||
customer: {
|
||||
email: userDetails.email,
|
||||
@@ -224,7 +224,7 @@ function ConfirmAddFund({
|
||||
|
||||
// Create request data object with required parameters for making the payment
|
||||
const reqData = {
|
||||
amount: amount * 100,
|
||||
amount: amount,
|
||||
card_uid,
|
||||
credit_reference,
|
||||
currency,
|
||||
@@ -289,7 +289,7 @@ function ConfirmAddFund({
|
||||
|
||||
// Prepare request data
|
||||
const reqData = {
|
||||
amount: amount * 100,
|
||||
amount: amount,
|
||||
cardnumber: cardNum.replace(/\s/g, ""),
|
||||
credit_reference,
|
||||
cvc: cvv,
|
||||
|
||||
@@ -170,9 +170,9 @@ function ConfirmNairaWithdraw({
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="green"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="feather feather-check-circle"
|
||||
>
|
||||
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14"></path>
|
||||
@@ -186,9 +186,9 @@ function ConfirmNairaWithdraw({
|
||||
width="100"
|
||||
height="100"
|
||||
stroke="red"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
strokeWidth="2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
className="feather feather-x-circle"
|
||||
>
|
||||
<circle cx="12" cy="12" r="10"></circle>
|
||||
|
||||
@@ -25,7 +25,7 @@ function NairaWithdraw({
|
||||
},
|
||||
newAccount: {
|
||||
country: wallet.walletCountry
|
||||
? wallet.walletCountry[0][0]
|
||||
? wallet.walletCountry[0]?.code
|
||||
: wallet.country,
|
||||
bank: state?.newAccount?.amount || "",
|
||||
accountNumber: state?.newAccount?.amount || "",
|
||||
@@ -34,7 +34,6 @@ function NairaWithdraw({
|
||||
city: state?.newAccount?.amount || "",
|
||||
},
|
||||
};
|
||||
|
||||
const [errorMsgs, setErrorMsgs] = useState(initialValues);
|
||||
|
||||
let [sendMoneyFee, setSendMoneyFee] = useState({
|
||||
@@ -69,7 +68,7 @@ function NairaWithdraw({
|
||||
// Handling card change
|
||||
const handleBankOptions = (event) => {
|
||||
let bankCountry = wallet.walletCountry
|
||||
? wallet.walletCountry[0][0]
|
||||
? wallet.walletCountry[0]?.code
|
||||
: wallet.country
|
||||
setBankName((prev) => ({ loading: true, data: [] }));
|
||||
apiCall
|
||||
@@ -120,7 +119,7 @@ function NairaWithdraw({
|
||||
}
|
||||
setAllCountries((prev) => ({
|
||||
loading: false,
|
||||
data: res.data.signup_country,
|
||||
data: res.data.result_list,
|
||||
}));
|
||||
})
|
||||
.catch((error) => {
|
||||
@@ -287,7 +286,7 @@ function NairaWithdraw({
|
||||
getRecipients();
|
||||
}, []);
|
||||
|
||||
console.log("Testing Wallet Country", wallet?.walletCountry[0][0]);
|
||||
console.log("Testing Wallet Country", wallet?.walletCountry[0]?.code);
|
||||
|
||||
return (
|
||||
<ModalCom action={action} situation={situation} className="edit-popup">
|
||||
@@ -560,7 +559,7 @@ function NairaWithdraw({
|
||||
</label>
|
||||
<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]}
|
||||
{wallet.walletCountry[0]?.country}
|
||||
</span>
|
||||
</div>
|
||||
{/* <select
|
||||
|
||||
@@ -41,13 +41,13 @@ const WalletRoutes = () => {
|
||||
apiCall
|
||||
.getSignupCountryData()
|
||||
.then((res) => {
|
||||
if (res.data.internal_return < 0) {
|
||||
if (res?.data?.internal_return < 0) {
|
||||
setAllCountries((prev) => ({ loading: false, data: [] }));
|
||||
return;
|
||||
}
|
||||
setAllCountries((prev) => ({
|
||||
loading: false,
|
||||
data: res.data.signup_country,
|
||||
data: res.data.result_list,
|
||||
}));
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import WalletItemCard from "./WalletItemCard";
|
||||
import WalletItemCardFamily from "./WalletItemCardFamily";
|
||||
|
||||
/**
|
||||
* Renders a list of wallet items or a loading spinner depending on the state of the `wallet` object.
|
||||
@@ -7,9 +9,27 @@ import WalletItemCard from "./WalletItemCard";
|
||||
export default function WalletBox({ wallet, payment, countries }) {
|
||||
const { loading, data } = wallet;
|
||||
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
const accountType = userDetails?.account_type === "FAMILY";
|
||||
|
||||
return (
|
||||
<div className="my-wallet-wrapper w-full mb-10">
|
||||
<div className="main-wrapper w-full">
|
||||
{accountType ?
|
||||
<div className="balance-inquery w-auto grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-[repeat(auto-fill,_minmax(354px,_1fr))] min-[1440px]:grid-cols-[repeat(auto-fill,_minmax(415px,_1fr))] gap-5 mb-11 h-auto">
|
||||
{loading ? (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
<LoadingSpinner size="16" color="sky-blue" />
|
||||
</div>
|
||||
) : (
|
||||
data.length > 0 && data.map((item) => (
|
||||
<div key={item.wallet_uid} className="lg:w-full h-full mb-10 lg:mb-0">
|
||||
<WalletItemCardFamily walletItem={item} payment={payment} countries={countries} />
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
:
|
||||
<div className="balance-inquery w-auto grid sm:grid-cols-2 lg:grid-cols-2 xl:grid-cols-[repeat(auto-fill,_minmax(354px,_1fr))] min-[1440px]:grid-cols-[repeat(auto-fill,_minmax(415px,_1fr))] gap-5 mb-11 h-auto">
|
||||
{loading ? (
|
||||
<div className="w-full h-full flex items-center justify-center">
|
||||
@@ -23,6 +43,7 @@ export default function WalletBox({ wallet, payment, countries }) {
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -11,8 +11,7 @@ import WalletAction from "./WalletAction";
|
||||
* Renders a card displaying information about a wallet item.
|
||||
*/
|
||||
export default function WalletItemCard({ walletItem, payment, countries }) {
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
const accountType = userDetails?.account_type === "FAMILY";
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [creditPopup, setCreditPopup] = useState({ show: false, data: {} });
|
||||
|
||||
@@ -36,8 +35,8 @@ export default function WalletItemCard({ walletItem, payment, countries }) {
|
||||
};
|
||||
|
||||
const currentWalletCurrency = countries
|
||||
.map((country) => country)
|
||||
.filter((country) => country[0] === walletItem.country);
|
||||
// .map((country) => country)
|
||||
.filter((country) => country.code === walletItem.country);
|
||||
|
||||
const image = walletItem.code
|
||||
? `${walletItem.code.toLowerCase()}.svg`
|
||||
@@ -91,13 +90,11 @@ export default function WalletItemCard({ walletItem, payment, countries }) {
|
||||
|
||||
<div className="my-2 w-full h-[1px] bg-white"></div>
|
||||
|
||||
{!accountType && (
|
||||
<WalletAction
|
||||
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
|
||||
payment={payment}
|
||||
openPopUp={openPopUp}
|
||||
/>
|
||||
)}
|
||||
<WalletAction
|
||||
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
|
||||
payment={payment}
|
||||
openPopUp={openPopUp}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{creditPopup.show && (
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import React, { useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import background from "../../assets/images/bg-sky-blue.jpg"; //shape/balance-bg.svg";
|
||||
import localImgLoad from "../../lib/localImgLoad";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
import { PriceFormatter } from "../Helpers/PriceFormatter";
|
||||
import CreditPopup from "./Popup/CreditPopup";
|
||||
import WalletAction from "./WalletAction";
|
||||
|
||||
/**
|
||||
* Renders a card displaying information about a wallet item.
|
||||
*/
|
||||
export default function WalletItemCardFamily({ walletItem, payment, countries }) {
|
||||
|
||||
const dispatch = useDispatch();
|
||||
const [creditPopup, setCreditPopup] = useState({ show: false, data: {} });
|
||||
|
||||
/**
|
||||
* Opens the credit popup.
|
||||
* @param {Object} value - The value object.
|
||||
*/
|
||||
const openPopUp = (value) => {
|
||||
setCreditPopup({
|
||||
show: true,
|
||||
data: { ...value },
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes the credit popup and dispatches a table reload action.
|
||||
*/
|
||||
const closePopUp = () => {
|
||||
setCreditPopup({ show: false, data: {} });
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
};
|
||||
|
||||
const currentWalletCurrency = countries
|
||||
// .map((country) => country)
|
||||
.filter((country) => country.code === walletItem.country);
|
||||
|
||||
const image = walletItem.code
|
||||
? `${walletItem.code.toLowerCase()}.svg`
|
||||
: "default.png";
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
className="current-balance-widget w-full h-full rounded-2xl overflow-hidden flex flex-col items-center gap-2 p-8 justify-between"
|
||||
style={{
|
||||
background: `url(${background}) 0% 0% / cover no-repeat`,
|
||||
}}
|
||||
>
|
||||
<div className="wallet w-full flex justify-between items-center gap-3">
|
||||
<div className="min-w-[100px] min-h-[100px] max-w-min md:max-w-[150px] max-h-min md:max-h-[150px] rounded-full bg-[#e3e3e3] flex justify-center items-center">
|
||||
<img
|
||||
src={localImgLoad(`images/currency/${image}`)}
|
||||
className="w-full h-full"
|
||||
alt="currency-icon"
|
||||
/>
|
||||
</div>
|
||||
<div className="balance w-full mt-2 flex justify-center">
|
||||
<div className="">
|
||||
<p className="text-base sm:text-lg text-white opacity-[70%] tracking-wide mb-2 sm:mb-6">
|
||||
Current Balance
|
||||
</p>
|
||||
<p className="text-[44px] lg:text-[62px] font-bold text-white tracking-wide leading-10 xxs:scale-100 lg:scale-100 xl:scale-125">
|
||||
{PriceFormatter(
|
||||
walletItem.amount * 0.01,
|
||||
walletItem.code,
|
||||
undefined,
|
||||
"text-[2rem]"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="my-2 w-full h-[1px] bg-white"></div>
|
||||
|
||||
{/* <WalletAction
|
||||
walletItem={{ ...walletItem, walletCountry: currentWalletCurrency }}
|
||||
payment={payment}
|
||||
openPopUp={openPopUp}
|
||||
/> */}
|
||||
</div>
|
||||
|
||||
{creditPopup.show && (
|
||||
<CreditPopup
|
||||
details={creditPopup.data}
|
||||
walletItem={walletItem}
|
||||
onClose={closePopUp}
|
||||
situation={openPopUp}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -6,8 +6,11 @@ import CommonHead from "../UserHeader/CommonHead";
|
||||
import usersService from "../../services/UsersService";
|
||||
import LoadingSpinner from "../Spinners/LoadingSpinner";
|
||||
import OthersInterestedTable from "./OthersInterestedTable";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { tableReload } from "../../store/TableReloads";
|
||||
|
||||
export default function ManageInterestOffer(props) {
|
||||
const dispatch = useDispatch()
|
||||
const navigate = useNavigate()
|
||||
const apiCall = new usersService()
|
||||
|
||||
@@ -89,6 +92,7 @@ export default function ManageInterestOffer(props) {
|
||||
setRedirectTime(prev => prev - 1)
|
||||
}, 1000);
|
||||
setRequestStatus({loading: false, status: true, message: `Offer ${name}ed`, processType: ''})
|
||||
dispatch(tableReload({ type: "WALLETTABLE" }));
|
||||
setTimeout(()=>{
|
||||
navigate('/offer-interest', {replace: true})
|
||||
clearInterval(intervalTime)
|
||||
|
||||
@@ -37,11 +37,17 @@ export default function Header({ logoutModalHandler, sidebarHandler }) {
|
||||
const { walletDetails } = useSelector((state) => state?.walletDetails); // WALLET STORE
|
||||
|
||||
const image = localStorage.getItem("session_token")
|
||||
? `${userDetails.session_image_server}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/profile/${userDetails.uid}`
|
||||
? userDetails.account_type === "FAMILY"
|
||||
? `${userDetails.session_image_server}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/family/${sessionStorage.getItem("family_uid")}`
|
||||
: `${userDetails.session_image_server}${localStorage.getItem(
|
||||
"session_token"
|
||||
)}/profile/${userDetails.uid}`
|
||||
: "";
|
||||
|
||||
// 9308RDR122
|
||||
|
||||
const handlerBalance = () => {
|
||||
setbalanceValue.toggle();
|
||||
if (notificationDropdown) {
|
||||
|
||||
@@ -23,6 +23,7 @@ export default function Layout({ children }) {
|
||||
localStorage.removeItem("session_token");
|
||||
localStorage.removeItem("member_id");
|
||||
localStorage.removeItem("uid");
|
||||
sessionStorage.removeItem("family_uid");
|
||||
// localStorage.clear();
|
||||
// toast.success("Come Back Soon", {
|
||||
// icon: `🙂`,
|
||||
@@ -78,7 +79,7 @@ export default function Layout({ children }) {
|
||||
{children && children}
|
||||
</div>
|
||||
<div className="nft-right-side-content 2xl:w-[270px] w-full h-full 2xl:flex justify-center relative flex-[20%]">
|
||||
<RightSideBar />
|
||||
<RightSideBar myJobList={userJobList} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,7 +87,7 @@ export default function Layout({ children }) {
|
||||
</div>
|
||||
{logoutModal && (
|
||||
<ModalCom action={logoutModalHandler} situation={logoutModal}>
|
||||
<div className="logout-modal-wrapper lg:w-[460px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-wrapper w-11/12 sm:w-[460px] bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b dark:border-[#5356fb29] border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Confirm
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useSelector } from "react-redux";
|
||||
import { NavLink } from "react-router-dom";
|
||||
//import SideStatistics from "./SideStatistics";
|
||||
|
||||
export default function RightSideBar() {
|
||||
export default function RightSideBar({myJobList}) {
|
||||
const filterDatas = ["Last 15 days", "Last Month", "Last 6 month"];
|
||||
const [filterDataSet, setFilterDataSet] = useState([10, 30, 20, 40]);
|
||||
const dataSetHandler = (value) => {
|
||||
@@ -154,7 +154,168 @@ export default function RightSideBar() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/*<SideStatistics />*/}
|
||||
|
||||
{/*JOB LINKS*/}
|
||||
{myJobList?.data?.result_list?.length > 0 &&
|
||||
<div className="top-platform mt-6 bg-white dark:bg-dark-white rounded-2xl py-8 2xl:w-[268px] w-full 2xl:mb-10 2xl:border-none border ">
|
||||
{/* heading */}
|
||||
<div className="px-8 heading flex justify-between items-center mb-3.5">
|
||||
<h3 className="text-xl font-bold text-dark-gray dark:text-white">
|
||||
Job Links
|
||||
</h3>
|
||||
<div></div>
|
||||
</div>
|
||||
|
||||
<div className="platform-list">
|
||||
<div className="px-8 item flex space-x-3 items-center mb-4">
|
||||
{/* image */}
|
||||
<div className="w-8 h-8 rounded-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
id="history"
|
||||
>
|
||||
<g data-name="14">
|
||||
<circle cx="9" cy="14" r="7" fill="#ffd54f"></circle>
|
||||
<path
|
||||
fill="#ef6c00"
|
||||
d="M21 9H17a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2zM21 5H3A1 1 0 0 1 3 3H21a1 1 0 0 1 0 2zM21 13H19a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zM21 17H19a1 1 0 0 1 0-2h2a1 1 0 0 1 0 2zM21 21H17a1 1 0 0 1 0-2h4a1 1 0 0 1 0 2z"
|
||||
></path>
|
||||
<path
|
||||
fill="#ff8f00"
|
||||
d="M10,10a1,1,0,0,0-2,0v3.59L6.29,15.29a1,1,0,1,0,1.41,1.41l2-2A1,1,0,0,0,10,14Z"
|
||||
></path>
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
{/* name */}
|
||||
<div>
|
||||
<p className="text-thin-light-gray text-base font-medium">
|
||||
<NavLink to="/my-active-jobs">Active Task</NavLink>
|
||||
</p>
|
||||
</div>
|
||||
{/* action */}
|
||||
</div>
|
||||
{/* {userDetails && userDetails?.account_type !== "FAMILY" && (
|
||||
<>
|
||||
</>
|
||||
)} */}
|
||||
<div className="px-8 item flex space-x-3 items-center mb-4">
|
||||
{/* image */}
|
||||
<div className="w-8 h-8 rounded-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24"
|
||||
id="add-user"
|
||||
>
|
||||
<path
|
||||
fill="#9bcb5b"
|
||||
d="M12 13.4c-2 0-3.7-1.6-3.7-3.6s1.6-3.7 3.6-3.7 3.7 1.6 3.7 3.6-1.6 3.6-3.6 3.7zm0-6.2c-1.4 0-2.6 1.1-2.6 2.6 0 1.4 1.1 2.6 2.6 2.6s2.6-1.1 2.6-2.6c-.1-1.5-1.2-2.6-2.6-2.6z"
|
||||
></path>
|
||||
<path
|
||||
fill="#9bcb5b"
|
||||
d="M16.6 17.9c-.3 0-.5-.2-.6-.5 0-2.2-1.8-4-4-4s-4 1.8-4 4c0 .3-.3.5-.6.5-.2 0-.4-.2-.5-.5 0-2.8 2.3-5.1 5.1-5.1s5.1 2.3 5.1 5.1c0 .3-.2.5-.5.5z"
|
||||
></path>
|
||||
<path
|
||||
fill="#0376bc"
|
||||
d="M12 23.7C5.5 23.7.3 18.4.3 12 .3 5.5 5.6.3 12 .3c2.6 0 5.1.9 7.2 2.5.2.2.2.6 0 .8-.2.2-.4.2-.7.1-1.9-1.4-4.1-2.2-6.5-2.2C6.2 1.4 1.4 6.2 1.4 12S6.2 22.6 12 22.6 22.6 17.8 22.6 12c0-2.4-.8-4.6-2.2-6.5-.2-.3-.1-.6.2-.8.2-.1.5-.1.7.1 1.6 2 2.5 4.6 2.4 7.2 0 6.4-5.3 11.7-11.7 11.7z"
|
||||
></path>
|
||||
<circle cx="20.2" cy="20.3" r="2.4" fill="#fff"></circle>
|
||||
<path
|
||||
fill="#9bcb5b"
|
||||
d="M18 18.1c.6-.6 1.4-.9 2.2-.9.8 0 1.6.3 2.2.9s1 1.4.9 2.2c0 .8-.3 1.6-.9 2.2s-1.4 1-2.2.9c-.8 0-1.6-.3-2.2-.9s-1-1.4-.9-2.2c-.1-.8.3-1.7.9-2.2zm3.8 2.5V20h-1.3v-1.3h-.6V20h-1.3v.6h1.3v1.3h.6v-1.3h1.3z"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
{/* name */}
|
||||
<div>
|
||||
<p className="text-thin-light-gray text-base font-medium">
|
||||
<NavLink to="/my-review-jobs">Review Pending</NavLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="px-8 item flex space-x-3 items-center mb-4">
|
||||
{/* image */}
|
||||
<div className="w-8 h-8 rounded-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
id="InternationalUser"
|
||||
>
|
||||
<path
|
||||
fill="#e6e7f9"
|
||||
d="M38.5 31.1c2.1-1.1 3.9-2.5 5.4-4.4h-3.2c-.6 1.6-1.3 3.1-2.2 4.4zm-5.8 1.4c2-.7 3.7-2.8 4.9-5.8h-4.9v5.8zm8.8-8.6h4.2c.9-1.8 1.5-3.7 1.7-5.8h-5.1c-.1 2-.4 4-.8 5.8zm2.4-17.3c-1.5-1.8-3.3-3.3-5.4-4.4.8 1.2 1.6 2.7 2.2 4.4h3.2zm-5.3 2.9h-5.9v5.8h6.7c-.1-2.1-.4-4.1-.8-5.8zm3.7 5.8h5.1c-.2-2.1-.8-4-1.7-5.8h-4.2c.4 1.8.7 3.7.8 5.8zM32.7.8v5.8h4.9c-1.2-2.9-2.9-5.1-4.9-5.8zm6.7 17.3h-6.7v5.8h5.9c.4-1.8.7-3.8.8-5.8zM25 6.6h4.9V.8c-2 .7-3.7 2.9-4.9 5.8zm-1.8 8.7h6.7V9.5H24c-.4 1.7-.7 3.7-.8 5.8zm6.7 17.2v-5.8H25c1.2 3 2.9 5.1 4.9 5.8zM24.1 2.3c-2.1 1.1-3.9 2.5-5.4 4.4H22c.5-1.7 1.3-3.2 2.1-4.4zM24 23.9h5.9v-5.8h-6.7c.1 2 .4 4 .8 5.8z"
|
||||
className="colorc1e5ff svgShape"
|
||||
></path>
|
||||
<path
|
||||
fill="#ff6699"
|
||||
d="M3.7 44.7c0 1.6 1.2 2.8 2.8 2.8 1.3 0 2.4-.9 2.7-2.2.3 1.3 1.4 2.2 2.7 2.2 1.5 0 2.8-1.3 2.8-2.8V30.1h3.1V18.8c0-4.2-3.3-7.5-7.4-7.5H8c-4.1 0-7.4 3.4-7.4 7.5v11.3h3.1v14.6z"
|
||||
className="colorff99b0 svgShape"
|
||||
></path>
|
||||
<path
|
||||
fill="#998da0"
|
||||
d="M9.2 10.3c2.4 0 4.4-2.2 4.4-4.9S11.6.5 9.2.5C6.8.5 4.8 2.7 4.8 5.4s2 4.9 4.4 4.9z"
|
||||
className="colorffd499 svgShape"
|
||||
></path>
|
||||
<path
|
||||
fill="#e6e7f9"
|
||||
d="M16.9 9.5c-.3.6-.5 1.2-.8 1.8 1.3 1 2.3 2.4 2.9 4h1.3c.1-2 .3-4 .8-5.8h-4.2zm2.9 9.3v5.1h1.4c-.4-1.8-.7-3.8-.8-5.8h-.6c-.1.2 0 .4 0 .7zm2.2 7.9h-2.2v1.2c1.3 1.3 2.7 2.4 4.4 3.2-.9-1.3-1.7-2.8-2.2-4.4z"
|
||||
className="colorc1e5ff svgShape"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
{/* name */}
|
||||
<div>
|
||||
<p className="text-thin-light-gray text-base font-medium">
|
||||
<NavLink to="/my-pastdue-jobs">Past Due</NavLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Line */}
|
||||
<div className="my-4 mx-auto w-10/12 h-[2px] bg-slate-500 dark:bg-white rounded-full"></div>
|
||||
|
||||
<div className="px-8 item flex space-x-3 items-center mb-4">
|
||||
{/* image */}
|
||||
<div className="w-8 h-8 rounded-full">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 48 48"
|
||||
id="InternationalUser"
|
||||
>
|
||||
<path
|
||||
fill="#e6e7f9"
|
||||
d="M38.5 31.1c2.1-1.1 3.9-2.5 5.4-4.4h-3.2c-.6 1.6-1.3 3.1-2.2 4.4zm-5.8 1.4c2-.7 3.7-2.8 4.9-5.8h-4.9v5.8zm8.8-8.6h4.2c.9-1.8 1.5-3.7 1.7-5.8h-5.1c-.1 2-.4 4-.8 5.8zm2.4-17.3c-1.5-1.8-3.3-3.3-5.4-4.4.8 1.2 1.6 2.7 2.2 4.4h3.2zm-5.3 2.9h-5.9v5.8h6.7c-.1-2.1-.4-4.1-.8-5.8zm3.7 5.8h5.1c-.2-2.1-.8-4-1.7-5.8h-4.2c.4 1.8.7 3.7.8 5.8zM32.7.8v5.8h4.9c-1.2-2.9-2.9-5.1-4.9-5.8zm6.7 17.3h-6.7v5.8h5.9c.4-1.8.7-3.8.8-5.8zM25 6.6h4.9V.8c-2 .7-3.7 2.9-4.9 5.8zm-1.8 8.7h6.7V9.5H24c-.4 1.7-.7 3.7-.8 5.8zm6.7 17.2v-5.8H25c1.2 3 2.9 5.1 4.9 5.8zM24.1 2.3c-2.1 1.1-3.9 2.5-5.4 4.4H22c.5-1.7 1.3-3.2 2.1-4.4zM24 23.9h5.9v-5.8h-6.7c.1 2 .4 4 .8 5.8z"
|
||||
className="colorc1e5ff svgShape"
|
||||
></path>
|
||||
<path
|
||||
fill="#ff6699"
|
||||
d="M3.7 44.7c0 1.6 1.2 2.8 2.8 2.8 1.3 0 2.4-.9 2.7-2.2.3 1.3 1.4 2.2 2.7 2.2 1.5 0 2.8-1.3 2.8-2.8V30.1h3.1V18.8c0-4.2-3.3-7.5-7.4-7.5H8c-4.1 0-7.4 3.4-7.4 7.5v11.3h3.1v14.6z"
|
||||
className="colorff99b0 svgShape"
|
||||
></path>
|
||||
<path
|
||||
fill="#998da0"
|
||||
d="M9.2 10.3c2.4 0 4.4-2.2 4.4-4.9S11.6.5 9.2.5C6.8.5 4.8 2.7 4.8 5.4s2 4.9 4.4 4.9z"
|
||||
className="colorffd499 svgShape"
|
||||
></path>
|
||||
<path
|
||||
fill="#e6e7f9"
|
||||
d="M16.9 9.5c-.3.6-.5 1.2-.8 1.8 1.3 1 2.3 2.4 2.9 4h1.3c.1-2 .3-4 .8-5.8h-4.2zm2.9 9.3v5.1h1.4c-.4-1.8-.7-3.8-.8-5.8h-.6c-.1.2 0 .4 0 .7zm2.2 7.9h-2.2v1.2c1.3 1.3 2.7 2.4 4.4 3.2-.9-1.3-1.7-2.8-2.2-4.4z"
|
||||
className="colorc1e5ff svgShape"
|
||||
></path>
|
||||
</svg>
|
||||
</div>
|
||||
{/* name */}
|
||||
<div>
|
||||
<p className="text-thin-light-gray text-base font-medium">
|
||||
<NavLink to="/job-groups">Job Groups</NavLink>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -63,6 +63,7 @@ export default function Resources(props) {
|
||||
loading: false,
|
||||
msg: "success",
|
||||
data: res?.result_list,
|
||||
image:res?.session_image_server
|
||||
}));
|
||||
} catch (error) {
|
||||
setUploadedFiles((prev) => ({
|
||||
|
||||
@@ -13,7 +13,6 @@ export default function MyUploadedFiles({ uploadedFiles }) {
|
||||
indexOfFirstItem,
|
||||
indexOfLastItem
|
||||
);
|
||||
|
||||
const handlePagination = (e) => {
|
||||
handlePagingFunc(e, setCurrentPage);
|
||||
};
|
||||
@@ -45,10 +44,11 @@ export default function MyUploadedFiles({ uploadedFiles }) {
|
||||
currentFiles.map((value, idx) => {
|
||||
let addedDate = value?.added?.split(" ")[0];
|
||||
let formattedSize = formatFileSize(value?.file_size);
|
||||
let imageLink = `${uploadedFiles?.image}${localStorage.getItem('session_token')}/myfile/${value.file_uid}`
|
||||
return (
|
||||
<tr
|
||||
key={value?.file_uid}
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
|
||||
>
|
||||
<td className=" py-4">
|
||||
<div className="flex space-x-2 items-center w-full">
|
||||
@@ -102,17 +102,17 @@ export default function MyUploadedFiles({ uploadedFiles }) {
|
||||
</td>
|
||||
<td className="text-right py-4 px-2">
|
||||
<div className="flex justify-center items-center">
|
||||
<button
|
||||
type="button"
|
||||
// onClick={() => {
|
||||
// navigate("/manage-active-job", {
|
||||
// state: { ...value, pathname },
|
||||
// });
|
||||
// }}
|
||||
className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
<a
|
||||
href={imageLink}
|
||||
title="download"
|
||||
// className="w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
>
|
||||
View
|
||||
</button>
|
||||
<img
|
||||
src={localImgLoad('images/icons/download-arrow.svg')}
|
||||
alt='download-link'
|
||||
className="w-auto h-6 flex justify-center items-center"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -157,7 +157,7 @@ export default function ProductUploadField({
|
||||
fieldClass="px-6"
|
||||
type="text"
|
||||
name="name"
|
||||
placeholder="RaidParty Fighters"
|
||||
placeholder="Item Name"
|
||||
inputHandler={inh}
|
||||
value={datas.itemName}
|
||||
/>
|
||||
@@ -186,7 +186,7 @@ export default function ProductUploadField({
|
||||
<textarea
|
||||
value={datas.description}
|
||||
onChange={(e) => dscrphn(e)}
|
||||
placeholder="provide a detailed description of your item."
|
||||
placeholder="Enter detail description"
|
||||
rows="7"
|
||||
className="w-full h-full px-7 py-4 border border-light-purple dark:border-[#5356fb29] rounded-[20px] text-dark-gray dark:text-white bg-[#FAFAFA] dark:bg-[#11131F] focus:ring-0 focus:outline-none"
|
||||
/>
|
||||
|
||||
@@ -144,14 +144,14 @@ export default function UploadProduct({uploadTypes}) {
|
||||
}
|
||||
|
||||
let reqData = { // PAYLOAD FOR API CALL
|
||||
file_name: selectedFile.substring(0,21).replace(/ /gi, ""),//selectedFile.replace(/[ -]/gi, ""),
|
||||
// file_name: selectedFile.substring(0,21).replace(/ /gi, ""),
|
||||
file_name: `myfile.${imgDetails?.type?.split('/')[1]}`,
|
||||
file_size: imgDetails.size,
|
||||
file_type: imgDetails.type,
|
||||
file_data: img.file,
|
||||
file_data: img.file?.split(",")[1],
|
||||
title: itemName,
|
||||
description: description,
|
||||
msg_type: 'FILE',
|
||||
// action: 'WRENCHBOARD_RESOURCE_MYFILES',
|
||||
action: 11307
|
||||
}
|
||||
|
||||
@@ -602,7 +602,10 @@ export default function UploadProduct({uploadTypes}) {
|
||||
|
||||
//FUNCTIONS to check if file upload type is valid
|
||||
const isValidFile = (file, supportedFile=[]) => {
|
||||
let fileType = file.type.split("/")[1];
|
||||
let fileType = file.type.split("/")[1].toLowerCase();
|
||||
if(fileType=='jpg' || fileType=='jpeg'){ //forcing both JPG and JPEG TO RETURN JPG AS FILE TYPE
|
||||
fileType = 'jpg'
|
||||
}
|
||||
let valid = supportedFile.filter(item => (
|
||||
item.name.toLowerCase() == fileType.toLowerCase()
|
||||
))
|
||||
@@ -615,7 +618,10 @@ const isValidFile = (file, supportedFile=[]) => {
|
||||
|
||||
//FUNCTIONS TO CHECK IF FILE SIZE IS VALID
|
||||
const isValidFileSize = (file, supportedFile=[]) => {
|
||||
let fileType = file.type.split("/")[1];
|
||||
let fileType = file.type.split("/")[1].toLowerCase();
|
||||
if(fileType=='jpg' || fileType=='jpeg'){ //forcing both JPG and JPEG TO RETURN JPG AS FILE TYPE
|
||||
fileType = 'jpg'
|
||||
}
|
||||
let fileSize = file.size;
|
||||
let valid = supportedFile.filter(item => (
|
||||
item.name.toLowerCase() == fileType.toLowerCase()
|
||||
@@ -627,6 +633,6 @@ const isValidFileSize = (file, supportedFile=[]) => {
|
||||
return {status: false, message: `File must not exceed ${valid[0].max_size_mb}MB`}
|
||||
}
|
||||
}else{
|
||||
return false
|
||||
return {status: false, message: `Cannot read file size, try again`}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ function DeleteJobPopout({ details, onClose, situation }) {
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation}>
|
||||
<div className="logout-modal-wrapper lg:w-[600px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-wrapper lg:w-[600px] bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Delete Job
|
||||
|
||||
@@ -52,12 +52,19 @@ const EditJobPopOut = ({
|
||||
}) => {
|
||||
const dispatch = useDispatch();
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
|
||||
const uploadedImage = `${userDetails.session_image_server}${localStorage.getItem('session_token')}/job/${details?.job_uid}`
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
|
||||
const [taskImage, setTaskImage] = useState(uploadedImage)
|
||||
const uploadedImage = `${
|
||||
userDetails.session_image_server
|
||||
}${localStorage.getItem("session_token")}/job/${details?.job_uid}`;
|
||||
|
||||
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
const [taskImage, setTaskImage] = useState(uploadedImage);
|
||||
|
||||
let [uploadStatus, setUploadStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
@@ -113,68 +120,103 @@ const EditJobPopOut = ({
|
||||
);
|
||||
|
||||
const taskImgChangeHandler = (e) => {
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0] //UPLOADED FILE
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"]; // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0]; //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
if(!acceptedFormat.includes(fileFormat)){ //CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat.slice(0, -1).join(', ')} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({loading: false, status: false, message:msg})
|
||||
return setTimeout(()=>{
|
||||
if (!acceptedFormat.includes(fileFormat)) {
|
||||
//CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat
|
||||
.slice(0, -1)
|
||||
.join(", ")} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({ loading: false, status: false, message: msg });
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
|
||||
return setTimeout(()=>{
|
||||
if (uploadedFile.size > 5 * 1048576) {
|
||||
// CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "File must not exceed 5MB",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = { // PAYLOAD FOR API CALL
|
||||
let reqData = {
|
||||
// PAYLOAD FOR API CALL
|
||||
job_uid: details?.job_uid,
|
||||
file_name: uploadedFile?.name.slice(0,19),
|
||||
file_name: uploadedFile?.name.slice(0, 19),
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: 'FILE',
|
||||
action: 11303
|
||||
}
|
||||
setUploadStatus({loading: true, status: false, message:'Loading...'})
|
||||
jobApi.sendFiles(reqData).then(res=>{
|
||||
if(res.status != 200 || res.data.internal_return < 0){
|
||||
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
|
||||
}
|
||||
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
|
||||
setTaskImage(event.target.result);
|
||||
setTimeout(() => {
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
navigate("/myjobs", { replace: true });
|
||||
onClose();
|
||||
}, 1000);
|
||||
}).catch(error=>{
|
||||
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
|
||||
}).finally(()=>{
|
||||
setTimeout(()=>{
|
||||
setUploadStatus({loading: false, status: false, message: ''})
|
||||
},5000)
|
||||
})
|
||||
msg_type: "FILE",
|
||||
action: 11303,
|
||||
};
|
||||
setUploadStatus({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: "Loading...",
|
||||
});
|
||||
jobApi
|
||||
.sendFiles(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
return setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Something went wrong, try again",
|
||||
});
|
||||
}
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Uploaded successfully",
|
||||
});
|
||||
setTaskImage(event.target.result);
|
||||
setTimeout(() => {
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
navigate("/myjobs", { replace: true });
|
||||
onClose();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Network error, try again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the user is using iOS
|
||||
const isIOS = /MacIntel|MacPPC/.test(navigator.platform) && !window.MSStream;
|
||||
|
||||
// Check if the user is using Windows
|
||||
const isWindows = /Windows/.test(navigator.userAgent);
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation} className="edit-popup">
|
||||
<div className="logout-modal-wrapper lg:w-[600px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<ModalCom action={onClose} situation={situation}>
|
||||
<div className="logout-modal-wrapper w-11/12 lg:w-[600px] bg-white dark:bg-dark-white lg:rounded-2xl">
|
||||
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Edit Job
|
||||
@@ -325,19 +367,20 @@ const EditJobPopOut = ({
|
||||
role="group"
|
||||
aria-labelledby="checked-group"
|
||||
>
|
||||
{categories && Object.entries(categories)?.map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
{categories &&
|
||||
Object.entries(categories)?.map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="flex gap-1 w-full items-center dark:text-white"
|
||||
>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
<span className="h-5 text-sm italic text-[#cf3917]">
|
||||
{props.errors.category &&
|
||||
props.touched.category &&
|
||||
@@ -349,31 +392,40 @@ const EditJobPopOut = ({
|
||||
<div className="w-full flex items-center gap-2 mb-2">
|
||||
{/* FOR TASK IMAGE */}
|
||||
<div className="w-1/2 relative max-h-[130px] min-h-[130px]">
|
||||
<input
|
||||
<input
|
||||
id="task_image"
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={taskImgChangeHandler}
|
||||
/>
|
||||
{taskImage ?
|
||||
<div className="w-full absolute -top-5">
|
||||
<img src={taskImage} className="max-h-[150px] min-h-[150px] w-full object-cover" alt="uplaoded task image" />
|
||||
<span onClick={()=>setTaskImage('')} className="p-2 absolute text-sm top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white/80 hover:bg-white hover:shadow-md transition-all duration-500 cursor-pointer text-slate-800">Remove Image</span>
|
||||
</div>
|
||||
:
|
||||
<label
|
||||
className="absolute -top-5 h-[150px] w-full flex flex-col justify-center items-center bg-slate-100 dark:bg-[#11131F] cursor-pointer input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold" htmlFor='task_image'>
|
||||
{taskImage ? (
|
||||
<div className="w-full absolute -top-5">
|
||||
<img
|
||||
src={taskImage}
|
||||
className="max-h-[150px] min-h-[150px] w-full object-cover"
|
||||
alt="uploaded task"
|
||||
/>
|
||||
<span
|
||||
onClick={() => setTaskImage("")}
|
||||
className="p-2 absolute text-sm top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white/80 hover:bg-white hover:shadow-md transition-all duration-500 cursor-pointer text-slate-800"
|
||||
>
|
||||
Remove Image
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<label
|
||||
className="absolute -top-5 h-[150px] w-full flex flex-col justify-center items-center bg-slate-100 dark:bg-[#11131F] cursor-pointer input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold"
|
||||
htmlFor="task_image"
|
||||
>
|
||||
Select Task Image
|
||||
</label>
|
||||
}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
{/* END OF TASK IMAGE */}
|
||||
|
||||
<div className="field w-1/2">
|
||||
<div
|
||||
className={`flex items-center justify-between`}
|
||||
>
|
||||
<div className={`flex items-center justify-between`}>
|
||||
<label
|
||||
className="w-full input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex flex-col"
|
||||
htmlFor="timeline_days"
|
||||
@@ -433,11 +485,23 @@ const EditJobPopOut = ({
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
|
||||
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
|
||||
<div className="w-full">
|
||||
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
|
||||
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
|
||||
</div>
|
||||
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
|
||||
<div className="w-full">
|
||||
{uploadStatus.message && !uploadStatus.loading && (
|
||||
<p
|
||||
className={`text-center ${
|
||||
uploadStatus.status
|
||||
? "text-green-500"
|
||||
: "text-red-500"
|
||||
}`}
|
||||
>
|
||||
{uploadStatus.message}
|
||||
</p>
|
||||
)}
|
||||
{uploadStatus.loading && (
|
||||
<p className="text-center">{uploadStatus.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
|
||||
<div className="flex items-center space-x-4 mr-2 mt-2">
|
||||
@@ -448,7 +512,9 @@ const EditJobPopOut = ({
|
||||
type="submit"
|
||||
className="w-[120px] h-[40px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
|
||||
disabled={requestStatus.loading || uploadStatus.loading}
|
||||
disabled={
|
||||
requestStatus.loading || uploadStatus.loading
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
|
||||
@@ -23,8 +23,14 @@ const validationSchema = Yup.object().shape({
|
||||
});
|
||||
|
||||
function JobListPopout({ details, onClose, situation }) {
|
||||
|
||||
const [selectedTab, setSelectedTab] = useState('public')
|
||||
const tabs = ['public', 'individual', 'group']
|
||||
|
||||
const dispatch = useDispatch()
|
||||
|
||||
const [requestStatus, setRequestStatus] = useState({message:'', status:false})
|
||||
|
||||
const [familyList, setFamilyList] = useState([]);
|
||||
let [loader, setLoader] = useState({
|
||||
member: false,
|
||||
@@ -96,7 +102,7 @@ const dispatch = useDispatch()
|
||||
else if (name === "individual")
|
||||
setErrMsg({ jobFields: { individual: "please enter email" } });
|
||||
else if (name === "group")
|
||||
setErrMsg({ jobFields: { group: "please select a family member" } });
|
||||
setErrMsg({ jobFields: { group: "please select a group" } });
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
setErrMsg({ jobFields: "" });
|
||||
@@ -150,9 +156,12 @@ const dispatch = useDispatch()
|
||||
// for group input
|
||||
reqData = {
|
||||
...jobReq,
|
||||
email: values?.individual,
|
||||
email: '',
|
||||
group_id: values?.group,
|
||||
action: 13025,
|
||||
assign_mode: 110033,
|
||||
assign_mode: 110044,
|
||||
duration: details?.timeline_days,
|
||||
// duration: 0,
|
||||
};
|
||||
setLoader({ jobFields: { group: true } });
|
||||
} else {
|
||||
@@ -161,21 +170,56 @@ const dispatch = useDispatch()
|
||||
|
||||
try {
|
||||
const res = await apiCall.assignJobTask(reqData);
|
||||
let { data } = await res;
|
||||
setLoader({ jobFields: false });
|
||||
let { status, data } = await res;
|
||||
if(status != 200 || data.internal_return < 0){
|
||||
setRequestStatus({message:'Unable to complete', status:false})
|
||||
return setTimeout(()=>{
|
||||
setLoader({ jobFields: false });
|
||||
setRequestStatus({message:'', status:false})
|
||||
},3000)
|
||||
}
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
onClose();
|
||||
throw new Response(data);
|
||||
setRequestStatus({message:'Successful', status:true})
|
||||
setTimeout(()=>{
|
||||
setLoader({ jobFields: false });
|
||||
onClose();
|
||||
throw new Response(data);
|
||||
},3000)
|
||||
} catch (error) {
|
||||
setLoader({ jobFields: false });
|
||||
throw new Error(error);
|
||||
setRequestStatus({message:'Unable to complete', status:false})
|
||||
setTimeout(()=>{
|
||||
setRequestStatus({message:'', status:false})
|
||||
setLoader({ jobFields: false });
|
||||
throw new Error(error);
|
||||
},3000)
|
||||
}
|
||||
};
|
||||
|
||||
const [groupList, setGroupList] = useState({loading: true, groups: [], members: []})
|
||||
|
||||
// FUNCTION TO POPULATE USER GROUP LIST
|
||||
useEffect(()=>{
|
||||
// setGroupList({loading: true, groups: [], members: []})
|
||||
apiCall.jobGroupList({}).then(res => {
|
||||
const {status, data} = res
|
||||
if(status != 200 || data?.internal_return < 0){
|
||||
setGroupList({loading: false, groups: [], members: []})
|
||||
return
|
||||
}
|
||||
if(data.result_list.length < 0){
|
||||
setGroupList({loading: false, groups: [], members: []})
|
||||
return
|
||||
}
|
||||
setGroupList({loading: false, groups: data.result_list, members: data.result_list_member})
|
||||
}).catch(error => {
|
||||
setGroupList({loading: false, groups: [], members: []})
|
||||
})
|
||||
},[])
|
||||
|
||||
// console.log("Job List P >> ", details)
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation} className="job-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 overflow-y-auto">
|
||||
<ModalCom action={onClose} situation={situation} className="">
|
||||
<div className="logout-modal-wrapper w-[90%] md:w-[768px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
|
||||
<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-base md:text-lg font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
{details.title}
|
||||
@@ -250,6 +294,20 @@ const dispatch = useDispatch()
|
||||
|
||||
{/* ACTION SECTION */}
|
||||
<div className="p-4 w-full md:w-2/4 h-full">
|
||||
<div className="grid grid-cols-3 my-4 border-b-2">
|
||||
{tabs.map(item => (
|
||||
<button
|
||||
className={`px-4 py-1 rounded-t-2xl ${selectedTab == item ? 'btn-gradient border-[2px] text-white' : 'bg-white text-[#000] border-t-[2px]'}`}
|
||||
value={item}
|
||||
name={item}
|
||||
onClick={()=>setSelectedTab(item)}
|
||||
>
|
||||
{item[0].toUpperCase() + item.slice(1)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{selectedTab == 'family' &&
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema.fields.family}
|
||||
@@ -267,20 +325,22 @@ const dispatch = useDispatch()
|
||||
data={familyList}
|
||||
btnText="Assign to family"
|
||||
optionText="Select Family"
|
||||
loader={loader?.jobFields.family}
|
||||
loader={loader?.jobFields?.family}
|
||||
errorHandler={errorHandler}
|
||||
/>
|
||||
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
|
||||
{" "}
|
||||
{props?.values.family === "" && (
|
||||
<span>{errMsg.jobFields.family}</span>
|
||||
{props?.values?.family === "" && (
|
||||
<span>{errMsg?.jobFields?.family}</span>
|
||||
)}
|
||||
</p>{" "}
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
|
||||
}
|
||||
|
||||
{selectedTab == 'public' &&
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema.fields.public}
|
||||
@@ -298,20 +358,22 @@ const dispatch = useDispatch()
|
||||
data={publicArray}
|
||||
btnText="Show Task to Public"
|
||||
optionText="Select Duration"
|
||||
loader={loader?.jobFields.public}
|
||||
loader={loader?.jobFields?.public}
|
||||
errorHandler={errorHandler}
|
||||
/>
|
||||
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
|
||||
{" "}
|
||||
{props?.values.public === "" && (
|
||||
<span>{errMsg.jobFields.public}</span>
|
||||
<span>{errMsg?.jobFields?.public}</span>
|
||||
)}
|
||||
</p>{" "}
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
}
|
||||
|
||||
{selectedTab == 'individual' &&
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema.fields.individual}
|
||||
@@ -329,21 +391,23 @@ const dispatch = useDispatch()
|
||||
placeholder="Enter email of individual"
|
||||
inputHandler={props?.handleChange}
|
||||
btnText="Send Offer to Individual"
|
||||
loader={loader?.jobFields.individual}
|
||||
loader={loader?.jobFields?.individual}
|
||||
errorHandler={errorHandler}
|
||||
/>
|
||||
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
|
||||
{" "}
|
||||
{props?.values.individual === "" && (
|
||||
<span>{errMsg.jobFields.individual}</span>
|
||||
<span>{errMsg?.jobFields?.individual}</span>
|
||||
)}
|
||||
</p>{" "}
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
}
|
||||
|
||||
{ process.env.REACT_APP_SHOW_OFFER_GROUP_JOB != 0 &&
|
||||
{/* { process.env.REACT_APP_SHOW_OFFER_GROUP_JOB != 0 && } */}
|
||||
{selectedTab == 'group' &&
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
validationSchema={validationSchema.fields.group}
|
||||
@@ -359,14 +423,15 @@ const dispatch = useDispatch()
|
||||
inputName="group"
|
||||
value={props?.values.group}
|
||||
btnText="Send Order to Group"
|
||||
optionText="Group"
|
||||
loader={loader?.jobFields.group}
|
||||
optionText="Select Group"
|
||||
loader={loader?.jobFields?.group}
|
||||
errorHandler={errorHandler}
|
||||
data={groupList}
|
||||
/>
|
||||
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
|
||||
{" "}
|
||||
{props?.values.group === "" && (
|
||||
<span>{errMsg.jobFields.group}</span>
|
||||
<span>{errMsg?.jobFields?.group}</span>
|
||||
)}
|
||||
</p>
|
||||
</Form>
|
||||
@@ -374,6 +439,10 @@ const dispatch = useDispatch()
|
||||
}}
|
||||
</Formik>
|
||||
}
|
||||
|
||||
{requestStatus.message &&
|
||||
<p className={`mt-4 w-full text-lg ${requestStatus.status ? 'text-emerald-600' : 'text-red-600'}`}>{requestStatus.message}</p>
|
||||
}
|
||||
</div>
|
||||
{/* END OF ACTION SECTION */}
|
||||
</div>
|
||||
@@ -427,27 +496,52 @@ const JobFieldInput = ({
|
||||
className={`input-field placeholder:text-base text-dark-gray w-full h-full bg-white outline-none px-2`}
|
||||
value={value}
|
||||
>
|
||||
<option value="">{optionText}</option>
|
||||
{Array.isArray(data) &&
|
||||
data?.map((item, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{inputName === "family" && item?.last_login !== "" && (
|
||||
<option value={item?.family_uid} key={idx}>
|
||||
{`${item?.firstname} ${item?.lastname}`}
|
||||
{/* <option value="">{optionText}</option> */}
|
||||
{(inputName == 'family' || inputName == 'public') &&
|
||||
Array.isArray(data) &&
|
||||
<>
|
||||
<option value="">{optionText}</option>
|
||||
{ data?.map((item, idx) => (
|
||||
<React.Fragment key={idx}>
|
||||
{inputName === "family" && item?.last_login !== "" && (
|
||||
<option value={item?.family_uid} key={idx}>
|
||||
{`${item?.firstname} ${item?.lastname}`}
|
||||
</option>
|
||||
)}
|
||||
{inputName === "public" && (
|
||||
<option value={item?.duration} key={idx}>
|
||||
{item?.name}
|
||||
</option>
|
||||
)}
|
||||
{/* {inputName === "group" && (
|
||||
<option value={item?.group_id} key={idx}>
|
||||
{item?.group_name}
|
||||
</option>
|
||||
)} */}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
{(inputName == 'group') &&
|
||||
<>
|
||||
{data.loading ?
|
||||
<option value={''}>
|
||||
Loading...
|
||||
</option>
|
||||
: data?.groups?.length > 0 ?
|
||||
<>
|
||||
<option value="">{optionText}</option>
|
||||
{ data?.groups?.map((item, index)=>(
|
||||
<option value={item?.group_id} key={index}>
|
||||
{item?.group_name}
|
||||
</option>
|
||||
)}
|
||||
{inputName === "public" && (
|
||||
<option value={item?.duration} key={idx}>
|
||||
{item?.name}
|
||||
</option>
|
||||
)}
|
||||
{inputName === "group" && (
|
||||
<option value={item?.family_uid} key={idx}>
|
||||
{item?.firstname}
|
||||
</option>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
))}
|
||||
</>
|
||||
:
|
||||
<option value="">No Group Found</option>
|
||||
}
|
||||
</>
|
||||
}
|
||||
</Field>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -133,9 +133,9 @@ function OfferJobPopout({ details, onClose, situation }) {
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div className="md:flex bg-white rounded-lg shadow-lg">
|
||||
<div className="md:flex bg-white dark:bg-dark-white rounded-lg shadow-lg">
|
||||
<div className="p-4 w-full md:w-3/4 md:border-r-2">
|
||||
<p className="text-lg my-5 font-semibold text-slate-900 tracking-wide">
|
||||
<p className="text-lg my-5 font-semibold text-slate-900 dark:text-white tracking-wide">
|
||||
{details.title}
|
||||
</p>
|
||||
|
||||
|
||||
@@ -133,8 +133,8 @@ function PendingJobsPopout({ details, onClose, situation }) {
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} 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 overflow-y-auto">
|
||||
<ModalCom action={onClose} situation={situation}>
|
||||
<div className="logout-modal-wrapper w-[90%] md:w-[768px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
|
||||
<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">
|
||||
Manage Pending Item
|
||||
|
||||
@@ -21,10 +21,12 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
const [loadProfileDetails, setLoadProfileDetails] = useState([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const { jobListTable, walletTable } = useSelector((state) => state.tableReload);
|
||||
const { jobListTable, walletTable } = useSelector(
|
||||
(state) => state.tableReload
|
||||
);
|
||||
|
||||
const {
|
||||
userDetails: { username, uid, session },
|
||||
userDetails: { username, uid, session},
|
||||
} = useSelector((state) => state?.userDetails); // CHECKS IF USER Details are avaliable, to determine if user is active
|
||||
|
||||
let loggedIn = username && session && uid ? true : false; // variable to determine if user is logged in
|
||||
@@ -35,6 +37,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
localStorage.removeItem("uid");
|
||||
localStorage.removeItem("member_id");
|
||||
localStorage.removeItem("session_token");
|
||||
sessionStorage.removeItem("family_uid");
|
||||
navigate("/login", { replace: true }); // redirects user to login page after session expires
|
||||
};
|
||||
|
||||
@@ -181,13 +184,15 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const getMyWalletList = async () => {
|
||||
dispatch(updateWalletDetails({ loading: true, data:[] }));
|
||||
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 }));
|
||||
dispatch(
|
||||
updateWalletDetails({ loading: false, data: res.data?.result_list })
|
||||
);
|
||||
} catch (error) {
|
||||
dispatch(updateWalletDetails({ loading: false, data:[] }));
|
||||
dispatch(updateWalletDetails({ loading: false, data: [] }));
|
||||
console.log("Error getting mode");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1145,6 +1145,41 @@ class usersService {
|
||||
return this.postAuxEnd("/familytransfer", postData);
|
||||
}
|
||||
|
||||
|
||||
// FUNCTION GET JOB GROUP LIST
|
||||
jobGroupList(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
action: 13045,
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/jobgrouplist", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO ADD JOB GROUP
|
||||
jobGroupAdd(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/jobgroupadd", postData);
|
||||
}
|
||||
|
||||
// FUNCTION TO ADD JOB GROUP MEMBER
|
||||
groupMemberAdd(reqData) {
|
||||
var postData = {
|
||||
uid: localStorage.getItem("uid"),
|
||||
member_id: localStorage.getItem("member_id"),
|
||||
sessionid: localStorage.getItem("session_token"),
|
||||
...reqData,
|
||||
};
|
||||
return this.postAuxEnd("/groupmemberadd", postData);
|
||||
}
|
||||
|
||||
/*
|
||||
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(username)
|
||||
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(password)
|
||||
|
||||
@@ -4,8 +4,8 @@ import ModalCom from "../components/Helpers/ModalCom";
|
||||
|
||||
function AddJobPage({ action, situation, categories }) {
|
||||
return (
|
||||
<ModalCom action={action} situation={situation} className="edit-popup">
|
||||
<div className="lg:w-[600px] w-full lg:overflow-hidden lg:rounded-2xl bg-white dark:bg-dark-white dark:text-white">
|
||||
<ModalCom action={action} situation={situation}>
|
||||
<div className="lg:w-[600px] w-11/12 lg:overflow-hidden lg:rounded-2xl bg-white dark:bg-dark-white dark:text-white">
|
||||
<div className="heading flex justify-between items-center py-6 md:px-[30px] px-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
|
||||
<p className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
|
||||
Create New Job
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from "react";
|
||||
import AppDownload from "../components/AppDownload/AppDownload";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
function AppDownloadPage() {
|
||||
return (
|
||||
<>
|
||||
<AppDownload />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default AppDownloadPage;
|
||||
@@ -6,4 +6,4 @@ export default function FamilyManagePage() {
|
||||
<FamilyManage />
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import FamilySettings from "../components/FamilyAcc/FamilySettings";
|
||||
|
||||
export default function FamilySettingsPage() {
|
||||
return (
|
||||
<>
|
||||
<FamilySettings />
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react'
|
||||
import JobGroups from '../components/JobGroups/JobGroups'
|
||||
|
||||
export default function JobGroupsPage() {
|
||||
return (
|
||||
<>
|
||||
<JobGroups />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -39,6 +39,7 @@ function ManageActiveJobs() {
|
||||
loading: false,
|
||||
error: false,
|
||||
data: res.data.result_list,
|
||||
image: res.data.session_image_server
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||