387 lines
13 KiB
JavaScript
387 lines
13 KiB
JavaScript
import React, { useState, useEffect, useCallback } from "react";
|
|
import MapUsers from "./MapUsers";
|
|
import { Link, NavLink, useNavigate } from "react-router-dom";
|
|
|
|
import showToast from "../../services/toastService";
|
|
import Avatar from "../common/Avatar";
|
|
import moment from "moment";
|
|
|
|
import ProjectRepository from "../../repositories/ProjectRepository";
|
|
import { useDispatch } from "react-redux";
|
|
import { changeMaster } from "../../slices/localVariablesSlice";
|
|
import useMaster from "../../hooks/masterHook/useMaster";
|
|
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
|
import { ASSIGN_TO_PROJECT } from "../../utils/constants";
|
|
import ConfirmModal from "../common/ConfirmModal";
|
|
import eventBus from "../../services/eventBus";
|
|
|
|
const Teams = ({ project }) => {
|
|
const dispatch = useDispatch();
|
|
|
|
const { data, loading } = useMaster();
|
|
const [isModalOpen, setIsModelOpen] = useState(false);
|
|
const [error, setError] = useState("");
|
|
const [empJobRoles, setEmpJobRoles] = useState(null);
|
|
const [employees, setEmployees] = useState([]);
|
|
const [filteredEmployees, setFilteredEmployees] = useState([]);
|
|
const [removingEmployeeId, setRemovingEmployeeId] = useState(null);
|
|
const [assignedLoading, setAssignedLoading] = useState(false);
|
|
const [ employeeLodaing, setEmployeeLoading ] = useState( false );
|
|
const [ activeEmployee, setActiveEmployee ] = useState( true )
|
|
const [deleteEmployee,setDeleteEmplyee] = useState(null)
|
|
|
|
const navigate = useNavigate();
|
|
|
|
const HasAssignUserPermission = useHasUserPermission( ASSIGN_TO_PROJECT );
|
|
const[IsDeleteModal,setIsDeleteModal] = useState(false)
|
|
|
|
const fetchEmployees = async () => {
|
|
try {
|
|
setEmployeeLoading(true);
|
|
|
|
// if (!empRoles) {
|
|
ProjectRepository.getProjectAllocation(project.id)
|
|
.then((response) => {
|
|
setEmployees(response.data);
|
|
setFilteredEmployees( response.data.filter( ( emp ) => emp.isActive ) );
|
|
setEmployeeLoading(false);
|
|
})
|
|
.catch((error) => {
|
|
setError("Failed to fetch data.");
|
|
setEmployeeLoading(false);
|
|
});
|
|
} catch (err) {
|
|
setError("Failed to fetch activities.");
|
|
}
|
|
};
|
|
|
|
const submitAllocations = (items,added) => {
|
|
ProjectRepository.manageProjectAllocation(items)
|
|
.then((response) => {
|
|
fetchEmployees();
|
|
if ( added )
|
|
{
|
|
showToast("Employee Assigned Successfully", "success");
|
|
}else{
|
|
showToast("Removed Employee Successfully", "success");
|
|
}
|
|
setRemovingEmployeeId(null);
|
|
setAssignedLoading( false );
|
|
setDeleteEmplyee( null )
|
|
closeDeleteModal()
|
|
})
|
|
.catch((error) => {
|
|
const message = error.response.data.message || error.message || "Error Occured during Api Call";
|
|
showToast( message, "error" );
|
|
closeDeleteModal()
|
|
});
|
|
};
|
|
|
|
const removeAllocation = (item) => {
|
|
setRemovingEmployeeId(item.id);
|
|
submitAllocations([
|
|
{
|
|
empID: item.employeeId,
|
|
jobRoleId: item.jobRoleId,
|
|
projectId: project.id,
|
|
status: false,
|
|
},
|
|
] ,false);
|
|
|
|
};
|
|
|
|
const handleEmpAlicationFormSubmit = (allocaionObj) => {
|
|
let items = allocaionObj.map((item) => {
|
|
return {
|
|
empID: item.empID,
|
|
jobRoleId: item.jobRoleId,
|
|
projectId: project.id,
|
|
status: true,
|
|
};
|
|
});
|
|
|
|
submitAllocations(items, true);
|
|
|
|
// Force switch to active view after assignment
|
|
setActiveEmployee(true);
|
|
setFilteredEmployees(employees.filter((emp) => emp.isActive));
|
|
|
|
// Also update dropdown select if needed
|
|
const dropdown = document.querySelector('select[name="DataTables_Table_0_length"]');
|
|
if (dropdown) dropdown.value = "true";
|
|
};
|
|
|
|
|
|
const getRole = (jobRoleId) => {
|
|
if (loading) return "Loading...";
|
|
if (!Array.isArray(empJobRoles)) return "Unassigned";
|
|
if (!jobRoleId) return "Unassigned";
|
|
|
|
const role = empJobRoles.find((b) => b.id == jobRoleId);
|
|
return role ? role.name : "Unassigned";
|
|
};
|
|
const openModel = () => {
|
|
setIsModelOpen(true);
|
|
};
|
|
|
|
const onModelClose = () => {
|
|
setIsModelOpen(false);
|
|
const modalElement = document.getElementById("user-model");
|
|
if (modalElement) {
|
|
modalElement.classList.remove("show");
|
|
modalElement.style.display = "none";
|
|
document.body.classList.remove("modal-open");
|
|
document.querySelector(".modal-backdrop").remove();
|
|
}
|
|
const modalBackdropElement = document.querySelector(".modal-backdrop");
|
|
if (modalBackdropElement) {
|
|
modalBackdropElement.remove();
|
|
}
|
|
document.body.style.overflow = "auto";
|
|
};
|
|
|
|
useEffect(() => {
|
|
dispatch(changeMaster("Job Role"));
|
|
}, [dispatch]);
|
|
|
|
|
|
useEffect(() => {
|
|
fetchEmployees();
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
if (data) {
|
|
setEmpJobRoles(data);
|
|
}
|
|
}, [data]);
|
|
|
|
const handleFilterEmployee = (e) => {
|
|
const filterValue = e.target.value;
|
|
if ( filterValue === "true" )
|
|
{
|
|
setActiveEmployee(true)
|
|
setFilteredEmployees(employees.filter((emp) => emp.isActive));
|
|
} else {
|
|
setFilteredEmployees( employees.filter( ( emp ) => !emp.isActive ) );
|
|
setActiveEmployee(false)
|
|
}
|
|
};
|
|
|
|
const deleteModalOpen = (item) =>
|
|
{
|
|
setDeleteEmplyee(item)
|
|
setIsDeleteModal(true)
|
|
}
|
|
const closeDeleteModal = ()=> setIsDeleteModal(false)
|
|
|
|
const handler = useCallback(
|
|
(msg) => {
|
|
if (msg.projectIds.some((item) => item === project.id)) {
|
|
fetchEmployees();
|
|
}
|
|
},
|
|
[]
|
|
);
|
|
|
|
useEffect(() => {
|
|
eventBus.on("assign_project_all", handler);
|
|
return () => eventBus.off("assign_project_all", handler);
|
|
}, [handler]);
|
|
|
|
const employeeHandler = useCallback(
|
|
(msg) => {
|
|
if(filteredEmployees.some((item) => item.employeeId == msg.employeeId)){
|
|
fetchEmployees();
|
|
}
|
|
},[filteredEmployees]
|
|
);
|
|
|
|
useEffect(() => {
|
|
eventBus.on("employee",employeeHandler);
|
|
return () => eventBus.off("employee",employeeHandler)
|
|
},[employeeHandler])
|
|
|
|
return (
|
|
<>
|
|
<div
|
|
className="modal fade"
|
|
id="user-model"
|
|
tabIndex="-1"
|
|
aria-labelledby="userModalLabel"
|
|
aria-hidden="true"
|
|
>
|
|
<MapUsers
|
|
projectId={project?.id}
|
|
onClose={onModelClose}
|
|
empJobRoles={empJobRoles}
|
|
onSubmit={handleEmpAlicationFormSubmit}
|
|
allocation={employees}
|
|
assignedLoading={assignedLoading}
|
|
setAssignedLoading={setAssignedLoading}
|
|
></MapUsers>
|
|
</div>
|
|
|
|
|
|
{IsDeleteModal && (
|
|
<div
|
|
className={`modal fade ${IsDeleteModal ? "show" : ""}`}
|
|
tabIndex="-1"
|
|
role="dialog"
|
|
style={{
|
|
display: IsDeleteModal ? "block" : "none",
|
|
backgroundColor: IsDeleteModal ? "rgba(0,0,0,0.5)" : "transparent",
|
|
}}
|
|
aria-hidden="false"
|
|
>
|
|
|
|
<ConfirmModal
|
|
type={"delete"}
|
|
header={"Removed Employee"}
|
|
message={"Are you sure you want delete?"}
|
|
onSubmit={removeAllocation}
|
|
onClose={closeDeleteModal}
|
|
loading={employeeLodaing}
|
|
paramData={deleteEmployee}
|
|
/>
|
|
</div>
|
|
)}
|
|
|
|
<div className="card card-action mb-6">
|
|
<div className="card-body">
|
|
<div className="row">
|
|
<div className="col-12 d-flex justify-content-between mb-1">
|
|
<div
|
|
className="dataTables_length text-start py-2 px-2"
|
|
id="DataTables_Table_0_length"
|
|
>
|
|
<label>
|
|
<select
|
|
name="DataTables_Table_0_length"
|
|
aria-controls="DataTables_Table_0"
|
|
className="form-select form-select-sm"
|
|
onChange={handleFilterEmployee}
|
|
// value={false}
|
|
aria-label=""
|
|
defaultValue="true"
|
|
>
|
|
<option value="true">Active Employee</option>
|
|
<option value="false">In-Active Employee</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
className={`link-button btn-sm m-1 ${
|
|
HasAssignUserPermission ? "" : "d-none"
|
|
}`}
|
|
data-bs-toggle="modal"
|
|
data-bs-target="#user-model"
|
|
>
|
|
<i className="bx bx-plus-circle me-2"></i>
|
|
Assign Employee
|
|
</button>
|
|
</div>
|
|
</div>
|
|
<div className="table-responsive text-nowrap">
|
|
{employeeLodaing && <p>Loading..</p>}
|
|
{!employeeLodaing && employees && employees.length > 0 && (
|
|
<table className="table ">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Assigned Date</th>
|
|
{!activeEmployee && <th>Release Date</th>}
|
|
<th>Project Role</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="table-border-bottom-0">
|
|
{filteredEmployees &&
|
|
filteredEmployees.map((item) => (
|
|
<tr key={item.id}>
|
|
<td>
|
|
<div className="d-flex justify-content-start align-items-center">
|
|
<Avatar
|
|
firstName={item.firstName}
|
|
lastName={item.lastName}
|
|
></Avatar>
|
|
<div className="d-flex flex-column">
|
|
<a
|
|
onClick={() =>
|
|
navigate(`/employee/${item.employeeId}?for=account`)
|
|
}
|
|
className="text-heading text-truncate cursor-pointer"
|
|
>
|
|
<span className="fw-normal">
|
|
{item.firstName} {item.middleName}{" "}
|
|
{item.lastName}
|
|
</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</td>
|
|
<td>
|
|
{" "}
|
|
{moment(item.allocationDate).format(
|
|
"DD-MMM-YYYY"
|
|
)}{" "}
|
|
</td>
|
|
{!activeEmployee && <td>
|
|
{item.reAllocationDate
|
|
? moment(item.reAllocationDate).format(
|
|
"DD-MMM-YYYY"
|
|
)
|
|
: "Present"}
|
|
</td>}
|
|
<td>
|
|
<span className="badge bg-label-primary me-1">
|
|
{getRole(item.jobRoleId)}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
{item.isActive && (
|
|
<button
|
|
aria-label="Delete"
|
|
type="button"
|
|
title="Remove from project"
|
|
className="btn p-0 dropdown-toggle hide-arrow"
|
|
onClick={() => deleteModalOpen(item)}
|
|
>
|
|
{" "}
|
|
{removingEmployeeId === item.id ? (
|
|
<div
|
|
className="spinner-border spinner-border-sm text-primary"
|
|
role="status"
|
|
>
|
|
<span className="visually-hidden">
|
|
Loading...
|
|
</span>
|
|
</div>
|
|
) : (
|
|
<i className="bx bx-trash me-1 text-danger"></i>
|
|
)}
|
|
</button>
|
|
)}
|
|
{!item.isActive && <span>Not in project</span>}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
)}
|
|
{!employeeLodaing && filteredEmployees.length === 0 && (
|
|
<div className="text-center text-muted py-3">
|
|
{activeEmployee
|
|
? "No active employees assigned to the project"
|
|
: "No inactive employees assigned to the project"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default Teams;
|