Compare commits

...

16 Commits

Author SHA1 Message Date
victorAnumudu 29cec58122 login box adjustment 2024-01-25 21:54:31 +01:00
ameye d258158a13 Merge branch 'bg-change' of WrenchBoard/Users-Wrench into master 2024-01-25 18:01:41 +00:00
victorAnumudu 1c47fa283a Merge branch 'master' into bg-change 2024-01-25 18:57:08 +01:00
victorAnumudu d1b07e6e66 added background based on country 2024-01-25 18:55:51 +01:00
ameye 648f228e45 Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-25 15:50:17 +00:00
Ebube 551a302ede added the amount checker 2024-01-25 16:39:13 +01:00
ameye 108c82b2f8 Merge branch 'family-type' of WrenchBoard/Users-Wrench into master 2024-01-25 12:44:16 +00:00
victorAnumudu f46713ef00 Merge branch 'master' into family-type 2024-01-25 08:17:59 +01:00
victorAnumudu 992993b710 family type dropdown added 2024-01-25 08:16:55 +01:00
ameye 617df4200e Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-24 21:57:28 +00:00
Ebube a47e398e87 Added background color to the group list component 2024-01-24 20:41:25 +01:00
ameye 281c4c7ab7 Merge branch 'relative-list' of WrenchBoard/Users-Wrench into master 2024-01-24 13:12:48 +00:00
victorAnumudu 3c901e5d0d fixed relative list 2024-01-24 13:43:12 +01:00
ameye 3bcbaae4c3 Merge branch 'add-relative' of WrenchBoard/Users-Wrench into master 2024-01-24 12:19:30 +00:00
ameye 17e972d603 Merge branch 'Jobs-Manager-Side' of WrenchBoard/Users-Wrench into master 2024-01-24 12:19:23 +00:00
Ebube 0e4b1af1ce Jobs Manager Side task done 2024-01-24 01:11:49 +01:00
26 changed files with 801 additions and 559 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

+16 -8
View File
@@ -1,20 +1,28 @@
import React, { lazy } from "react"; import React, { lazy, useContext } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { localImgLoad } from "../../lib"; import { localImgLoad } from "../../lib";
import DarkModeContext from "../Contexts/DarkModeContext";
export default function LoginLayout({ slogan, children }) { export default function LoginLayout({ slogan, children }) {
const bgImg = localImgLoad('images/left-wrenchboard.jpg') const bgImg = localImgLoad('images/left-wrenchboard.jpg')
const bgImgNig = localImgLoad('images/wrench-home-back-nigeria.jpg')
const bgImgCom = localImgLoad('images/wrench-home-back-common.jpg')
const {countryMode} = useContext(DarkModeContext);
return ( return (
<div className={`layout-wrapper login`}> <div className={`h-screen overflow-y-auto bg-cover bg-center`}
<div className={`w-full min-h-screen overflow-y-auto lg:grid grid-cols-2`}> style={{backgroundImage: `url(${ countryMode == 'NG' ? bgImgNig : bgImgCom})`}}
<div >
<div className={`w-full grid grid-cols-1 lg:grid-cols-2`}>
{/* <div
className={`auth-bg hidden lg:block bg-blue-50 relative bg-cover bg-no-repeat border-0 after:content-[''] after:absolute after:inset-0`} className={`auth-bg hidden lg:block bg-blue-50 relative bg-cover bg-no-repeat border-0 after:content-[''] after:absolute after:inset-0`}
style={{backgroundImage: `url(${bgImg})`}} style={{backgroundImage: `url(${bgImg})`}}
> >
</div> </div> */}
<div className="p-5 sm:p-7 flex place-content-center"> <div className="p-5 sm:p-7 flex place-content-center lg:col-start-2">
<div className="py-10 w-11/12 h-full flex flex-col justify-between items-center content-wrapper login shadow-md xl:bg-white dark:bg-dark-white rounded-[0.475rem]"> <div className="py-10 w-full sm:w-11/12 max-w-2xl shadow-md bg-slate-50 dark:bg-dark-white rounded-[0.475rem]">
<div className="w-full flex justify-center items-center"> <div className="w-full flex justify-center items-center">
{children && children} {children && children}
</div> </div>
@@ -48,7 +56,7 @@ export default function LoginLayout({ slogan, children }) {
</div> </div>
</div> </div>
<p className="py-1 text-black text-[15px] px-2 font-medium flex items-center gap-1"> <p className="py-1 text-black text-[15px] px-2 font-medium flex items-center gap-1">
<span className="">&copy; {new Date().getFullYear()} -</span> <span className="dark:text-white">&copy; {new Date().getFullYear()} -</span>
<Link to="/" className="text-[#009ef7] ml-1"> <Link to="/" className="text-[#009ef7] ml-1">
WrenchBoard WrenchBoard
</Link>{" "} </Link>{" "}
@@ -99,7 +99,7 @@ export default function ForgotPassword() {
</Link> </Link>
</div> </div>
<div className="flex place-content-center"> <div className="flex place-content-center">
<div className="w-11/12 sm:max-w-[400px]"> <div className="w-10/12">
{msgSuccess == null ? {msgSuccess == null ?
<> <>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
+1 -1
View File
@@ -264,7 +264,7 @@ export default function Login() {
</div> </div>
{/* <div className="content-wrapper login shadow-md w-10/12 mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5"> */} {/* <div className="content-wrapper login shadow-md w-10/12 mx-auto flex justify-center items-center xl:bg-white dark:bg-dark-white 2xl:w-[828px] rounded-[0.475rem] sm:p-7 p-5"> */}
<div className="flex place-content-center"> <div className="flex place-content-center">
<div className="w-11/12 sm:max-w-[530px]"> <div className="w-10/12">
{/* HIDES THIS IF USER SESSION HAS EXPIRED */} {/* HIDES THIS IF USER SESSION HAS EXPIRED */}
{sessionExpired != "true" && ( {sessionExpired != "true" && (
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
+1 -1
View File
@@ -158,7 +158,7 @@ export default function SignUp() {
</Link> </Link>
</div> </div>
<div className="flex place-content-center"> <div className="flex place-content-center">
<div className="w-11/12 sm:max-w-[530px]"> <div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]"> <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Create Account Create Account
@@ -19,7 +19,7 @@ export default function UpdatePassword() {
<AuthLayout slogan="Welcome to myFit"> <AuthLayout slogan="Welcome to myFit">
{updated === false ? ( {updated === false ? (
<div className="flex place-content-center"> <div className="flex place-content-center">
<div className="w-11/12 sm:max-w-[600px]"> <div className="w-10/12">
<div className="title-area relative flex flex-col justify-center items-center mb-7"> <div className="title-area relative flex flex-col justify-center items-center mb-7">
<h1 className="sm:text-5xl text-4xl font-bold leading-[74px] text-dark-gray dark:text-white"> <h1 className="sm:text-5xl text-4xl font-bold leading-[74px] text-dark-gray dark:text-white">
Update Password Update Password
@@ -132,7 +132,7 @@ export default function VerifyLink() {
</Link> </Link>
</div> </div>
<div className="flex place-content-center"> <div className="flex place-content-center">
<div className="w-11/12 sm:max-w-[500px]"> <div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]"> <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
{linkSuccess {linkSuccess
@@ -152,7 +152,7 @@ const VerifyPassword = () => {
{requestStatus.loading ? ( {requestStatus.loading ? (
<LoadingSpinner color="sky-blue" size="16" height="h-300px" /> <LoadingSpinner color="sky-blue" size="16" height="h-300px" />
) : !requestStatus.loading && requestStatus.status ? ( ) : !requestStatus.loading && requestStatus.status ? (
<div className="w-11/12 sm:max-w-[500px]"> <div className="w-10/12">
{linkSuccess == null ? ( {linkSuccess == null ? (
<> <>
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
@@ -18,7 +18,7 @@ export default function VerifyYou() {
</Link> </Link>
</div> </div>
<div className="flex place-content-center"> <div className="flex place-content-center">
<div className="w-11/12 sm:max-w-[500px]"> <div className="w-10/12">
<div className="title-area flex flex-col justify-center items-center relative text-center mb-7"> <div className="title-area flex flex-col justify-center items-center relative text-center mb-7">
<h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]"> <h1 className="text-[#181c32] font-semibold dark:text-white mb-3 leading-[27.3px] text-[22.75px]">
Let's verify your email now Let's verify your email now
@@ -38,7 +38,7 @@ const validationSchema = Yup.object().shape({
}; };
export default function InviteRelative({action, situation, setReloadRelList}) { export default function InviteRelative({action, situation, setReloadRelList, relativeList}) {
const api = new usersService() const api = new usersService()
@@ -49,8 +49,6 @@ export default function InviteRelative({action, situation, setReloadRelList}) {
}); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER }); // STATE FOR KNOWING WHEN A REQUEST IS MADE TO THE SERVER
const handleInvite = (values) => { const handleInvite = (values) => {
// delete values.firstname
// delete values.lasttname
setRequestStatus({loading: true, status: false, message: ""}) setRequestStatus({loading: true, status: false, message: ""})
api.inviteFamilyRelative(values).then(response => { api.inviteFamilyRelative(values).then(response => {
let {status, data} = response let {status, data} = response
@@ -169,7 +167,7 @@ export default function InviteRelative({action, situation, setReloadRelList}) {
{/* Type */} {/* Type */}
<div className="md:flex md:space-x-7 items-end mb-6"> <div className="md:flex md:space-x-7 items-end mb-6">
<div className="field w-full mb-6 md:mb-0"> {/* <div className="field w-full mb-6 md:mb-0">
<InputCom <InputCom
fieldClass="px-6" fieldClass="px-6"
label="Type" label="Type"
@@ -181,6 +179,52 @@ export default function InviteRelative({action, situation, setReloadRelList}) {
blurHandler={props.handleBlur} blurHandler={props.handleBlur}
error={(props.errors.family_type && props.touched.family_type) ? props.errors.family_type : '' } error={(props.errors.family_type && props.touched.family_type) ? props.errors.family_type : '' }
/> />
</div> */}
<div className="field w-full mb-6 xl:mb-0">
<label
htmlFor="family_type"
className="input-label text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold flex item-center gap-1"
>
Family Type
{props.errors.family_type && props.touched.family_type && (
<span className="text-[12px] text-red-500">
{props.errors.family_type}
</span>
)}
</label>
<select
id="family_type"
name="family_type"
value={props.values.family_type}
className={`input-field p-2 mt-3 rounded-full 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}
>
{relativeList?.loading ? (
<option className="text-slate-500 text-lg" value="">
Loading...
</option>
) : relativeList?.family_types?.length ? (
<>
<option className="text-slate-500 text-lg" value="">
Select Family Type
</option>
{relativeList?.family_types?.map((item, index) => (
<option
key={index}
className="text-slate-500 text-lg"
value={item?.ty}
>
{item?.ty}
</option>
))}
</>
) : (
<option className="text-slate-500 text-lg" value="">
No Options Found! Try Again
</option>
)}
</select>
</div> </div>
<div className="field w-full flex justify-end"> <div className="field w-full flex justify-end">
<div className="flex"> <div className="flex">
@@ -24,21 +24,22 @@ export default function RelativeTable({relativeList}) {
{relativeList && relativeList?.length > 0 ? ( {relativeList && relativeList?.length > 0 ? (
currentRelativeList.map((value, index) => ( currentRelativeList.map((value, index) => (
<tr key={value.uid || index} className="border-b dark:border-[#5356fb29] hover:bg-gray-50"> <tr key={value.uid || index} className="border-b dark:border-[#5356fb29] hover:bg-gray-50">
<td className='py-2'> <td className='p-2'>
<div className="flex flex-col"> <div className="flex flex-col">
<h1 className="font-bold text-xl text-dark-gray dark:text-white"> <h1 className="font-bold text-xl text-dark-gray dark:text-white">
{/* {value.firstname && value.firstname} {value.lastname && value.lastname} */} Firstname Lastname {value.firstname && value.firstname} {value.lastname && value.lastname}
</h1> </h1>
<span className="text-sm text-thin-light-gray"> <span className="text-sm text-thin-light-gray">
{/* {value.email && value.email} */} email.gmail.com {value.email && value.email}
</span> </span>
</div> </div>
</td> </td>
<td className='py-2'> <td className='p-2'>
family type {/* <span>Family Type</span> */}
<span>{value.family_type && value.family_type.toUpperCase()}</span>
</td> </td>
<td className='py-2 text-right'> <td className='p-2 text-right'>
{0} {value.status && value.status}
</td> </td>
</tr> </tr>
)) ))
@@ -12,7 +12,7 @@ const Relatives = () => {
const [invitePopout, setInvitePopout] = useState(false) const [invitePopout, setInvitePopout] = useState(false)
const [relativeList, setRelativeList] = useState({loading: true, data:[]}) const [relativeList, setRelativeList] = useState({loading: true, result_list:[], family_types:[]})
const showInviteMemberPopout = () => { const showInviteMemberPopout = () => {
setInvitePopout(true) setInvitePopout(true)
@@ -21,10 +21,14 @@ const Relatives = () => {
const getRelativeList = () => { const getRelativeList = () => {
setRelativeList(prev => ({...prev, loading: true})) setRelativeList(prev => ({...prev, loading: true}))
api.getFamilyRelativeList().then(response => { api.getFamilyRelativeList().then(response => {
setRelativeList({loading:false, data:response.data.family_types}) let {status, data} = response
// console.log('RESPONSE',response.data) if(status != 200 || !data){
setRelativeList({loading:false, result_list:[], family_types:[]})
return
}
setRelativeList({loading:false, result_list:data?.result_list, family_types:data?.family_types})
}).catch(error => { }).catch(error => {
setRelativeList({loading:false, data:[]}) setRelativeList({loading:false, result_list:[], family_types:[]})
}) })
} }
@@ -43,7 +47,7 @@ const Relatives = () => {
{relativeList.loading ? {relativeList.loading ?
<LoadingSpinner size='8' height='h-full' /> <LoadingSpinner size='8' height='h-full' />
: :
<RelativeTable relativeList={relativeList.data} /> <RelativeTable relativeList={relativeList.result_list} />
} }
</div> </div>
</div> </div>
@@ -54,6 +58,7 @@ const Relatives = () => {
action={()=>setInvitePopout(false)} action={()=>setInvitePopout(false)}
situation={invitePopout} situation={invitePopout}
setReloadRelList={setReloadRelList} setReloadRelList={setReloadRelList}
relativeList={relativeList}
/> />
} }
{/* END OF INVITE RELATIVE POPOUT */} {/* END OF INVITE RELATIVE POPOUT */}
+1 -1
View File
@@ -73,7 +73,7 @@ export default function DeleteGroup({action, situation, details}) {
</div> </div>
<div className="mb-6"> <div className="mb-6">
<p className="text-xl text-center tracking-wide text-dark-gray dark:text-white"> <p className="text-xl text-center tracking-wide text-dark-gray dark:text-white">
Are you sure, you want to delete <br /> <span>'{details?.group_name}'</span> Are you sure, you want to delete <br /> <span>'{details?.group_name}'</span> group?
</p> </p>
</div> </div>
<div className="flex space-x-2.5"> <div className="flex space-x-2.5">
+3 -3
View File
@@ -23,7 +23,7 @@ export default function DeleteMember({action, situation, details}) {
<div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl"> <div className="logout-modal-wrapper lg:w-[500px] h-full lg:h-auto bg-white dark:bg-dark-white lg:rounded-2xl">
<div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] "> <div className="logout-modal-header w-full flex items-center justify-between lg:px-10 lg:py-8 px-[30px] py-[23px] border-b border-light-purple dark:border-[#5356fb29] ">
<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">
Delete Member Remove Member
</h1> </h1>
<button <button
type="button" type="button"
@@ -73,7 +73,7 @@ export default function DeleteMember({action, situation, details}) {
</div> </div>
<div className="mb-6"> <div className="mb-6">
<p className="text-xl text-center tracking-wide text-dark-gray dark:text-white"> <p className="text-xl text-center tracking-wide text-dark-gray dark:text-white">
Are you sure, you want to delete <br /> <span>'{details?.firstname} {details.lastname}'</span> Are you sure, you want to remove <br /> <span>'{details?.firstname} {details.lastname}'</span>
</p> </p>
</div> </div>
<div className="flex space-x-2.5"> <div className="flex space-x-2.5">
@@ -92,7 +92,7 @@ export default function DeleteMember({action, situation, details}) {
type="button" type="button"
className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full" className="text-white primary-gradient text-18 tracking-wide px-4 py-3 rounded-full"
> >
Confirm Delete Remove
</button> </button>
)} )}
</div> </div>
+92 -60
View File
@@ -1,72 +1,104 @@
import React, { useState } from 'react' import React, { useState } from "react";
import InputCom from '../../components/Helpers/Inputs/InputCom/index' import AddGroup from "./AddGroup";
import DeleteGroup from './DeleteGroup' import DeleteGroup from "./DeleteGroup";
import AddGroup from './AddGroup' import { localImgLoad } from "../../lib";
export default function GroupList({groupList, selectedGroup, changeSelectedGroup, setUpdateList}) { export default function GroupList({
groupList,
selectedGroup,
changeSelectedGroup,
setUpdateList,
}) {
const [deletePopout, setDeletePopout] = useState({
status: false,
data: {},
});
const [deletePopout, setDeletePopout] = useState({ const [addGroupPopout, setAddGroupPopout] = useState(false);
status: false,
data: {}
})
const [addGroupPopout, setAddGroupPopout] = useState(false) const handleAddGroup = () => {
setAddGroupPopout(true);
const handleAddGroup = () => { };
setAddGroupPopout(true)
}
const handleDeleteGroup = (item) => {
setDeletePopout({
status: true,
data: {...item}
})
}
const handleDeleteGroup = (item) => {
setDeletePopout({
status: true,
data: { ...item },
});
};
return ( return (
<> <>
<div className='p-5 w-full lg:w-[400px] min-h-[300px] bg-sky-100 dark:bg-dark-gray rounded-2xl'> <div className="p-5 w-full lg:w-[400px] min-h-[300px] bg-sky-100 dark:bg-dark-gray rounded-2xl flex flex-col">
{/* <h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>Jobs Groups</h1> */} {/* <h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>Jobs Groups</h1> */}
<div className='flex justify-end items-center'> <div className="flex justify-end items-center">
<button onClick={handleAddGroup} className='py-2 px-4 flex justify-center items-center bg-sky-blue hover:bg-sky-600 text-base rounded-full text-white font-bold'>Add Group</button> <button
</div> onClick={handleAddGroup}
className="py-2 px-4 flex justify-center items-center bg-sky-blue hover:bg-sky-600 text-base rounded-full text-white font-bold"
{groupList && groupList.length < 1 ? >
<h1 className='my-5 text-lg tracking-wide text-slate-900 dark:text-slate-100'>No Group Found!</h1> Add Group
: </button>
<div className='my-4 max-h-[400px] overflow-y-auto'>
<div className='flex flex-col'>
{groupList.map(item=> (
<div key={item.group_uid} className='p-2 flex gap-2 items-center justify-between w-full'>
<div className='flex gap-2 items-center'>
<input type='radio' name='grouplist' value={item.group_id} checked={selectedGroup?.id == item?.group_id} onChange={changeSelectedGroup} className='w-[20px] h-[20px] outline-none' />
<p className='text-sm lg:text-base text-slate-900 dark:text-slate-100'>{item.group_name}</p>
</div>
<button onClick={()=>{handleDeleteGroup(item)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5 flex justify-center items-center'>X</button>
</div>
))}
</div>
</div>
}
</div> </div>
{deletePopout.status &&
<DeleteGroup
action={()=>setDeletePopout({status:false, data:{}})}
situation={deletePopout.status}
details={deletePopout.data}
/>
}
{addGroupPopout && {groupList && groupList.length < 1 ? (
<AddGroup <h1 className="my-5 text-lg tracking-wide text-slate-900 dark:text-slate-100">
action={()=>setAddGroupPopout(false)} No Group Found!
situation={addGroupPopout} </h1>
setUpdateList={setUpdateList} ) : (
/> <div className="my-4 max-h-[596px] bg-[#fffef6] rounded overflow-y-auto flex-1">
} <div className="flex flex-col">
{groupList.map((item) => (
<div
key={item.group_uid}
className="p-2 flex gap-2 items-center justify-between w-full"
>
<div className="flex gap-2 items-center">
<input
type="radio"
name="grouplist"
value={item.group_id}
checked={selectedGroup?.id == item?.group_id}
onChange={changeSelectedGroup}
className="w-[20px] h-[20px] outline-none"
/>
<p className="text-sm lg:text-base text-slate-900 dark:text-slate-100">
{item.group_name}
</p>
</div>
<button
onClick={() => {
handleDeleteGroup(item);
}}
className="flex relative items-center justify-center border-0 w-6 h-6"
>
<img
src={localImgLoad("images/icons/remove_grp.svg")}
alt="remove-icon"
/>
</button>
</div>
))}
</div>
</div>
)}
</div>
{deletePopout.status && (
<DeleteGroup
action={() => setDeletePopout({ status: false, data: {} })}
situation={deletePopout.status}
details={deletePopout.data}
/>
)}
{addGroupPopout && (
<AddGroup
action={() => setAddGroupPopout(false)}
situation={addGroupPopout}
setUpdateList={setUpdateList}
/>
)}
</> </>
) );
} }
+109 -89
View File
@@ -1,98 +1,118 @@
import React, { useState } from 'react' import React, { useState } from "react";
import { handlePagingFunc } from '../Pagination/HandlePagination'; import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from '../Pagination/PaginatedList'; import PaginatedList from "../Pagination/PaginatedList";
import DeleteMember from './DeleteMember'; import DeleteMember from "./DeleteMember";
import { localImgLoad } from "../../lib";
export default function GroupMemberTable({selectedList}) { export default function GroupMemberTable({ selectedList }) {
// Handle Pagination
// Handle Pagination const [currentPage, setCurrentPage] = useState(0);
const [currentPage, setCurrentPage] = useState(0); const indexOfFirstItem = Number(currentPage);
const indexOfFirstItem = Number(currentPage); const indexOfLastItem =
const indexOfLastItem =Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE); Number(indexOfFirstItem) + Number(process.env.REACT_APP_ITEM_PER_PAGE);
const currentSelectedList = selectedList?.slice(indexOfFirstItem, indexOfLastItem); const currentSelectedList = selectedList?.slice(
indexOfFirstItem,
const handlePagination = (e) => { indexOfLastItem
handlePagingFunc(e, setCurrentPage); );
};
const [deletePopout, setDeletePopout] = useState({ const handlePagination = (e) => {
status: false, handlePagingFunc(e, setCurrentPage);
data: {} };
})
const handleDeleteMember = (item) => { const [deletePopout, setDeletePopout] = useState({
setDeletePopout({ status: false,
status: true, data: {},
data: {...item} });
})
} const handleDeleteMember = (item) => {
setDeletePopout({
return ( status: true,
<div className={`w-full p-8 bg-white dark:bg-dark-gray overflow-hidden rounded-2xl section-shadow`}> data: { ...item },
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[400px]"> });
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400"> };
<tbody>
<> return (
{selectedList && selectedList?.length > 0 ? ( <div
currentSelectedList.map((value, index) => ( className={`w-full p-8 dark:bg-dark-gray overflow-hidden rounded-2xl section-shado bg-[#fffef6]`}
<tr key={value.uid} className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"> >
{/* <td className="p-1">{value?.firstname}</td> <div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[400px]">
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<>
{selectedList && selectedList?.length > 0 ? (
currentSelectedList.map((value, index) => (
<tr
key={value.uid}
className="bg-white dark:bg-dark-white border-b dark:border-[#5356fb29] hover:bg-gray-50"
>
{/* <td className="p-1">{value?.firstname}</td>
<td className="p-1">{value?.lastname}</td> <td className="p-1">{value?.lastname}</td>
<td className="p-1">{value?.email}</td> <td className="p-1">{value?.email}</td>
<td className="p-1 text-right"> <td className="p-1 text-right">
<button onClick={()=>{handleDeleteMember(value)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5'>X</button> <button onClick={()=>{handleDeleteMember(value)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5'>X</button>
</td> */} </td> */}
<td className='py-2'> <td className="py-2">
<div className="flex flex-col"> <div className="flex flex-col">
<h1 className="font-bold text-xl text-dark-gray dark:text-white"> <h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.firstname && value.firstname} {value.lastname && value.lastname} {value.firstname && value.firstname}{" "}
</h1> {value.lastname && value.lastname}
<span className="text-sm text-thin-light-gray"> </h1>
{value.email && value.email} <span className="text-sm text-thin-light-gray">
</span> {value.email && value.email}
</div> </span>
</td> </div>
<td className='py-2 text-right'> </td>
<button onClick={()=>{handleDeleteMember(value)}} className='rounded-lg text-sm bg-red-500 hover:bg-red-400 text-white font-bold py-1 px-2.5'>X</button> <td className="py-2 text-right">
</td> <button
</tr> onClick={() => {
)) handleDeleteMember(value);
) : ( }}
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap"> className="flex relative items-center justify-center border-0 w-8 h-8"
<td className="p-2">No Members Found</td> >
</tr> <img
)} src={localImgLoad("images/icons/remove_grp.svg")}
</> alt="remove-icon"
</tbody> />
</table> </button>
</td>
{/* PAGINATION BUTTON */} </tr>
<PaginatedList ))
onClick={handlePagination} ) : (
prev={currentPage == 0 ? true : false} <tr className="font-semibold text-xl text-dark-gray dark:text-white whitespace-nowrap">
next={ <td className="p-2">No Members Found. Please add</td>
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >= </tr>
selectedList?.length )}
? true </>
: false </tbody>
} </table>
data={selectedList}
start={indexOfFirstItem} {/* PAGINATION BUTTON */}
stop={indexOfLastItem} <PaginatedList
/> onClick={handlePagination}
{/* END OF PAGINATION BUTTON */} prev={currentPage == 0 ? true : false}
</div> next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
{/* DELETE MEMBER POPOUT */} selectedList?.length
{deletePopout.status && ? true
<DeleteMember : false
action={()=>setDeletePopout({status:false, data:{}})} }
situation={deletePopout.status} data={selectedList}
details={deletePopout.data} start={indexOfFirstItem}
/> stop={indexOfLastItem}
} />
{/* END OF DELETE MEMBER POPOUT */} {/* END OF PAGINATION BUTTON */}
</div> </div>
);
}; {/* DELETE MEMBER POPOUT */}
{deletePopout.status && (
<DeleteMember
action={() => setDeletePopout({ status: false, data: {} })}
situation={deletePopout.status}
details={deletePopout.data}
/>
)}
{/* END OF DELETE MEMBER POPOUT */}
</div>
);
}
+105 -65
View File
@@ -1,79 +1,119 @@
import React, { useEffect, useState } from 'react' import React, { useEffect, useState } from "react";
import Layout from '../Partials/Layout' import Layout from "../Partials/Layout";
import GroupList from './GroupList' import GroupList from "./GroupList";
import MemberList from './MemberList' import MemberList from "./MemberList";
import LoadingSpinner from '../Spinners/LoadingSpinner' import LoadingSpinner from "../Spinners/LoadingSpinner";
import usersService from '../../services/UsersService' import usersService from "../../services/UsersService";
export default function JobGroups() { export default function JobGroups() {
const userApi = new usersService();
const userApi = new usersService(); const [updateList, setUpdateList] = useState(false);
const [updateList, setUpdateList] = useState(false) const [groupList, setGroupList] = useState({
loading: true,
groups: [],
members: [],
});
const [selectedGroup, setSelectedGroup] = useState({
const [groupList, setGroupList] = useState({ id: "",
loading:true, name: "",
groups: [], data: [],
members: [] });
})
const [selectedGroup, setSelectedGroup] = useState({id:'', name:'', data: []}) const changeSelectedGroup = (e) => {
let groupID = e.target.value;
const activeMembers = groupList?.members?.filter(
(item) => item.group_id == groupID
);
const activeGroup = groupList?.groups?.filter(
(item) => item.group_id == groupID
);
setSelectedGroup({
id: groupID,
name: activeGroup[0]?.group_name,
data: activeMembers,
});
};
const changeSelectedGroup = (e) => { useEffect(() => {
let groupID = e.target.value setGroupList({ loading: true, groups: [], members: [] });
const activeMembers = groupList?.members?.filter(item => item.group_id == groupID) userApi
const activeGroup = groupList?.groups?.filter(item => item.group_id == groupID) .jobGroupList({})
setSelectedGroup({id: groupID, name:activeGroup[0]?.group_name, data:activeMembers}) .then((res) => {
} const { status, data } = res;
if (status != 200 || data?.internal_return < 0) {
useEffect(()=>{ setGroupList({ loading: false, groups: [], members: [] });
setGroupList({loading: true, groups: [], members: []}) return;
userApi.jobGroupList({}).then(res => { }
const {status, data} = res if (data.result_list.length < 0) {
if(status != 200 || data?.internal_return < 0){ setGroupList({ loading: false, groups: [], members: [] });
setGroupList({loading: false, groups: [], members: []}) return;
return }
} setGroupList({
if(data.result_list.length < 0){ loading: false,
setGroupList({loading: false, groups: [], members: []}) groups: data.result_list,
return members: data.result_list_member,
} });
setGroupList({loading: false, groups: data.result_list, members: data.result_list_member}) if (selectedGroup.id == "") {
if(selectedGroup.id == ''){ let activeGroupId = data.result_list[0].group_id;
let activeGroupId = data.result_list[0].group_id let activeGroup = data.result_list[0].group_name;
let activeGroup = data.result_list[0].group_name let activeMembers = data.result_list_member?.filter(
let activeMembers = data.result_list_member?.filter(item => item.group_id == activeGroupId) (item) => item.group_id == activeGroupId
setSelectedGroup({id: activeGroupId, name:activeGroup, data:activeMembers}) );
}else{ setSelectedGroup({
let activeMembers = data.result_list_member?.filter(item => item.group_id == selectedGroup?.id) id: activeGroupId,
setSelectedGroup({id: selectedGroup?.id, name:selectedGroup?.name, data:activeMembers}) name: activeGroup,
} data: activeMembers,
}).catch(error => { });
setGroupList({loading: false, groups: [], members: []}) } else {
console.log(error) let activeMembers = data.result_list_member?.filter(
}) (item) => item.group_id == selectedGroup?.id
},[updateList]) );
setSelectedGroup({
id: selectedGroup?.id,
name: selectedGroup?.name,
data: activeMembers,
});
}
})
.catch((error) => {
setGroupList({ loading: false, groups: [], members: [] });
console.log(error);
});
}, [updateList]);
return ( return (
<Layout> <Layout>
<div> <div>
<h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>Jobs Groups</h1> <h1 className="mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100">
</div> Job Groups
<div className='p-5 w-full min-h-[400px] flex flex-col lg:flex-row gap-3 lg:gap-6 rounded-lg shadow-md bg-white dark:bg-dark-white'> </h1>
{groupList.loading ? </div>
<div className='w-full h-[400px] flex justify-center items-center'> <div className="p-5 w-full min-h-[400px] flex flex-col lg:flex-row gap-3 lg:gap-6 rounded-lg shadow-md bg-white dark:bg-dark-white">
<LoadingSpinner size='16' /> {groupList.loading ? (
</div> <div className="w-full h-[400px] flex justify-center items-center">
: <LoadingSpinner size="16" />
<> </div>
<GroupList groupList={groupList?.groups} selectedGroup={selectedGroup} changeSelectedGroup={changeSelectedGroup} setUpdateList={setUpdateList} /> ) : (
<MemberList groupList={groupList?.groups} selectedGroup={selectedGroup} setUpdateList={setUpdateList} /> <>
</> <GroupList
} groupList={groupList?.groups}
</div> selectedGroup={selectedGroup}
changeSelectedGroup={changeSelectedGroup}
setUpdateList={setUpdateList}
/>
<MemberList
groupList={groupList?.groups}
selectedGroup={selectedGroup}
setUpdateList={setUpdateList}
/>
</>
)}
</div>
</Layout> </Layout>
) );
} }
+2 -2
View File
@@ -6,8 +6,8 @@ import GroupMemberTable from "./GroupMemberTable";
import EmailValidator from "../../lib/EmailValidator"; import EmailValidator from "../../lib/EmailValidator";
import usersService from "../../services/UsersService";
import { apiConst } from "../../lib/apiConst"; import { apiConst } from "../../lib/apiConst";
import usersService from "../../services/UsersService";
export default function MemberList({ export default function MemberList({
groupList, groupList,
@@ -277,7 +277,7 @@ export default function MemberList({
)} )}
</div> </div>
</div> </div>
<div className="my-2 flex flex-col min-h-[300px]"> <div className="my-2 flex flex-col min-h-[300px] h-full">
{selectedGroup?.data?.length < 1 ? ( {selectedGroup?.data?.length < 1 ? (
<h1 className="my-5 text-lg lg:text-xl tracking-wide text-slate-900 dark:text-slate-100"> <h1 className="my-5 text-lg lg:text-xl tracking-wide text-slate-900 dark:text-slate-100">
No Member Found, Please Add No Member Found, Please Add
@@ -1,12 +1,10 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useLocation, useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import localImgLoad from "../../lib/localImgLoad";
import { PriceFormatter } from "../Helpers/PriceFormatter"; import { PriceFormatter } from "../Helpers/PriceFormatter";
import { handlePagingFunc } from "../Pagination/HandlePagination"; import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList"; import PaginatedList from "../Pagination/PaginatedList";
export default function MyActiveJobTable({ MyJobList, className }) { export default function MyActiveJobTable({ MyJobList, className }) {
const navigate = useNavigate(); const navigate = useNavigate();
let { pathname } = useLocation(); let { pathname } = useLocation();
@@ -45,7 +43,11 @@ export default function MyActiveJobTable({ MyJobList, className }) {
value?.currency_code, value?.currency_code,
value?.currency value?.currency
); );
let image = `${MyJobList.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}` let image = `${
MyJobList.session_image_server
}${localStorage.getItem("session_token")}/job/${
value.job_uid
}`;
return ( return (
<tr <tr
key={index} key={index}
@@ -112,7 +114,7 @@ export default function MyActiveJobTable({ MyJobList, className }) {
className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white" className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
> >
{value.owner_status == "OWNER" {value.owner_status == "OWNER"
? "Manage" ? "Review"
: "Send Updates"} : "Send Updates"}
</button> </button>
</div> </div>
+57 -58
View File
@@ -1,19 +1,17 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { useCallback, useEffect, useMemo, useState } from "react";
import dataImage2 from "../../assets/images/data-table-user-2.png";
import SelectBox from "../Helpers/SelectBox";
import DeleteJobPopout from "../jobPopout/DeleteJobPopout";
import JobListPopout from "../jobPopout/JobListPopout";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import usersService from "../../services/UsersService"; import usersService from "../../services/UsersService";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import SelectBox from "../Helpers/SelectBox";
import { handlePagingFunc } from "../Pagination/HandlePagination"; import { handlePagingFunc } from "../Pagination/HandlePagination";
import PaginatedList from "../Pagination/PaginatedList"; import PaginatedList from "../Pagination/PaginatedList";
import LoadingSpinner from "../Spinners/LoadingSpinner";
import DeleteJobPopout from "../jobPopout/DeleteJobPopout";
import EditJobPopOut from "../jobPopout/EditJobPopout"; import EditJobPopOut from "../jobPopout/EditJobPopout";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import EditIcon from "../../assets/images/icon-edit.svg";
import DeleteIcon from "../../assets/images/icon-delete.svg"; import DeleteIcon from "../../assets/images/icon-delete.svg";
import localImgLoad from "../../lib/localImgLoad"; import EditIcon from "../../assets/images/icon-edit.svg";
import JobListPopout from "../jobPopout/JobListPopout";
export default function MyJobTable({ MyJobList, reloadJobList, className }) { export default function MyJobTable({ MyJobList, reloadJobList, className }) {
// Getting the categories // Getting the categories
@@ -42,7 +40,6 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
// Get Country Api // Get Country Api
const getCountryList = useCallback(async () => { const getCountryList = useCallback(async () => {
try { try {
const res = await userApi.getSignupCountryData(); const res = await userApi.getSignupCountryData();
if (res.status === 200 && res.data.internal_return >= 0) { if (res.status === 200 && res.data.internal_return >= 0) {
@@ -59,7 +56,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
throw new Error(error); throw new Error(error);
} }
}, [userApi, country]); }, [userApi, country]);
console.log('MY COUNTRY', myCountry)
useEffect(() => { useEffect(() => {
getCountryList(); getCountryList();
}, [getCountryList]); }, [getCountryList]);
@@ -201,6 +198,55 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
); );
}; };
const MyJobListTable = () => (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[520px]">
<table className="table-auto min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<>
{MyJobList &&
MyJobList?.data?.result_list &&
MyJobList.data?.result_list.length > 0 ? (
filteredCurrentJobList?.length ? (
filteredCurrentJobList.map((value, index) => (
<JobListItem
index={index}
key={index}
value={value}
image_server={MyJobList?.data.session_image_server}
/>
))
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Jobs Available In This Category!</td>
</tr>
)
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Jobs Available!</td>
</tr>
)}
</>
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
currentJobList?.length
? true
: false
}
data={currentJobList}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
);
return ( return (
<div <div
className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${ className={`update-table w-full p-8 bg-white dark:bg-dark-white overflow-hidden rounded-2xl section-shadow min-h-[520px] ${
@@ -223,54 +269,7 @@ export default function MyJobTable({ MyJobList, reloadJobList, className }) {
{MyJobList?.loading ? ( {MyJobList?.loading ? (
<LoadingSpinner size="16" color="sky-blue" /> <LoadingSpinner size="16" color="sky-blue" />
) : ( ) : (
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[520px]"> <MyJobListTable />
<table className="table-auto min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
<tbody>
<>
{MyJobList &&
MyJobList?.data?.result_list &&
MyJobList.data?.result_list.length > 0 ? (
filteredCurrentJobList?.length ? (
filteredCurrentJobList.map((value, index) => (
<JobListItem
index={index}
key={index}
value={value}
image_server={MyJobList?.data.session_image_server}
/>
))
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">
No Jobs Available In This Category!
</td>
</tr>
)
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Jobs Avaliable!</td>
</tr>
)}
</>
</tbody>
</table>
{/* PAGINATION BUTTON */}
<PaginatedList
onClick={handlePagination}
prev={currentPage == 0 ? true : false}
next={
currentPage + Number(process.env.REACT_APP_ITEM_PER_PAGE) >=
currentJobList?.length
? true
: false
}
data={currentJobList}
start={indexOfFirstItem}
stop={indexOfLastItem}
/>
{/* END OF PAGINATION BUTTON */}
</div>
)} )}
{/* Job List Popout */} {/* Job List Popout */}
@@ -6,9 +6,7 @@ import PendingJobsPopout from "../jobPopout/PendingJobsPopout";
import { PriceFormatter } from "../Helpers/PriceFormatter"; import { PriceFormatter } from "../Helpers/PriceFormatter";
import localImgLoad from "../../lib/localImgLoad"; import localImgLoad from "../../lib/localImgLoad";
export default function MyPendingJobTable({ MyJobList, className }) { export default function MyPendingJobTable({ MyJobList, className }) {
let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW let [jobPopout, setJobPopout] = useState({ show: false, data: {} }); // STATE TO HOLD THE VALUE OF THE ALERT DETAILS AND DETERMINE WHEN TO SHOW
const [currentPage, setCurrentPage] = useState(0); const [currentPage, setCurrentPage] = useState(0);
@@ -19,7 +17,7 @@ export default function MyPendingJobTable({ MyJobList, className }) {
indexOfFirstItem, indexOfFirstItem,
indexOfLastItem indexOfLastItem
); );
const handlePagination = (e) => { const handlePagination = (e) => {
handlePagingFunc(e, setCurrentPage); handlePagingFunc(e, setCurrentPage);
}; };
@@ -46,7 +44,11 @@ export default function MyPendingJobTable({ MyJobList, className }) {
value?.currency_code, value?.currency_code,
value?.currency value?.currency
); );
let image = `${MyJobList.session_image_server}${localStorage.getItem('session_token')}/job/${value.job_uid}` let image = `${
MyJobList.session_image_server
}${localStorage.getItem("session_token")}/job/${
value.job_uid
}`;
return ( return (
<tr <tr
key={index} key={index}
@@ -107,7 +109,9 @@ export default function MyPendingJobTable({ MyJobList, className }) {
}} }}
className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white" className="px-4 h-11 flex justify-center items-center btn-gradient text-base rounded-full text-white"
> >
{value.owner_status == 'OWNER' ? 'Manage' : 'Send Updates'} {value.owner_status == "OWNER"
? "Manage"
: "View"}
</button> </button>
</td> </td>
</tr> </tr>
+16 -1
View File
@@ -5,6 +5,13 @@ function Default({ children }) {
// dark mode setup // dark mode setup
const [theme, setTheme] = useState(null); const [theme, setTheme] = useState(null);
// country mode setup
const [countryMode, setCountryMode] = useState(localStorage.getItem('cnt') ? localStorage.getItem('cnt')?.toUpperCase() : '')
const queryParams = new URLSearchParams(location?.search);
const country = queryParams.get("cnt")?.toUpperCase();
useEffect(() => { useEffect(() => {
if (window.matchMedia("(prefers-color-scheme: dark)").matches) { if (window.matchMedia("(prefers-color-scheme: dark)").matches) {
setTheme("dark"); setTheme("dark");
@@ -24,9 +31,17 @@ function Default({ children }) {
const handleThemeSwitch = () => { const handleThemeSwitch = () => {
setTheme(theme === "dark" ? "light" : "dark"); setTheme(theme === "dark" ? "light" : "dark");
}; };
useEffect(()=>{
if(country){
setCountryMode(country)
localStorage.setItem('cnt', country)
}
},[countryMode])
return ( return (
<> <>
<DarkModeContext.Provider value={{ theme, handleThemeSwitch }}> <DarkModeContext.Provider value={{ theme, handleThemeSwitch, countryMode }}>
{children && children} {children && children}
</DarkModeContext.Provider> </DarkModeContext.Provider>
</> </>
+1 -1
View File
@@ -24,7 +24,7 @@ export default function Layout({ children }) {
localStorage.removeItem("member_id"); localStorage.removeItem("member_id");
localStorage.removeItem("uid"); localStorage.removeItem("uid");
sessionStorage.removeItem("family_uid"); sessionStorage.removeItem("family_uid");
// localStorage.clear(); localStorage.clear();
// toast.success("Come Back Soon", { // toast.success("Come Back Soon", {
// icon: `🙂`, // icon: `🙂`,
// }); // });
+310 -239
View File
@@ -1,13 +1,14 @@
import { Field, Form, Formik } from "formik"; import { Field, Form, Formik } from "formik";
import React, { useCallback, useEffect, useMemo, useState } from "react"; import React, { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import * as Yup from "yup"; import * as Yup from "yup";
import usersService from "../../services/UsersService"; import usersService from "../../services/UsersService";
import { tableReload } from "../../store/TableReloads";
import InputCom from "../Helpers/Inputs/InputCom/index"; import InputCom from "../Helpers/Inputs/InputCom/index";
import ModalCom from "../Helpers/ModalCom"; import ModalCom from "../Helpers/ModalCom";
import { PriceFormatter } from "../Helpers/PriceFormatter";
import LoadingSpinner from "../Spinners/LoadingSpinner"; import LoadingSpinner from "../Spinners/LoadingSpinner";
import Detail from "./popoutcomponent/Detail"; import Detail from "./popoutcomponent/Detail";
import { tableReload } from "../../store/TableReloads";
import { useDispatch } from "react-redux";
const validationSchema = Yup.object().shape({ const validationSchema = Yup.object().shape({
family: Yup.string().required("This is required "), family: Yup.string().required("This is required "),
@@ -23,13 +24,15 @@ const validationSchema = Yup.object().shape({
}); });
function JobListPopout({ details, onClose, situation }) { function JobListPopout({ details, onClose, situation }) {
const [selectedTab, setSelectedTab] = useState("public");
const tabs = ["public", "individual", "group"];
const [selectedTab, setSelectedTab] = useState('public') const dispatch = useDispatch();
const tabs = ['public', 'individual', 'group']
const dispatch = useDispatch() const [requestStatus, setRequestStatus] = useState({
message: "",
const [requestStatus, setRequestStatus] = useState({message:'', status:false}) status: false,
});
const [familyList, setFamilyList] = useState([]); const [familyList, setFamilyList] = useState([]);
let [loader, setLoader] = useState({ let [loader, setLoader] = useState({
@@ -43,6 +46,24 @@ const [requestStatus, setRequestStatus] = useState({message:'', status:false})
}); });
const apiCall = useMemo(() => new usersService(), []); const apiCall = useMemo(() => new usersService(), []);
const { walletDetails } = useSelector((state) => state.walletDetails);
const getWalletDetail = (currency) => {
// A FUNCTION TO GET USER BALANCE BASED ON TASK CURRENCY
const walletChecker = walletDetails?.data.find(
(item) => item.description === currency
);
return walletChecker
? {
description: walletChecker.description,
country: walletChecker.country,
code: walletChecker.code,
amount: walletChecker.amount,
}
: 0;
};
const taskWalletSelector = getWalletDetail(details?.currency);
// member listing // member listing
const memberList = useCallback(async () => { const memberList = useCallback(async () => {
@@ -147,7 +168,7 @@ const [requestStatus, setRequestStatus] = useState({message:'', status:false})
// for group input // for group input
reqData = { reqData = {
...jobReq, ...jobReq,
email: '', email: "",
group_id: values?.group, group_id: values?.group,
assign_mode: 110044, assign_mode: 110044,
duration: details?.timeline_days, duration: details?.timeline_days,
@@ -161,52 +182,63 @@ const [requestStatus, setRequestStatus] = useState({message:'', status:false})
try { try {
const res = await apiCall.assignJobTask(reqData); const res = await apiCall.assignJobTask(reqData);
let { status, data } = await res; let { status, data } = await res;
if(status != 200 || data.internal_return < 0){ if (status != 200 || data.internal_return < 0) {
setRequestStatus({message:'Unable to complete', status:false}) setRequestStatus({ message: "Unable to complete", status: false });
return setTimeout(()=>{ return setTimeout(() => {
setLoader({ jobFields: false }); setLoader({ jobFields: false });
setRequestStatus({message:'', status:false}) setRequestStatus({ message: "", status: false });
},3000) }, 3000);
} }
dispatch(tableReload({ type: "JOBTABLE" })); dispatch(tableReload({ type: "JOBTABLE" }));
setRequestStatus({message:'Successful', status:true}) setRequestStatus({ message: "Successful", status: true });
setTimeout(()=>{ setTimeout(() => {
setLoader({ jobFields: false }); setLoader({ jobFields: false });
onClose(); onClose();
throw new Response(data); throw new Response(data);
},3000) }, 3000);
} catch (error) { } catch (error) {
setRequestStatus({message:'Unable to complete', status:false}) setRequestStatus({ message: "Unable to complete", status: false });
setTimeout(()=>{ setTimeout(() => {
setRequestStatus({message:'', status:false}) setRequestStatus({ message: "", status: false });
setLoader({ jobFields: false }); setLoader({ jobFields: false });
throw new Error(error); throw new Error(error);
},3000) }, 3000);
} }
}; };
const [groupList, setGroupList] = useState({loading: true, groups: [], members: []}) const [groupList, setGroupList] = useState({
loading: true,
groups: [],
members: [],
});
// FUNCTION TO POPULATE USER GROUP LIST // FUNCTION TO POPULATE USER GROUP LIST
useEffect(()=>{ useEffect(() => {
// setGroupList({loading: true, groups: [], members: []}) // setGroupList({loading: true, groups: [], members: []})
apiCall.jobGroupList({}).then(res => { apiCall
const {status, data} = res .jobGroupList({})
if(status != 200 || data?.internal_return < 0){ .then((res) => {
setGroupList({loading: false, groups: [], members: []}) const { status, data } = res;
return if (status != 200 || data?.internal_return < 0) {
setGroupList({ loading: false, groups: [], members: [] });
return;
} }
if(data.result_list.length < 0){ if (data.result_list.length < 0) {
setGroupList({loading: false, groups: [], members: []}) setGroupList({ loading: false, groups: [], members: [] });
return return;
} }
setGroupList({loading: false, groups: data.result_list, members: data.result_list_member}) setGroupList({
}).catch(error => { loading: false,
setGroupList({loading: false, groups: [], members: []}) groups: data.result_list,
}) members: data.result_list_member,
},[]) });
})
.catch((error) => {
setGroupList({ loading: false, groups: [], members: [] });
});
}, []);
// console.log("Job List P >> ", details) console.log("wallet >> ", walletDetails, details, taskWalletSelector);
return ( return (
<ModalCom action={onClose} situation={situation} className=""> <ModalCom action={onClose} situation={situation} className="">
<div className="logout-modal-wrapper w-[90%] md:w-[768px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto"> <div className="logout-modal-wrapper w-[90%] md:w-[768px] bg-white dark:bg-dark-white lg:rounded-2xl overflow-y-auto">
@@ -241,7 +273,7 @@ const [requestStatus, setRequestStatus] = useState({message:'', status:false})
</button> </button>
</div> </div>
<div className="md:grid grid-cols-2 bg-white dark:bg-dark-white rounded-lg shadow-lg"> <div className="md:grid grid-cols-2 bg-white dark:bg-dark-white rounded-lg shadow-lg">
<div className="p-4 pb-3 w-full md:border-r-2"> <div className="px-4 pb-3 w-full md:border-r-2">
{/* <p className='text-lg font-semibold text-slate-900 tracking-wide'>{details.title}</p> */} {/* <p className='text-lg font-semibold text-slate-900 tracking-wide'>{details.title}</p> */}
{/* INPUT SECTION */} {/* INPUT SECTION */}
@@ -264,7 +296,7 @@ const [requestStatus, setRequestStatus] = useState({message:'', status:false})
<Detail <Detail
label="Created" label="Created"
value={new Date(details?.created).toDateString()} value={new Date(details?.created).toDateString()}
/> />
</div> </div>
<div className=""> <div className="">
@@ -282,169 +314,193 @@ const [requestStatus, setRequestStatus] = useState({message:'', status:false})
</div> </div>
</div> </div>
{/* ACTION SECTION */} <>
<div className="p-4 w-ful flex flex-col justify-between"> {/* ACTION SECTION */}
<h1 className="text-lg mt-3 font-medium tracking-wide text-black dark:text-white">Send this Task to:</h1> {+taskWalletSelector.amount > +details.price ? (
<div className="flex flex-col grow"> <div className="px-4 pb-3 w-full flex flex-col justify-between">
<div className="grid grid-cols-3 mt-4"> <h1 className="text-lg mt-3 font-medium tracking-wide text-black dark:text-white">
{tabs.map(item => ( Send this Task to:
<button </h1>
// className={`px-4 py-1 rounded-t-2xl ${selectedTab == item ? 'btn-gradient border-[2px] text-white' : 'bg-white text-[#000] border-t-[2px]'}`} <div className="flex flex-col grow">
className={`px-4 py-1 rounded-t-2xl border-t-[2px] transition-all duration-200 flex flex-col justify-center items-center ${selectedTab == item ? 'bg-red-50 dark:bg-[#D85A5A] text-slate-600 font-extrabold' : 'bg-white text-[#000]'}`} <div className="grid grid-cols-3 mt-4">
value={item} {tabs.map((item) => (
name={item} <button
onClick={()=>setSelectedTab(item)} // className={`px-4 py-1 rounded-t-2xl ${selectedTab == item ? 'btn-gradient border-[2px] text-white' : 'bg-white text-[#000] border-t-[2px]'}`}
> className={`px-4 py-1 rounded-t-2xl border-t-[2px] transition-all duration-200 flex flex-col justify-center items-center ${
<div className={`mb-[1px] h-6 w-6 border-4 rounded-full transition-all duration-200 ${selectedTab == item ? 'border-white bg-emerald-500' : 'border-red-50 dark:border-[#D85A5A] bg-white'}`}></div> selectedTab == item
{item[0].toUpperCase() + item.slice(1)} ? "bg-red-50 dark:bg-[#D85A5A] text-slate-600 font-extrabold"
</button> : "bg-white text-[#000]"
))} }`}
</div> value={item}
<div className="grow flex flex-col bg-red-50 dark:bg-[#D85A5A] rounded-b-2xl"> name={item}
{selectedTab == 'family' && onClick={() => setSelectedTab(item)}
<Formik >
initialValues={initialValues} <div
validationSchema={validationSchema.fields.family} className={`mb-[1px] h-6 w-6 border-4 rounded-full transition-all duration-200 ${
onSubmit={jobFieldHandler} selectedTab == item
> ? "border-white bg-emerald-500"
{(props) => { : "border-red-50 dark:border-[#D85A5A] bg-white"
return ( }`}
<Form className="hidden"> ></div>
{/* Assign to Family */} {item[0].toUpperCase() + item.slice(1)}
<JobFieldInput </button>
label="Assign to family" ))}
select={true} </div>
inputName="family" <div className="grow flex flex-col bg-red-50 dark:bg-[#D85A5A] rounded-b-2xl">
value={props?.values.family} {selectedTab == "family" && (
data={familyList} <Formik
btnText="Assign to family" initialValues={initialValues}
optionText="Select Family" validationSchema={validationSchema.fields.family}
loader={loader?.jobFields?.family} onSubmit={jobFieldHandler}
errorHandler={errorHandler} >
parentClass='w-full flex flex-col gap-4' {(props) => {
/> return (
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide"> <Form className="hidden">
{" "} {/* Assign to Family */}
{props?.values?.family === "" && ( <JobFieldInput
<span>{errMsg?.jobFields?.family}</span> label="Assign to family"
)} select={true}
</p>{" "} inputName="family"
</Form> value={props?.values.family}
); data={familyList}
}} btnText="Assign to family"
</Formik> optionText="Select Family"
} loader={loader?.jobFields?.family}
errorHandler={errorHandler}
{selectedTab == 'public' && parentClass="w-full flex flex-col gap-4"
<Formik />
initialValues={initialValues} <p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
validationSchema={validationSchema.fields.public} {" "}
onSubmit={jobFieldHandler} {props?.values?.family === "" && (
> <span>{errMsg?.jobFields?.family}</span>
{(props) => { )}
return ( </p>{" "}
<Form className=""> </Form>
{/* Offer this job to public input */} );
<JobFieldInput }}
label="Offer this job to public" </Formik>
select={true} )}
inputName="public"
value={props?.values.public}
data={publicArray}
btnText="Show Task to Public"
optionText="Select Duration"
loader={loader?.jobFields?.public}
errorHandler={errorHandler}
parentClass='w-full flex flex-col gap-4'
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.public === "" && (
<span>{errMsg?.jobFields?.public}</span>
)}
</p>{" "}
</Form>
);
}}
</Formik>
}
{selectedTab == 'individual' && {selectedTab == "public" && (
<Formik <Formik
initialValues={initialValues} initialValues={initialValues}
validationSchema={validationSchema.fields.individual} validationSchema={validationSchema.fields.public}
onSubmit={jobFieldHandler} onSubmit={jobFieldHandler}
> >
{(props) => { {(props) => {
return ( return (
<Form className=""> <Form className="">
{/* Offer this job to individual input */} {/* Offer this job to public input */}
<JobFieldInput <JobFieldInput
label="Offer this job to individual" label="Offer this job to public"
input={true} select={true}
inputName="individual" inputName="public"
value={props?.values.individual} value={props?.values.public}
placeholder="Enter email of individual" data={publicArray}
inputHandler={props?.handleChange} btnText="Show Task to Public"
btnText="Send Offer to Individual" optionText="Select Duration"
loader={loader?.jobFields?.individual} loader={loader?.jobFields?.public}
errorHandler={errorHandler} errorHandler={errorHandler}
parentClass='w-full flex flex-col gap-4' parentClass="w-full flex flex-col gap-4"
/> />
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide"> <p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "} {" "}
{props?.values.individual === "" && ( {props?.values.public === "" && (
<span>{errMsg?.jobFields?.individual}</span> <span>{errMsg?.jobFields?.public}</span>
)} )}
</p>{" "} </p>{" "}
</Form> </Form>
); );
}} }}
</Formik> </Formik>
} )}
{/* { process.env.REACT_APP_SHOW_OFFER_GROUP_JOB != 0 && } */}
{selectedTab == 'group' &&
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.group}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="">
{/* Offer this job to your group input */}
<JobFieldInput
label="Offer this job to your Group"
select={true}
inputName="group"
value={props?.values.group}
btnText="Send Order to Group"
optionText="Select Group"
loader={loader?.jobFields?.group}
errorHandler={errorHandler}
data={groupList}
parentClass='w-full flex flex-col gap-4'
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.group === "" && (
<span>{errMsg?.jobFields?.group}</span>
)}
</p>
</Form>
);
}}
</Formik>
}
</div>
</div>
{requestStatus.message && {selectedTab == "individual" && (
<p className={`mt-4 w-full text-lg ${requestStatus.status ? 'text-emerald-600' : 'text-red-600'}`}>{requestStatus.message}</p> <Formik
} initialValues={initialValues}
</div> validationSchema={validationSchema.fields.individual}
{/* END OF ACTION SECTION */} onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="">
{/* Offer this job to individual input */}
<JobFieldInput
label="Offer this job to individual"
input={true}
inputName="individual"
value={props?.values.individual}
placeholder="Enter email of individual"
inputHandler={props?.handleChange}
btnText="Send Offer to Individual"
loader={loader?.jobFields?.individual}
errorHandler={errorHandler}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.individual === "" && (
<span>{errMsg?.jobFields?.individual}</span>
)}
</p>{" "}
</Form>
);
}}
</Formik>
)}
{/* { process.env.REACT_APP_SHOW_OFFER_GROUP_JOB != 0 && } */}
{selectedTab == "group" && (
<Formik
initialValues={initialValues}
validationSchema={validationSchema.fields.group}
onSubmit={jobFieldHandler}
>
{(props) => {
return (
<Form className="">
{/* Offer this job to your group input */}
<JobFieldInput
label="Offer this job to your Group"
select={true}
inputName="group"
value={props?.values.group}
btnText="Send Order to Group"
optionText="Select Group"
loader={loader?.jobFields?.group}
errorHandler={errorHandler}
data={groupList}
parentClass="w-full flex flex-col gap-4"
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.group === "" && (
<span>{errMsg?.jobFields?.group}</span>
)}
</p>
</Form>
);
}}
</Formik>
)}
<p
className={`text-center w-full text-lg ${
requestStatus.status
? "text-emerald-600"
: "text-red-600"
}`}
>
{requestStatus.message && requestStatus.message}
</p>
</div>
</div>
</div>
) : (
<ZeroBalanceChecker {...taskWalletSelector} />
)}
{/* END OF ACTION SECTION */}
</>
</div> </div>
</div> </div>
</ModalCom> </ModalCom>
@@ -480,7 +536,7 @@ const JobFieldInput = ({
> >
{label && ( {label && (
<label <label
className="input-label border-2 w-full border-sky-700 py-4 px-2 text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block tracking-wide" className="input-label border-2 w-full border-sky-700 rounded py-4 px-2 text-[#181c32] dark:text-white text-[13.975px] leading-[20.9625px] font-semibold block tracking-wide"
htmlFor={inputName} htmlFor={inputName}
> >
{label} {label}
@@ -497,51 +553,50 @@ const JobFieldInput = ({
value={value} value={value}
> >
{/* <option value="">{optionText}</option> */} {/* <option value="">{optionText}</option> */}
{(inputName == 'family' || inputName == 'public') && {(inputName == "family" || inputName == "public") &&
Array.isArray(data) && Array.isArray(data) && (
<> <>
<option value="">{optionText}</option> <option value="">{optionText}</option>
{ data?.map((item, idx) => ( {data?.map((item, idx) => (
<React.Fragment key={idx}> <React.Fragment key={idx}>
{inputName === "family" && item?.last_login !== "" && ( {inputName === "family" &&
<option value={item?.family_uid} key={idx}> item?.last_login !== "" && (
{`${item?.firstname} ${item?.lastname}`} <option value={item?.family_uid} key={idx}>
</option> {`${item?.firstname} ${item?.lastname}`}
)} </option>
{inputName === "public" && ( )}
<option value={item?.duration} key={idx}> {inputName === "public" && (
{item?.name} <option value={item?.duration} key={idx}>
</option> {item?.name}
)} </option>
{/* {inputName === "group" && ( )}
{/* {inputName === "group" && (
<option value={item?.group_id} key={idx}> <option value={item?.group_id} key={idx}>
{item?.group_name} {item?.group_name}
</option> </option>
)} */} )} */}
</React.Fragment> </React.Fragment>
))}
</>
}
{(inputName == 'group') &&
<>
{data.loading ?
<option value={''}>
Loading...
</option>
: data?.groups?.length > 0 ?
<>
<option value="">{optionText}</option>
{ data?.groups?.map((item, index)=>(
<option value={item?.group_id} key={index}>
{item?.group_name}
</option>
))} ))}
</> </>
: )}
<option value="">No Group Found</option> {inputName == "group" && (
} <>
</> {data.loading ? (
} <option value={""}>Loading...</option>
) : data?.groups?.length > 0 ? (
<>
<option value="">{optionText}</option>
{data?.groups?.map((item, index) => (
<option value={item?.group_id} key={index}>
{item?.group_name}
</option>
))}
</>
) : (
<option value="">No Group Found</option>
)}
</>
)}
</Field> </Field>
</div> </div>
</div> </div>
@@ -597,3 +652,19 @@ const publicArray = [
{ duration: 21, name: "3 weeks" }, { duration: 21, name: "3 weeks" },
{ duration: 28, name: "4 weeks" }, { duration: 28, name: "4 weeks" },
]; ];
const ZeroBalanceChecker = ({ amount, code, country }) => {
let thePrice = PriceFormatter(amount * 0.01, code, country);
return (
<div className="px-4 pb-3 w-full flex flex-col gap-5 items-center">
<h1 className="text-lg mt-3 font-medium tracking-wide text-black dark:text-white">
Wallet Balance:{` ${code} ${amount}`}
</h1>
<p className="font-semibold text-center text-red-500 text-lg">You do not have sufficient balance to assign this task</p>
<button className="btn-gradient w-48 h-[46px] text-white rounded-full text-base bg-pink flex justify-center items-center">
Add Credit to Wallet
</button>
</div>
);
};
+1
View File
@@ -38,6 +38,7 @@ const AuthRoute = ({ redirectPath = "/login", children }) => {
localStorage.removeItem("member_id"); localStorage.removeItem("member_id");
localStorage.removeItem("session_token"); localStorage.removeItem("session_token");
sessionStorage.removeItem("family_uid"); sessionStorage.removeItem("family_uid");
localStorage.clear();
navigate("/login", { replace: true }); // redirects user to login page after session expires navigate("/login", { replace: true }); // redirects user to login page after session expires
}; };