.
This commit was merged in pull request #212.
This commit is contained in:
@@ -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
|
||||||
@@ -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;
|
||||||
@@ -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>
|
||||||
|
|||||||
@@ -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} />
|
||||||
|
|||||||
@@ -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 &&
|
||||||
|
|||||||
@@ -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;
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,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,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";
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user