Compare commits

...

9 Commits

Author SHA1 Message Date
victorAnumudu 71f987cbff made group offer display when show offer to group is true 2024-01-08 09:47:48 +01:00
victorAnumudu e8c3dca731 assign task to group implementation 2024-01-07 19:45:32 +01:00
ameye 85d15ac2f1 Merge branch 'add-group-member' of WrenchBoard/Users-Wrench into master 2024-01-07 11:57:34 +00:00
ameye 236dfeae2c Merge branch 'add-group-member-form' of WrenchBoard/Users-Wrench into master 2024-01-07 11:57:29 +00:00
victorAnumudu 734acd9dd5 placeholder renamed 2024-01-07 08:01:11 +01:00
victorAnumudu 3f9534b968 placeholder renamed 2024-01-07 07:59:54 +01:00
victorAnumudu 13d69efbaf changed group member form style 2024-01-07 07:57:06 +01:00
victorAnumudu 151aa4ba0c made selected group active after adding a member 2024-01-05 20:00:55 +01:00
ameye 4985256c3a Merge branch 'group-page-bug-fixed' of WrenchBoard/Users-Wrench into master 2024-01-05 17:14:12 +00:00
4 changed files with 231 additions and 123 deletions
+17 -21
View File
@@ -30,38 +30,34 @@ export default function GroupMemberTable({selectedList}) {
return (
<div className={`w-full p-8 bg-white dark:bg-dark-gray overflow-hidden rounded-2xl section-shadow`}>
<div className="relative w-full overflow-x-auto sm:rounded-lg flex flex-col justify-between min-h-[300px]">
<table className="table-auto min-w-full text-sm text-left text-gray-500 dark:text-gray-400">
<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>
<>
<tr className='font-bold text-sm text-dark-gray dark:text-white whitespace-nowrap'>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th></th>
</tr>
<tr>
<td className='p-[1px] bg-slate-400 dark:bg-white rounded-full' colSpan="4"></td>
</tr>
{selectedList && selectedList?.length > 0 ? (
// currentSelectedList?.length ? (
currentSelectedList.map((value, index) => (
<tr key={value.uid} className="font-medium text-sm text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-1">{value?.firstname}</td>
<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?.email}</td>
<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>
</td> */}
<td className='py-2'>
<div className="flex flex-col">
<h1 className="font-bold text-xl text-dark-gray dark:text-white">
{value.firstname && value.firstname} {value.lastname && value.lastname}
</h1>
<span className="text-sm text-thin-light-gray">
{value.email && value.email}
</span>
</div>
</td>
<td className='py-2 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>
</td>
</tr>
))
// ) : (
// <tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
// <td className="p-2">
// No Members Found
// </td>
// </tr>
// )
) : (
<tr className="font-bold text-xl text-dark-gray dark:text-white whitespace-nowrap">
<td className="p-2">No Members Found</td>
+10 -5
View File
@@ -20,7 +20,7 @@ export default function JobGroups() {
members: []
})
const [selectedGroup, setSelectedGroup] = useState({id:'', data: []})
const [selectedGroup, setSelectedGroup] = useState({id:'', name:'', data: []})
const changeSelectedGroup = (e) => {
let groupID = e.target.value
@@ -42,10 +42,15 @@ export default function JobGroups() {
return
}
setGroupList({loading: false, groups: data.result_list, members: data.result_list_member})
let activeGroupId = data.result_list[0].group_id
let activeGroup = data.result_list[0].group_name
let activeMembers = data.result_list_member?.filter(item => item.group_id == activeGroupId)
setSelectedGroup({id: activeGroupId, name:activeGroup, data:activeMembers})
if(selectedGroup.id == ''){
let activeGroupId = data.result_list[0].group_id
let activeGroup = data.result_list[0].group_name
let activeMembers = data.result_list_member?.filter(item => item.group_id == activeGroupId)
setSelectedGroup({id: activeGroupId, name:activeGroup, data:activeMembers})
}else{
let activeMembers = data.result_list_member?.filter(item => item.group_id == selectedGroup?.id)
setSelectedGroup({id: selectedGroup?.id, name:selectedGroup?.name, data:activeMembers})
}
}).catch(error => {
setGroupList({loading: false, groups: [], members: []})
console.log(error)
+96 -58
View File
@@ -20,11 +20,42 @@ export default function MemberList({groupList, selectedGroup, setUpdateList}) {
const handleFieldsChange = ({target:{name, value}}) => {
setFields(prev => ({...prev, [name]:value}))
let error = requestState?.errors?.indexOf(name) //// checks if the input field was in error array and removes it when the input changes
if(error >= 0){
let oldErrorArr = requestState.errors
let newErrorArr = oldErrorArr.splice(error, 1)
setRequestState(prev => ({...prev, errors:oldErrorArr}))
// let error = requestState?.errors?.indexOf(name) //// checks if the input field was in error array and removes it when the input changes
// if(error >= 0){
// let oldErrorArr = requestState.errors
// let newErrorArr = oldErrorArr.splice(error, 1)
// setRequestState(prev => ({...prev, errors:oldErrorArr}))
// }
if(value == ''){
setRequestState({
loading: false,
status: false,
message: '',
data: [],
errors: [name]
})
}else{
let error = requestState?.errors?.indexOf(name) //// checks if the input field was in error array and removes it when the input changes
if(error >= 0){
let oldErrorArr = requestState.errors
let newErrorArr = oldErrorArr.splice(error, 1)
setRequestState(prev => ({...prev, errors:oldErrorArr}))
}
}
if(name == 'email'){
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
if (!EmailValidator(value)) {
setRequestState({
loading: false,
status: false,
message: '',
data: [],
errors: ['email']
})
}
}
}
@@ -33,26 +64,26 @@ export default function MemberList({groupList, selectedGroup, setUpdateList}) {
status: false,
message: '',
data: [],
errors: []
errors: ['email', 'firstname', 'lastname']
})
const addMember = () => {
let errors = Object.keys(fields).filter((item) => { // CHECKS FOR EMPTY STRINGS
if(typeof item == 'string' && fields[item] === ''){
return item
}
})
// let errors = Object.keys(fields).filter((item) => { // CHECKS FOR EMPTY STRINGS
// if(typeof item == 'string' && fields[item] === ''){
// return item
// }
// })
if(errors.length){
setRequestState({
loading: false,
status: false,
message: '',
data: [],
errors: [...errors]
})
return
}
// if(errors.length){
// setRequestState({
// loading: false,
// status: false,
// message: '',
// data: [],
// errors: [...errors]
// })
// return
// }
//checks if email is a valid email address
let regEx = /^[^0-9][a-zA-Z0-9._%+-]+@[a-zA-Z]+(\.[a-zA-Z]+)+$/;
@@ -155,54 +186,61 @@ export default function MemberList({groupList, selectedGroup, setUpdateList}) {
<>
<h1 className='mb-5 text-lg lg:text-2xl tracking-wide font-bold text-slate-900 dark:text-slate-100'>{selectedGroup?.name}</h1>
<div className='w-full flex flex-col-reverse lg:flex-col'>
<div className='py-3 w-full sm:w-[500px] xl:w-full xxl:w-[500px]'>
<div className='relative grid grid-cols-1 sm:grid-cols-2 gap-2'>
<div className='h-10 relative'>
<input
id='firstname'
name='firstname'
// className={`peer px-5 w-full h-full rounded-full outline-none placeholder:text-transparent transition-all duration-500 text-black dark:text-black bg-gradient-to-br from-slate-50/80 via-slate-200/80 to-slate-100/80 focus:from-slate-100/80 focus:to-slate-50/80`}
className={`peer px-5 w-full h-full rounded-full outline-none border placeholder:text-slate-600 transition-all duration-500 text-black dark:text-black bg-gradient-to-br from-slate-50/80 via-slate-200/80 to-slate-100/80 focus:from-slate-100/80 focus:to-slate-50/80 ${requestState.errors.includes('firstname') ? 'border-red-500' : 'border-transparent'}`}
type='text'
placeholder='First Name'
value={fields.firstname}
onChange={handleFieldsChange}
<div className='py-3 w-full'>
<div className='relative grid grid-cols-1 sm:grid-cols-2 gap-2 place-content-center'>
<div className="input-item">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={fields.firstname}
inputHandler={handleFieldsChange}
placeholder="First Name"
// label="Firstname"
name="firstname"
type="text"
// iconName="message"
/>
{/* <label htmlFor='firstname' className={`text-sm text-black dark:text-slate-500 absolute left-5 -top-4 translate-y-0 peer-focus:-top-3 peer-focus:translate-y-0 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 transition-all duration-500 ${requestState.errors.includes('firstname') ? 'text-red-500' : ''}`}>First Name</label> */}
</div>
<div className='h-10 relative'>
<input
id='lastname'
name='lastname'
// className={`peer px-5 w-full h-full rounded-full outline-none placeholder:text-transparent transition-all duration-500 text-black dark:text-black bg-gradient-to-br from-slate-50/80 via-slate-200/80 to-slate-100/80 focus:from-slate-100/80 focus:to-slate-50/80`}
className={`peer px-5 w-full h-full rounded-full outline-none border placeholder:text-slate-600 transition-all duration-500 text-black dark:text-black bg-gradient-to-br from-slate-50/80 via-slate-200/80 to-slate-100/80 focus:from-slate-100/80 focus:to-slate-50/80 ${requestState.errors.includes('lastname') ? 'border-red-500' : 'border-transparent'}`}
type='text'
placeholder='Last Name'
value={fields.lastname}
onChange={handleFieldsChange}
<div className="input-item">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={fields.lastname}
inputHandler={handleFieldsChange}
placeholder="Last Name"
// label="Lastname"
name="lastname"
type="text"
// iconName="message"
/>
{/* <label htmlFor='lastname' className={`text-sm text-black dark:text-slate-500 absolute left-5 -top-4 translate-y-0 peer-focus:-top-3 peer-focus:translate-y-0 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 transition-all duration-500 ${requestState.errors.includes('lastname') ? 'text-red-500' : ''}`}>LastName</label> */}
</div>
<div className='h-10 relative'>
<input
id='email'
name='email'
// className={`peer px-5 w-full h-full rounded-full outline-none placeholder:text-transparent transition-all duration-500 text-black dark:text-black bg-gradient-to-br from-slate-50/80 via-slate-200/80 to-slate-100/80 focus:from-slate-100/80 focus:to-slate-50/80`}
className={`peer px-5 w-full h-full rounded-full outline-none border placeholder:text-slate-600 transition-all duration-500 text-black dark:text-black bg-gradient-to-br from-slate-50/80 via-slate-200/80 to-slate-100/80 focus:from-slate-100/80 focus:to-slate-50/80 ${requestState.errors.includes('email') ? 'border-red-500' : 'border-transparent'}`}
type='email'
placeholder='Email'
value={fields.email}
onChange={handleFieldsChange}
<div className="input-item w-full sm:w-[150%]">
<InputCom
labelClass="tracking-wider"
fieldClass="sm:px-6 px-2"
value={fields.email}
inputHandler={handleFieldsChange}
placeholder="Email"
// label="Email"
name="email"
type="email"
// iconName="message"
/>
{/* <label htmlFor='email' className={`text-sm text-black dark:text-slate-500 absolute left-5 -top-4 translate-y-0 peer-focus:-top-3 peer-focus:translate-y-0 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 transition-all duration-500 ${requestState.errors.includes('email') ? 'text-red-500' : ''}`}>Email</label> */}
</div>
<div className='flex justify-end items-center'>
<div className='flex justify-end items-end'>
{requestState.loading ?
<LoadingSpinner size='8' color='sky-blue' />
:
<button onClick={addMember} disabled={requestState.loading || requestState.status} 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 Member</button>
<button
onClick={addMember}
disabled={requestState.loading || requestState.status || requestState.errors.length}
className={`py-2 px-4 h-[42px] flex justify-center items-center text-base rounded-full text-white font-bold transition-all duration-500 ${requestState.loading || requestState.status || requestState.errors.length ? 'bg-sky-blue/50' : 'bg-sky-blue hover:bg-sky-600'} `}
>
Add Member
</button>
}
</div>
{!requestState.loading && requestState.message &&
<p className={`text-lg absolute -bottom-7 left-0 ${requestState.status ? 'text-green-500' : 'text-red-500'}`}>{requestState.message}</p>
}
+108 -39
View File
@@ -25,6 +25,8 @@ const validationSchema = Yup.object().shape({
function JobListPopout({ details, onClose, situation }) {
const dispatch = useDispatch()
const [requestStatus, setRequestStatus] = useState({message:'', status:false})
const [familyList, setFamilyList] = useState([]);
let [loader, setLoader] = useState({
member: false,
@@ -96,7 +98,7 @@ const dispatch = useDispatch()
else if (name === "individual")
setErrMsg({ jobFields: { individual: "please enter email" } });
else if (name === "group")
setErrMsg({ jobFields: { group: "please select a family member" } });
setErrMsg({ jobFields: { group: "please select a group" } });
} finally {
setTimeout(() => {
setErrMsg({ jobFields: "" });
@@ -150,9 +152,12 @@ const dispatch = useDispatch()
// for group input
reqData = {
...jobReq,
email: values?.individual,
email: '',
group_id: values?.group,
action: 13025,
assign_mode: 110033,
assign_mode: 110044,
duration: details?.timeline_days,
// duration: 0,
};
setLoader({ jobFields: { group: true } });
} else {
@@ -161,17 +166,51 @@ const dispatch = useDispatch()
try {
const res = await apiCall.assignJobTask(reqData);
let { data } = await res;
setLoader({ jobFields: false });
let { status, data } = await res;
if(status != 200 || data.internal_return < 0){
setRequestStatus({message:'Unable to complete', status:false})
return setTimeout(()=>{
setRequestStatus({message:'', status:false})
},3000)
}
dispatch(tableReload({ type: "JOBTABLE" }));
onClose();
throw new Response(data);
setRequestStatus({message:'Successful', status:true})
setTimeout(()=>{
setLoader({ jobFields: false });
onClose();
throw new Response(data);
},3000)
} catch (error) {
setLoader({ jobFields: false });
throw new Error(error);
setRequestStatus({message:'Unable to complete', status:false})
setTimeout(()=>{
setRequestStatus({message:'', status:false})
setLoader({ jobFields: false });
throw new Error(error);
},3000)
}
};
const [groupList, setGroupList] = useState({loading: true, groups: [], members: []})
// FUNCTION TO POPULATE USER GROUP LIST
useEffect(()=>{
// setGroupList({loading: true, groups: [], members: []})
apiCall.jobGroupList({}).then(res => {
const {status, data} = res
if(status != 200 || data?.internal_return < 0){
setGroupList({loading: false, groups: [], members: []})
return
}
if(data.result_list.length < 0){
setGroupList({loading: false, groups: [], members: []})
return
}
setGroupList({loading: false, groups: data.result_list, members: data.result_list_member})
}).catch(error => {
setGroupList({loading: false, groups: [], members: []})
})
},[])
// console.log("Job List P >> ", details)
return (
<ModalCom action={onClose} situation={situation} className="job-popup">
@@ -246,6 +285,10 @@ const dispatch = useDispatch()
/>
<p>{errMsg.deliveryDetail}</p>
</div>
{requestStatus.message &&
<p className={`w-full text-lg ${requestStatus.status ? 'text-emerald-600' : 'text-red-600'}`}>{requestStatus.message}</p>
}
</div>
{/* ACTION SECTION */}
@@ -267,13 +310,13 @@ const dispatch = useDispatch()
data={familyList}
btnText="Assign to family"
optionText="Select Family"
loader={loader?.jobFields.family}
loader={loader?.jobFields?.family}
errorHandler={errorHandler}
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.family === "" && (
<span>{errMsg.jobFields.family}</span>
{props?.values?.family === "" && (
<span>{errMsg?.jobFields?.family}</span>
)}
</p>{" "}
</Form>
@@ -298,13 +341,13 @@ const dispatch = useDispatch()
data={publicArray}
btnText="Show Task to Public"
optionText="Select Duration"
loader={loader?.jobFields.public}
loader={loader?.jobFields?.public}
errorHandler={errorHandler}
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.public === "" && (
<span>{errMsg.jobFields.public}</span>
<span>{errMsg?.jobFields?.public}</span>
)}
</p>{" "}
</Form>
@@ -329,13 +372,13 @@ const dispatch = useDispatch()
placeholder="Enter email of individual"
inputHandler={props?.handleChange}
btnText="Send Offer to Individual"
loader={loader?.jobFields.individual}
loader={loader?.jobFields?.individual}
errorHandler={errorHandler}
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.individual === "" && (
<span>{errMsg.jobFields.individual}</span>
<span>{errMsg?.jobFields?.individual}</span>
)}
</p>{" "}
</Form>
@@ -359,14 +402,15 @@ const dispatch = useDispatch()
inputName="group"
value={props?.values.group}
btnText="Send Order to Group"
optionText="Group"
loader={loader?.jobFields.group}
optionText="Select Group"
loader={loader?.jobFields?.group}
errorHandler={errorHandler}
data={groupList}
/>
<p className="h-4 text-[13px] font-light italic text-red-600 tracking-wide">
{" "}
{props?.values.group === "" && (
<span>{errMsg.jobFields.group}</span>
<span>{errMsg?.jobFields?.group}</span>
)}
</p>
</Form>
@@ -427,27 +471,52 @@ const JobFieldInput = ({
className={`input-field placeholder:text-base text-dark-gray w-full h-full bg-white outline-none px-2`}
value={value}
>
<option value="">{optionText}</option>
{Array.isArray(data) &&
data?.map((item, idx) => (
<React.Fragment key={idx}>
{inputName === "family" && item?.last_login !== "" && (
<option value={item?.family_uid} key={idx}>
{`${item?.firstname} ${item?.lastname}`}
{/* <option value="">{optionText}</option> */}
{(inputName == 'family' || inputName == 'public') &&
Array.isArray(data) &&
<>
<option value="">{optionText}</option>
{ data?.map((item, idx) => (
<React.Fragment key={idx}>
{inputName === "family" && item?.last_login !== "" && (
<option value={item?.family_uid} key={idx}>
{`${item?.firstname} ${item?.lastname}`}
</option>
)}
{inputName === "public" && (
<option value={item?.duration} key={idx}>
{item?.name}
</option>
)}
{/* {inputName === "group" && (
<option value={item?.group_id} key={idx}>
{item?.group_name}
</option>
)} */}
</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>
)}
{inputName === "public" && (
<option value={item?.duration} key={idx}>
{item?.name}
</option>
)}
{inputName === "group" && (
<option value={item?.family_uid} key={idx}>
{item?.firstname}
</option>
)}
</React.Fragment>
))}
))}
</>
:
<option value="">No Group Found</option>
}
</>
}
</Field>
</div>
</div>