Compare commits

..

9 Commits

Author SHA1 Message Date
victorAnumudu 13da84099c lock job popout 2024-08-23 15:52:16 +01:00
ameye 3b6f2a4ca0 Merge branch 'job-lock' of WrenchBoard/Users-Wrench into master 2024-08-22 14:11:14 +00:00
victorAnumudu bdc67590d1 job lock key add 2024-08-22 15:01:04 +01:00
ameye 89d2682eaf Merge branch 'depends-on-select-tag' of WrenchBoard/Users-Wrench into master 2024-08-22 11:59:06 +00:00
victorAnumudu 41a617a265 depends on select tage added 2024-08-22 11:39:15 +01:00
ameye c72765b38c Merge branch 'auth-layout-adjustment' of WrenchBoard/Users-Wrench into master 2024-08-16 04:40:41 +00:00
victorAnumudu fe3306cb98 auth layout adjusted 2024-08-15 21:26:09 +01:00
ameye 13b96a3b8d Merge branch 'signup-bug' of WrenchBoard/Users-Wrench into master 2024-08-07 20:27:54 +00:00
CHIEFSOFT\ameye bc2a4340f2 Headers fix 2024-08-01 11:46:15 -04:00
11 changed files with 724 additions and 403 deletions
+12 -3
View File
@@ -6,9 +6,18 @@
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="wrenchboard.com is an online marketplace to make money from your skills, find, buy and sell professional services. Connect with freelancers to get work done faster. Trade your freelance services."/>
<meta name="title" content="WrenchBoard: Find a Freelancer | Sell Professional Services"/><meta name="keywords" content="Online Jobs, Online Workers, work online Nigeria, hire a freelancer, hire freelancers, freelance marketplace, freelancer hire, freelance service, freelance professional services, How to make money online, find workers online, Online Services, digital services, freelancers community in Ghana, freelancers community in Nigeria, freelancer site in Africa, Best freelance website in Africa, Freelance Designers, Photographers, Writers in Nigeria, freelancers, freelance outsourcing in Nigeria, freelance IT services in Nigeria, hire freelancers online in Nigeria, freelance services online in Nigeria, freelance contractor in Nigeria, freelance sites in Nigeria, freelance jobs in Nigeria, freelance projects in Nigeria, freelance jobs online in Nigeria, professional freelancers in Nigeria, buy professional services in Nigeria, professional services jobs, professional business services, professional service providers in Nigeria, freelancing services, freelancing sites in Nigeria, freelancers for hire in Nigeria, freelancer search in Nigeria, search freelancer in Nigeria, find freelancers in Nigeria, Find workers US. Outsource from US to Nigeria, find a freelancer in Nigeria, freelancing projects in Nigeria, web freelancing in Nigeria, outsourcing sites freelancers in Nigeria, website for freelancers in Nigeria, marketplace for freelancers "/>
<meta
name="description"
content="WrenchBoard.com is the place to set family goals and reward achievements. Find tasks to earn from, or send tasks for others to perform for you."
/>
<meta
name="title"
content="WrenchBoard: Reward Accomplishments | Get Family Engaged"
/>
<meta
name="keywords"
content="Empower families to reward accomplishment, set goals, and encourage kids to understand goals, earning, and the benefit of savings in one app experience."
/>
<link rel="manifest" href="/manifest.json"/>
<script>
!function(e,a,t,n,g,c,o){e.GoogleAnalyticsObject=g,e.ga=e.ga||function(){(e.ga.q=e.ga.q||[]).push(arguments)},e.ga.l=1*new Date,c=a.createElement(t),o=a.getElementsByTagName(t)[0],c.async=1,c.src="https://www.google-analytics.com/analytics.js",o.parentNode.insertBefore(c,o)}(window,document,"script",0,"ga"),ga("create","UA-54829827-4","auto"),ga("send","pageview")</script><script defer="defer" src="/static/js/main.787e423f.js"></script><link href="/static/css/main.418eaf65.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body><script>var LHC_API=LHC_API||{};LHC_API.args={mode:"widget",lhc_base_url:"//chat.live.wrenchboard.com/",wheight:450,wwidth:350,pheight:520,pwidth:500,leaveamessage:!0,check_messages:!1},function(){var e=document.createElement("script");e.type="text/javascript",e.setAttribute("crossorigin","anonymous"),e.async=!0;var t=new Date;e.src="//chat.live.wrenchboard.com/design/defaulttheme/js/widgetv2/index.js?"+t.getFullYear()+t.getMonth()+t.getDate();var a=document.getElementsByTagName("script")[0];a.parentNode.insertBefore(e,a)}()
+44 -39
View File
@@ -13,11 +13,12 @@ export default function LoginLayout({ slogan, children }) {
return (
<div
className={`h-screen overflow-y-auto bg-cover bg-center`}
className={`min-h-screen overflow-y-auto bg-cover bg-center flex flex-col justify-between items-center`}
style={{
backgroundImage: `url(${countryMode == "NG" ? bgImgNig : bgImgCom})`,
}}
>
<div className={`w-full grid grid-cols-1 xl:grid-cols-2`}>
{/* <div
className={`auth-bg hidden xl:block bg-blue-50 relative bg-cover bg-no-repeat border-0 after:content-[''] after:absolute after:inset-0`}
@@ -29,47 +30,51 @@ export default function LoginLayout({ slogan, children }) {
<div className="w-full flex justify-center items-center">
{children && children}
</div>
<div className="w-full flex flex-col justify-center items-center px-10">
<div className="w-full flex justify-center items-center pt-5">
<div className="flex items-center">
<a
href="https://www.wrenchboard.com/about-us"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
About
</a>
<a
href="https://www.wrenchboard.com/service"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Services
</a>
<a
href="https://www.wrenchboard.com/contact"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Contact Us
</a>
</div>
</div>
<p className="py-1 text-black text-[15px] px-2 font-medium flex items-center gap-1">
<span className="dark:text-white">
&copy; {new Date().getFullYear()} -
</span>
<Link to="/" className="text-[#009ef7] ml-1">
WrenchBoard
</Link>{" "}
</p>
</div>
</div>
</div>
</div>
<div className='w-full shadow-md bg-slate-50 dark:bg-dark-white'>
<div className="w-full flex flex-col md:flex-row justify-center items-center px-10 py-2">
<div className="flex justify-center items-center">
<div className="flex items-center">
<a
href="https://www.wrenchboard.com/about-us"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
About
</a>
<a
href="https://www.wrenchboard.com/service"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Services
</a>
<a
href="https://www.wrenchboard.com/contact"
className="text-[#a1a5b7] text-[15px] px-2 font-medium hover:text-[#009ef7]"
target="_blank"
rel="noreferrer"
>
Contact Us
</a>
</div>
</div>
<p className="text-black text-[15px] px-2 font-medium flex items-center gap-1">
<span className="dark:text-white">
&copy; {new Date().getFullYear()} -
</span>
<Link to="/" className="text-[#009ef7] ml-1">
WrenchBoard
</Link>{" "}
</p>
</div>
</div>
</div>
);
}
+1 -1
View File
@@ -563,7 +563,7 @@ 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
This site is protected by a Captcha. <br />Our Privacy Policy and
Terms of Service apply.
</div>
</>
+7 -3
View File
@@ -10,6 +10,7 @@ export default function AvailableJobsCard({
hidden = false,
contentDisplay,
image_server,
marketPlaceProduct
}) {
//debugger;
const [marketPopUp, setMarketPopUp] = useState({ show: false, data: {} });
@@ -103,9 +104,10 @@ export default function AvailableJobsCard({
{/* <div className="product-two-options flex justify-between mb-5 relative"></div> */}
<div className="flex justify-between">
<div className="flex items-center space-x-2">
<div>
<p className="w-full font-bold text-xl tracking-wide text-dark-gray dark:text-white">
<div className='w-full'>
<p className="w-full flex gap-1 items-center font-bold text-xl tracking-wide text-dark-gray dark:text-white">
{/* {thePrice} | {datas.timeline_days} day(s) */}
{datas?.offer_depend_uid && <i className="fa-solid fa-lock p-1 text-red-500 text-[12px]"></i>}
{thePrice}
</p>
<p className="text-sm text-lighter-gray">
@@ -171,7 +173,8 @@ export default function AvailableJobsCard({
</div>
<div className="block sm:flex flex-wrap gap-4">
<p className="text-sm text-thin-light-gray flex flext-start gap-1">
<p className="text-sm text-thin-light-gray flex flext-start gap-1 items-center">
{datas?.offer_depend_uid && <i className="fa-solid fa-lock p-1 text-red-500 text-[12px]"></i>}
Price: <span className="text-purple">{thePrice}</span>
</p>
<p className="text-sm text-thin-light-gray">
@@ -220,6 +223,7 @@ export default function AvailableJobsCard({
setMarketPopUp({ show: false, data: {} });
}}
situation={marketPopUp.show}
marketPlaceProduct={marketPlaceProduct}
/>
)}
@@ -150,6 +150,7 @@ export default function MainSection({
contentDisplay={contentDisplay}
image_server={image_server}
datas={datum}
marketPlaceProduct={marketPlaceProduct}
/>
</div>
))
@@ -0,0 +1,252 @@
import React, {useEffect, useState} from 'react'
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import usersService from "../../../services/UsersService";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
export default function LockJob({details, marketPlaceProduct, ManageInterest, manageInt, handleInputChange, MarketDetail, marketMsg, errMsg, textValue}) {
const apiCall = new usersService()
const [completedTask, setCompletedTask] = useState({
loading: true,
data: []
})
let thePrice = PriceFormatter(
details?.price * 0.01,
details?.currency_code,
details?.currency
);
let cleanedText = details?.job_description
?.replace(/&lt;/g, "<")
.replace(/&gt;/g, ">")
.replace(/&quot;/g, '"')
.replace(/&amp;/g, "&");
let dependOn = marketPlaceProduct?.filter(item => item?.job_uid == details?.offer_depend_uid)[0]
useEffect(()=>{
apiCall.getVerifyCompletedTask({offer_depend_uid:details?.offer_depend_uid}).then(res => {
console.log('RES', res.data)
setCompletedTask({loading:false, data:res?.data?.result_list})
}).catch(err =>{
setCompletedTask({loading:false, data:[]})
})
},[])
return (
<>
{completedTask.loading ?
<div className='w-full md:col-span-4 flex justify-center items-center min-h-[500px]'>
<LoadingSpinner
size='10'
/>
</div>
:
<>
<div className="px-4 py-1 w-full md:col-span-3 md:border-r-1">
<div className="min-h-[300px]">
<h2 className="w-full flex gap-1 items-center font-semibold text-slate-900 dark:text-white tracking-wide">
{details?.offer_depend_uid && <i className="fa-solid fa-lock p-1 text-red-500 text-[12px]"></i>}
{details?.title}
</h2>
{/* INPUT SECTION */}
{[
{
name: "Description",
content: details.description,
},
{
name: "",
content: {
text: `Timeline: ${details.timeline_days} day(s) -- `,
bold: `Budget: ${thePrice}`,
},
},
{
name: "Delivery Detail",
content: cleanedText,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-1 flex flex-col items-start`} key={idx}>
<label className="py-1 job-label w-full">
{name}
</label>
<div
className={`w-full p-2 text-slate-900 dark:text-white market-pop rounded-2xl ${
name == "Description"
? "min-h-[100px] max-h-[100px] h-full overflow-y-auto break-words bg-slate-50"
: name == "Delivery Detail" ? " overflow-y-auto h-full min-h-[100px] max-h-[100px] bg-slate-50"
: "h-full flex items-center"
}`}
>
{danger ? (
<p
className={`dark:text-white`}
dangerouslySetInnerHTML={{
__html: danger && content,
}}
/>
) : (
<p className={`w-full text-slate-900 dark:text-white`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<>
{/* <hr className="mb-1" /> */}
<span className='flex w-full mb-1 h-[1px] bg-slate-500'></span>
<span className="flex items-center gap-2 dark:text-white">
{content?.text}
<strong>{thePrice}</strong>
</span>
<span className='flex w-full mt-1 h-[1px] bg-slate-500'></span>
{/* <hr className="mt-1" /> */}
</>
)}
</>
) : (
""
)}
</p>
)}
</div>
</div>
))}
</div>
<hr className='my-3' />
{completedTask.loading ?
<p className='py-3 w-full text-center text-lg'>Loading...</p>
:completedTask?.data?.filter(item => item?.job_uid == details.offer_depend_uid).length > 0 ?
<div className='w-full'>
<label className="job-label w-full flex gap-2 items-center">
If you have any questions about this task:
<span className={`text-sm ${marketMsg.state ? 'text-[#57cd89]' : 'text-red-500'}`}>
{marketMsg.state && "Message Sent!"}
{errMsg.market && "Failed to send"}
</span>
</label>
<div className="w-full flex items-center gap-3">
<div className="w-full">
<textarea
className={`p-1 w-full text-sm text-slate-900 dark:text-white ${
marketMsg.loading && "italic text-[#9CA3AF]"
} bg-transparent outline-none border-2 border-slate-300 rounded-md`}
rows="3"
style={{ resize: "none" }}
placeholder="Enter message here ..."
value={marketMsg.loading ? "Sending..." : textValue}
onChange={handleInputChange}
/>
</div>
<div className="relative flex flex-col">
<button
className="rounded-full flex justify-center items-center w-12 h-11 bg-yellow-500 text-white"
name="market-message"
onClick={MarketDetail}
disabled={marketMsg.loading}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : (
// "Send Message"
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 11 20"
id="Arrow"
className="w-[0.7rem]"
>
<path
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]"
></path>
</svg>
)}
</button>
</div>
</div>
</div>
:
<div className='w-full'>
<h1 className='text-red-600 text-lg'>This task depends on the task below</h1>
<div className='my-1 w-full'>
<h2 className="w-full flex gap-1 items-center font-semibold text-slate-900 dark:text-white tracking-wide">
{dependOn?.offer_depend_uid && <i className="fa-solid fa-lock p-1 text-red-500 text-[12px]"></i>}
{dependOn?.title}
</h2>
</div>
<div className={`p-2 flex flex-col items-start`}>
<p className="py-1 job-label w-full">Description</p>
<div className={`w-full p-2 text-slate-900 dark:text-white market-pop rounded-2xl bg-slate-50 break-words min-h-[100px] max-h-[100px]`}>
{dependOn?.description}
</div>
</div>
</div>
}
</div>
<div className="py-2 w-full md:col-span-1 h-full flex flex-col rounded-2xl">
<div className="mx-auto bg-[#f1f8ff] dark:bg-[#C2C8D3] px-4 rounded-md w-full h-full md:min-h-[420px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center pb-4 gap-2">
<p className="job-label w-full">
Interested?
</p>
<hr />
{completedTask.loading ?
<p className='py-3 w-full text-center text-lg'>Loading...</p>
:completedTask?.data?.filter(item => item?.job_uid == details.offer_depend_uid).length > 0 ?
<button
className="btn-gradient text-white px-2 py-2 border-4 border-slate-300 text-lg lg:text-xl font-medium rounded-2xl"
name="market-interest"
onClick={ManageInterest}
>
{" "}
<div className="flex md:flex-col justify-center gap-2">
<span>Notify</span>
<span>Owner</span>
</div>
</button>
:
<h1 className='text-red-600 text-base font-bold'>This task depends on completion of another task</h1>
}
<>
{manageInt.loading ? (
<p className="text-sm italic">please wait...</p>
) : (
<>
{manageInt?.msg !== "" && (
<p
className={`text-sm italic ${
manageInt?.state ? "text-green-500" : "text-red-500"
}`}
>
{manageInt?.msg}
</p>
)}
</>
)}
</>
</div>
<div className="text-slate-900">
<p className="flex items-center tracking-wide">
<span className="job-label">Interest: </span> <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1 flex flex-col">
<span className="job-label">Expire: </span>
<span> {new Date(details.expire).toLocaleString()} </span>
</p>
</div>
</div>
</div>
</>
}
</>
)
}
+188 -170
View File
@@ -5,8 +5,9 @@ import ModalCom from "../../Helpers/ModalCom";
import { PriceFormatter } from "../../Helpers/PriceFormatter";
import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { SocketValues } from "../../Contexts/SocketIOContext";
import LockJob from "./LockJob";
const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
const MarketPopUp = ({ details, onClose, situation, marketInt, marketPlaceProduct }) => {
let {sendJobInterestToOwner} = SocketValues() // function to emit job interest request
const emitOfferInterest = () => {
@@ -143,185 +144,202 @@ const MarketPopUp = ({ details, onClose, situation, marketInt }) => {
</div>
<div className="md:grid md:grid-cols-4 bg-white dark:bg-dark-white text-slate-900 dark:text-white rounded-lg">
<div className="px-4 py-1 w-full md:col-span-3 md:border-r-1">
<div className="min-h-[300px]">
<h2 className="font-semibold text-slate-900 dark:text-white tracking-wide">
{details?.title}
</h2>
{details?.offer_depend_uid ?
<LockJob
marketPlaceProduct={marketPlaceProduct}
details={details}
ManageInterest={ManageInterest}
manageInt={manageInt}
marketMsg={marketMsg}
errMsg={errMsg}
textValue={textValue}
MarketDetail={MarketDetail}
handleInputChange={handleInputChange}
/>
:
<>
<div className="px-4 py-1 w-full md:col-span-3 md:border-r-1">
<div className="min-h-[300px]">
<h2 className="w-full flex gap-1 items-center font-semibold text-slate-900 dark:text-white tracking-wide">
{details?.offer_depend_uid && <i className="fa-solid fa-lock p-1 text-red-500 text-[12px]"></i>}
{details?.title}
</h2>
{/* INPUT SECTION */}
{[
{
name: "Description",
content: details.description,
},
{
name: "",
content: {
text: `Timeline: ${details.timeline_days} day(s) -- `,
bold: `Budget: ${thePrice}`,
{/* INPUT SECTION */}
{[
{
name: "Description",
content: details.description,
},
},
{
name: "Delivery Detail",
content: cleanedText,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-start`} key={idx}>
<label className="py-2 job-label w-full md:w-[19%]">
{name}
</label>
<div
className={`w-full p-2 md:w-3/4 text-slate-900 dark:text-white market-pop rounded-2xl ${
name == "Description"
? "min-h-[150px] max-h-[150px] h-full overflow-y-auto break-words bg-slate-50"
: name == "Delivery Detail" ? " overflow-y-auto h-full min-h-[150px] max-h-[150px] bg-slate-50"
: "h-full flex items-center"
}`}
>
{danger ? (
<p
className={`dark:text-white`}
dangerouslySetInnerHTML={{
__html: danger && content,
}}
/>
) : (
<p className={`w-full text-slate-900 dark:text-white`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<>
{/* <hr className="mb-1" /> */}
<span className='flex w-full mb-1 h-[1px] bg-slate-500'></span>
<span className="flex items-center gap-2 dark:text-white">
{content?.text}
<strong>{thePrice}</strong>
</span>
<span className='flex w-full mt-1 h-[1px] bg-slate-500'></span>
{/* <hr className="mt-1" /> */}
</>
)}
</>
) : (
""
)}
</p>
)}
</div>
</div>
))}
</div>
<hr className='my-1' />
<div className='w-full'>
<label className="job-label w-full flex gap-2 items-center">
If you have any questions about this task:
<span className={`text-sm ${marketMsg.state ? 'text-[#57cd89]' : 'text-red-500'}`}>
{marketMsg.state && "Message Sent!"}
{errMsg.market && "Failed to send"}
</span>
</label>
<div className="w-full flex items-center gap-3">
<div className="w-full">
<textarea
className={`p-1 w-full text-sm text-slate-900 dark:text-white ${
marketMsg.loading && "italic text-[#9CA3AF]"
} bg-transparent outline-none border-2 border-slate-300 rounded-md`}
rows="3"
style={{ resize: "none" }}
placeholder="Enter message here ..."
value={marketMsg.loading ? "Sending..." : textValue}
onChange={handleInputChange}
/>
</div>
<div className="relative flex flex-col">
<button
className="rounded-full flex justify-center items-center w-12 h-11 bg-yellow-500 text-white"
name="market-message"
onClick={MarketDetail}
disabled={marketMsg.loading}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : (
// "Send Message"
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 11 20"
id="Arrow"
className="w-[0.7rem]"
{
name: "",
content: {
text: `Timeline: ${details.timeline_days} day(s) -- `,
bold: `Budget: ${thePrice}`,
},
},
{
name: "Delivery Detail",
content: cleanedText,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-start`} key={idx}>
<label className="py-2 job-label w-full md:w-[19%]">
{name}
</label>
<div
className={`w-full p-2 md:w-3/4 text-slate-900 dark:text-white market-pop rounded-2xl ${
name == "Description"
? "min-h-[150px] max-h-[150px] h-full overflow-y-auto break-words bg-slate-50"
: name == "Delivery Detail" ? " overflow-y-auto h-full min-h-[150px] max-h-[150px] bg-slate-50"
: "h-full flex items-center"
}`}
>
<path
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]"
></path>
</svg>
)}
</button>
{/* <span className="text-sm text-[#57cd89]">
{marketMsg.state && "Sent!"}
{errMsg.market && "Failed"}
</span> */}
</div>
</div>
</div>
</div>
<div className="py-2 w-full md:col-span-1 h-full flex flex-col rounded-2xl">
<div className="mx-auto bg-[#f1f8ff] dark:bg-[#C2C8D3] px-4 rounded-md w-full h-full md:min-h-[420px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center pb-4 gap-2">
<p className="job-label w-full">
Interested?
</p>
<hr />
<button
className="btn-gradient text-white px-2 py-2 border-4 border-slate-300 text-lg lg:text-xl font-medium rounded-2xl"
name="market-interest"
onClick={ManageInterest}
>
{" "}
<div className="flex md:flex-col justify-center gap-2">
<span>Notify</span>
<span>Owner</span>
</div>
</button>
<>
{manageInt.loading ? (
<p className="text-sm italic">please wait...</p>
) : (
<>
{manageInt?.msg !== "" && (
{danger ? (
<p
className={`text-sm italic ${
manageInt?.state ? "text-green-500" : "text-red-500"
}`}
>
{manageInt?.msg}
className={`dark:text-white`}
dangerouslySetInnerHTML={{
__html: danger && content,
}}
/>
) : (
<p className={`w-full text-slate-900 dark:text-white`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<>
{/* <hr className="mb-1" /> */}
<span className='flex w-full mb-1 h-[1px] bg-slate-500'></span>
<span className="flex items-center gap-2 dark:text-white">
{content?.text}
<strong>{thePrice}</strong>
</span>
<span className='flex w-full mt-1 h-[1px] bg-slate-500'></span>
{/* <hr className="mt-1" /> */}
</>
)}
</>
) : (
""
)}
</p>
)}
</>
)}
</>
</div>
</div>
))}
</div>
<div className="text-slate-900">
<p className="flex items-center tracking-wide">
<span className="job-label">Interest: </span> <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1 flex flex-col">
<span className="job-label">Expire: </span>
<span> {new Date(details.expire).toLocaleString()} </span>
</p>
<hr className='my-1' />
<div className='w-full'>
<label className="job-label w-full flex gap-2 items-center">
If you have any questions about this task:
<span className={`text-sm ${marketMsg.state ? 'text-[#57cd89]' : 'text-red-500'}`}>
{marketMsg.state && "Message Sent!"}
{errMsg.market && "Failed to send"}
</span>
</label>
<div className="w-full flex items-center gap-3">
<div className="w-full">
<textarea
className={`p-1 w-full text-sm text-slate-900 dark:text-white ${
marketMsg.loading && "italic text-[#9CA3AF]"
} bg-transparent outline-none border-2 border-slate-300 rounded-md`}
rows="3"
style={{ resize: "none" }}
placeholder="Enter message here ..."
value={marketMsg.loading ? "Sending..." : textValue}
onChange={handleInputChange}
/>
</div>
<div className="relative flex flex-col">
<button
className="rounded-full flex justify-center items-center w-12 h-11 bg-yellow-500 text-white"
name="market-message"
onClick={MarketDetail}
disabled={marketMsg.loading}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : (
// "Send Message"
<svg
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 11 20"
id="Arrow"
className="w-[0.7rem]"
>
<path
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]"
></path>
</svg>
)}
</button>
{/* <span className="text-sm text-[#57cd89]">
{marketMsg.state && "Sent!"}
{errMsg.market && "Failed"}
</span> */}
</div>
</div>
</div>
</div>
</div>
{/* END OF ACTION SECTION */}
<div className="py-2 w-full md:col-span-1 h-full flex flex-col rounded-2xl">
<div className="mx-auto bg-[#f1f8ff] dark:bg-[#C2C8D3] px-4 rounded-md w-full h-full md:min-h-[420px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center pb-4 gap-2">
<p className="job-label w-full">
Interested?
</p>
<hr />
<button
className="btn-gradient text-white px-2 py-2 border-4 border-slate-300 text-lg lg:text-xl font-medium rounded-2xl"
name="market-interest"
onClick={ManageInterest}
>
{" "}
<div className="flex md:flex-col justify-center gap-2">
<span>Notify</span>
<span>Owner</span>
</div>
</button>
<>
{manageInt.loading ? (
<p className="text-sm italic">please wait...</p>
) : (
<>
{manageInt?.msg !== "" && (
<p
className={`text-sm italic ${
manageInt?.state ? "text-green-500" : "text-red-500"
}`}
>
{manageInt?.msg}
</p>
)}
</>
)}
</>
</div>
<div className="text-slate-900">
<p className="flex items-center tracking-wide">
<span className="job-label">Interest: </span> <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1 flex flex-col">
<span className="job-label">Expire: </span>
<span> {new Date(details.expire).toLocaleString()} </span>
</p>
</div>
</div>
</div>
</>
}
</div>
<div className="modal-footer-wrapper">
<button
className="custom-btn bg-transparent border border-red-500 text-red-500 ml-auto"
+1
View File
@@ -165,6 +165,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
setWalletItem={setWalletItem}
openWallet={openPopUp}
situation={jobPopout.show}
myJobList={MyJobList}
/>
)}
{/* End of Job List Popout */}
+205 -187
View File
@@ -10,17 +10,19 @@ import LoadingSpinner from "../Spinners/LoadingSpinner";
import Detail from "./popoutcomponent/Detail";
import { SocketValues } from "../Contexts/SocketIOContext";
// .required("This is required ")
const validationSchema = Yup.object().shape({
family: Yup.string().required("This is required "),
family: Yup.string(),
public: Yup.string(),
individual: Yup.string()
.email("Invalid email format")
.matches(
/^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$/,
"Invalid email format"
)
.required("Email is required"),
),
group: Yup.string(),
depend_uid: Yup.string(),
});
function JobListPopout({
@@ -29,6 +31,7 @@ function JobListPopout({
situation,
openWallet,
setWalletItem,
myJobList
}) {
let {marketUpdate} = SocketValues() // destructures 'SEND MESSAGE' and 'JOIN ROOM' FUNCTIONS FROM SOCKET
@@ -46,12 +49,7 @@ function JobListPopout({
const [familyList, setFamilyList] = useState([]);
let [loader, setLoader] = useState({
member: false,
jobFields: {
family: false,
public: false,
individual: false,
group: false,
},
jobFields: false
});
const apiCall = useMemo(() => new usersService(), []);
@@ -106,6 +104,7 @@ function JobListPopout({
public: "",
individual: "",
group: "",
depend_uid: ''
};
let [textArea, setTextArea] = useState(details?.job_detail);
@@ -136,11 +135,12 @@ function JobListPopout({
} finally {
setTimeout(() => {
setErrMsg({ jobFields: "" });
}, 3000);
}, 5000);
}
};
const jobFieldHandler = async (values, helpers) => {
setLoader({ jobFields: true });
let { job_id, job_uid } = details;
if (!textArea) {
@@ -163,15 +163,14 @@ function JobListPopout({
family_uid: values?.family,
assign_mode: 110011,
};
setLoader({ jobFields: { family: true } });
} else if (values?.public !== "") {
// for public input
reqData = {
...jobReq,
duration: Number(values?.public),
assign_mode: 110022,
depend_uid: values?.depend_uid
};
setLoader({ jobFields: { public: true } });
} else if (values?.individual !== "") {
// for individual input
reqData = {
@@ -179,7 +178,6 @@ function JobListPopout({
email: values?.individual,
assign_mode: 110033,
};
setLoader({ jobFields: { individual: true } });
} else if (values?.group !== "") {
// for group input
reqData = {
@@ -190,8 +188,8 @@ function JobListPopout({
duration: details?.timeline_days,
// duration: 0,
};
setLoader({ jobFields: { group: true } });
} else {
setLoader({ jobFields: false });
return;
}
@@ -203,7 +201,7 @@ function JobListPopout({
return setTimeout(() => {
setLoader({ jobFields: false });
setRequestStatus({ message: "", status: false });
}, 3000);
}, 5000);
}
marketUpdate('market', 'full-markets-jobs') // sends an event to the socket to update market lists
dispatch(tableReload({ type: "JOBTABLE" })); // reloads my job page
@@ -213,14 +211,14 @@ function JobListPopout({
setLoader({ jobFields: false });
onClose();
// throw new Response(data);
}, 3000);
}, 5000);
} catch (error) {
setRequestStatus({ message: "Unable to complete", status: false });
setTimeout(() => {
setRequestStatus({ message: "", status: false });
setLoader({ jobFields: false });
throw new Error(error);
}, 3000);
}, 5000);
}
};
@@ -355,142 +353,198 @@ function JobListPopout({
))}
</div>
<div className="grow flex flex-col bg-red-50 dark:bg-[#D85A5A] rounded-b-2xl">
{selectedTab == "family" && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.family}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="hidden">
{/* Assign to Family */}
<JobFieldInput
label="Assign to family"
select={true}
inputName="family"
value={props?.values.family}
data={familyList}
btnText="Assign to family"
optionText="Select Family"
loader={loader?.jobFields?.family}
errorHandler={errorHandler}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values?.family === "" && (
<span>{errMsg?.jobFields?.family}</span>
)}
</p>{" "}
</Form>
);
}}
</Formik>
)}
{selectedTab == "public" && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.public}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="">
{/* Offer this job to public input */}
<JobFieldInput
label="Offer this job to public"
select={true}
inputName="public"
value={props?.values.public}
data={publicArray}
btnText="Show Task to Public"
optionText="Select Duration"
loader={loader?.jobFields?.public}
errorHandler={errorHandler}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.public === "" && (
<span>{errMsg?.jobFields?.public}</span>
)}
</p>{" "}
</Form>
);
}}
</Formik>
)}
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.public}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="p-3">
<div className='flex items-center justify-start mt-1 mb-2.5'>
<label
className="input-label border-2 w-full border-sky-700 rounded py-4 px-2 text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block tracking-wide"
// htmlFor={''}
>
{selectedTab == 'family' ?
'Assign to family'
: selectedTab == 'public' ?
'Offer this job to public'
: selectedTab == 'individual' ?
'Offer this job to individual'
: selectedTab == 'group' ?
'Offer this job to your Group'
:
null
}
</label>
</div>
<div className='w-full'>
{/* ASSIGN TO FAMILY */}
{selectedTab == "family" && (
<div className='hidden w-full'>
<JobFieldInput
select={true}
inputName="family"
value={props?.values.family}
data={familyList}
optionText="Select Family"
errorHandler={errorHandler}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values?.family === "" && (
<span>{errMsg?.jobFields?.family}</span>
)}
</p>{" "}
</div>
)}
{selectedTab == "individual" && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.individual}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="">
{/* Offer this job to individual input */}
<JobFieldInput
label="Offer this job to individual"
input={true}
inputName="individual"
value={props?.values.individual}
placeholder="Enter email of individual"
inputHandler={props?.handleChange}
btnText="Send Offer to Individual"
loader={loader?.jobFields?.individual}
errorHandler={errorHandler}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.individual === "" && (
<span>{errMsg?.jobFields?.individual}</span>
)}
</p>{" "}
</Form>
);
}}
</Formik>
)}
{/* ASSIGN TO PUBLIC/MARKET */}
{selectedTab == "public" && (
<div className='w-full'>
<div className='mb-3 w-full flex flex-col xxs:flex-row items-center gap-1'>
<label className='w-full xxs:max-w-[120px] xxs:text-right'>Depends on</label>
{/* <JobFieldInput
input={true}
select={true}
inputName="depend_uid"
value={props?.values.depend_uid}
data={myJobList}
optionText="None"
parentClass="w-full flex flex-col gap-4"
/> */}
<div
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-[1rem] h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base`}
>
<select
className='px-2 w-full h-full bg-white border-0 outline-0'
name="depend_uid"
value={props?.values.depend_uid}
onChange={props.handleChange}
>
<>
{myJobList.loading ? (
<option value={""}>Loading...</option>
) : myJobList?.data?.result_list?.length > 0 ? (
<>
<option value="">None</option>
{myJobList?.data?.result_list?.filter(item => item.job_uid != details.job_uid)?.map((item, index) => (
<option value={item?.job_uid} key={item.job_uid}>
{item?.title}
</option>
))}
</>
) : (
<option value="">No Job Found</option>
)}
</>
</select>
</div>
</div>
<div className='mb-3 w-full flex flex-col xxs:flex-row items-center gap-1'>
<label className='w-full xxs:max-w-[120px] xxs:text-right'>Duration <span className='text-red-600 tracking-wide'>{(props?.values.public === "" && errMsg?.jobFields?.public) && '*'}</span></label>
<div className='w-full'>
<JobFieldInput
select={true}
inputName="public"
value={props?.values.public}
data={publicArray}
optionText="Select Duration"
parentClass="w-full flex flex-col gap-4"
/>
{/* <p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.public === "" && (
<span>{errMsg?.jobFields?.public}</span>
)}
</p>{" "} */}
</div>
</div>
</div>
)}
{/* { process.env.REACT_APP_SHOW_OFFER_GROUP_JOB != 0 && } */}
{selectedTab == "group" && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.group}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="">
{/* Offer this job to your group input */}
<JobFieldInput
label="Offer this job to your Group"
select={true}
inputName="group"
value={props?.values.group}
btnText="Send Order to Group"
optionText="Select Group"
loader={loader?.jobFields?.group}
errorHandler={errorHandler}
data={groupList}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.group === "" && (
<span>{errMsg?.jobFields?.group}</span>
)}
</p>
</Form>
);
}}
</Formik>
)}
{/* ASSIGN TO INDIVIDUAL */}
{selectedTab == "individual" && (
<div className='w-full'>
<JobFieldInput
input={true}
inputName="individual"
value={props?.values.individual}
placeholder="Enter email of individual"
inputHandler={props?.handleChange}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.individual === "" && (
<span>{errMsg?.jobFields?.individual}</span>
)}
</p>{" "}
</div>
)}
{/* ASSIGN TO GROUP */}
{selectedTab == "group" && (
<div className='w-full'>
<JobFieldInput
select={true}
inputName="group"
value={props?.values.group}
optionText="Select Group"
data={groupList}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.group === "" && (
<span>{errMsg?.jobFields?.group}</span>
)}
</p>
</div>
)}
</div>
<div className="mt-3 mb-1 flex justify-end items-center">
<button
className={`px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white`}
type="submit"
name={
selectedTab == 'family' ?
'family'
: selectedTab == 'public' ?
'public'
: selectedTab == 'individual' ?
'individual'
: selectedTab == 'group' ?
'group'
:
null
}
onClick={errorHandler}
// disabled={!value}
>
{loader?.jobFields ?
<LoadingSpinner size={5} />
: selectedTab == 'family' ?
'Assign to family'
: selectedTab == 'public' ?
'Show Task to Public'
: selectedTab == 'individual' ?
'Send Offer to Individual'
: selectedTab == 'group' ?
'Send Order to Group'
:
null
}
</button>
</div>
</Form>
);
}}
</Formik>
<p
className={`text-center w-full text-lg ${
requestStatus.status
@@ -530,30 +584,15 @@ const JobFieldInput = ({
select,
label,
labelClass,
btnText,
parentClass,
optionText,
errorHandler,
loader,
data,
}) => {
return (
<div className="field w-full h-full px-3 pt-5 pb-3 flex flex-col justify-between gap-4">
<div className="field w-full flex flex-col justify-between gap-4">
{select && (
<>
<div className={`input-com ${parentClass}`}>
<div
className={`flex items-center justify-start mb-2.5 ${labelClass}`}
>
{label && (
<label
className="input-label border-2 w-full border-sky-700 rounded py-4 px-2 text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block tracking-wide"
htmlFor={inputName}
>
{label}
</label>
)}
</div>
<div
className={`input-wrapper border border-[#f5f8fa] dark:border-[#5e6278] w-full rounded-[1rem] h-[42px] overflow-hidden relative font-medium leading-6 bg-clip-padding text-[#5e6278] dark:text-gray-100 bg-[#f5f8fa] dark:bg-[#5e6278] text-base ${inputClass}`}
>
@@ -616,8 +655,6 @@ const JobFieldInput = ({
{input && (
<InputCom
fieldClass="px-6"
label={label}
labelClass="tracking-wide border-2 w-full border-sky-700 py-4 px-2"
type="email"
name={inputName}
placeholder={placeholder}
@@ -627,25 +664,6 @@ const JobFieldInput = ({
parentClass={`${parentClass}`}
/>
)}
{/* btn */}
<div className="my-1 flex justify-end items-center">
<button
type="submit"
name={inputName}
onClick={errorHandler}
// className={`px-2 py-1 text-sm text-white btn-gradient tracking-wide rounded-md ${
// !value && "disabled:grayscale-[65%] transition duration-300"
// }`}
className={`px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white ${
!value && ""
}
`}
// disabled={!value}
>
{loader ? <LoadingSpinner size={5} /> : btnText}
</button>
</div>
</div>
);
};
+1
View File
@@ -183,6 +183,7 @@ export const apiConst = {
WRENCHBOARD_JOB_EXTEND_EXPIRE: 13041,
WRENCHBOARD_JOB_RESEND_MESSAGE: 13042,
WRENCHBOARD_JOB_CANCEL_OFFER: 13043,
WRENCHBOARD_VERIFY_COMPLETED_TASK: 13033,
WRENCHBOARD_JOB_JOBGROUPS: 13045,
WRENCHBOARD_JOB_JOBGROUPADD: 13046,
+12
View File
@@ -1481,6 +1481,18 @@ class usersService {
return this.postAuxEnd("/jobmanagerfiles", postData);
}
// API FUNCTION FOR GETTING LIST OF VERIFY COMPLETED TASK
getVerifyCompletedTask(reqData) {
var postData = {
member_uid: localStorage.getItem("uid"),
member_id: localStorage.getItem("member_id"),
sessionid: localStorage.getItem("session_token"),
action: apiConst.WRENCHBOARD_VERIFY_COMPLETED_TASK,
...reqData
};
return this.postAuxEnd("/verifycompleted", postData);
}
/*
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(username)
- 20:27:30.118 FLOG_MAX [757411]: REQ_STRING(password)