252 lines
7.5 KiB
JavaScript
252 lines
7.5 KiB
JavaScript
import React, { useState, useEffect } from "react";
|
|
import Avatar from "../../common/Avatar";
|
|
import { useDebounce } from "../../../utils/appUtils";
|
|
import { useSelectedProject } from "../../../slices/apiDataManager";
|
|
import { useOrganizationEmployees } from "../../../hooks/useOrganization";
|
|
import {
|
|
useEmployeesByProjectAllocated,
|
|
useManageProjectAllocation,
|
|
useProjectAssignedServices,
|
|
} from "../../../hooks/useProjects";
|
|
import useMaster, { useServices } from "../../../hooks/masterHook/useMaster";
|
|
import showToast from "../../../services/toastService";
|
|
|
|
const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
|
|
const selectedProject = useSelectedProject();
|
|
const debounceSearchTerm = useDebounce(searchTerm, 500);
|
|
|
|
const {
|
|
data: employeesData = [],
|
|
isLoading,
|
|
isError,
|
|
error,
|
|
} = useOrganizationEmployees(
|
|
selectedProject,
|
|
organizationId,
|
|
debounceSearchTerm
|
|
);
|
|
|
|
const { projectEmployees, loading: employeeLodaing } =
|
|
useEmployeesByProjectAllocated(selectedProject, null);
|
|
|
|
const { data: jobRoles } = useMaster();
|
|
const { data: services } = useProjectAssignedServices(selectedProject);
|
|
|
|
const [employees, setEmployees] = useState([]);
|
|
|
|
const { mutate: handleAssignEmployee, isPending } =
|
|
useManageProjectAllocation({
|
|
onSuccessCallback: () => {
|
|
closeModal();
|
|
},
|
|
onErrorCallback: () => {
|
|
closeModal();
|
|
},
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (employeesData?.data?.length > 0) {
|
|
const available = employeesData.data.filter((emp) => {
|
|
const projEmp = projectEmployees.find((pe) => pe.employeeId === emp.id);
|
|
return !projEmp || projEmp.isActive === false;
|
|
});
|
|
|
|
setEmployees(
|
|
available.map((emp) => ({
|
|
...emp,
|
|
isChecked: false,
|
|
jobRole: emp?.jobRoleId || null,
|
|
serviceId: "",
|
|
errors: {},
|
|
}))
|
|
);
|
|
}
|
|
}, [employeesData, projectEmployees, organizationId]);
|
|
|
|
const handleCheckboxChange = (index) => {
|
|
setEmployees((prev) => {
|
|
const newArr = [...prev];
|
|
newArr[index].isChecked = !newArr[index].isChecked;
|
|
newArr[index].errors = {};
|
|
return newArr;
|
|
});
|
|
};
|
|
|
|
const handleSelectChange = (index, field, value) => {
|
|
setEmployees((prev) => {
|
|
const newArr = [...prev];
|
|
newArr[index][field] = value;
|
|
newArr[index].errors[field] = "";
|
|
return newArr;
|
|
});
|
|
};
|
|
|
|
const onSubmit = () => {
|
|
const checkedEmployees = employees.filter((emp) => emp.isChecked);
|
|
|
|
setEmployees((prev) => prev.map((emp) => ({ ...emp, errors: {} })));
|
|
|
|
if (checkedEmployees.length === 0) {
|
|
showToast("Select at least one employee", "info");
|
|
return;
|
|
}
|
|
|
|
let hasError = false;
|
|
const newEmployees = employees.map((emp) => {
|
|
const empErrors = {};
|
|
if (emp.isChecked) {
|
|
if (!emp.jobRole) {
|
|
empErrors.jobRole = "Job role is required";
|
|
hasError = true;
|
|
}
|
|
if (!emp.serviceId) {
|
|
empErrors.serviceId = "Service is required";
|
|
hasError = true;
|
|
}
|
|
}
|
|
return { ...emp, errors: empErrors };
|
|
});
|
|
|
|
setEmployees(newEmployees);
|
|
|
|
if (hasError) return; // stop submit if validation fails
|
|
|
|
const payload = checkedEmployees.map((emp) => ({
|
|
employeeId: emp.id,
|
|
jobRoleId: emp.jobRole,
|
|
serviceId: emp.serviceId,
|
|
projectId: selectedProject,
|
|
status: true,
|
|
}));
|
|
|
|
handleAssignEmployee({ payload,actionType:"assign"} );
|
|
|
|
setEmployees((prev) =>
|
|
prev.map((emp) => ({
|
|
...emp,
|
|
isChecked: false,
|
|
jobRole: "",
|
|
serviceId: "",
|
|
errors: {},
|
|
}))
|
|
);
|
|
};
|
|
|
|
if (isLoading) {
|
|
return ( <div className="page-min-h d-flex justify-content-center align-items-center "><p className="text-muted">Loading employees...</p></div>) ;
|
|
}
|
|
|
|
if (isError) {
|
|
return (
|
|
<div className="page-min-h d-flex justify-content-center align-items-center ">
|
|
{error?.status === 400 ? (
|
|
<p className="m-0">Enter employee you want to find.</p>
|
|
) : (
|
|
<p className="m-0 dange-text">Something went wrong. Please try again later.</p>
|
|
)}
|
|
</div>
|
|
|
|
);
|
|
}
|
|
|
|
if (employees.length === 0) {
|
|
return(<div className="page-min-h d-flex justify-content-center align-items-center "><p className="text-muted">No available employees to assign.</p></div>) ;
|
|
}
|
|
|
|
|
|
return (
|
|
<div className=" position-relative">
|
|
<table className="table" style={{ maxHeight: "80px", overflowY: "auto" }}>
|
|
<thead className=" position-sticky top-0">
|
|
<tr>
|
|
<th>Employee</th>
|
|
<th>Service</th>
|
|
<th>Job Role</th>
|
|
<th>Select</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{employees.map((emp, index) => (
|
|
<tr key={emp.id}>
|
|
<td>
|
|
<div className="d-flex align-items-center">
|
|
<Avatar firstName={emp.firstName} lastName={emp.lastName} />
|
|
<span className="ms-2 fw-semibold">
|
|
{emp.firstName} {emp.lastName}
|
|
</span>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<select
|
|
value={emp.serviceId}
|
|
disabled={!emp.isChecked}
|
|
onChange={(e) =>
|
|
handleSelectChange(index, "serviceId", e.target.value)
|
|
}
|
|
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
|
|
emp.errors.serviceId ? "is-invalid" : ""
|
|
}`}
|
|
>
|
|
<option value="">Select Service</option>
|
|
{services?.map((s) => (
|
|
<option key={s.id} value={s.id}>
|
|
{s.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
{emp.errors.serviceId && (
|
|
<div className="danger-text">{emp.errors.serviceId}</div>
|
|
)}
|
|
</td>
|
|
<td>
|
|
<select
|
|
value={emp.jobRole}
|
|
disabled={!emp.isChecked}
|
|
onChange={(e) =>
|
|
handleSelectChange(index, "jobRole", e.target.value)
|
|
}
|
|
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
|
|
emp.errors.jobRole ? "is-invalid" : ""
|
|
}`}
|
|
>
|
|
<option value="">Select Job Role</option>
|
|
{jobRoles?.map((r) => (
|
|
<option key={r.id} value={r.id}>
|
|
{r.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
{emp.errors.jobRole && (
|
|
<div className="danger-text">{emp.errors.jobRole}</div>
|
|
)}
|
|
</td>
|
|
<td>
|
|
<input
|
|
type="checkbox"
|
|
className="form-check-input"
|
|
checked={emp.isChecked}
|
|
onChange={() => handleCheckboxChange(index)}
|
|
/>
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
<div className="position-sticky bottom-0 bg-white d-flex justify-content-end gap-3 z-25 ">
|
|
<button
|
|
type="button"
|
|
className="btn btn-sm btn-label-secondary"
|
|
onClick={() => closeModal()}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button onClick={onSubmit} className="btn btn-primary">
|
|
{isPending ? "Please Wait..." : "Assign to Project"}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default TeamEmployeeList;
|