Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
validationSchema as VS,
|
||||
getWalletDetail,
|
||||
useDispatch,
|
||||
useSelector,
|
||||
usersService,
|
||||
@@ -20,21 +19,14 @@ const validationSchema = VS;
|
||||
function AddJob({ popUpHandler, categories }) {
|
||||
const ApiCall = new usersService();
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
|
||||
let dispatch = useDispatch();
|
||||
|
||||
const getWalletDetails = getWalletDetail(
|
||||
userDetails.country,
|
||||
walletDetails
|
||||
);
|
||||
const walletAmount = getWalletDetails; // GETTING USER BALANCE BASED ON COUNTRY SELECTED
|
||||
const initialValues = { ...IV, country: walletAmount?.description };
|
||||
const [requestStatus, setRequestStatus] = useState(initialReqState); // Holds state when submit button is pressed
|
||||
|
||||
const handleAddJob = async (values, helpers) => {
|
||||
const reqData = {
|
||||
country: walletAmount?.country,
|
||||
country: values?.country,
|
||||
price: Number(values.price) * 100,
|
||||
title: values?.title,
|
||||
description: values?.description,
|
||||
@@ -80,18 +72,10 @@ function AddJob({ popUpHandler, categories }) {
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the user is using iOS
|
||||
const isIOS = /MacIntel|MacPPC/.test(navigator.platform) && !window.MSStream;
|
||||
|
||||
// Check if the user is using Windows
|
||||
const isWindows = /Windows/.test(navigator.userAgent);
|
||||
|
||||
// console.log(isIOS, isWindows, navigator);
|
||||
|
||||
return (
|
||||
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
|
||||
<Formik
|
||||
initialValues={initialValues}
|
||||
initialValues={IV}
|
||||
validationSchema={validationSchema}
|
||||
onSubmit={handleAddJob}
|
||||
>
|
||||
@@ -103,24 +87,50 @@ function AddJob({ popUpHandler, categories }) {
|
||||
{/* inputs starts here */}
|
||||
<div className="xl:flex xl:space-x-7 mb-[5px]">
|
||||
<div className="field w-full mb-6 xl:mb-0">
|
||||
<InputCom
|
||||
fieldClass="px-6 cursor-default"
|
||||
label="Currency"
|
||||
labelClass="tracking-wide"
|
||||
// inputBg="bg-slate-100"
|
||||
// inputClass="input-curve border border-light-purple"
|
||||
type="text"
|
||||
<label
|
||||
htmlFor="country"
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
|
||||
>
|
||||
Currency
|
||||
{props.errors.country && props.touched.country && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{props.errors.country}
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
<select
|
||||
id="country"
|
||||
name="country"
|
||||
value={props.values.country}
|
||||
inputHandler={props.handleChange}
|
||||
blurHandler={props.handleBlur}
|
||||
disable={true}
|
||||
error={
|
||||
props.errors.country &&
|
||||
props.touched.country &&
|
||||
props.errors.country
|
||||
}
|
||||
/>
|
||||
className={`input-field p-2 mt-3 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-10 bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
|
||||
onChange={props.handleChange}
|
||||
onBlur={props.handleBlur}
|
||||
>
|
||||
{walletDetails?.loading ? (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Loading...
|
||||
</option>
|
||||
) : walletDetails.data.length ? (
|
||||
<>
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Select a currency
|
||||
</option>
|
||||
{walletDetails.data?.map((item, index) => (
|
||||
<option
|
||||
key={index}
|
||||
className="text-slate-500 text-lg"
|
||||
value={item?.country}
|
||||
>
|
||||
{item?.description}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
No Options Found! Try Again
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{/* Price */}
|
||||
@@ -187,7 +197,7 @@ function AddJob({ popUpHandler, categories }) {
|
||||
<div className="sm:w-[60%] w-full">
|
||||
<label
|
||||
htmlFor="Job Delivery Details"
|
||||
className='input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
|
||||
>
|
||||
Job Delivery Details
|
||||
{props.errors.job_detail &&
|
||||
@@ -210,52 +220,46 @@ function AddJob({ popUpHandler, categories }) {
|
||||
</div>
|
||||
|
||||
<div className="sm:w-[35%] w-full">
|
||||
<label
|
||||
<div
|
||||
htmlFor="Job Categories"
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
|
||||
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
|
||||
id="checked-group"
|
||||
>
|
||||
Categories ({`multiple categories - ${isIOS ? "⌘" : isWindows ? "Ctrl+" : ""}`})
|
||||
{props.errors.category && props.touched.category && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{props.errors.category}
|
||||
</span>
|
||||
)}
|
||||
|
||||
</label>
|
||||
<select
|
||||
id="category"
|
||||
name="category"
|
||||
value={props.values.category}
|
||||
className={`input-field p-2 mt-1 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-[4.7rem] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
|
||||
onChange={props.handleChange}
|
||||
onBlur={props.handleBlur}
|
||||
multiple
|
||||
Categories
|
||||
</div>
|
||||
<div
|
||||
className="sm:flex-col flex flex-wrap px-3 mt-3"
|
||||
role="group"
|
||||
aria-labelledby="checked-group"
|
||||
>
|
||||
{walletDetails?.loading ? (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Loading...
|
||||
</option>
|
||||
) : walletDetails.data.length ? (
|
||||
{categories ? (
|
||||
<>
|
||||
{/* <option className="text-slate-500 text-lg" value="">
|
||||
Select a Category
|
||||
</option> */}
|
||||
{Object?.entries(categories).map(([key, value]) => (
|
||||
<option
|
||||
<label
|
||||
key={key}
|
||||
className="text-slate-500 "
|
||||
value={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
{value}
|
||||
</option>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
No Options Found! Try Again
|
||||
</option>
|
||||
<label className="flex gap-1 w-full items-center">
|
||||
<Field type="checkbox" name="category" />
|
||||
<span className="text-[13.975px]">null</span>
|
||||
</label>
|
||||
)}
|
||||
</select>
|
||||
<span className="h-5 text-sm italic text-[#cf3917]">
|
||||
{props.errors.category &&
|
||||
props.touched.category &&
|
||||
"please select a category"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -54,12 +54,17 @@ const EditJobPopOut = ({
|
||||
const { userDetails } = useSelector((state) => state.userDetails);
|
||||
const { walletDetails } = useSelector((state) => state.walletDetails);
|
||||
|
||||
|
||||
const uploadedImage = `${userDetails.session_image_server}${localStorage.getItem('session_token')}/job/${details?.job_uid}`
|
||||
const uploadedImage = `${
|
||||
userDetails.session_image_server
|
||||
}${localStorage.getItem("session_token")}/job/${details?.job_uid}`;
|
||||
|
||||
const [taskImage, setTaskImage] = useState(uploadedImage)
|
||||
const [taskImage, setTaskImage] = useState(uploadedImage);
|
||||
|
||||
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
let [uploadStatus, setUploadStatus] = useState({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "",
|
||||
}); // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
|
||||
|
||||
let [requestStatus, setRequestStatus] = useState({
|
||||
loading: false,
|
||||
@@ -115,70 +120,99 @@ const EditJobPopOut = ({
|
||||
);
|
||||
|
||||
const taskImgChangeHandler = (e) => {
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0] //UPLOADED FILE
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"]; // ARRAY OF SUPPORTED FORMATS
|
||||
let uploadedFile = e.target.files[0]; //UPLOADED FILE
|
||||
|
||||
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
|
||||
if(!acceptedFormat.includes(fileFormat)){ //CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat.slice(0, -1).join(', ')} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({loading: false, status: false, message:msg})
|
||||
return setTimeout(()=>{
|
||||
if (!acceptedFormat.includes(fileFormat)) {
|
||||
//CHECKING FOR CORRECT UPLOAD FORMAT
|
||||
const msg = `Please select ${acceptedFormat
|
||||
.slice(0, -1)
|
||||
.join(", ")} or ${acceptedFormat.slice(-1)}`;
|
||||
setUploadStatus({ loading: false, status: false, message: msg });
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
|
||||
return setTimeout(()=>{
|
||||
if (uploadedFile.size > 5 * 1048576) {
|
||||
// CHECKING FOR CORRECT FILE SIZE
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "File must not exceed 5MB",
|
||||
});
|
||||
return setTimeout(() => {
|
||||
// profileImgInput.current.value = '' // clear the input
|
||||
setUploadStatus({loading: false, status: false, message:''})
|
||||
},5000)
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
if (e.target.value !== "") {
|
||||
const imgReader = new FileReader();
|
||||
imgReader.onload = (event) => {
|
||||
let base64Img = imgReader.result.split(",")[1];
|
||||
let reqData = { // PAYLOAD FOR API CALL
|
||||
let reqData = {
|
||||
// PAYLOAD FOR API CALL
|
||||
job_uid: details?.job_uid,
|
||||
file_name: uploadedFile?.name.slice(0,19),
|
||||
file_name: uploadedFile?.name.slice(0, 19),
|
||||
file_size: uploadedFile?.size,
|
||||
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
|
||||
file_data: base64Img,
|
||||
msg_type: 'FILE',
|
||||
action: 11303
|
||||
}
|
||||
setUploadStatus({loading: true, status: false, message:'Loading...'})
|
||||
jobApi.sendFiles(reqData).then(res=>{
|
||||
if(res.status != 200 || res.data.internal_return < 0){
|
||||
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
|
||||
}
|
||||
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
|
||||
setTaskImage(event.target.result);
|
||||
setTimeout(() => {
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
navigate("/myjobs", { replace: true });
|
||||
onClose();
|
||||
}, 1000);
|
||||
}).catch(error=>{
|
||||
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
|
||||
}).finally(()=>{
|
||||
setTimeout(()=>{
|
||||
setUploadStatus({loading: false, status: false, message: ''})
|
||||
},5000)
|
||||
})
|
||||
msg_type: "FILE",
|
||||
action: 11303,
|
||||
};
|
||||
setUploadStatus({
|
||||
loading: true,
|
||||
status: false,
|
||||
message: "Loading...",
|
||||
});
|
||||
jobApi
|
||||
.sendFiles(reqData)
|
||||
.then((res) => {
|
||||
if (res.status != 200 || res.data.internal_return < 0) {
|
||||
return setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Something went wrong, try again",
|
||||
});
|
||||
}
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: true,
|
||||
message: "Uploaded successfully",
|
||||
});
|
||||
setTaskImage(event.target.result);
|
||||
setTimeout(() => {
|
||||
dispatch(tableReload({ type: "JOBTABLE" }));
|
||||
navigate("/myjobs", { replace: true });
|
||||
onClose();
|
||||
}, 1000);
|
||||
})
|
||||
.catch((error) => {
|
||||
setUploadStatus({
|
||||
loading: false,
|
||||
status: false,
|
||||
message: "Network error, try again",
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
setUploadStatus({ loading: false, status: false, message: "" });
|
||||
}, 5000);
|
||||
});
|
||||
};
|
||||
imgReader.readAsDataURL(e.target.files[0]);
|
||||
}
|
||||
};
|
||||
|
||||
// Check if the user is using iOS
|
||||
const isIOS = /MacIntel|MacPPC/.test(navigator.platform) && !window.MSStream;
|
||||
// Check if the user is using iOS
|
||||
const isIOS = /MacIntel|MacPPC/.test(navigator.platform) && !window.MSStream;
|
||||
|
||||
// Check if the user is using Windows
|
||||
const isWindows = /Windows/.test(navigator.userAgent);
|
||||
// Check if the user is using Windows
|
||||
const isWindows = /Windows/.test(navigator.userAgent);
|
||||
|
||||
return (
|
||||
<ModalCom action={onClose} situation={situation} className="edit-popup">
|
||||
@@ -321,82 +355,77 @@ const EditJobPopOut = ({
|
||||
</div>
|
||||
|
||||
<div className="sm:w-[35%] w-full">
|
||||
<label
|
||||
htmlFor="Job Categories"
|
||||
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
|
||||
>
|
||||
Categories ({`multiple categories - ${isIOS ? "⌘" : isWindows ? "Ctrl+" : ""}`})
|
||||
{props.errors.category && props.touched.category && (
|
||||
<span className="text-[12px] text-red-500">
|
||||
{props.errors.category}
|
||||
</span>
|
||||
)}
|
||||
|
||||
</label>
|
||||
<select
|
||||
id="category"
|
||||
name="category"
|
||||
value={props.values.category}
|
||||
className={`input-field p-2 mt-1 rounded-md placeholder:text-base text-dark-gray dark:text-white w-full h-[4.7rem] bg-slate-100 dark:bg-[#11131F] focus:ring-0 focus:outline-none border`}
|
||||
onChange={props.handleChange}
|
||||
onBlur={props.handleBlur}
|
||||
multiple
|
||||
>
|
||||
{walletDetails?.loading ? (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
Loading...
|
||||
</option>
|
||||
) : walletDetails.data.length ? (
|
||||
<>
|
||||
{/* <option className="text-slate-500 text-lg" value="">
|
||||
Select a Category
|
||||
</option> */}
|
||||
{Object?.entries(categories).map(([key, value]) => (
|
||||
<option
|
||||
<div
|
||||
htmlFor="Job Categories"
|
||||
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block"'
|
||||
id="checked-group"
|
||||
>
|
||||
Categories
|
||||
</div>
|
||||
<div
|
||||
className="sm:flex-col flex flex-wrap px-3 mt-3"
|
||||
role="group"
|
||||
aria-labelledby="checked-group"
|
||||
>
|
||||
{categories &&
|
||||
Object.entries(categories)?.map(([key, value]) => (
|
||||
<label
|
||||
key={key}
|
||||
className="text-slate-500 "
|
||||
value={key}
|
||||
className="flex gap-1 w-full items-center"
|
||||
>
|
||||
{value}
|
||||
</option>
|
||||
<Field
|
||||
type="checkbox"
|
||||
name="category"
|
||||
value={key}
|
||||
/>
|
||||
<span className="text-[13.975px]">{value}</span>
|
||||
</label>
|
||||
))}
|
||||
</>
|
||||
) : (
|
||||
<option className="text-slate-500 text-lg" value="">
|
||||
No Options Found! Try Again
|
||||
</option>
|
||||
)}
|
||||
</select>
|
||||
</div>
|
||||
<span className="h-5 text-sm italic text-[#cf3917]">
|
||||
{props.errors.category &&
|
||||
props.touched.category &&
|
||||
"please select a category"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full flex items-center gap-2 mb-2">
|
||||
{/* FOR TASK IMAGE */}
|
||||
<div className="w-1/2 relative max-h-[130px] min-h-[130px]">
|
||||
<input
|
||||
<input
|
||||
id="task_image"
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
className="hidden"
|
||||
type="file"
|
||||
accept="image/*"
|
||||
onChange={taskImgChangeHandler}
|
||||
/>
|
||||
{taskImage ?
|
||||
<div className="w-full absolute -top-5">
|
||||
<img src={taskImage} className="max-h-[150px] min-h-[150px] w-full object-cover" alt="uploaded task" />
|
||||
<span onClick={()=>setTaskImage('')} className="p-2 absolute text-sm top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white/80 hover:bg-white hover:shadow-md transition-all duration-500 cursor-pointer text-slate-800">Remove Image</span>
|
||||
</div>
|
||||
:
|
||||
<label
|
||||
className="absolute -top-5 h-[150px] w-full flex flex-col justify-center items-center bg-slate-100 dark:bg-[#11131F] cursor-pointer input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold" htmlFor='task_image'>
|
||||
{taskImage ? (
|
||||
<div className="w-full absolute -top-5">
|
||||
<img
|
||||
src={taskImage}
|
||||
className="max-h-[150px] min-h-[150px] w-full object-cover"
|
||||
alt="uploaded task"
|
||||
/>
|
||||
<span
|
||||
onClick={() => setTaskImage("")}
|
||||
className="p-2 absolute text-sm top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 bg-white/80 hover:bg-white hover:shadow-md transition-all duration-500 cursor-pointer text-slate-800"
|
||||
>
|
||||
Remove Image
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<label
|
||||
className="absolute -top-5 h-[150px] w-full flex flex-col justify-center items-center bg-slate-100 dark:bg-[#11131F] cursor-pointer input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold"
|
||||
htmlFor="task_image"
|
||||
>
|
||||
Select Task Image
|
||||
</label>
|
||||
}
|
||||
</label>
|
||||
)}
|
||||
</div>
|
||||
{/* END OF TASK IMAGE */}
|
||||
|
||||
<div className="field w-1/2">
|
||||
<div
|
||||
className={`flex items-center justify-between`}
|
||||
>
|
||||
<div className={`flex items-center justify-between`}>
|
||||
<label
|
||||
className="w-full input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex flex-col"
|
||||
htmlFor="timeline_days"
|
||||
@@ -456,11 +485,23 @@ const EditJobPopOut = ({
|
||||
))}
|
||||
{/* End of error or success display */}
|
||||
|
||||
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
|
||||
<div className="w-full">
|
||||
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
|
||||
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
|
||||
</div>
|
||||
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
|
||||
<div className="w-full">
|
||||
{uploadStatus.message && !uploadStatus.loading && (
|
||||
<p
|
||||
className={`text-center ${
|
||||
uploadStatus.status
|
||||
? "text-green-500"
|
||||
: "text-red-500"
|
||||
}`}
|
||||
>
|
||||
{uploadStatus.message}
|
||||
</p>
|
||||
)}
|
||||
{uploadStatus.loading && (
|
||||
<p className="text-center">{uploadStatus.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
|
||||
<div className="flex items-center space-x-4 mr-2 mt-2">
|
||||
@@ -471,7 +512,9 @@ const EditJobPopOut = ({
|
||||
type="submit"
|
||||
className="w-[120px] h-[40px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
|
||||
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
|
||||
disabled={requestStatus.loading || uploadStatus.loading}
|
||||
disabled={
|
||||
requestStatus.loading || uploadStatus.loading
|
||||
}
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
|
||||
Reference in New Issue
Block a user