Merge pull request 'pramod_Bug#23EmplAssignToProject' (#44) from pramod_Bug#23EmplAssignToProject into Issues_April_4W

Reviewed-on: #44
This commit is contained in:
Vikas Nale 2025-04-21 11:12:42 +00:00
commit d4fed67912
6 changed files with 260 additions and 488 deletions

View File

@ -1,60 +0,0 @@
import React, { useState, useEffect } from 'react';
import RoleBadge from './RoleBadge';
import Avatar from '../common/Avatar';
const AssignEmployeeCard = ({
employee,
jobRoles,
isChecked,
onRoleChange,
onCheckboxChange,
}) => {
const [currentJobRole, setCurrentJobRole] = useState(employee.jobRoleId);
useEffect(() => {
setCurrentJobRole(employee.jobRoleId);
}, [employee.jobRoleId]);
const handleRoleChange = (newRoleId) => {
setCurrentJobRole(newRoleId);
onRoleChange(employee.id, newRoleId);
};
const handleCheckboxChange = () => {
if (!employee.isActive) {
onCheckboxChange(employee.id);
}
};
return (
<div className="assign-employee-card d-flex justify-content-between align-items-center px-2 py-1 border-bottom">
<div className="d-flex align-items-center flex-grow-1">
<div className="avatar me-3">
<Avatar firstName={employee.firstName} lastName={employee.l} />
</div>
<div className="employee-info">
<p className="mb-0 d-none d-sm-block">{employee.firstName} {employee.lastName} </p>
</div>
</div>
<div className="d-flex align-items-center">
<RoleBadge
JobRoles={jobRoles}
currentJobRole={currentJobRole}
onRoleChange={handleRoleChange}
/>
<input
className="form-check-input ms-3"
type="checkbox"
checked={isChecked}
disabled={employee.isActive}
onChange={handleCheckboxChange}
/>
</div>
</div>
);
};
export default AssignEmployeeCard;

View File

@ -0,0 +1,130 @@
import React, { useState, useEffect } from "react";
import Avatar from "../common/Avatar";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
export const roleSchema = z.object({
jobRole: z.string().min(1, "Job Role is required"),
isChecked: z.literal(true, {
errorMap: () => ({ message: "You must check the box first" }),
}),
} );
const AssignEmployeeTable = ({
employee,
jobRoles,
isChecked,
onRoleChange,
onCheckboxChange,
}) => {
const [currentJobRole, setCurrentJobRole] = useState(employee.jobRoleId?.toString() || "");
const {
register,
handleSubmit,
formState: { errors },
setValue,
trigger,
getValues,
} = useForm({
resolver: zodResolver(roleSchema),
defaultValues: {
jobRole: currentJobRole,
isChecked: isChecked || false,
},
});
useEffect(() => {
const jobRoleStr = employee.jobRoleId?.toString() || "";
setCurrentJobRole(jobRoleStr);
setValue("jobRole", jobRoleStr);
setValue("isChecked", isChecked);
}, [employee.jobRoleId, isChecked, setValue]);
const onSubmit = (data) => {
setCurrentJobRole(data.jobRole);
onRoleChange(employee.id, data.jobRole);
};
const handleRoleSelect = async (e) => {
const value = e.target.value;
setValue("jobRole", value);
const isValid = await trigger(); // Validate the form
if (isValid) {
const values = getValues();
onRoleChange(employee.id, values.jobRole);
}
};
const handleCheckboxChange = async () => {
if (!employee.isActive) {
setValue("isChecked", !isChecked);
const isValid = await trigger(); // Validate after setting value
if (isValid) {
const values = getValues();
onCheckboxChange(employee.id);
onRoleChange(employee.id, values.jobRole); // Update role if it changed
}
}
};
return (
<tr className="odd py-0">
<td className="sorting_1 py-0" colSpan={2}>
<div className="d-flex justify-content-start align-items-center user-name py-0">
<Avatar firstName={employee.firstName} lastName={employee.lastName} />
<div className="d-flex flex-column">
<span className="text-heading text-truncate cursor-pointer fw-normal">
{employee.firstName} {employee.lastName}
</span>
</div>
</div>
</td>
<td className="text-end justify-content-end d-none d-sm-table-cell">
<div className="d-flex justify-content-end">
<select
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${errors.jobRole ? "is-invalid" : ""}`}
{...register("jobRole")}
onChange={handleRoleSelect}
>
<option value="">Select a Job Role</option>
{jobRoles.map((role) => (
<option key={role.id} value={role.id}>
{role.name}
</option>
))}
</select>
</div>
{errors.jobRole && (
<div className="invalid-feedback">{errors.jobRole.message}</div>
)}
{errors.isChecked && (
<div className="invalid-feedback d-block">
{errors.isChecked.message}
</div>
)}
</td>
<td className="text-start d-none d-sm-table-cell">
<input
className="form-check-input ms-3"
type="checkbox"
checked={isChecked}
disabled={employee.isActive}
onChange={handleCheckboxChange}
/>
</td>
</tr>
);
};
export default AssignEmployeeTable;

View File

@ -1,8 +1,9 @@
import React, { useState, useEffect } from "react";
import EmployeeRepository from "../../repositories/EmployeeRepository";
import AssignEmployeeCard from "./AssignEmployeeCard";
import {useAllEmployees} from "../../hooks/useEmployees";
import { useAllEmployees } from "../../hooks/useEmployees";
import useSearch from "../../hooks/useSearch";
import AssignEmployeeTable from "./AssignEmployeeTable";
import showToast from "../../services/toastService";
const MapUsers = ({
projectId,
@ -11,61 +12,59 @@ const MapUsers = ({
onSubmit,
allocation,
}) => {
const {employeesList, loading, error} = useAllEmployees();
const { employeesList, loading, error } = useAllEmployees();
const [selectedEmployees, setSelectedEmployees] = useState([]);
const [searchText, setSearchText] = useState("");
const handleAllocationData = Array.isArray( allocation ) ? allocation : [];
const handleAllocationData = Array.isArray(allocation) ? allocation : [];
const allocationEmployees = employeesList.map((employee) => {
const allocationItem = handleAllocationData.find((alloc) => alloc.employeeId === employee.id);
const allocationItem = handleAllocationData.find(
(alloc) => alloc.employeeId === employee.id
);
return {
...employee,
isActive: allocationItem ? allocationItem.isActive : false,
jobRoleId: allocationItem ? allocationItem.jobRoleId : employee.jobRoleId,
};
} );
});
function parseDate(dateStr) {
return new Date(dateStr.split(".")[0]);
}
function parseDate(dateStr) {
return new Date(dateStr.split('.')[0]);
}
const latestAllocations = handleAllocationData.reduce((acc, alloc) => {
const latestAllocations = handleAllocationData.reduce((acc, alloc) => {
const existingAlloc = acc[alloc.employeeId];
if (!existingAlloc) {
acc[alloc.employeeId] = alloc;
acc[alloc.employeeId] = alloc;
} else {
const existingDate = parseDate(
existingAlloc.reAllocationDate || existingAlloc.allocationDate
);
const newDate = parseDate(alloc.reAllocationDate || alloc.allocationDate);
const existingDate = parseDate(existingAlloc.reAllocationDate || existingAlloc.allocationDate);
const newDate = parseDate(alloc.reAllocationDate || alloc.allocationDate);
if (newDate > existingDate) {
acc[alloc.employeeId] = alloc;
}
if (newDate > existingDate) {
acc[alloc.employeeId] = alloc;
}
}
return acc;
}, {});
}, {});
const allocationEmployeesData = employeesList
const allocationEmployeesData = employeesList
.map((employee) => {
const allocationItem = latestAllocations[employee.id];
return {
...employee,
isActive: allocationItem ? allocationItem.isActive : false,
};
const allocationItem = latestAllocations[employee.id];
return {
...employee,
isActive: allocationItem ? allocationItem.isActive : false,
};
})
.filter( ( employee ) => employee.isActive === false );
.filter((employee) => employee.isActive === false);
const { filteredData, setSearchQuery } = useSearch(allocationEmployeesData, searchText);
const { filteredData, setSearchQuery } = useSearch(
allocationEmployeesData,
searchText
);
const handleRoleChange = (employeeId, newRoleId) => {
setSelectedEmployees((prevSelectedEmployees) =>
@ -78,7 +77,9 @@ const allocationEmployeesData = employeesList
const handleCheckboxChange = (employeeId) => {
setSelectedEmployees((prevSelectedEmployees) => {
const updatedEmployees = [...prevSelectedEmployees];
const employeeIndex = updatedEmployees.findIndex((emp) => emp.id === employeeId);
const employeeIndex = updatedEmployees.findIndex(
(emp) => emp.id === employeeId
);
if (employeeIndex !== -1) {
const isSelected = !updatedEmployees[employeeIndex].isSelected;
@ -93,14 +94,19 @@ const allocationEmployeesData = employeesList
});
};
const handleSubmit = () => {
const selected = selectedEmployees
.filter((emp) => emp.isSelected)
.map((emp) => ({ empID: emp.id, jobRoleId: emp.jobRoleId }));
onSubmit(selected);
if (selected.length > 0) {
console.log(selected);
onSubmit(selected);
setSelectedEmployees([]);
} else {
showToast("Please select Employee", "error");
}
};
console.log(allocationEmployeesData)
return (
<>
<div className="modal-dialog modal-dialog-scrollable mx-sm-auto mx-1 modal-lg modal-simple modal-edit-user">
@ -118,20 +124,39 @@ const allocationEmployeesData = employeesList
</div>
</div>
<div className="modal-body p-sm-4 p-0">
<ul className="list-group border-none mb-2">
{loading && !employeesList && <p>Loading...</p>}
{filteredData.map((emp) => (
<AssignEmployeeCard
key={emp.id}
employee={emp}
jobRoles={empJobRoles}
isChecked={emp.isSelected}
onRoleChange={handleRoleChange}
onCheckboxChange={handleCheckboxChange}
/>
) )}
{filteredData.length == 0 && <p>No Data Found</p>}
</ul>
<table
className="datatables-users table border-top dataTable no-footer dtr-column "
id="DataTables_Table_0"
aria-describedby="DataTables_Table_0_info"
style={{ width: "100%" }}
>
<thead></thead>
<tbody>
{loading && <p>Loading...</p>}
{!loading &&
allocationEmployeesData.length === 0 &&
filteredData.length === 0 && <p>No employees available.</p>}
{!loading &&
allocationEmployeesData.length > 0 &&
filteredData.length === 0 && (
<p>No matching employees found.</p>
)}
{filteredData.length > 0 &&
filteredData.map((emp) => (
<AssignEmployeeTable
key={emp.id}
employee={emp}
jobRoles={empJobRoles}
isChecked={emp.isSelected}
onRoleChange={handleRoleChange}
onCheckboxChange={handleCheckboxChange}
/>
))}
</tbody>
</table>
</div>
<div className="modal-footer">
<button className="btn btn-sm btn-success" onClick={handleSubmit}>

View File

@ -1,57 +0,0 @@
import React from 'react';
const RoleBadge = ({ JobRoles, currentJobRole, onRoleChange }) => {
const handleRoleSelect = (newRoleId) => {
onRoleChange(newRoleId);
};
const validJobRoles = Array.isArray(JobRoles) ? JobRoles : [];
const selectedRole = validJobRoles.find((role) => role.id === currentJobRole);
const selectedRoleName = selectedRole ? selectedRole.name : 'Select Job Role';
if (validJobRoles.length === 0) {
return (
<div className="badge bg-label-warning d-flex align-items-center lead">
<span className="ms-1 px-2 py-1">No Roles Available</span>
</div>
);
}
return (
<div className="dropdown py-2 px-3 cursor-pointer">
<div
className=" dropdown-toggle d-flex align-items-center lead"
id="dropdownMenuButton"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<span className="ms-1 px-2 py-1">
<small>{selectedRoleName}</small>
</span>
</div>
<ul
className="dropdown-menu"
aria-labelledby="dropdownMenuButton"
style={{ maxHeight: '200px', overflowY: 'auto' }}
>
{validJobRoles.map((role) => (
<li key={role.id}>
<a
className="dropdown-item"
onClick={(e) => {
e.preventDefault();
handleRoleSelect(role.id);
}}
>
{role.name}
</a>
</li>
))}
</ul>
</div>
);
};
export default RoleBadge;

View File

@ -5,28 +5,24 @@ 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 { 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";
const Teams = ( {project} ) =>
{
const dispatch = useDispatch()
dispatch( changeMaster( 'Job Role' ) )
const {data,loading} = useMaster()
const Teams = ({ project }) => {
const dispatch = useDispatch();
dispatch(changeMaster("Job Role"));
const { data, loading } = useMaster();
const [isModalOpen, setIsModelOpen] = useState(false);
const [error, setError] = useState("");
const [empJobRoles, setEmpJobRoles] = useState(null);
const [clearFormTrigger, setClearFormTrigger] = useState(false);
const [employees, setEmployees] = useState([]);
const [ filteredEmployees, setFilteredEmployees ] = useState( [] );
const HasAssignUserPermission = useHasUserPermission( ASSIGN_TO_PROJECT )
const [filteredEmployees, setFilteredEmployees] = useState([]);
const HasAssignUserPermission = useHasUserPermission(ASSIGN_TO_PROJECT);
const fetchEmployees = async () => {
try {
@ -69,7 +65,6 @@ const Teams = ( {project} ) =>
};
const handleEmpAlicationFormSubmit = (allocaionObj) => {
let items = allocaionObj.map((item) => {
return {
empID: item.empID,
@ -79,12 +74,10 @@ const Teams = ( {project} ) =>
};
});
submitAllocations(items)
submitAllocations(items);
};
const getRole = ( jobRoleId ) =>
{
const getRole = (jobRoleId) => {
if (loading) return "Loading...";
if (!Array.isArray(empJobRoles)) return "Unassigned";
if (!jobRoleId) return "Unassigned";
@ -105,25 +98,22 @@ const Teams = ( {project} ) =>
document.body.classList.remove("modal-open");
document.querySelector(".modal-backdrop").remove();
}
const modalBackdropElement = document.querySelector('.modal-backdrop');
const modalBackdropElement = document.querySelector(".modal-backdrop");
if (modalBackdropElement) {
modalBackdropElement.remove();
}
document.body.style.overflow = 'auto'
document.body.style.overflow = "auto";
};
useEffect(() => {
fetchEmployees();
}, [] );
}, []);
useEffect( () =>
{
if ( data )
{
setEmpJobRoles(data)
useEffect(() => {
if (data) {
setEmpJobRoles(data);
}
},[data])
}, [data]);
const handleFilterEmployee = (e) => {
const filterValue = e.target.value;
@ -136,24 +126,24 @@ const Teams = ( {project} ) =>
return (
<>
{isModalOpen && (
<div
className={`modal fade `}
id="user-model"
tabIndex="-1"
aria-hidden="true"
>
<MapUsers
projectId={project.id}
onClose={onModelClose}
empJobRoles={empJobRoles}
onSubmit={handleEmpAlicationFormSubmit}
allocation={employees}
clearTrigger={clearFormTrigger}
onClearComplete={() => setClearFormTrigger(false)}
></MapUsers>
</div>
)}
<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}
clearTrigger={clearFormTrigger}
onClearComplete={() => setClearFormTrigger(false)}
></MapUsers>
</div>
<div className="card card-action mb-6">
<div className="card-body">
<div className="row">
@ -179,13 +169,14 @@ const Teams = ( {project} ) =>
</div>
<button
type="button"
className={`link-button link-button-sm m-1 ${HasAssignUserPermission ? "":"d-none"}`}
className={`link-button btn-sm m-1 ${
HasAssignUserPermission ? "" : "d-none"
}`}
data-bs-toggle="modal"
data-bs-target="#user-model"
onClick={() => openModel()}
>
<i className="bx bx-plus-circle me-2"></i>
Assign Users
Assign Employee
</button>
</div>
</div>
@ -198,7 +189,6 @@ const Teams = ( {project} ) =>
<th>Assigned Date</th>
<th>Release Date</th>
<th>Role</th>
<th>Actions</th>
</tr>
</thead>
@ -278,259 +268,3 @@ const Teams = ( {project} ) =>
export default Teams;
// const Teams = ({ project }) => {
// const dispatch = useDispatch()
// const {data, loading} = useMaster()
// const navigate = useNavigate()
// const [isModalOpen, setIsModelOpen] = useState(false);
// const [error, setError] = useState("");
// const [empJobRoles, setEmpJobRoles] = useState([]);
// const [clearFormTrigger, setClearFormTrigger] = useState(false);
// const [employees, setEmployees] = useState([]);
// const [ filteredEmployees, setFilteredEmployees ] = useState( [] );
// useEffect( () =>
// {
// dispatch( changeMaster( 'Job Role' ) )
// },[dispatch])
// const fetchEmployees = useCallback(async () => {
// try {
// const response = await ProjectRepository.getProjectAllocation(project.id);
// setEmployees(response.data);
// setFilteredEmployees(response.data.filter((emp) => emp.isActive));
// } catch (error) {
// console.error(error);
// setError("Failed to fetch data.");
// }
// }, [project.id]);
// useEffect(() => {
// fetchEmployees();
// }, [fetchEmployees]);
// useEffect(() => {
// if (data) setEmpJobRoles(data);
// }, [ data ] );
// const submitAllocations = (items) => {
// ProjectRepository.manageProjectAllocation(items)
// .then((response) => {
// showToast("Details updated successfully.", "success");
// setClearFormTrigger(true);
// fetchEmployees();
// })
// .catch((error) => {
// showToast(error.message, "error");
// });
// };
// const removeAllocation = (item) => {
// submitAllocations([
// {
// empID: item.employeeId,
// jobRoleId: item.jobRoleId,
// projectId: project.id,
// status: false,
// },
// ]);
// };
// const handleEmpAlicationFormSubmit = (allocaionObj) => {
// console.log("Form submitted:", allocaionObj);
// let items = allocaionObj.map((item) => {
// return {
// empID: item.empID,
// jobRoleId: item.jobRoleId,
// projectId: project.id,
// status: true,
// };
// });
// submitAllocations(items)
// };
// 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'
// };
// const handleFilterEmployee = (e) => {
// const filterValue = e.target.value === "true";
// setFilteredEmployees(
// employees.filter((emp) => emp.isActive === filterValue)
// );
// };
// return (
// <>
// {isModalOpen && (
// <div
// className={`modal fade `}
// id="user-model"
// tabIndex="-1"
// aria-hidden="true"
// >
// <MapUsers
// projectId={project.id}
// onClose={onModelClose}
// empJobRoles={empJobRoles}
// onSubmit={handleEmpAlicationFormSubmit}
// allocation={employees}
// clearTrigger={clearFormTrigger}
// onClearComplete={() => setClearFormTrigger(false)}
// ></MapUsers>
// </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}
// 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 link-button-sm m-1"
// data-bs-toggle="modal"
// data-bs-target="#user-model"
// onClick={() => openModel()}
// >
// <i className="bx bx-plus-circle me-2"></i>
// Assign Users
// </button>
// </div>
// </div>
// <div className="table-responsive text-nowrap">
// {employees && employees.length > 0 ? (
// <table className="table ">
// <thead>
// <tr>
// <th>Name</th>
// <th>Assigned Date</th>
// <th>Release Date</th>
// <th>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
// href="#"
// onClick={()=> navigate(`/employee/${item.employeeId}`)}
// className="text-heading text-truncate"
// >
// <span className="fw-medium">
// {item.firstName} {item.lastName}
// </span>
// </a>
// </div>
// </div>
// </td>
// <td>
// {" "}
// {moment(item.allocationDate).format(
// "DD-MMM-YYYY"
// )}{" "}
// </td>
// <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={() => removeAllocation(item)}
// >
// {" "}
// <i className="bx bx-trash me-1 text-danger"></i>{" "}
// </button>
// )}
// {!item.isActive && <span>Not in project</span>}
// </td>
// </tr>
// ))}
// </tbody>
// </table>
// ) : (
// <span>No employees assigned to the project</span>
// )}
// </div>
// </div>
// </div>
// </>
// );
// };
// export default Teams;

View File

@ -18,7 +18,7 @@ const TaskPlannng = () => {
const {projects,loading:project_listLoader,error:projects_error} = useProjects();
const dispatch = useDispatch();
const selectedProject = useSelector((store)=>store.localVariables.projectId);
console.log(selectedProject)
const [project, setProject] = useState(null);
const [projectDetails, setProjectDetails] = useState(null);