Compare commits

..

16 Commits

Author SHA1 Message Date
ebube 0756747143 updated payload 2023-12-11 20:30:44 -08:00
ameye 99eb1591a2 Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-12-11 19:46:01 +00:00
ameye 85b9aab229 Merge branch 'upload_filename' of WrenchBoard/Users-Wrench into master 2023-12-11 19:45:55 +00:00
ebube 67a0a34288 Fixed a typo 2023-12-10 10:19:56 -08:00
ebube 814e4c9693 Fixed the amount in naira add credit payload to flutterwave 2023-12-03 23:55:43 -08:00
victorAnumudu e74eb38caf defaulted filename to myfile 2023-11-29 19:56:28 +01:00
ameye 67db1a72bf Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-29 14:32:03 +00:00
ebube 41e46d8030 Reverted updates 2023-11-29 06:30:36 -08:00
ebube 8707411dda added a linux checker 2023-11-28 21:02:17 -08:00
ameye 327a4a42ae Merge branch 'Server-path-added' of WrenchBoard/Users-Wrench into master 2023-11-29 01:33:58 +00:00
ebube 455f4001f7 Final Check 2023-11-28 09:18:54 -08:00
ebube 775bcd5005 Clean up...added feature to edit popup 2023-11-28 09:11:52 -08:00
ebube 492bda021f Select Option Bug Fixed 2023-11-28 07:41:31 -08:00
ebube eacfae19f0 Currency Bug Fix 2023-11-28 07:26:34 -08:00
ebube 376cf44a9c Currency option set to fixed and category option set 2023-11-28 06:41:32 -08:00
ameye 1aab0c2910 Merge branch 'upload_download_link' of WrenchBoard/Users-Wrench into master 2023-11-28 11:56:05 +00:00
8 changed files with 271 additions and 167 deletions
+20 -82
View File
@@ -1,74 +1,28 @@
import { Field, Form, Formik } from "formik";
import React, { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import {
validationSchema as VS,
useDispatch,
useSelector,
usersService,
initialValues as IV,
initialReqState,
useState,
tableReload,
Formik,
InputCom,
Field,
Form,
LoadingSpinner,
} from "./settings";
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Currency is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(499, "Maximum 499 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select at least one checkbox"),
});
const validationSchema = VS;
function AddJob({ popUpHandler, categories }) {
const ApiCall = new usersService();
const { walletDetails } = useSelector((state) => state.walletDetails);
let dispatch = useDispatch();
let initialValues = {
// initial values for formik
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
let [requestStatus, setRequestStatus] = useState({
loading: false,
status: false,
message: "",
}); // Holds state when submit button is pressed
// const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
// const walletChecker = walletDetails?.data.find(
// (item) => item.country === country
// );
// return walletChecker ? walletChecker.amount : 0;
// };
const [requestStatus, setRequestStatus] = useState(initialReqState); // Holds state when submit button is pressed
const handleAddJob = async (values, helpers) => {
const reqData = {
@@ -81,21 +35,6 @@ function AddJob({ popUpHandler, categories }) {
category: values.category?.join("@"),
};
// const walletAmount = getWalletDetail(reqData.country); // GETTING USER BALANCE BASED ON COUNTRY SELECTED
// if (reqData.price > walletAmount) {
// setRequestStatus({
// loading: false,
// status: false,
// message: "Insufficient Balance",
// });
// setTimeout(() => {
// setRequestStatus({ loading: false, status: false, message: "" });
// }, 1500);
// return;
// }
setRequestStatus({ loading: true, status: false, message: "" });
try {
@@ -136,7 +75,7 @@ function AddJob({ popUpHandler, categories }) {
return (
<div className="add-job p-5 w-full bg-white dark:bg-dark-white dark:text-white rounded-md flex flex-col justify-between">
<Formik
initialValues={initialValues}
initialValues={IV}
validationSchema={validationSchema}
onSubmit={handleAddJob}
>
@@ -258,7 +197,7 @@ function AddJob({ popUpHandler, categories }) {
<div className="sm:w-[60%] w-full">
<label
htmlFor="Job Delivery Details"
className='className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1'
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex items-center gap-1"
>
Job Delivery Details
{props.errors.job_detail &&
@@ -406,7 +345,6 @@ function AddJob({ popUpHandler, categories }) {
<button
type="submit"
className="w-[152px] h-[46px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
>
Add Job
</button>
+100
View File
@@ -0,0 +1,100 @@
import { Field, Form, Formik } from "formik";
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup";
import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom";
import LoadingSpinner from "../Spinners/LoadingSpinner";
// Initialize state for request values
const initialReqState = {
loading: false,
status: false,
message: "",
};
// For form initial values
const initialValues = {
// initial values for formik
country: "",
price: "",
title: "",
description: "",
job_detail: "",
timeline_days: "",
category: [],
};
// const getWalletDetail = (country) => { // A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
// const walletChecker = walletDetails?.data.find(
// (item) => item.country === country
// );
// return walletChecker ? walletChecker.amount : 0;
// };
// To get the validation schema
const validationSchema = Yup.object().shape({
country: Yup.string()
.min(1, "Minimum 3 characters")
.max(25, "Maximum 25 characters")
.required("Currency is required"),
price: Yup.string()
.typeError("Invalid number")
.min(1, "Price must be greater than 0")
.test("no-e", "Invalid number", (value) => {
if (value && /\d+e/.test(value)) {
return false;
}
return true;
})
.required("Price is required"),
title: Yup.string()
.min(5, "Minimum 5 characters")
.max(149, "Maximum 149 characters")
.required("Title is required"),
description: Yup.string()
.min(5, "Minimum 5 characters")
.max(299, "Maximum 299 characters")
.required("Description is required"),
job_detail: Yup.string()
.min(3, "Minimum 3 characters")
.max(499, "Maximum 499 characters")
.required("Details is required"),
timeline_days: Yup.number()
.typeError("you must specify a number")
.min(1, "Price must be greater than 0")
.required("Timeline is required"),
category: Yup.array().min(1, "Select category"),
});
const getWalletDetail = (countryParams, walletDetails) => {
// A FUNCTION TO GET USER BALANCE BASED ON COUNTRY SELECTED
const walletChecker = walletDetails?.data.find(
(item) => item.country === countryParams
);
return walletChecker
? {
description: walletChecker.description,
country: walletChecker.country,
}
: "";
};
export {
Field,
Form,
Formik,
useState,
useEffect,
useDispatch,
useSelector,
usersService,
InputCom,
LoadingSpinner,
initialReqState,
initialValues,
validationSchema,
getWalletDetail,
tableReload
};
@@ -34,7 +34,7 @@ function PastDueJobAction({jobDetails}) {
let reqData = { // API PAYLOADS
contract: jobDetails.contract,
contract_uid: jobDetails.contract_uid,
job_action: 'REQUEST_CANCEL',
job_action: 'CANCEL_CONTRACT',
}
if(!checked){ // CHECKS IF CHECKBOX IS SELECTED
@@ -21,7 +21,7 @@ export default function MyPastDueJobs(props) {
<span
className={`${selectTab === "today" ? "block" : "hidden"}`}
>
Pass Due Job(s)
Past Due Job(s)
</span>
</h1>
</div>
@@ -130,7 +130,7 @@ function ConfirmAddFund({
public_key: __confirmData?.flutterwave_key,
tx_ref: __confirmData?.credit_reference,
currency: "NGN",
amount: Number(__confirmData.amount),
amount: Number(__confirmData.amount) * 0.01,
payment_options: "card,mobilemoney,ussd",
customer: {
email: userDetails.email,
+2 -2
View File
@@ -144,14 +144,14 @@ export default function UploadProduct({uploadTypes}) {
}
let reqData = { // PAYLOAD FOR API CALL
file_name: selectedFile.substring(0,21).replace(/ /gi, ""),//selectedFile.replace(/[ -]/gi, ""),
// file_name: selectedFile.substring(0,21).replace(/ /gi, ""),
file_name: `myfile.${imgDetails?.type?.split('/')[1]}`,
file_size: imgDetails.size,
file_type: imgDetails.type,
file_data: img.file?.split(",")[1],
title: itemName,
description: description,
msg_type: 'FILE',
// action: 'WRENCHBOARD_RESOURCE_MYFILES',
action: 11307
}
+144 -78
View File
@@ -52,12 +52,19 @@ const EditJobPopOut = ({
}) => {
const dispatch = useDispatch();
const { userDetails } = useSelector((state) => state.userDetails);
const uploadedImage = `${userDetails.session_image_server}${localStorage.getItem('session_token')}/job/${details?.job_uid}`
const { walletDetails } = useSelector((state) => state.walletDetails);
const [taskImage, setTaskImage] = useState(uploadedImage)
const uploadedImage = `${
userDetails.session_image_server
}${localStorage.getItem("session_token")}/job/${details?.job_uid}`;
let [uploadStatus, setUploadStatus] = useState({loading: false, status: false, message:''}) // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
const [taskImage, setTaskImage] = useState(uploadedImage);
let [uploadStatus, setUploadStatus] = useState({
loading: false,
status: false,
message: "",
}); // HOLDS STATE FOR UPLOAD PROFILE PICTURE STATUS
let [requestStatus, setRequestStatus] = useState({
loading: false,
@@ -113,65 +120,100 @@ const EditJobPopOut = ({
);
const taskImgChangeHandler = (e) => {
setUploadStatus({loading: false, status: false, message:''})
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"] // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0] //UPLOADED FILE
setUploadStatus({ loading: false, status: false, message: "" });
let acceptedFormat = ["jpeg", "jpg", "png", "bmp", "gif"]; // ARRAY OF SUPPORTED FORMATS
let uploadedFile = e.target.files[0]; //UPLOADED FILE
const fileFormat = uploadedFile?.type?.split("/")[1]?.toLowerCase();
if(!acceptedFormat.includes(fileFormat)){ //CHECKING FOR CORRECT UPLOAD FORMAT
const msg = `Please select ${acceptedFormat.slice(0, -1).join(', ')} or ${acceptedFormat.slice(-1)}`;
setUploadStatus({loading: false, status: false, message:msg})
return setTimeout(()=>{
if (!acceptedFormat.includes(fileFormat)) {
//CHECKING FOR CORRECT UPLOAD FORMAT
const msg = `Please select ${acceptedFormat
.slice(0, -1)
.join(", ")} or ${acceptedFormat.slice(-1)}`;
setUploadStatus({ loading: false, status: false, message: msg });
return setTimeout(() => {
// profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if(uploadedFile.size > 5*1048576){ // CHECKING FOR CORRECT FILE SIZE
setUploadStatus({loading: false, status: false, message:'File must not exceed 5MB'})
return setTimeout(()=>{
if (uploadedFile.size > 5 * 1048576) {
// CHECKING FOR CORRECT FILE SIZE
setUploadStatus({
loading: false,
status: false,
message: "File must not exceed 5MB",
});
return setTimeout(() => {
// profileImgInput.current.value = '' // clear the input
setUploadStatus({loading: false, status: false, message:''})
},5000)
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
}
if (e.target.value !== "") {
const imgReader = new FileReader();
imgReader.onload = (event) => {
let base64Img = imgReader.result.split(",")[1];
let reqData = { // PAYLOAD FOR API CALL
let reqData = {
// PAYLOAD FOR API CALL
job_uid: details?.job_uid,
file_name: uploadedFile?.name.slice(0,19),
file_name: uploadedFile?.name.slice(0, 19),
file_size: uploadedFile?.size,
file_type: uploadedFile?.type?.split("/")[0]?.toLowerCase(),
file_data: base64Img,
msg_type: 'FILE',
action: 11303
}
setUploadStatus({loading: true, status: false, message:'Loading...'})
jobApi.sendFiles(reqData).then(res=>{
if(res.status != 200 || res.data.internal_return < 0){
return setUploadStatus({loading: false, status: false, message: 'Something went wrong, try again'})
}
setUploadStatus({loading: false, status: true, message: 'Uploaded successfully'})
setTaskImage(event.target.result);
setTimeout(() => {
dispatch(tableReload({ type: "JOBTABLE" }));
navigate("/myjobs", { replace: true });
onClose();
}, 1000);
}).catch(error=>{
setUploadStatus({loading: false, status: false, message: 'Network error, try again'})
}).finally(()=>{
setTimeout(()=>{
setUploadStatus({loading: false, status: false, message: ''})
},5000)
})
msg_type: "FILE",
action: 11303,
};
setUploadStatus({
loading: true,
status: false,
message: "Loading...",
});
jobApi
.sendFiles(reqData)
.then((res) => {
if (res.status != 200 || res.data.internal_return < 0) {
return setUploadStatus({
loading: false,
status: false,
message: "Something went wrong, try again",
});
}
setUploadStatus({
loading: false,
status: true,
message: "Uploaded successfully",
});
setTaskImage(event.target.result);
setTimeout(() => {
dispatch(tableReload({ type: "JOBTABLE" }));
navigate("/myjobs", { replace: true });
onClose();
}, 1000);
})
.catch((error) => {
setUploadStatus({
loading: false,
status: false,
message: "Network error, try again",
});
})
.finally(() => {
setTimeout(() => {
setUploadStatus({ loading: false, status: false, message: "" });
}, 5000);
});
};
imgReader.readAsDataURL(e.target.files[0]);
}
};
// Check if the user is using iOS
const isIOS = /MacIntel|MacPPC/.test(navigator.platform) && !window.MSStream;
// Check if the user is using Windows
const isWindows = /Windows/.test(navigator.userAgent);
return (
<ModalCom action={onClose} situation={situation} className="edit-popup">
<div className="logout-modal-wrapper lg:w-[600px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
@@ -325,19 +367,20 @@ const EditJobPopOut = ({
role="group"
aria-labelledby="checked-group"
>
{categories && Object.entries(categories)?.map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
{categories &&
Object.entries(categories)?.map(([key, value]) => (
<label
key={key}
className="flex gap-1 w-full items-center"
>
<Field
type="checkbox"
name="category"
value={key}
/>
<span className="text-[13.975px]">{value}</span>
</label>
))}
<span className="h-5 text-sm italic text-[#cf3917]">
{props.errors.category &&
props.touched.category &&
@@ -349,31 +392,40 @@ const EditJobPopOut = ({
<div className="w-full flex items-center gap-2 mb-2">
{/* FOR TASK IMAGE */}
<div className="w-1/2 relative max-h-[130px] min-h-[130px]">
<input
<input
id="task_image"
className="hidden"
type="file"
accept="image/*"
className="hidden"
type="file"
accept="image/*"
onChange={taskImgChangeHandler}
/>
{taskImage ?
<div className="w-full absolute -top-5">
<img src={taskImage} className="max-h-[150px] min-h-[150px] w-full object-cover" alt="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"
@@ -433,11 +485,23 @@ const EditJobPopOut = ({
))}
{/* End of error or success display */}
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
<div className="w-full">
{uploadStatus.message && !uploadStatus.loading && <p className={`text-center ${uploadStatus.status ? 'text-green-500':'text-red-500'}`}>{uploadStatus.message}</p>}
{uploadStatus.loading && <p className="text-center">{uploadStatus.message}</p>}
</div>
{/* DISPLAYS TASK IMAGE UPLOADING STATUS */}
<div className="w-full">
{uploadStatus.message && !uploadStatus.loading && (
<p
className={`text-center ${
uploadStatus.status
? "text-green-500"
: "text-red-500"
}`}
>
{uploadStatus.message}
</p>
)}
{uploadStatus.loading && (
<p className="text-center">{uploadStatus.message}</p>
)}
</div>
<div className="w-full border-t border-light-purple dark:border-[#5356fb29] flex justify-end items-center">
<div className="flex items-center space-x-4 mr-2 mt-2">
@@ -448,7 +512,9 @@ const EditJobPopOut = ({
type="submit"
className="w-[120px] h-[40px] flex justify-center items-center btn-gradient text-base rounded-full text-white"
// className='w-20 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white'
disabled={requestStatus.loading || uploadStatus.loading}
disabled={
requestStatus.loading || uploadStatus.loading
}
>
Save
</button>
+2 -2
View File
@@ -133,9 +133,9 @@ function OfferJobPopout({ details, onClose, situation }) {
</svg>
</button>
</div>
<div className="md:flex bg-white rounded-lg shadow-lg">
<div className="md:flex bg-white dark:bg-dark-white rounded-lg shadow-lg">
<div className="p-4 w-full md:w-3/4 md:border-r-2">
<p className="text-lg my-5 font-semibold text-slate-900 tracking-wide">
<p className="text-lg my-5 font-semibold text-slate-900 dark:text-white tracking-wide">
{details.title}
</p>