buckets can assign employee

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

View File

@ -1,25 +1,57 @@
import React, { useState } from "react";
import { useSortableData } from "../../hooks/useSortableData";
const EmployeeList = ({ employees }) => {
const [selectedIds, setSelectedIds] = useState([]);
import React,{useState,useEffect} from 'react'
import {useSortableData} from '../../hooks/useSortableData';
const EmployeeList = ( {employees, onChange, assignedEmployee = []} ) =>
{
const [employeefiltered, setEmployeeFilter] = useState([]);
const [employeeStatusList, setEmployeeStatusList] = 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) => {
setSelectedIds((prev) =>
prev.includes(id) ? prev?.filter((empId) => empId !== id) : [...prev, id]
);
setEmployeeStatusList((prev) => {
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 = () => {
console.log("Selected Employee IDs:", selectedIds);
const isChecked = (id) => {
const found = employeeStatusList.find((emp) => emp.employeeId === id);
return found?.isActive || false;
};
// Sorting
const {
items: sortedEmployees,
requestSort,
sortConfig,
} = useSortableData(employees, {
} = useSortableData(employeefiltered, {
key: (e) => `${e?.firstName} ${e?.lastName}`,
direction: "asc",
});
@ -34,21 +66,13 @@ const EmployeeList = ({ employees }) => {
};
const filteredEmployees = sortedEmployees?.filter((employee) => {
const fullName =
`${employee?.firstName} ${employee?.lastName}`?.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)
const fullName = `${employee?.firstName} ${employee?.lastName}`?.toLowerCase();
return fullName.includes(searchTerm.toLowerCase());
});
return (
<>
<div className="d-flex justify-content-between mt-2">
<p className="m-0 fs-6 fw-normal">Add Employee</p>
<div className="d-flex justify-content-between align-items-center mt-2">
<p className="m-0 fw-normal">Add Employee</p>
<div className="px-1">
<input
type="search"
@ -72,27 +96,25 @@ const EmployeeList = ({ employees }) => {
>
<span className="ps-2">Name {getSortIcon()}</span>
</th>
<th className="text-start">Role</th>
<th scope="col">Status</th>
<th className="text-start">Bucket</th>
<th className="text-start">Email</th>
</tr>
</thead>
<tbody>
{employees.length === 0 ? (
<tr>
<td colSpan={4} >
<div className="d-flex justify-content-center align-items-center py-5">
No Employee Available
</div>
<td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5">
No Employee Available
</div>
</td>
</tr>
) : filteredEmployees.length === 0 ? (
<tr className="my-4">
<td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5">
No Matchinng Employee Found.
</div>
<td colSpan={4}>
<div className="d-flex justify-content-center align-items-center py-5">
No Matching Employee Found.
</div>
</td>
</tr>
) : (
@ -103,36 +125,18 @@ const EmployeeList = ({ employees }) => {
<input
className="form-check-input me-3 mt-1"
type="checkbox"
checked={selectedIds.includes(employee.id)}
checked={isChecked(employee.id)}
onChange={() => handleCheckboxChange(employee.id)}
/>
<div>
<p className="fw-semibold mb-0">
<p className="fw-normal mb-0">
{`${employee.firstName} ${employee.lastName}`}
</p>
<small className="text-muted">{employee.email}</small>
</div>
</div>
</td>
<td className="text-start">
<small className="text-muted">{employee.jobRole}</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>
<small className="text-muted">{employee.email}</small>
</td>
</tr>
))

View File

@ -16,7 +16,8 @@ import ConfirmModal from "../common/ConfirmModal";
const ManageBucket = () => {
const [bucketList, setBucketList] = useState([]);
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 [isSubmitting, setSubmitting] = useState(false);
const [selected_bucket, select_bucket] = useState(null);
@ -54,28 +55,89 @@ const ManageBucket = () => {
const onSubmit = async (data) => {
setSubmitting(true);
try {
const cache_buckets = getCachedData("buckets") || [];
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) {
let payload = { ...data, id: selected_bucket.id };
const payload = { ...data, id: selected_bucket.id };
// 1. Update bucket details
response = await DirectoryRepository.UpdateBuckets(
selected_bucket.id,
payload
);
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.map((bucket) =>
bucket.id === selected_bucket.id ? response?.data : bucket
);
cacheData("buckets", 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");
} else {
}
// CREATE new bucket
else {
response = await DirectoryRepository.CreateBuckets(data);
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = [...cache_buckets, response?.data];
cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets);
setBucketList( updatedBuckets );
showToast("Bucket Created Successfully", "success");
}
@ -86,18 +148,20 @@ const ManageBucket = () => {
error?.message ||
"Error occurred during API call";
showToast(message, "error");
} finally {
setSubmitting(false);
}
};
const handleDeleteContact = async () => {
try {
const resp = await DirectoryRepository.DeleteBucket( deleteBucket );
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.filter((bucket) =>
bucket.id != deleteBucket
);
cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets);
const resp = await DirectoryRepository.DeleteBucket(deleteBucket);
const cache_buckets = getCachedData("buckets") || [];
const updatedBuckets = cache_buckets.filter(
(bucket) => bucket.id != deleteBucket
);
cacheData("buckets", updatedBuckets);
setBucketList(updatedBuckets);
showToast("Bucket deleted successfully", "success");
setDeleteBucket(null);
} catch (error) {
@ -131,7 +195,6 @@ const ManageBucket = () => {
const name = bucket.name?.toLowerCase();
return name?.includes(term);
});
return (
<>
{deleteBucket && (
@ -173,13 +236,14 @@ const ManageBucket = () => {
placeholder="Search Bucket ..."
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
<i
className={`bx bx-refresh cursor-pointer fs-4 ${loading ? "spin" : ""
}`}
title="Refresh"
onClick={() => rrefetch()}
/>
/>
<i
className={`bx bx-refresh cursor-pointer fs-4 ${
loading ? "spin" : ""
}`}
title="Refresh"
onClick={() => refetch()}
/>
</div>
)}
@ -251,36 +315,37 @@ const ManageBucket = () => {
</td>
</tr>
)}
{sortedBucktesList.map((bucket) => (
<tr key={bucket.id}>
<td colSpan={2} className="text-start text-wrap">
<i className="bx bx-right-arrow-alt me-1"></i>{" "}
{bucket.name}
</td>
<td
className="text-start d-none d-sm-table-cell text-wrap"
style={{ width: "60%" }}
>
{bucket.description}
</td>
<td className="justify-content-center">
<div className="d-flex justify-content-center align-items-center gap-2">
<i
className="bx bx-edit bx-sm text-primary cursor-pointer"
onClick={() => {
select_bucket(bucket);
setAction_bucket(true);
}}
></i>
<i
className="bx bx-trash bx-sm text-danger cursor-pointer"
onClick={() => setDeleteBucket(bucket?.id)}
></i>
<i className="bx bx-user-plus cursor-pointer"></i>
</div>
</td>
</tr>
))}
{!loading &&
sortedBucktesList.map((bucket) => (
<tr key={bucket.id}>
<td colSpan={2} className="text-start text-wrap">
<i className="bx bx-right-arrow-alt me-1"></i>{" "}
{bucket.name}
</td>
<td
className="text-start d-none d-sm-table-cell text-wrap"
style={{ width: "60%" }}
>
{bucket.description}
</td>
<td className="justify-content-center">
<div className="d-flex justify-content-center align-items-center gap-2">
<i
className="bx bx-edit bx-sm text-primary cursor-pointer"
onClick={() => {
select_bucket(bucket);
setAction_bucket(true);
}}
></i>
<i
className="bx bx-trash bx-sm text-danger cursor-pointer"
onClick={() => setDeleteBucket(bucket?.id)}
></i>
<i className="bx bx-user-plus cursor-pointer"></i>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
@ -310,9 +375,18 @@ const ManageBucket = () => {
</small>
)}
</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">
<button
type="reset"
onClick={()=>handleBack()}
className="btn btn-sm btn-secondary"
disabled={isSubmitting}
>
@ -327,8 +401,6 @@ const ManageBucket = () => {
</button>
</div>
</form>
<EmployeeList employees={employeesList} />
</>
)}
</div>