buckets can assign employee

This commit is contained in:
Pramod Mahajan 2025-05-28 16:40:54 +05:30
parent eb6d4e413d
commit 813032ead2
2 changed files with 185 additions and 109 deletions

View File

@ -1,25 +1,57 @@
import React, { useState } from "react"; import React,{useState,useEffect} from 'react'
import { useSortableData } from "../../hooks/useSortableData"; import {useSortableData} from '../../hooks/useSortableData';
const EmployeeList = ( {employees, onChange, assignedEmployee = []} ) =>
const EmployeeList = ({ employees }) => { {
const [selectedIds, setSelectedIds] = useState([]); const [employeefiltered, setEmployeeFilter] = useState([]);
const [employeeStatusList, setEmployeeStatusList] = useState([]);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
// Populate filtered list on load
useEffect(() => {
setEmployeeFilter(employees?.filter((emp) => emp.email != null) || []);
}, [employees]);
// Initialize checked employees based on assignedEmployee prop
useEffect(() => {
if (Array.isArray(assignedEmployee)) {
const initialStatus = assignedEmployee.map((id) => ({
employeeId: id,
isActive: true,
}));
setEmployeeStatusList(initialStatus);
}
}, [assignedEmployee]);
// Send updated list to parent
useEffect(() => {
if (onChange) {
onChange(employeeStatusList);
}
}, [employeeStatusList]);
const handleCheckboxChange = (id) => { const handleCheckboxChange = (id) => {
setSelectedIds((prev) => setEmployeeStatusList((prev) => {
prev.includes(id) ? prev?.filter((empId) => empId !== id) : [...prev, id] const exists = prev.find((emp) => emp.employeeId === id);
); if (exists) {
return prev.map((emp) =>
emp.employeeId === id ? { ...emp, isActive: !emp.isActive } : emp
);
} else {
return [...prev, { employeeId: id, isActive: true }];
}
});
}; };
const getSelectedEmployees = () => { const isChecked = (id) => {
console.log("Selected Employee IDs:", selectedIds); const found = employeeStatusList.find((emp) => emp.employeeId === id);
return found?.isActive || false;
}; };
// Sorting
const { const {
items: sortedEmployees, items: sortedEmployees,
requestSort, requestSort,
sortConfig, sortConfig,
} = useSortableData(employees, { } = useSortableData(employeefiltered, {
key: (e) => `${e?.firstName} ${e?.lastName}`, key: (e) => `${e?.firstName} ${e?.lastName}`,
direction: "asc", direction: "asc",
}); });
@ -34,21 +66,13 @@ const EmployeeList = ({ employees }) => {
}; };
const filteredEmployees = sortedEmployees?.filter((employee) => { const filteredEmployees = sortedEmployees?.filter((employee) => {
const fullName = const fullName = `${employee?.firstName} ${employee?.lastName}`?.toLowerCase();
`${employee?.firstName} ${employee?.lastName}`?.toLowerCase(); return fullName.includes(searchTerm.toLowerCase());
// const email = employee.email.toLowerCase();
// const role = employee.jobRole.toLowerCase();
const term = searchTerm?.toLowerCase();
return fullName.includes(term);
// email.includes(term) ||
// role.includes(term)
}); });
return ( return (
<> <>
<div className="d-flex justify-content-between mt-2"> <div className="d-flex justify-content-between align-items-center mt-2">
<p className="m-0 fs-6 fw-normal">Add Employee</p> <p className="m-0 fw-normal">Add Employee</p>
<div className="px-1"> <div className="px-1">
<input <input
type="search" type="search"
@ -72,27 +96,25 @@ const EmployeeList = ({ employees }) => {
> >
<span className="ps-2">Name {getSortIcon()}</span> <span className="ps-2">Name {getSortIcon()}</span>
</th> </th>
<th className="text-start">Role</th> <th className="text-start">Email</th>
<th scope="col">Status</th>
<th className="text-start">Bucket</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{employees.length === 0 ? ( {employees.length === 0 ? (
<tr> <tr>
<td colSpan={4} > <td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5"> <div className="d-flex justify-content-center align-items-center py-5">
No Employee Available No Employee Available
</div> </div>
</td> </td>
</tr> </tr>
) : filteredEmployees.length === 0 ? ( ) : filteredEmployees.length === 0 ? (
<tr className="my-4"> <tr className="my-4">
<td colSpan={4}> <td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5"> <div className="d-flex justify-content-center align-items-center py-5">
No Matchinng Employee Found. No Matching Employee Found.
</div> </div>
</td> </td>
</tr> </tr>
) : ( ) : (
@ -103,36 +125,18 @@ const EmployeeList = ({ employees }) => {
<input <input
className="form-check-input me-3 mt-1" className="form-check-input me-3 mt-1"
type="checkbox" type="checkbox"
checked={selectedIds.includes(employee.id)} checked={isChecked(employee.id)}
onChange={() => handleCheckboxChange(employee.id)} onChange={() => handleCheckboxChange(employee.id)}
/> />
<div> <div>
<p className="fw-semibold mb-0"> <p className="fw-normal mb-0">
{`${employee.firstName} ${employee.lastName}`} {`${employee.firstName} ${employee.lastName}`}
</p> </p>
<small className="text-muted">{employee.email}</small>
</div> </div>
</div> </div>
</td> </td>
<td className="text-start"> <td className="text-start">
<small className="text-muted">{employee.jobRole}</small> <small className="text-muted">{employee.email}</small>
</td>
<td>
<span
className={`badge rounded-pill px-3 py-1 ${
employee.isActive
? "bg-success-subtle text-success"
: "bg-danger-subtle text-danger"
}`}
>
{employee.isActive ? "Active" : "Inactive"}
</span>
</td>
<td className="text-start">
<small className="text-muted">
<i className="fa fa-hashtag" aria-hidden="true"></i>{" "}
{employee.jobRole}
</small>
</td> </td>
</tr> </tr>
)) ))

View File

@ -16,7 +16,8 @@ import ConfirmModal from "../common/ConfirmModal";
const ManageBucket = () => { const ManageBucket = () => {
const [bucketList, setBucketList] = useState([]); const [bucketList, setBucketList] = useState([]);
const { employeesList } = useAllEmployees(false); const { employeesList } = useAllEmployees(false);
const { buckets, loading,refetch } = useBuckets(); const [selectedEmployee, setSelectEmployee] = useState([]);
const { buckets, loading, refetch } = useBuckets();
const [action_bucket, setAction_bucket] = useState(false); const [action_bucket, setAction_bucket] = useState(false);
const [isSubmitting, setSubmitting] = useState(false); const [isSubmitting, setSubmitting] = useState(false);
const [selected_bucket, select_bucket] = useState(null); const [selected_bucket, select_bucket] = useState(null);
@ -54,28 +55,89 @@ const ManageBucket = () => {
const onSubmit = async (data) => { const onSubmit = async (data) => {
setSubmitting(true); setSubmitting(true);
try { try {
const cache_buckets = getCachedData("buckets") || [];
let response; let response;
// Utility: Compare arrays regardless of order
const arraysAreEqual = (a, b) => {
if (a.length !== b.length) return false;
const setA = new Set(a);
const setB = new Set(b);
return [...setA].every((id) => setB.has(id));
};
// UPDATE existing bucket
if (selected_bucket) { if (selected_bucket) {
let payload = { ...data, id: selected_bucket.id }; const payload = { ...data, id: selected_bucket.id };
// 1. Update bucket details
response = await DirectoryRepository.UpdateBuckets( response = await DirectoryRepository.UpdateBuckets(
selected_bucket.id, selected_bucket.id,
payload payload
); );
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.map((bucket) => const updatedBuckets = cache_buckets.map((bucket) =>
bucket.id === selected_bucket.id ? response?.data : bucket bucket.id === selected_bucket.id ? response?.data : bucket
); );
cacheData("buckets", updatedBuckets); cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets); setBucketList(updatedBuckets);
// 2. Update employee assignments if they changed
const existingEmployeeIds = selected_bucket?.employeeIds || [];
const employeesToUpdate = selectedEmployee.filter((emp) => {
const isExisting = existingEmployeeIds.includes(emp.employeeId);
return (!isExisting && emp.isActive) || (isExisting && !emp.isActive);
});
// Create a filtered list of active employee IDs to compare
const newActiveEmployeeIds = selectedEmployee
.filter((emp) => {
const isExisting = existingEmployeeIds.includes(emp.employeeId);
return (
(!isExisting && emp.isActive) || (isExisting && !emp.isActive)
);
})
.map((emp) => emp.employeeId);
if (
!arraysAreEqual(newActiveEmployeeIds, existingEmployeeIds) &&
employeesToUpdate.length != 0
) {
try {
response = await DirectoryRepository.AssignedBuckets(
selected_bucket.id,
employeesToUpdate
);
} catch (assignError) {
const assignMessage =
assignError?.response?.data?.message ||
assignError?.message ||
"Error assigning employees.";
showToast(assignMessage, "error");
}
}
const updatedData = cache_buckets?.map((bucket) =>
bucket.id === response?.data?.id ? response.data : bucket
);
cacheData( "buckets", updatedData );
setBucketList(updatedData)
showToast("Bucket Updated Successfully", "success"); showToast("Bucket Updated Successfully", "success");
} else { }
// CREATE new bucket
else {
response = await DirectoryRepository.CreateBuckets(data); response = await DirectoryRepository.CreateBuckets(data);
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = [...cache_buckets, response?.data]; const updatedBuckets = [...cache_buckets, response?.data];
cacheData("buckets", updatedBuckets); cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets); setBucketList( updatedBuckets );
showToast("Bucket Created Successfully", "success"); showToast("Bucket Created Successfully", "success");
} }
@ -86,18 +148,20 @@ const ManageBucket = () => {
error?.message || error?.message ||
"Error occurred during API call"; "Error occurred during API call";
showToast(message, "error"); showToast(message, "error");
} finally {
setSubmitting(false);
} }
}; };
const handleDeleteContact = async () => { const handleDeleteContact = async () => {
try { try {
const resp = await DirectoryRepository.DeleteBucket( deleteBucket ); const resp = await DirectoryRepository.DeleteBucket(deleteBucket);
const cache_buckets = getCachedData("buckets") || []; const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.filter((bucket) => const updatedBuckets = cache_buckets.filter(
bucket.id != deleteBucket (bucket) => bucket.id != deleteBucket
); );
cacheData("buckets", updatedBuckets); cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets); setBucketList(updatedBuckets);
showToast("Bucket deleted successfully", "success"); showToast("Bucket deleted successfully", "success");
setDeleteBucket(null); setDeleteBucket(null);
} catch (error) { } catch (error) {
@ -131,7 +195,6 @@ const ManageBucket = () => {
const name = bucket.name?.toLowerCase(); const name = bucket.name?.toLowerCase();
return name?.includes(term); return name?.includes(term);
}); });
return ( return (
<> <>
{deleteBucket && ( {deleteBucket && (
@ -173,13 +236,14 @@ const ManageBucket = () => {
placeholder="Search Bucket ..." placeholder="Search Bucket ..."
value={searchTerm} value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)} onChange={(e) => setSearchTerm(e.target.value)}
/> />
<i <i
className={`bx bx-refresh cursor-pointer fs-4 ${loading ? "spin" : "" className={`bx bx-refresh cursor-pointer fs-4 ${
}`} loading ? "spin" : ""
title="Refresh" }`}
onClick={() => rrefetch()} title="Refresh"
/> onClick={() => refetch()}
/>
</div> </div>
)} )}
@ -251,36 +315,37 @@ const ManageBucket = () => {
</td> </td>
</tr> </tr>
)} )}
{sortedBucktesList.map((bucket) => ( {!loading &&
<tr key={bucket.id}> sortedBucktesList.map((bucket) => (
<td colSpan={2} className="text-start text-wrap"> <tr key={bucket.id}>
<i className="bx bx-right-arrow-alt me-1"></i>{" "} <td colSpan={2} className="text-start text-wrap">
{bucket.name} <i className="bx bx-right-arrow-alt me-1"></i>{" "}
</td> {bucket.name}
<td </td>
className="text-start d-none d-sm-table-cell text-wrap" <td
style={{ width: "60%" }} className="text-start d-none d-sm-table-cell text-wrap"
> style={{ width: "60%" }}
{bucket.description} >
</td> {bucket.description}
<td className="justify-content-center"> </td>
<div className="d-flex justify-content-center align-items-center gap-2"> <td className="justify-content-center">
<i <div className="d-flex justify-content-center align-items-center gap-2">
className="bx bx-edit bx-sm text-primary cursor-pointer" <i
onClick={() => { className="bx bx-edit bx-sm text-primary cursor-pointer"
select_bucket(bucket); onClick={() => {
setAction_bucket(true); select_bucket(bucket);
}} setAction_bucket(true);
></i> }}
<i ></i>
className="bx bx-trash bx-sm text-danger cursor-pointer" <i
onClick={() => setDeleteBucket(bucket?.id)} className="bx bx-trash bx-sm text-danger cursor-pointer"
></i> onClick={() => setDeleteBucket(bucket?.id)}
<i className="bx bx-user-plus cursor-pointer"></i> ></i>
</div> <i className="bx bx-user-plus cursor-pointer"></i>
</td> </div>
</tr> </td>
))} </tr>
))}
</tbody> </tbody>
</table> </table>
</div> </div>
@ -310,9 +375,18 @@ const ManageBucket = () => {
</small> </small>
)} )}
</div> </div>
{selected_bucket && (
<EmployeeList
employees={employeesList}
onChange={(data) => setSelectEmployee(data)}
assignedEmployee={selected_bucket?.employeeIds}
/>
)}
<div className="mt-2 d-flex justify-content-center gap-3"> <div className="mt-2 d-flex justify-content-center gap-3">
<button <button
type="reset" onClick={()=>handleBack()}
className="btn btn-sm btn-secondary" className="btn btn-sm btn-secondary"
disabled={isSubmitting} disabled={isSubmitting}
> >
@ -327,8 +401,6 @@ const ManageBucket = () => {
</button> </button>
</div> </div>
</form> </form>
<EmployeeList employees={employeesList} />
</> </>
)} )}
</div> </div>