buckets can assign employee
This commit is contained in:
parent
eb6d4e413d
commit
813032ead2
@ -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>
|
||||||
))
|
))
|
||||||
|
@ -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>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user