marco.pms.web/src/components/Project/Team/TeamEmployeeList.jsx

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;