This commit was merged in pull request #212.
This commit is contained in:
2023-06-27 10:06:51 +01:00
parent 0977650bf4
commit 4b897cb3a9
17 changed files with 667 additions and 633 deletions
+50 -45
View File
@@ -1,59 +1,64 @@
/* eslint-disable no-underscore-dangle */ import { useEffect, useState } from "react";
import React, { useEffect, useState } from "react";
function CountDown({ lastDate = "" }) { function CountDown({ lastDate = "" }) {
// const [showDate, setDate] = useState(0); // State to store the countdown values
const [showHour, setHour] = useState(0); const [countdownValues, setCountdownValues] = useState({
const [showMinute, setMinute] = useState(0); showHour: 0,
const [showSecound, setDateSecound] = useState(0); showMinute: 0,
// count Down showSecond: 0,
const provideDate = new Date(lastDate); });
// format date
const year = provideDate.getFullYear();
const month = provideDate.getMonth();
// console.log(month);
const date = provideDate.getDate();
// console.log(date);
const hours = provideDate.getHours();
// console.log(hours);
const minutes = provideDate.getMinutes();
// console.log(minutes);
const seconds = provideDate.getSeconds();
// console.log(seconds);
// date calculation logic useEffect(() => {
const _seconds = 1000; if (lastDate) {
const _minutes = _seconds * 60; // Interval function to update countdown values
const _hours = _minutes * 60; const intervalId = setInterval(() => {
const _date = _hours * 24; const now = new Date().getTime();
const targetDate = new Date(lastDate).getTime();
const distance = targetDate - now;
// interval function
const startInterval = () => {
const timer = setInterval(() => {
const now = new Date();
const distance =
new Date(year, month, date, hours, minutes, seconds).getTime() -
now.getTime();
if (distance < 0) { if (distance < 0) {
clearInterval(timer); // If the countdown has reached zero or gone past the target date, clear the interval
clearInterval(intervalId);
setCountdownValues({
showHour: 0,
showMinute: 0,
showSecond: 0,
});
return; return;
} }
// setDate(Math.floor(distance / _date));
setMinute(Math.floor((distance % _hours) / _minutes));
setHour(Math.floor((distance % _date) / _hours));
setDateSecound(Math.floor((distance % _minutes) / _seconds));
}, 1000);
};
// effect // Calculate the countdown values (days, hours, minutes, seconds)
useEffect(() => { const days = Math.floor(distance / (1000 * 60 * 60 * 24));
if (lastDate !== "") { const hours = Math.floor(
startInterval(); (distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
} );
const minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((distance % (1000 * 60)) / 1000);
// since we don't have a slot for days...
const totalHours = days * 24 + hours;
// Update the countdown values in the state
setCountdownValues({
showHour: totalHours,
showMinute: minutes,
showSecond: seconds,
}); });
}, 1000);
// Clean up the interval on component unmount or when the lastDate prop changes
return () => clearInterval(intervalId);
}
}, [lastDate]);
// Destructure the countdown values from the state
const { showHour, showMinute, showSecond } = countdownValues;
return ( return (
<span> <span>
{showHour} : {showMinute} : {showSecound} {showHour < 10 ? "0" + showHour : showHour} :{" "}
{showMinute < 10 ? "0" + showMinute : showMinute} :{" "}
{showSecond < 10 ? "0" + showSecond : showSecond}
</span> </span>
); );
} }
@@ -0,0 +1,12 @@
import React from 'react'
function Detail({label, value, bg,}) {
return (
<>
<label className='w-full md:w-1/4 text-slate-900 tracking-wide font-semibold'>{label}</label>
<p className={`p-1 w-full md:w-3/4 text-sm text-slate-900 ${bg ? bg : null}`}>{value}</p>
</>
)
}
export default Detail
+189 -261
View File
@@ -1,283 +1,69 @@
import React, { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import Detail from "./Detail";
import ModalCom from "../../Helpers/ModalCom"; import ModalCom from "../../Helpers/ModalCom";
import { toast } from "react-toastify";
import { Form, Formik } from "formik";
import usersService from "../../../services/UsersService"; import usersService from "../../../services/UsersService";
import { toast } from "react-toastify";
import LoadingSpinner from "../../Spinners/LoadingSpinner"; import LoadingSpinner from "../../Spinners/LoadingSpinner";
import { PriceFormatter } from "../../Helpers/PriceFormatter"; import { PriceFormatter } from "../../Helpers/PriceFormatter";
const MarketPopUp = ({ details, onClose, situation, marketInt }) => { const showSuccessToast = (message) => {
const [marketMsg, setMarketMsg] = useState({ toast.success(message, {
loading: false, autoClose: 3000,
data: {}, hideProgressBar: true,
state: undefined,
}); });
const [manageInt, setManageInt] = useState({ };
loading: false,
data: {}, function MarketPopUp({ details, onClose, situation }) {
state: undefined, const [pendingJobLoader, setPendingJobLoader] = useState({
msg: "", extend: false,
offer: false,
}); });
const [textValue, setTextValue] = useState("");
const handleInputChange = ({ target: { value } }) => {
setTextValue(value);
};
const apiCall = useMemo(() => new usersService(), []); const apiCall = useMemo(() => new usersService(), []);
const marketCalls = useCallback( const handlePendingJobsBtn = useCallback(
async (e) => { async ({ target: { name } }) => {
let { job_uid, offer_code } = details;
let reqData;
let pendingData = { job_uid, offer_code };
try { try {
const nameOfCall = e?.target?.name; if (name === "extend") {
const { offer_code } = details; setPendingJobLoader({ extend: true });
const reqData = { offer_code }; reqData = { ...pendingData };
// let { data } =
await apiCall.pendingJobExtend(reqData);
// console.log("This is for extend", data);
showSuccessToast("Job has been extended by a week!");
} else if (name === "offer") {
setPendingJobLoader({ offer: true });
reqData = { ...pendingData };
// let { data } =
await apiCall.pendingJobSendTome(reqData);
// console.log("This is for offer", data);
showSuccessToast("Offer sent, check your email");
} else return;
if (nameOfCall === "market-message") {
setMarketMsg({ loading: true });
if (!textValue) return;
reqData.yourmessage = textValue;
const marketMessage = await apiCall.MarketMessage(reqData);
const marketMessageRes = marketMessage?.data;
if (marketMessageRes?.internal_return < 0) {
toast.warn("Something wrong happened", {
autoClose: 2000,
hideProgressBar: true,
});
onClose();
return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} else {
setManageInt({ loading: true });
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = manageInt?.data;
if (manageIntRes?.internal_return < 0) {
setManageInt({
loading: false,
msg: `Error - ${manageIntRes?.status}`,
data: manageIntRes,
state: false,
});
} else {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: true,
});
}
setTimeout(() => setManageInt({ msg: "" }), 3000);
return;
}
} catch (error) {
throw new Error(error);
} finally {
setTimeout(() => { setTimeout(() => {
setTextValue(""); setPendingJobLoader({ extend: false, offer: false });
setMarketMsg({ loading: false }); onClose();
}, 2000); }, 2700);
} catch (error) {
setPendingJobLoader({ extend: false, offer: false });
throw new Error(error);
} }
}, },
[apiCall, details, onClose, textValue] [onClose, apiCall, details]
); );
let thePrice = PriceFormatter(
details?.price * 0.01,
details?.currency_code,
details?.currency
);
// let addedIntDate = marketInt?.added?.split(" ")[0];
let expireIntDate = marketInt?.expire?.split(" ")[0];
return ( return (
<ModalCom action={onClose} situation={situation} className="edit-popup"> <ModalCom action={onClose} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper md:w-[750px] md:h-[660px] h-full bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto"> <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">
<div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px]"> <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"> <h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
{details.offer_code} Manage Pending Item
</h1> </h1>
<CloseIcon onClose={onClose} />
</div>
<div className="md:flex bg-white rounded-lg">
<div className="p-4 w-full md:w-[75%] md:border-r-1">
<div className="min-h-[263px]">
<h2 className="font-semibold text-slate-900 dark:text-black tracking-wide">
{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: details.job_description,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-center`} key={idx}>
<label className="w-full md:w-[19%] text-slate-900 tracking-wide font-semibold whitespace-pre-wrap">
{name}
</label>
<div
className={`w-full md:w-3/4 text-slate-900 market-pop ${
name !== "Delivery Detail"
? " h-full max-h-28 flex items-center"
: " overflow-y-auto max-h-[100px]"
}`}
>
{danger ? (
<p
className={``}
dangerouslySetInnerHTML={{
__html: danger && content?.replace(/"/g, ""),
}}
/>
) : (
<p className={`w-full text-slate-900`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<>
<hr className="mb-1" />
<span className="flex items-center gap-2">
{content?.text}
<strong>{thePrice}</strong>
</span>
<hr className="mt-1" />
</>
)}
</>
) : (
""
)}
</p>
)}
</div>
</div>
))}
</div>
<hr />
<div className="my-3 w-full flex flex-col gap-3">
<div className="w-full">
<label className="w-full text-slate-900 tracking-wide">
If you have any questions about this task:
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
placeholder="Enter message here ..."
value={textValue}
onChange={handleInputChange}
/>
</div>
<button
className="self-end w-[150px] h-[52px] rounded-md text-base bg-yellow-500 text-white"
name="market-message"
onClick={marketCalls}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : !marketMsg.state ? (
"Send Message"
) : (
"Message Sent"
)}
</button>
</div>
</div>
<div className="w-full md:w-[23%] h-full ">
<div className="mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[498px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center py-4 gap-2">
<p className="w-full text-slate-900 tracking-wide my-1">
Interested in the task?
</p>
<hr />
<button
className="bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md inline-flex sm:flex-col flex-row sm:gap-0 gap-1 items-center justify-center"
name="market-interest"
onClick={marketCalls}
>
{" "}
<span>Send</span>
<span>Interest</span>
<span>Request</span>
</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="">
<p className="flex items-center tracking-wide">
Interest: <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1">Expire: {expireIntDate}</p>
</div>
</div>
<button
className="self-end w-[150px] mt-2 h-[52px] rounded-md text-base bg-transparent border border-red-500 text-red-500 mx-auto"
name="cancel"
onClick={onClose}
>
Cancel
</button>
</div>
{/* END OF ACTION SECTION */}
</div>
</div>
</ModalCom>
);
};
export default MarketPopUp;
const CloseIcon = ({ onClose }) => (
<button <button
type="button" type="button"
className="text-[#374557] dark:text-red-500" className="text-[#374557] dark:text-red-500"
@@ -303,4 +89,146 @@ const CloseIcon = ({ onClose }) => (
/> />
</svg> </svg>
</button> </button>
); </div>
<div className="md:flex bg-white rounded-lg shadow-lg">
<div className="p-4 w-full md:w-3/4 md:border-r-2">
<p className="text-base font-semibold text-slate-900 tracking-wide">
{details.title}
</p>
<div className="my-2 p-2 flex justify-start items-center space-x-2 bg-red-100 border-2 border-red-300">
<span className="w-8 h-8 text-center text-sm rounded-full flex justify-center items-center text-red-300 bg-yellow-100">
!
</span>
<div className="">
<p className="text-sm">
This Job have been sent to public view
</p>
<p className="text-sm text-slate-600">This Job will expire</p>
</div>
</div>
{/* INPUT SECTION */}
<div className="my-2 md:flex">
<Detail
label="Date Added"
value={details.offer_added || "default"}
/>
</div>
<div className="my-2 md:flex">
<Detail label="Description" value={details.description} />
</div>
<div className="my-2 md:flex">
<Detail
label="Offer Expire"
value={
details.expire &&
`${details.expire.split(" ")[0]} ${
details.expire.split(" ")[1].split(".")[0]
}`
}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Price"
// value={`${details.price * 0.01} ${details.currency}`}
value={PriceFormatter(details.price * 0.01, details?.currency_code, details.currency)}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Duration"
value={`${details.timeline_days} day(s)`}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Detail"
value={details.job_description || details.description}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Public Link"
value="https://work.wrenchboard.com/plb/viewjob/218B4BWB83"
bg="bg-slate-200"
/>
</div>
</div>
{/* ACTION SECTION */}
<div className="p-4 w-full md:w-1/4 h-full">
<p className="mb-6 text-sm">Actions</p>
<div className="mb-3">
<p className="px-2 py-1 text-sm bg-slate-100">
Job sent to public view
</p>
</div>
<div className="my-3">
<button
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={handlePendingJobsBtn}
name="extend"
>
{pendingJobLoader.extend ? (
<div className="w-[136px] flex justify-center items-center h-full">
<LoadingSpinner size={5} color="sky-blue" />
</div>
) : (
"Extend by a week"
)}
</button>
</div>
<div className="my-3">
<button
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={handlePendingJobsBtn}
name="offer"
>
{pendingJobLoader.offer ? (
<div className="w-[96px] flex justify-center items-center h-full">
<LoadingSpinner size={5} color="sky-blue" />
</div>
) : (
"Send to me"
)}
</button>
</div>
<div className="mt-10 md:mt-32 md:flex md:justify-center">
<button
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={onClose}
>
Cancel Offer
</button>
</div>
</div>
</div>
{/* close button */}
<div className="p-6 flex justify-center">
<button
onClick={onClose}
type="button"
className=" border-gradient text-18 tracking-wide px-2 py-2 rounded-full"
>
<span className="text-gradient">Close</span>
</button>
</div>
{/* end of close button */}
</div>
</ModalCom>
);
}
export default MarketPopUp;
+18 -9
View File
@@ -248,6 +248,8 @@ function ActiveJobs(props) {
} }
}, [passDue]); }, [passDue]);
console.log("AC JOBS >>", props);
return ( return (
<Layout> <Layout>
<div className="py-[20px] bg-white dark:bg-black dark:text-white px-4 rounded-2xl shadow-md md:flex justify-between items-start gap-16"> <div className="py-[20px] bg-white dark:bg-black dark:text-white px-4 rounded-2xl shadow-md md:flex justify-between items-start gap-16">
@@ -278,12 +280,14 @@ function ActiveJobs(props) {
<div className="w-full my-2"> <div className="w-full my-2">
<p className="w-full text-base text-right text-sky-blue"> <p className="w-full text-base text-right text-sky-blue">
{props.details.job_to && props.details.job_to} {props?.details && props.details.job_to}
</p> </p>
<div className="text-base text-slate-700 dark:text-white tracking-wide"> <div className="text-base text-slate-700 dark:text-white tracking-wide">
<p className="font-semibold text-black dark:text-white">Description: </p> <p className="font-semibold text-black dark:text-white">
Description:{" "}
</p>
<p className="p-2 border border-sky-blue"> <p className="p-2 border border-sky-blue">
{props.details?.description && props.details.description} {props?.details && props.details.description}
</p> </p>
</div> </div>
</div> </div>
@@ -297,11 +301,10 @@ function ActiveJobs(props) {
<div className="my-1"> <div className="my-1">
<p className="text-base text-slate-700"> <p className="text-base text-slate-700">
<span className="font-semibold">Due: </span> <span className="font-semibold">Due: </span>
{props.details?.delivery_date && {props?.details && props.details.delivery_date.split(" ")[0]}
props.details.delivery_date.split(" ")[0]}
</p> </p>
<p className="py-2 text-base text-slate-700"> <p className="py-2 text-base text-slate-700">
{props.details?.delivery_date && {props?.delivery_date &&
props.details.delivery_date.split(" ")[1]} props.details.delivery_date.split(" ")[1]}
</p> </p>
</div> </div>
@@ -310,7 +313,9 @@ function ActiveJobs(props) {
<p className="font-semibold">Due: </p> <p className="font-semibold">Due: </p>
<div className="flex flex-col justify-between"> <div className="flex flex-col justify-between">
<p className="text-base text-slate-700 tracking-wide"> <p className="text-base text-slate-700 tracking-wide">
<CountDown lastDate={props.details.delivery_date} /> <CountDown
lastDate={props?.details && props.details.delivery_date}
/>
</p> </p>
<div className="text-base text-slate-700 tracking-wide flex gap-[5px]"> <div className="text-base text-slate-700 tracking-wide flex gap-[5px]">
<span>Hrs</span> <span>Hrs</span>
@@ -322,14 +327,18 @@ function ActiveJobs(props) {
)} )}
<div className="my-1 text-base text-slate-700 tracking-wide flex items-center gap-3"> <div className="my-1 text-base text-slate-700 tracking-wide flex items-center gap-3">
<span className="font-semibold text-black dark:text-white">Duration: </span> <span className="font-semibold text-black dark:text-white">
Duration:{" "}
</span>
<span className=""> <span className="">
{props.details?.timeline_days && props.details.timeline_days}{" "} {props.details?.timeline_days && props.details.timeline_days}{" "}
day(s) day(s)
</span> </span>
</div> </div>
<div className="my-1 text-base text-slate-700 tracking-wide flex items-center gap-3"> <div className="my-1 text-base text-slate-700 tracking-wide flex items-center gap-3">
<span className="font-semibold text-black dark:text-white">No: </span> <span className="font-semibold text-black dark:text-white">
No:{" "}
</span>
<span className=""> <span className="">
{props.details?.contract && props.details.contract} {props.details?.contract && props.details.contract}
</span> </span>
@@ -79,7 +79,7 @@ function CurrentTaskAction({jobDetails}) {
<tr> <tr>
<td> <td>
<div className="flex justify-center items-center"> <div className="flex justify-center items-center">
<button onClick={popUpHandler} type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"> <button onClick={popUpHandler} type="button" className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Send of Review Send of Review
</button> </button>
</div> </div>
@@ -10,8 +10,8 @@ import ReviewJobAction from './ReviewJobAction'
import ReviewTaskAction from './ReviewTaskAction' import ReviewTaskAction from './ReviewTaskAction'
function IndexJobActions({details}) { // FUNCTION TO RENDER SPECIFIC JOB ACTION DEPENDING ON OWNER STATUS & STATUS DESCRIPTION function IndexJobActions({details}) { // FUNCTION TO RENDER SPECIFIC JOB ACTION DEPENDING ON OWNER STATUS & STATUS DESCRIPTION
let owner = details.owner_status let owner = details?.owner_status
let description = details.status_description let description = details?.status_description
switch(owner) { switch(owner) {
case 'OWNER': case 'OWNER':
return (()=>{ return (()=>{
@@ -119,7 +119,7 @@ function PastDueJobAction({jobDetails}) {
<tr> <tr>
<td> <td>
<div className="flex justify-center items-center"> <div className="flex justify-center items-center">
<button type="button" onClick={popUpHandler} className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"> <button type="button" onClick={popUpHandler} className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Cancel or Extend Timeline Cancel or Extend Timeline
</button> </button>
</div> </div>
@@ -20,7 +20,7 @@ function PastDueTaskAction() {
<tr> <tr>
<td> <td>
<div className="flex justify-center items-center"> <div className="flex justify-center items-center">
<button type="button" className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"> <button type="button" className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Request Extension Request Extension
</button> </button>
</div> </div>
@@ -108,7 +108,7 @@ function ReviewJobAction({jobDetails}) {
<tr> <tr>
<td> <td>
<div className="flex justify-center items-center"> <div className="flex justify-center items-center">
<button type="button" onClick={popUpHandler} className="w-120 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"> <button type="button" onClick={popUpHandler} className="w-[150px] h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white">
Reject or Accept Reject or Accept
</button> </button>
</div> </div>
+1
View File
@@ -10,6 +10,7 @@ export default function MyActiveJobs(props) {
setValue(value); setValue(value);
}; };
console.log("AMEYE LOC1", props.MyJobList); console.log("AMEYE LOC1", props.MyJobList);
return ( return (
<Layout> <Layout>
<CommonHead commonHeadData={props.commonHeadData} /> <CommonHead commonHeadData={props.commonHeadData} />
+3 -3
View File
@@ -60,6 +60,8 @@ export default function MyJobTable({ className, ActiveJobList }) {
<div className="h-auto w-full"> <div className="h-auto w-full">
{ActiveJobList?.data?.length > 0 && {ActiveJobList?.data?.length > 0 &&
currentTask?.map((task, idx) => { currentTask?.map((task, idx) => {
// find due date
const dueDate = task?.delivery_date.split(" ")[0];
let thePrice = PriceFormatter( let thePrice = PriceFormatter(
task?.price * 0.01, task?.price * 0.01,
task?.currency_code, task?.currency_code,
@@ -101,9 +103,7 @@ export default function MyJobTable({ className, ActiveJobList }) {
</span> </span>
<span className="text-sm text-thin-light-gray"> <span className="text-sm text-thin-light-gray">
Due Date: Due Date:
<span className="text-purple ml-1"> <span className="text-purple ml-1">{dueDate}</span>
{task?.delivery_date}
</span>
</span> </span>
<span className="text-sm text-thin-light-gray"> <span className="text-sm text-thin-light-gray">
Confirmation: Confirmation:
@@ -31,11 +31,6 @@ export default function MyWaitingJobTable({ MyJobList, className }) {
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full"> <div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between h-full">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400"> <table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody> <tbody>
{/*<tr className="text-base text-thin-light-gray border-b dark:border-[#5356fb29] default-border-b dark:border-[#5356fb29] ottom ">*/}
{/* <td className="py-4">All Product</td>*/}
{/* <td className="py-4 text-right">.</td>*/}
{/*</tr>*/}
{ {
<> <>
{MyJobList && {MyJobList &&
+258 -187
View File
@@ -1,69 +1,282 @@
import React, { useCallback, useMemo, useState } from "react"; import { useCallback, useMemo, useState } from "react";
import Detail from "./popoutcomponent/Detail";
import ModalCom from "../Helpers/ModalCom"; import ModalCom from "../Helpers/ModalCom";
import usersService from "../../services/UsersService";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import usersService from "../../services/UsersService";
import LoadingSpinner from "../Spinners/LoadingSpinner"; import LoadingSpinner from "../Spinners/LoadingSpinner";
import { PriceFormatter } from "../Helpers/PriceFormatter"; import { PriceFormatter } from "../Helpers/PriceFormatter";
const showSuccessToast = (message) => { const PendingJobsPopout = ({ details, onClose, situation, marketInt }) => {
toast.success(message, { const [marketMsg, setMarketMsg] = useState({
autoClose: 3000, loading: false,
hideProgressBar: true, data: {},
state: undefined,
});
const [manageInt, setManageInt] = useState({
loading: false,
data: {},
state: undefined,
msg: "",
}); });
};
function PendingJobsPopout({ details, onClose, situation }) { const [textValue, setTextValue] = useState("");
const [pendingJobLoader, setPendingJobLoader] = useState({
extend: false, const handleInputChange = ({ target: { value } }) => {
offer: false, setTextValue(value);
}); };
const apiCall = useMemo(() => new usersService(), []); const apiCall = useMemo(() => new usersService(), []);
const handlePendingJobsBtn = useCallback( const marketCalls = useCallback(
async ({ target: { name } }) => { async (e) => {
let { job_uid, offer_code } = details;
let reqData;
let pendingData = { job_uid, offer_code };
try { try {
if (name === "extend") { const nameOfCall = e?.target?.name;
setPendingJobLoader({ extend: true }); const { offer_code } = details;
reqData = { ...pendingData }; const reqData = { offer_code };
// let { data } =
await apiCall.pendingJobExtend(reqData);
// console.log("This is for extend", data);
showSuccessToast("Job has been extended by a week!");
} else if (name === "offer") {
setPendingJobLoader({ offer: true });
reqData = { ...pendingData };
// let { data } =
await apiCall.pendingJobSendTome(reqData);
// console.log("This is for offer", data);
showSuccessToast("Offer sent, check your email");
} else return;
setTimeout(() => { if (nameOfCall === "market-message") {
setPendingJobLoader({ extend: false, offer: false }); setMarketMsg({ loading: true });
if (!textValue) return;
reqData.yourmessage = textValue;
const marketMessage = await apiCall.MarketMessage(reqData);
const marketMessageRes = marketMessage?.data;
if (marketMessageRes?.internal_return < 0) {
toast.warn("Something wrong happened", {
autoClose: 2000,
hideProgressBar: true,
});
onClose(); onClose();
}, 2700); return;
}
toast.success("Message sent", {
autoClose: 2500,
hideProgressBar: true,
});
setMarketMsg({ data: marketMessageRes, state: true });
setTimeout(() => onClose(), 2000);
} else {
setManageInt({ loading: true });
const manageInt = await apiCall.MarketInterest(reqData);
const manageIntRes = manageInt?.data;
if (manageIntRes?.internal_return < 0) {
setManageInt({
loading: false,
msg: `Error - ${manageIntRes?.status}`,
data: manageIntRes,
state: false,
});
} else {
setManageInt({
loading: false,
msg: manageIntRes?.status,
data: manageIntRes,
state: true,
});
}
setTimeout(() => setManageInt({ msg: "" }), 3000);
return;
}
} catch (error) { } catch (error) {
setPendingJobLoader({ extend: false, offer: false });
throw new Error(error); throw new Error(error);
} finally {
setTimeout(() => {
setTextValue("");
setMarketMsg({ loading: false });
}, 2000);
} }
}, },
[onClose, apiCall, details] [apiCall, details, onClose, textValue]
); );
let thePrice = PriceFormatter(
details?.price * 0.01,
details?.currency_code,
details?.currency
);
// let addedIntDate = marketInt?.added?.split(" ")[0];
let expireIntDate = marketInt?.expire?.split(" ")[0];
return ( return (
<ModalCom action={onClose} situation={situation} className="edit-popup"> <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"> <div className="logout-modal-wrapper md:w-[750px] md:h-[660px] h-full 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"> <div className="logout-modal-header w-full flex items-center justify-between lg:p-6 px-[30px] py-[23px]">
<h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide"> <h1 className="text-26 font-bold text-dark-gray dark:text-white tracking-wide">
Manage Pending Item {details.offer_code}
</h1> </h1>
<CloseIcon onClose={onClose} />
</div>
<div className="md:flex bg-white rounded-lg">
<div className="p-4 w-full md:w-[75%] md:border-r-1">
<div className="min-h-[263px]">
<h2 className="font-semibold text-slate-900 dark:text-black tracking-wide">
{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: details.job_description,
danger: true,
},
].map(({ name, content, danger }, idx) => (
<div className={`my-3 md:flex items-center`} key={idx}>
<label className="w-full md:w-[19%] text-slate-900 tracking-wide font-semibold whitespace-pre-wrap">
{name}
</label>
<div
className={`w-full md:w-3/4 text-slate-900 market-pop ${
name !== "Delivery Detail"
? " h-full max-h-28 flex items-center"
: " overflow-y-auto max-h-[100px]"
}`}
>
{danger ? (
<p
className={``}
dangerouslySetInnerHTML={{
__html: danger && content?.replace(/"/g, ""),
}}
/>
) : (
<p className={`w-full text-slate-900`}>
{name !== "Delivery Detail" ? (
<>
{typeof content !== "object" ? content : null}
{typeof content === "object" && (
<>
<hr className="mb-1" />
<span className="flex items-center gap-2">
{content?.text}
<strong>{thePrice}</strong>
</span>
<hr className="mt-1" />
</>
)}
</>
) : (
""
)}
</p>
)}
</div>
</div>
))}
</div>
<hr />
<div className="my-3 w-full flex flex-col gap-3">
<div className="w-full">
<label className="w-full text-slate-900 tracking-wide">
If you have any questions about this task:
</label>
<textarea
className={`p-1 w-full text-sm text-slate-900 outline-none border border-slate-300 rounded-md`}
rows="5"
style={{ resize: "none" }}
placeholder="Enter message here ..."
value={textValue}
onChange={handleInputChange}
/>
</div>
<button
className="self-end w-[150px] h-[52px] rounded-md text-base bg-yellow-500 text-white"
name="market-message"
onClick={marketCalls}
>
{marketMsg.loading ? (
<LoadingSpinner size={5} color="white" />
) : !marketMsg.state ? (
"Send Message"
) : (
"Message Sent"
)}
</button>
</div>
</div>
<div className="w-full md:w-[23%] h-full ">
<div className="mx-auto bg-[#f1f8ff] p-4 rounded-md md:min-h-[498px] flex flex-col justify-between">
<div className="w-full flex flex-col justify-center py-4 gap-2">
<p className="w-full text-slate-900 tracking-wide my-1">
Interested in the task?
</p>
<hr />
<button
className="bg-[#57cd89] text-center text-lg font-semibold text-white py-2 px-4 rounded-md inline-flex sm:flex-col flex-row sm:gap-0 gap-1 items-center justify-center"
name="market-interest"
onClick={marketCalls}
>
{" "}
<span>Send</span>
<span>Interest</span>
<span>Request</span>
</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="">
<p className="flex items-center tracking-wide">
Interest: <b className="ml-1">{details.interest_count}</b>
</p>
<hr />
<p className="my-1">Expire: {expireIntDate}</p>
</div>
</div>
<button
className="self-end w-[150px] mt-2 h-[52px] rounded-md text-base bg-transparent border border-red-500 text-red-500 mx-auto"
name="cancel"
onClick={onClose}
>
Cancel
</button>
</div>
{/* END OF ACTION SECTION */}
</div>
</div>
</ModalCom>
);
};
export default PendingJobsPopout;
const CloseIcon = ({ onClose }) => (
<button <button
type="button" type="button"
className="text-[#374557] dark:text-red-500" className="text-[#374557] dark:text-red-500"
@@ -89,146 +302,4 @@ function PendingJobsPopout({ details, onClose, situation }) {
/> />
</svg> </svg>
</button> </button>
</div> );
<div className="md:flex bg-white rounded-lg shadow-lg">
<div className="p-4 w-full md:w-3/4 md:border-r-2">
<p className="text-base font-semibold text-slate-900 tracking-wide">
{details.title}
</p>
<div className="my-2 p-2 flex justify-start items-center space-x-2 bg-red-100 border-2 border-red-300">
<span className="w-8 h-8 text-center text-sm rounded-full flex justify-center items-center text-red-300 bg-yellow-100">
!
</span>
<div className="">
<p className="text-sm">
This Job have been sent to public view
</p>
<p className="text-sm text-slate-600">This Job will expire</p>
</div>
</div>
{/* INPUT SECTION */}
<div className="my-2 md:flex">
<Detail
label="Date Added"
value={details.offer_added || "default"}
/>
</div>
<div className="my-2 md:flex">
<Detail label="Description" value={details.description} />
</div>
<div className="my-2 md:flex">
<Detail
label="Offer Expire"
value={
details.expire &&
`${details.expire.split(" ")[0]} ${
details.expire.split(" ")[1].split(".")[0]
}`
}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Price"
// value={`${details.price * 0.01} ${details.currency}`}
value={PriceFormatter(details.price * 0.01, details?.currency_code, details.currency)}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Duration"
value={`${details.timeline_days} day(s)`}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Detail"
value={details.job_description || details.description}
/>
</div>
<div className="my-2 md:flex">
<Detail
label="Public Link"
value="https://work.wrenchboard.com/plb/viewjob/218B4BWB83"
bg="bg-slate-200"
/>
</div>
</div>
{/* ACTION SECTION */}
<div className="p-4 w-full md:w-1/4 h-full">
<p className="mb-6 text-sm">Actions</p>
<div className="mb-3">
<p className="px-2 py-1 text-sm bg-slate-100">
Job sent to public view
</p>
</div>
<div className="my-3">
<button
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={handlePendingJobsBtn}
name="extend"
>
{pendingJobLoader.extend ? (
<div className="w-[136px] flex justify-center items-center h-full">
<LoadingSpinner size={5} color="sky-blue" />
</div>
) : (
"Extend by a week"
)}
</button>
</div>
<div className="my-3">
<button
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={handlePendingJobsBtn}
name="offer"
>
{pendingJobLoader.offer ? (
<div className="w-[96px] flex justify-center items-center h-full">
<LoadingSpinner size={5} color="sky-blue" />
</div>
) : (
"Send to me"
)}
</button>
</div>
<div className="mt-10 md:mt-32 md:flex md:justify-center">
<button
className="px-2 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
onClick={onClose}
>
Cancel Offer
</button>
</div>
</div>
</div>
{/* close button */}
<div className="p-6 flex justify-center">
<button
onClick={onClose}
type="button"
className=" border-gradient text-18 tracking-wide px-2 py-2 rounded-full"
>
<span className="text-gradient">Close</span>
</button>
</div>
{/* end of close button */}
</div>
</ModalCom>
);
}
export default PendingJobsPopout;
+46 -32
View File
@@ -1,47 +1,61 @@
import React, {useState, useEffect} from 'react' import React, { useState, useEffect } from "react";
import ActiveJobs from '../components/MyActiveJobs/ActiveJobs' import ActiveJobs from "../components/MyActiveJobs/ActiveJobs";
import { useLocation, useNavigate } from 'react-router-dom' import { useLocation, useNavigate } from "react-router-dom";
import usersService from '../services/UsersService' import usersService from "../services/UsersService";
function ManageActiveJobs() { function ManageActiveJobs() {
const ApiCall = new usersService();
const ApiCall = new usersService() let navigate = useNavigate();
let { state } = useLocation();
let navigate = useNavigate() let [details, setDetails] = useState({}); // to hold state values
let {state} = useLocation()
let [details, setDetails] = useState({}) // to hold state values let [activeJobMesList, setActiveJobMesList] = useState({
loading: true,
error: false,
data: [],
});
let [activeJobMesList, setActiveJobMesList] = useState({loading: true, error: false, data: []}) let [activeJobMesListReload, setActiveJobMesListReload] = useState(false); // state to determine when ACTIVE JOB MESSAGE LIST RELOADS/RE-RENDERS
let [activeJobMesListReload, setActiveJobMesListReload] = useState(false)// state to determine when ACTIVE JOB MESSAGE LIST RELOADS/RE-RENDERS const getActiveJobMesList = () => {
// FUNCTION TO GET ACTIVE JOB MESSAGE LIST
const getActiveJobMesList = () => { // FUNCTION TO GET ACTIVE JOB MESSAGE LIST setActiveJobMesList({ loading: true, error: false, data: [] });
setActiveJobMesList({loading: true, error: false, data: []}) let contract = { contract: state.contract };
let contract = {contract: state.contract} ApiCall.activeJobMesList(contract)
ApiCall.activeJobMesList(contract).then((res)=>{ .then((res) => {
if(res.status != 200 || res.data.internal_return < 0){ if (res.status != 200 || res.data.internal_return < 0) {
setActiveJobMesList({loading: false, error: false, data: []}) setActiveJobMesList({ loading: false, error: false, data: [] });
return return;
} }
setActiveJobMesList({loading: false, error: false, data: res.data.result_list}) setActiveJobMesList({
}).catch((error)=>{ loading: false,
setActiveJobMesList({loading: false, error: true, data: []}) error: false,
data: res.data.result_list,
});
}) })
} .catch((error) => {
setActiveJobMesList({ loading: false, error: true, data: [] });
});
};
useEffect(()=>{ useEffect(() => {
if(!state){ if (!state) {
navigate('/', {replace: true}) navigate("/", { replace: true });
return return;
} }
setDetails(state) setDetails(state);
getActiveJobMesList() getActiveJobMesList();
},[activeJobMesListReload]) }, [activeJobMesListReload]);
return ( return (
<ActiveJobs details={state} activeJobMesList={activeJobMesList} reloadActiveJobList={setActiveJobMesListReload} /> <ActiveJobs
) details={state}
activeJobMesList={activeJobMesList}
reloadActiveJobList={setActiveJobMesListReload}
/>
);
} }
export default ManageActiveJobs export default ManageActiveJobs;
+3 -3
View File
@@ -1,14 +1,14 @@
import React, { useContext,useState, useEffect } from "react"; import React, { useContext, useState, useEffect } from "react";
import usersService from "../services/UsersService"; import usersService from "../services/UsersService";
//import MyJobs from "../components/MyJobs"; //import MyJobs from "../components/MyJobs";
import MyActiveJobs from "../components/MyActiveJobs"; import MyActiveJobs from "../components/MyActiveJobs";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
export default function MyActiveJobsPage() { export default function MyActiveJobsPage() {
let {commonHeadBanner} = useSelector(state => state.commonHeadBanner) let { commonHeadBanner } = useSelector((state) => state.commonHeadBanner);
const [MyJobList, setMyJobList] = useState([]); const [MyJobList, setMyJobList] = useState([]);
const api = new usersService(); const api = new usersService();
//TARGET ENDPOINT[POST]http://10.204.5.100:9083/en/wrench/api/v1/jobmanageractive //TARGET ENDPOINT[POST]http://10.204.5.100:9083/en/wrench/api/v1/jobmanageractive
const getMyJobList = async () => { const getMyJobList = async () => {
try { try {
const res = await api.getMyActiveJobList(); const res = await api.getMyActiveJobList();
+1 -1
View File
@@ -1,4 +1,4 @@
import React, { useContext, useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import MyTasks from "../components/MyTasks"; import MyTasks from "../components/MyTasks";
// import UsersService from "../services/UsersService"; // import UsersService from "../services/UsersService";
import usersService from "../services/UsersService"; import usersService from "../services/UsersService";
+1 -2
View File
@@ -1,6 +1,5 @@
import React, { useContext, useState, useEffect } from "react"; import { useState, useEffect } from "react";
import usersService from "../services/UsersService"; import usersService from "../services/UsersService";
import MyPendingJobs from "../components/MyPendingJobs";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import MyWaitingJobs from "../components/MyWaitingJobs"; import MyWaitingJobs from "../components/MyWaitingJobs";