529 lines
22 KiB
React
529 lines
22 KiB
React
import React, { lazy, Suspense, useRef, useState } from 'react'
|
|
import Layout from '../Partials/Layout'
|
|
import MediaLayout from '../Partials/MediaLayout'
|
|
import CustomBreadcrumb from '../Breadcrumb/CustomBreadcrumb'
|
|
import ActiveJobMessageMedia from '../MyActiveJobs/ActiveJobMessageMedia'
|
|
import LoadingSpinner from '../Spinners/LoadingSpinner'
|
|
import usersService from '../../services/UsersService'
|
|
import { useNavigate } from 'react-router-dom'
|
|
import { useSelector } from 'react-redux'
|
|
import { useReactToPrint } from 'react-to-print'
|
|
|
|
const Iframe = lazy(() => import("../Iframe/Iframe"));
|
|
|
|
export default function FamGames(props) {
|
|
|
|
const ApiCall = new usersService();
|
|
const navigate = useNavigate();
|
|
|
|
const { userDetails } = useSelector((state) => state.userDetails);
|
|
|
|
const [messageToSend, setMessageToSend] = useState(""); // State to hold the value of message to be sent
|
|
|
|
const [filesToSend, setFilesToSend] = useState([]); // State to hold the value of files to be sent
|
|
|
|
const [tab, setTab] = useState("message");
|
|
|
|
const [requestStatus, setRequestStatus] = useState({
|
|
loading: false,
|
|
status: false,
|
|
message: "",
|
|
});
|
|
|
|
// let [popUp, setPopUp] = useState(false); // STATE FOR POPOUT MODAL
|
|
|
|
const printRef = useRef();
|
|
// to handle printing
|
|
const handlePrint = useReactToPrint({
|
|
content: () => printRef.current,
|
|
});
|
|
|
|
// FUNCTION TO HANDLE POPOUT
|
|
const popUpHandler = () => {
|
|
// setPopUp((prev) => !prev);
|
|
};
|
|
|
|
// FUNCTION TO HANDLE MESSAGE CHANGE
|
|
const handleMessageChange = ({ target: { value } }) => {
|
|
setMessageToSend(value);
|
|
};
|
|
|
|
// FUNCTION TO HANDLE FILE UPlOAD CHANGE
|
|
const handleFileChange = ({ target: { files } }) => {
|
|
setRequestStatus({ loading: false, status: false, message: "" }); // State to determine error state
|
|
|
|
if (!files[0]) {
|
|
// IF NO FILE SELECTED RETURN
|
|
return;
|
|
}
|
|
if (files[0].size > Number(process.env.REACT_APP_MAX_FILE_SIZE)) {
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: false,
|
|
message: "File must be <= 1mb",
|
|
});
|
|
setTimeout(() => {
|
|
setRequestStatus({ loading: false, status: false, message: "" });
|
|
}, 5000);
|
|
return;
|
|
}
|
|
if (filesToSend.length >= Number(process.env.REACT_APP_TOTAL_NUM_FILE)) {
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: false,
|
|
message: `Total number of attachment is ${Number(
|
|
process.env.REACT_APP_TOTAL_NUM_FILE
|
|
)}`,
|
|
});
|
|
setTimeout(() => {
|
|
setRequestStatus({ loading: false, status: false, message: "" });
|
|
}, 5000);
|
|
return;
|
|
}
|
|
// INCLUDE FILE IF NO ERROR
|
|
setFilesToSend((prev) => [...prev, files[0]]);
|
|
};
|
|
|
|
// FUNCTION TO CLEAR ALL TYPED MESSAGE OR FILES
|
|
const handleClearAll = ({ target: { name } }) => {
|
|
if (tab == "message") {
|
|
setMessageToSend("");
|
|
} else if (tab == "files") {
|
|
setFilesToSend([]);
|
|
} else {
|
|
return;
|
|
}
|
|
};
|
|
|
|
// FUNCTION TO REMOVE AND IMAGE
|
|
const handleRemoveImage = (imageToDelete) => {
|
|
setFilesToSend((prev) =>
|
|
prev.filter((item) => item.name != imageToDelete.name)
|
|
);
|
|
};
|
|
|
|
// FUNCTION TO SEND TASK MESSAGE
|
|
const sendTaskMessage = () => {
|
|
let reqData = {
|
|
message: messageToSend,
|
|
msg_type: "TEXT",
|
|
contract: props.details.contract,
|
|
};
|
|
if (!reqData.message) {
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: false,
|
|
message: "Message is empty",
|
|
});
|
|
return setTimeout(() => {
|
|
setRequestStatus({ loading: false, status: false, message: "" });
|
|
}, 5000);
|
|
}
|
|
setRequestStatus({ loading: true, status: false, message: "" });
|
|
ApiCall.sendTaskMessage(reqData)
|
|
.then((res) => {
|
|
if (res.status != 200 || res.data.internal_return < 0) {
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: false,
|
|
message: "Message could not be sent, try again later",
|
|
});
|
|
return;
|
|
}
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: true,
|
|
message: "Message Sent Successfully",
|
|
});
|
|
// function to trigger socket to emit 'send_message'
|
|
// sendMessage(messageToSend, `${props.details.contract}-${props.details.contract_uid}`)
|
|
|
|
props.reloadActiveJobList((prev) => !prev); // MAKES ACTIVE JOB MESSAGE LIST TO RELOAD
|
|
setMessageToSend(""); // SENDS MESSAGE TO SEND BACK TO EMPTY STRINGS
|
|
})
|
|
.catch((error) => {
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: false,
|
|
message: "Opps! something went wrong",
|
|
});
|
|
})
|
|
.finally(() => {
|
|
setTimeout(() => {
|
|
setRequestStatus({ loading: false, status: false, message: "" });
|
|
}, 5000);
|
|
});
|
|
};
|
|
|
|
// FUNCTION TO SEND FILES
|
|
const sendFile = async () => {
|
|
setRequestStatus({ loading: true, status: false, message: "" });
|
|
|
|
if (!filesToSend.length) {
|
|
// checks if file to send is empty
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: false,
|
|
message: "No File(s) selected",
|
|
});
|
|
return setTimeout(() => {
|
|
setRequestStatus({ loading: false, status: false, message: "" });
|
|
}, 5000);
|
|
}
|
|
|
|
for (let i = 0; i <= filesToSend.length - 1; i++) {
|
|
// Loops through files to send array and trigger upload API call
|
|
|
|
const fileToBase64 = async () => {
|
|
// Converts file data to base64 string
|
|
// try {
|
|
// const base64String = await convertFileToBase64(filesToSend[i]);
|
|
// return base64String;
|
|
// } catch (error) {
|
|
// return false;
|
|
// }
|
|
};
|
|
|
|
// if(await !fileToBase64()){
|
|
// return
|
|
// }
|
|
|
|
let reqData = {
|
|
file_name: filesToSend[i].name,
|
|
file_size: filesToSend[i].size,
|
|
file_type: "image/png",
|
|
file_data: await fileToBase64(),
|
|
msg_type: "FILE",
|
|
contract: props.details.contract,
|
|
};
|
|
|
|
ApiCall.sendFiles(reqData)
|
|
.then((res) => {
|
|
// if(res.status != 200 || res.data.internal_return < 0){
|
|
// setRequestStatus({loading: false, status: false, message: 'Files(s) could not be sent, try again later'})
|
|
// return
|
|
// }
|
|
// setRequestStatus({loading: false, status: true, message: 'File(s) Uploaded Successfully'})
|
|
// props.reloadActiveJobList(prev => !prev) // MAKES ACTIVE JOB MESSAGE LIST TO RELOAD
|
|
// setFilesToSend([]) // SETS FILES TO SEND TO SEND BACK TO EMPTY ARRAY
|
|
})
|
|
.catch((error) => {
|
|
// setRequestStatus({loading: false, status: false, message: 'Opps! something went wrong'})
|
|
})
|
|
.finally(() => {
|
|
if (i == filesToSend.length - 1) {
|
|
setRequestStatus({
|
|
loading: false,
|
|
status: true,
|
|
message: "File(s) Uploaded Successfully",
|
|
});
|
|
setFilesToSend([]); // SETS FILES TO SEND TO SEND BACK TO EMPTY ARRAY
|
|
props.reloadActiveJobList((prev) => !prev); // MAKES ACTIVE JOB MESSAGE LIST TO RELOAD
|
|
setTimeout(() => {
|
|
setRequestStatus({ loading: false, status: false, message: "" });
|
|
}, 5000);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
|
|
return (
|
|
<MediaLayout
|
|
backpath={'/'}
|
|
title={'Games'}
|
|
>
|
|
{/* <div className='mb-4'>
|
|
<CustomBreadcrumb
|
|
title='Games and Interest'
|
|
breadcrumb = {
|
|
[
|
|
{ link: "/", title: "Home" },
|
|
{ link: "/work-in-progress", title: "Games and Interest", active: true},
|
|
]
|
|
}
|
|
/>
|
|
</div> */}
|
|
<div className="my-4 lg:flex justify-between items-start space-y-4 lg:space-x-4 lg:space-y-0">
|
|
<div className="w-full mb-4 border-b pb-4 lg:pb-0 lg:mb-0 lg:border-b-0">
|
|
|
|
<div className="mb-4 w-full h-screen max-h-[650px]">
|
|
<Suspense fallback={<Fallback />}>
|
|
<Iframe
|
|
src={process.env.REACT_APP_FAM_GAME_LINK}
|
|
title='Games'
|
|
/>
|
|
</Suspense>
|
|
</div>
|
|
|
|
<div className="w-full p-4 bg-white dark:bg-black rounded-2xl shadow-md md:flex md:justify-between gap-8">
|
|
<div className="w-full flex flex-col justify-between">
|
|
<div className="w-full h-30">
|
|
<div className='w-full h-full flex justify-center items-center text-5xl'>COMING SOON</div>
|
|
{/* <p className="w-full text-base text-right text-sky-blue">
|
|
{props?.details && props.details.job_to}
|
|
</p>
|
|
<div className="text-base tracking-wide">
|
|
<p className="font-semibold text-black dark:text-white tracking-wider">
|
|
Description:{" "}
|
|
</p>
|
|
<p className="p-2 ml-8 border-b border-sky-blue">
|
|
{props?.details && props.details.description}
|
|
</p>
|
|
</div>
|
|
<div className="mt-6 w-full lg:flex gap-8">
|
|
<div className="w-full text-base tracking-wide">
|
|
<p className="font-semibold text-black dark:text-white tracking-wider">
|
|
Delivery Detail:{" "}
|
|
</p>
|
|
<p className="p-2 ml-8">
|
|
{props?.details && props.details.job_description}
|
|
</p>
|
|
</div>
|
|
<div className="my-2 lg:my-0">
|
|
<IndexJobActions details={props.details} />
|
|
</div>
|
|
</div> */}
|
|
</div>
|
|
</div>
|
|
|
|
{/* job details */}
|
|
<div className="w-full md:w-[200px] h-20">
|
|
{/* <p className="text-base text-sky-blue">Delivery Detail</p> */}
|
|
{/* {passDue ? (
|
|
<div className="my-1">
|
|
<p className="text-base text-slate-700">
|
|
<span className="font-semibold">Due: </span>
|
|
{props?.details && props.details.delivery_date.split(" ")[0]}
|
|
</p>
|
|
{props?.delivery_date && (
|
|
<p className="py-2 text-base text-slate-700">
|
|
{props.details.delivery_date.split(" ")[1]}
|
|
</p>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="my-1 flex items-start gap-3">
|
|
<p className="font-semibold">Due: </p>
|
|
<div className="flex flex-col justify-between">
|
|
<p className="text-base text-slate-700 tracking-wide">
|
|
<CountDown
|
|
lastDate={props?.details && props.details.delivery_date}
|
|
/>
|
|
</p>
|
|
<div className="text-base text-slate-700 tracking-wide flex gap-[5px]">
|
|
<span>Hrs</span>
|
|
<span>Min</span>
|
|
<span>Sec</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)} */}
|
|
|
|
{/* <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">
|
|
Reward:{" "}
|
|
</span>
|
|
<span className="">{thePrice}</span>
|
|
</div>
|
|
|
|
<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="">
|
|
{props.details?.timeline_days && props.details.timeline_days}{" "}
|
|
day(s)
|
|
</span>
|
|
</div> */}
|
|
</div>
|
|
{/* end of job details */}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="w-full lg:w-1/6 grid grid-cols-1 md:grid-cols-2 lg:grid-cols-1 gap-4">
|
|
{/* TEXTAREA SECTION */}
|
|
<div className="w-full mb-3 hidden">
|
|
<div className="w-full">
|
|
<div className="pl-7 flex items-center gap-3">
|
|
<button
|
|
name="message"
|
|
onClick={(e) => setTab(e.target.name)}
|
|
className={`px-4 xl:px-1 xxl:px-4 text-sm py-1 rounded-t-2xl ${
|
|
tab == "message"
|
|
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
|
|
: "bg-white text-[#000] border-t-[2px]"
|
|
}`}
|
|
>
|
|
Send Message
|
|
</button>
|
|
<button
|
|
name="files"
|
|
onClick={(e) => setTab(e.target.name)}
|
|
className={`px-4 xl:px-1 xxl:px-4 text-sm py-1 rounded-t-2xl ${
|
|
tab == "files"
|
|
? "bg-[#4687ba] border-[2px] border-[#4687ba] text-white"
|
|
: "bg-white text-[#000] border-t-[2px]"
|
|
}`}
|
|
>
|
|
Send Files
|
|
</button>
|
|
</div>
|
|
{tab == "message" ? (
|
|
<textarea
|
|
className="p-4 w-full h-[150px] text-base text-slate-600 dark:text-white bg-white dark:bg-black border-4 border-[#4687ba] outline-none"
|
|
// rows="10"
|
|
style={{ resize: "none" }}
|
|
name="message"
|
|
onChange={handleMessageChange}
|
|
value={messageToSend}
|
|
autoFocus
|
|
/>
|
|
) : (
|
|
<div className="p-4 mb-2 h-[150px] text-base text-slate-600 border-4 border-[#4687ba]">
|
|
<div className="files flex">
|
|
<label
|
|
htmlFor="file"
|
|
className="custom-btn btn-gradient text-base text-white"
|
|
>
|
|
Select Files to Upload
|
|
</label>
|
|
<input
|
|
type="file"
|
|
id="file"
|
|
accept="image/*"
|
|
style={{ display: "none" }}
|
|
onChange={handleFileChange}
|
|
/>
|
|
</div>
|
|
<div className="selected_file my-2 overflow-y-auto">
|
|
{filesToSend.length > 0 &&
|
|
filesToSend.map((item, index) => (
|
|
<p key={index} className="flex items-center space-x-2">
|
|
<span>{item.name}</span>
|
|
<button
|
|
name="remove"
|
|
onClick={() => handleRemoveImage(item)}
|
|
className="px-2 flex justify-center items-center rounded-full border border-red-500 text-red-500"
|
|
>
|
|
x
|
|
</button>
|
|
</p>
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* ERROR DISPLAY AND SUBMIT BUTTON */}
|
|
<div className="w-full">
|
|
{/* error or success display */}
|
|
{requestStatus.message != "" &&
|
|
(!requestStatus.status ? (
|
|
<div
|
|
className={`relative p-4 my-4 text-[#912741] bg-[#fcd9e2] border-[#fbc6d3] mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
|
>
|
|
{requestStatus.message}
|
|
</div>
|
|
) : (
|
|
requestStatus.status && (
|
|
<div
|
|
className={`relative p-4 my-4 text-green-700 bg-slate-200 border-slate-800 mb-4 rounded-[0.475rem] text-md font-light leading-[19.5px] text-[13px]`}
|
|
>
|
|
{requestStatus.message}
|
|
</div>
|
|
)
|
|
))}
|
|
</div>
|
|
{/* End of error or success display */}
|
|
|
|
{/* Buttons Sections */}
|
|
<div className="py-1 grid grid-cols-1 xxs:grid-cols-3">
|
|
<div className="w-full col-span-3 col-start-1 xxs:col-span-2 xxs:col-start-2 flex justify-between items-center gap-4">
|
|
<button
|
|
type="button"
|
|
onClick={handleClearAll}
|
|
className="custom-btn border-gradient"
|
|
>
|
|
<span className="text-gradient">Clear</span>
|
|
</button>
|
|
{tab == "files" ? (
|
|
<button
|
|
onClick={sendFile}
|
|
type="button"
|
|
className="custom-btn btn-gradient text-white"
|
|
>
|
|
{requestStatus.loading ? (
|
|
<LoadingSpinner size="6" color="sky-blue" />
|
|
) : (
|
|
<>
|
|
<svg
|
|
xmlns="http://www.w3.org/2000/svg"
|
|
viewBox="0 0 24 24"
|
|
width="20"
|
|
height="20"
|
|
fill="white"
|
|
>
|
|
<path d="M12 2L2 12h3v8h14v-8h3L12 2zm0 16v-6h-2v6H7l5-5 5 5h-3z" />
|
|
</svg>
|
|
|
|
<span className="text-white">Upload Files</span>
|
|
</>
|
|
)}
|
|
</button>
|
|
) : (
|
|
<button
|
|
onClick={sendTaskMessage}
|
|
type="button"
|
|
className="custom-btn btn-gradient text-white"
|
|
>
|
|
{requestStatus.loading ? (
|
|
<LoadingSpinner size="6" color="sky-blue" />
|
|
) : (
|
|
<span className="text-white">Send</span>
|
|
)}
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{/* end of Buttons Sections */}
|
|
</div>
|
|
{/* END OF TEXTAREA */}
|
|
|
|
{/* MESSAGE SECTION */}
|
|
<div className="w-full p-4 bg-white dark:bg-black rounded-2xl shadow-md h-[400px]">
|
|
<div className='w-full h-full flex justify-center items-center text-5xl'>COMING SOON</div>
|
|
{/* <div className="flex justify-between items-center gap-5">
|
|
<p className="w-full text-lg font-bold text-dark-gray dark:text-white tracking-wide flex items-center gap-2 justify-between">
|
|
<span>Message</span>
|
|
<button
|
|
type="button"
|
|
onClick={popUpHandler}
|
|
className="text-[12px] tracking-wider text-gray-400 dark:text-slate-400"
|
|
>
|
|
View all
|
|
</button>
|
|
</p>
|
|
</div>
|
|
{props.activeJobMesList.loading ? (
|
|
<LoadingSpinner size="16" color="sky-blue" />
|
|
) : (
|
|
<ActiveJobMessageMedia activeJobMesList={props.activeJobMesList} />
|
|
)} */}
|
|
</div>
|
|
{/* END OF MESSAGE */}
|
|
</div>
|
|
</div>
|
|
</MediaLayout>
|
|
)
|
|
}
|
|
|
|
|
|
|
|
let Fallback = () => {
|
|
return (
|
|
<div className="w-full flex justify-center items-center">
|
|
<LoadingSpinner size='20' color='skyblue' height='h-screen max-h-[600px]' />
|
|
</div>
|
|
)
|
|
} |