From 3233043cf21bee04eeadc63fedfbadd65790570a Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Fri, 26 Sep 2025 11:38:06 +0530 Subject: [PATCH 1/4] initial setup for assign emp to project --- .../Project/Team/TeamAssignToProject.jsx | 42 ++++++++ .../Project/Team/TeamEmployeeList.jsx | 12 +++ src/components/Project/{ => Team}/Teams.jsx | 98 ++++++++++++------- src/hooks/useProjectAccess.js | 15 +-- src/pages/project/ProjectDetails.jsx | 2 +- 5 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 src/components/Project/Team/TeamAssignToProject.jsx create mode 100644 src/components/Project/Team/TeamEmployeeList.jsx rename src/components/Project/{ => Team}/Teams.jsx (85%) diff --git a/src/components/Project/Team/TeamAssignToProject.jsx b/src/components/Project/Team/TeamAssignToProject.jsx new file mode 100644 index 00000000..102b0be7 --- /dev/null +++ b/src/components/Project/Team/TeamAssignToProject.jsx @@ -0,0 +1,42 @@ +import React from "react"; + +const TeamAssignToProject = () => { + return ( +
+

Assign Employee To Project

+
+
+ + +
+ +
+ + +
+
+
+ + +
+ +
+ +
+
+ ); +}; + +export default TeamAssignToProject; diff --git a/src/components/Project/Team/TeamEmployeeList.jsx b/src/components/Project/Team/TeamEmployeeList.jsx new file mode 100644 index 00000000..da62ac91 --- /dev/null +++ b/src/components/Project/Team/TeamEmployeeList.jsx @@ -0,0 +1,12 @@ +import React from 'react' + +const TeamEmployeeList = ({serviceId,organizationId}) => { + const {} = use + return ( +
+ +
+ ) +} + +export default TeamEmployeeList diff --git a/src/components/Project/Teams.jsx b/src/components/Project/Team/Teams.jsx similarity index 85% rename from src/components/Project/Teams.jsx rename to src/components/Project/Team/Teams.jsx index 3e31c0cb..f684347a 100644 --- a/src/components/Project/Teams.jsx +++ b/src/components/Project/Team/Teams.jsx @@ -1,30 +1,32 @@ import React, { useState, useEffect, useCallback } from "react"; -import MapUsers from "./MapUsers"; +import MapUsers from "../MapUsers"; import { Link, NavLink, useNavigate, useParams } from "react-router-dom"; -import showToast from "../../services/toastService"; -import Avatar from "../common/Avatar"; +import showToast from "../../../services/toastService"; +import Avatar from "../../common/Avatar"; import moment from "moment"; -import ProjectRepository from "../../repositories/ProjectRepository"; +import ProjectRepository from "../../../repositories/ProjectRepository"; import { useDispatch, useSelector } 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"; +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"; import { useEmployeesByProjectAllocated, useManageProjectAllocation, useProjectAssignedServices, -} from "../../hooks/useProjects"; -import { useSelectedProject } from "../../slices/apiDataManager"; +} from "../../../hooks/useProjects"; +import { useSelectedProject } from "../../../slices/apiDataManager"; +import GlobalModel from "../../common/GlobalModel"; +import TeamAssignToProject from "./TeamAssignToProject"; const Teams = () => { const projectId = useSelectedProject(); const dispatch = useDispatch(); - + const [AssigTeam,setAssignTeam] = useState(false) const { data, loading } = useMaster(); const [isModalOpen, setIsModelOpen] = useState(false); const [error, setError] = useState(""); @@ -38,9 +40,9 @@ const Teams = () => { const [searchTerm, setSearchTerm] = useState(""); // State for search term const [selectedService, setSelectedService] = useState(null); - const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(projectId); - const handleToggleActive = e => setActiveEmployee(e.target.checked); - + const { data: assignedServices, isLoading: servicesLoading } = + useProjectAssignedServices(projectId); + const handleToggleActive = (e) => setActiveEmployee(e.target.checked); const handleServiceChange = (e) => { setSelectedService(e.target.value); @@ -167,7 +169,8 @@ const Teams = () => { const lowercasedSearchTerm = searchTerm.toLowerCase(); const searchedAndFiltered = statusFiltered.filter((item) => { - const fullName = `${item.firstName} ${item.middleName} ${item.lastName}`.toLowerCase(); + const fullName = + `${item.firstName} ${item.middleName} ${item.lastName}`.toLowerCase(); const roleName = getRole(item.jobRoleId).toLowerCase(); const orgName = (item.organizationName || "").toLowerCase(); const serviceName = (item.serviceName || "").toLowerCase(); @@ -183,7 +186,6 @@ const Teams = () => { setFilteredEmployees(searchedAndFiltered); }, [employees, activeEmployee, searchTerm, getRole]); - useEffect(() => { filterAndSearchEmployees(); }, [employees, activeEmployee, searchTerm, filterAndSearchEmployees]); @@ -252,6 +254,13 @@ const Teams = () => { > + + {AssigTeam && ( + setAssignTeam(false)}> + + + )} + {IsDeleteModal && ( {
- {!servicesLoading && assignedServices?.length > 0 && ( - assignedServices.length > 1 ? ( + {!servicesLoading && + assignedServices?.length > 0 && + (assignedServices.length > 1 ? (
+ {HasAssignUserPermission && ( + + className={`link-button btn-primary btn-sm `} + // data-bs-toggle="modal" + // data-bs-target="#user-model" + onClick={()=>setAssignTeam(true)} + > + + Assign Employee + + )}
@@ -387,7 +406,9 @@ const Teams = () => { {!activeEmployee && ( {item.reAllocationDate - ? moment(item.reAllocationDate).format("DD-MMM-YYYY") + ? moment(item.reAllocationDate).format( + "DD-MMM-YYYY" + ) : "Present"} )} @@ -410,7 +431,9 @@ const Teams = () => { className="spinner-border spinner-border-sm text-primary" role="status" > - Loading... + + Loading... +
) : ( @@ -424,7 +447,6 @@ const Teams = () => { ))} - )} {!employeeLodaing && filteredEmployees.length === 0 && (
diff --git a/src/hooks/useProjectAccess.js b/src/hooks/useProjectAccess.js index a3ff27ac..d932b157 100644 --- a/src/hooks/useProjectAccess.js +++ b/src/hooks/useProjectAccess.js @@ -6,21 +6,24 @@ import { VIEW_PROJECTS } from "../utils/constants"; import showToast from "../services/toastService"; export const useProjectAccess = (projectId) => { + const navigate = useNavigate(); + const { data: projectPermissions, isLoading, isFetched } = useAllProjectLevelPermissions(projectId); - const canView = useHasUserPermission(VIEW_PROJECTS); - const navigate = useNavigate(); + const canView = useHasUserPermission(VIEW_PROJECTS); + + const loading = isLoading || !isFetched; useEffect(() => { - if (projectId && isFetched && !isLoading && !canView) { + if (projectId && !loading && !canView) { showToast("You don't have permission to view project details", "warning"); navigate("/projects"); } - }, [projectId, isFetched, isLoading, canView, navigate]); + }, [projectId, loading, canView, navigate]); return { canView, - loading: isLoading || !isFetched, + loading, }; -}; +}; \ No newline at end of file diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index 7503f6ce..8d2f8277 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom"; import ProjectOverview from "../../components/Project/ProjectOverview"; import AboutProject from "../../components/Project/AboutProject"; import ProjectNav from "../../components/Project/ProjectNav"; -import Teams from "../../components/Project/Teams"; +import Teams from "../../components/Project/Team/Teams"; import ProjectInfra from "../../components/Project/ProjectInfra"; import Loader from "../../components/common/Loader"; import WorkPlan from "../../components/Project/WorkPlan"; From 2ef1fcfd1db611f9545fe98ed5525aea38778af3 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 01:15:07 +0530 Subject: [PATCH 2/4] successfullly assigned employe to project --- public/assets/vendor/css/core.css | 4 + .../Project/Team/TeamAssignToProject.jsx | 94 ++-- .../Project/Team/TeamEmployeeList.jsx | 256 ++++++++++- src/components/Project/Team/Teams.jsx | 432 ++++++------------ src/hooks/useOrganization.js | 25 +- src/hooks/useProjects.js | 12 +- src/repositories/OrganizationRespository.jsx | 28 +- src/repositories/ProjectRepository.jsx | 22 +- 8 files changed, 516 insertions(+), 357 deletions(-) diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index 9be75669..5acc33c5 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -18613,6 +18613,10 @@ li:not(:first-child) .dropdown-item, min-height: 70vh !important; } +.modal-min-h-{ + min-height: 60vh !important; +} + .flex-fill { flex: 1 1 auto !important; } diff --git a/src/components/Project/Team/TeamAssignToProject.jsx b/src/components/Project/Team/TeamAssignToProject.jsx index 102b0be7..0bab7756 100644 --- a/src/components/Project/Team/TeamAssignToProject.jsx +++ b/src/components/Project/Team/TeamAssignToProject.jsx @@ -1,40 +1,78 @@ -import React from "react"; +import React, { useState } from "react"; +import TeamEmployeeList from "./TeamEmployeeList"; +import { useOrganization } from "../../../hooks/useDirectory"; +import { useOrganizationsList } from "../../../hooks/useOrganization"; +import { useProjectAssignedOrganizations } from "../../../hooks/useProjects"; +import { useSelectedProject } from "../../../slices/apiDataManager"; -const TeamAssignToProject = () => { +const TeamAssignToProject = ({ closeModal }) => { + const [searchText, setSearchText] = useState(""); + const [selectedOrg, setSelectedOrg] = useState(null); + const project = useSelectedProject(); + const { data, isLoading, isError, error } = + useProjectAssignedOrganizations(project); return (

Assign Employee To Project

-
-
- - -
-
- - +
+
+
+ {isLoading ? ( + + ) : data?.length === 0 ? ( +

No organizations found

+ ) : ( + <> + + + + )} +
+
+
+
+ + setSearchText(e.target.value)} + /> +
-
- - +
- -
- -
); }; diff --git a/src/components/Project/Team/TeamEmployeeList.jsx b/src/components/Project/Team/TeamEmployeeList.jsx index da62ac91..920d1f6f 100644 --- a/src/components/Project/Team/TeamEmployeeList.jsx +++ b/src/components/Project/Team/TeamEmployeeList.jsx @@ -1,12 +1,250 @@ -import React from 'react' +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, +} from "../../../hooks/useProjects"; +import useMaster, { useServices } from "../../../hooks/masterHook/useMaster"; +import showToast from "../../../services/toastService"; -const TeamEmployeeList = ({serviceId,organizationId}) => { - const {} = use - return ( -
- -
- ) +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 } = useServices(); + + 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 }); + + setEmployees((prev) => + prev.map((emp) => ({ + ...emp, + isChecked: false, + jobRole: "", + serviceId: "", + errors: {}, + })) + ); + }; + +if (isLoading) { + return (

Loading employees...

) ; } -export default TeamEmployeeList +if (isError) { + return ( +
+ {error?.status === 400 ? ( +

Enter employee you want to find.

+ ) : ( +

Something went wrong. Please try again later.

+ )} +
+ + ); +} + +if (employees.length === 0) { + return(

No available employees to assign.

) ; +} + + + return ( +
+ + + + + + + + + + + {employees.map((emp, index) => ( + + + + + + + ))} + +
EmployeeServiceJob RoleSelect
+
+ + + {emp.firstName} {emp.lastName} + +
+
+ + {emp.errors.serviceId && ( +
{emp.errors.serviceId}
+ )} +
+ + {emp.errors.jobRole && ( +
{emp.errors.jobRole}
+ )} +
+ handleCheckboxChange(index)} + /> +
+
+ + +
+
+ ); +}; + +export default TeamEmployeeList; diff --git a/src/components/Project/Team/Teams.jsx b/src/components/Project/Team/Teams.jsx index f684347a..0d5d501d 100644 --- a/src/components/Project/Team/Teams.jsx +++ b/src/components/Project/Team/Teams.jsx @@ -24,24 +24,20 @@ import GlobalModel from "../../common/GlobalModel"; import TeamAssignToProject from "./TeamAssignToProject"; const Teams = () => { - const projectId = useSelectedProject(); + const selectedProject = useSelectedProject(); const dispatch = useDispatch(); - const [AssigTeam,setAssignTeam] = useState(false) - const { data, loading } = useMaster(); - const [isModalOpen, setIsModelOpen] = useState(false); - const [error, setError] = useState(""); - const [empJobRoles, setEmpJobRoles] = useState(null); + const [AssigTeam, setAssignTeam] = useState(false); const [employees, setEmployees] = useState([]); const [filteredEmployees, setFilteredEmployees] = useState([]); - const [removingEmployeeId, setRemovingEmployeeId] = useState(null); - const [assignedLoading, setAssignedLoading] = useState(false); - const [activeEmployee, setActiveEmployee] = useState(true); + const [selectedEmployee, setSelectedEmployee] = useState(null); const [deleteEmployee, setDeleteEmplyee] = useState(null); const [searchTerm, setSearchTerm] = useState(""); // State for search term const [selectedService, setSelectedService] = useState(null); + const [activeEmployee,setActiveEmployee] = useState(false) const { data: assignedServices, isLoading: servicesLoading } = - useProjectAssignedServices(projectId); + useProjectAssignedServices(selectedProject); + const {data:empJobRoles,loading} = useMaster() const handleToggleActive = (e) => setActiveEmployee(e.target.checked); const handleServiceChange = (e) => { @@ -56,7 +52,7 @@ const Teams = () => { projectEmployees, loading: employeeLodaing, refetch, - } = useEmployeesByProjectAllocated(projectId, selectedService); + } = useEmployeesByProjectAllocated(selectedProject, selectedService,null,activeEmployee); const { mutate: submitAllocations, isPending, @@ -65,7 +61,6 @@ const Teams = () => { } = useManageProjectAllocation({ onSuccessCallback: () => { setRemovingEmployeeId(null); - setAssignedLoading(false); setDeleteEmplyee(null); closeDeleteModal(); }, @@ -74,44 +69,20 @@ const Teams = () => { }, }); - const removeAllocation = (item) => { - setRemovingEmployeeId(item.id); + const handleDelete = (employee) => { + let payload = [ + { + employeeId: employee.employeeId, + jobRoleId: employee.jobRoleId, + projectId: selectedProject, + serviceId: selectedService, + status: false, + }, + ]; - submitAllocations({ - items: [ - { - empID: item.employeeId, - jobRoleId: item.jobRoleId, - projectId: projectId, - status: false, - }, - ], - added: false, - }); + submitAllocations({payload:payload}); }; - - const handleEmpAlicationFormSubmit = (allocaionObj) => { - let items = allocaionObj.map((item) => { - return { - empID: item.empID, - jobRoleId: item.jobRoleId, - projectId: projectId, - status: true, - }; - }); - - submitAllocations({ items, added: true }); - - setActiveEmployee(true); - setFilteredEmployees(employees.filter((emp) => emp.isActive)); - - const dropdown = document.querySelector( - 'select[name="DataTables_Table_0_length"]' - ); - if (dropdown) dropdown.value = "true"; - }; - - const getRole = (jobRoleId) => { + const getJobRole = (jobRoleId) => { if (loading) return "Loading..."; if (!Array.isArray(empJobRoles)) return "Unassigned"; if (!jobRoleId) return "Unassigned"; @@ -119,165 +90,47 @@ const Teams = () => { const role = empJobRoles.find((b) => b.id == jobRoleId); return role ? role.name : "Unassigned"; }; - const openModel = () => { - setIsModelOpen(true); - }; + // const employeeHandler = useCallback( + // (msg) => { + // if (filteredEmployees.some((emp) => emp.employeeId == msg.employeeId)) { + // refetch(); + // } + // }, + // [filteredEmployees, refetch] + // ); - 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(() => { - if (projectEmployees) { - setEmployees(projectEmployees); - const filtered = projectEmployees.filter((emp) => emp.isActive); - setFilteredEmployees(filtered); - } - }, [projectEmployees, employeeLodaing]); - - useEffect(() => { - if (data) { - setEmpJobRoles(data); - } - }, [data]); - const filterAndSearchEmployees = useCallback(() => { - const statusFiltered = employees.filter((emp) => - activeEmployee ? emp.isActive : !emp.isActive - ); - - if (searchTerm === "") { - setFilteredEmployees(statusFiltered); - return; - } - - const lowercasedSearchTerm = searchTerm.toLowerCase(); - - const searchedAndFiltered = statusFiltered.filter((item) => { - const fullName = - `${item.firstName} ${item.middleName} ${item.lastName}`.toLowerCase(); - const roleName = getRole(item.jobRoleId).toLowerCase(); - const orgName = (item.organizationName || "").toLowerCase(); - const serviceName = (item.serviceName || "").toLowerCase(); - - return ( - fullName.includes(lowercasedSearchTerm) || - roleName.includes(lowercasedSearchTerm) || - orgName.includes(lowercasedSearchTerm) || - serviceName.includes(lowercasedSearchTerm) - ); - }); - - setFilteredEmployees(searchedAndFiltered); - }, [employees, activeEmployee, searchTerm, getRole]); - - useEffect(() => { - filterAndSearchEmployees(); - }, [employees, activeEmployee, searchTerm, filterAndSearchEmployees]); - - const handleFilterEmployee = (e) => { - const filterValue = e.target.value; - setActiveEmployee(filterValue === "true"); - setSearchTerm(""); - }; - - const handleSearch = (e) => { - setSearchTerm(e.target.value); - }; - - const deleteModalOpen = (item) => { - setDeleteEmplyee(item); - setIsDeleteModal(true); - }; - const closeDeleteModal = () => setIsDeleteModal(false); - - const handler = useCallback( - (msg) => { - if (msg.projectIds.some((item) => item === projectId)) { - refetch(); - } - }, - [projectId, refetch] - ); - - 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)) { - refetch(); - } - }, - [filteredEmployees, refetch] - ); - - useEffect(() => { - eventBus.on("employee", employeeHandler); - return () => eventBus.off("employee", employeeHandler); - }, [employeeHandler]); + // useEffect(() => { + // eventBus.on("employee", employeeHandler); + // return () => eventBus.off("employee", employeeHandler); + // }, [employeeHandler]); return ( <> - - - {AssigTeam && ( - setAssignTeam(false)}> - + setAssignTeam(false)} + > + setAssignTeam(false)} /> )} - {IsDeleteModal && ( - removeAllocation(deleteEmployee)} - onClose={closeDeleteModal} - loading={isPending} - /> - )} + handleDelete(selectedEmployee)} + onClose={() => setSelectedEmployee(null)} + />
-
-
+
+ {/*
{!servicesLoading && assignedServices?.length > 0 && (assignedServices.length > 1 ? ( @@ -306,9 +159,9 @@ const Teams = () => { ) : (
{assignedServices[0].name}
))} -
+
*/}
-
+
{ {activeEmployee ? "Active Employees" : "Inactive Employees"}
-
- x`` +
{HasAssignUserPermission && (
{employeeLodaing &&

Loading..

} - {!employeeLodaing && - filteredEmployees && - filteredEmployees.length > 0 && ( - - - - - - - - {!activeEmployee && } - - - - - - {filteredEmployees && - filteredEmployees.map((item) => ( - - - - - - - - {!activeEmployee && ( - + + + + + + + + + + ))} + +
-
Name
-
ServicesOrganizationAssigned DateRelease DateProject RoleActions
- - {item.serviceName || "N/A"}{item.organizationName || "N/A"} - {moment(item.allocationDate).format("DD-MMM-YYYY")} - - {item.reAllocationDate - ? moment(item.reAllocationDate).format( - "DD-MMM-YYYY" + {projectEmployees && projectEmployees.length > 0 && ( + + + + + + + + + + + + + + {projectEmployees && + projectEmployees.map((emp) => ( + + - )} - - - - ))} - -
+
Name
+
ServicesOrganizationAssigned DateRelease DateProject RoleActions
+ - - {getRole(item.jobRoleId)} - - - {item.isActive ? ( - - ) : ( - Not in project - )} -
- )} - {!employeeLodaing && filteredEmployees.length === 0 && ( + + {emp.firstName} + {emp.lastName} + + + + +
{emp.serviceName || "N/A"}{emp.organizationName || "N/A"} + {moment(emp.allocationDate).format("DD-MMM-YYYY")} + + {emp.reAllocationDate + ? moment(emp.reAllocationDate).format("DD-MMM-YYYY") + : "Present"} + + + {getJobRole(emp.jobRoleId)} + + + {emp.isActive ? ( + + ) : ( + Not in project + )} +
+ )} + {/* {!employeeLodaing && filteredEmployees.length === 0 && (
{activeEmployee ? "No active employees assigned to the project" : "No inactive employees assigned to the project"}
- )} + )} */}
diff --git a/src/hooks/useOrganization.js b/src/hooks/useOrganization.js index dea19874..d6c93693 100644 --- a/src/hooks/useOrganization.js +++ b/src/hooks/useOrganization.js @@ -35,6 +35,8 @@ export const useOrganizationModal = () => { }; }; +// ================================Query============================================================= + export const useOrganizationBySPRID = (sprid) => { return useQuery({ queryKey: ["organization by", sprid], @@ -76,6 +78,25 @@ export const useOrganizationsList = ( }); }; +export const useOrganizationEmployees = ( + projectId, + organizationId, + searchString +) => { + return useQuery({ + queryKey: ["OrgEmployees", projectId, organizationId, searchString], + queryFn: async () => + await OrganizationRepository.getOrganizationEmployees( + projectId, + organizationId, + searchString + ), + enabled: !!projectId , + }); +}; + +// =================================Mutation======================================================== + export const useCreateOrganization = (onSuccessCallback) => { const useClient = useQueryClient(); return useMutation({ @@ -103,11 +124,11 @@ export const useAssignOrgToProject = (onSuccessCallback) => { mutationFn: async (payload) => await OrganizationRepository.assignOrganizationToProject(payload), onSuccess: (_, variables) => { - const {projectId} = variables + const { projectId } = variables; useClient.invalidateQueries({ queryKey: ["projectAssignedOrganiztions"], }); - useClient.invalidateQueries({ + useClient.invalidateQueries({ queryKey: ["projectAssignedOrganization", projectId], }); showToast("Organization successfully", "success"); diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index bdbd01c6..9225b0c4 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -54,7 +54,7 @@ export const useProjects = () => { export const useEmployeesByProjectAllocated = ( projectId, serviceId, - organizationId, + organizationId,emloyeeeStatus ) => { const { data = [], @@ -62,12 +62,12 @@ export const useEmployeesByProjectAllocated = ( refetch, error, } = useQuery({ - queryKey: ["empListByProjectAllocated", projectId, serviceId,organizationId], + queryKey: ["empListByProjectAllocated", projectId, serviceId,organizationId,emloyeeeStatus], queryFn: async () => { const res = await ProjectRepository.getProjectAllocation( - projectId, + projectId, serviceId, organizationId, - serviceId + emloyeeeStatus ); return res?.data || res; }, @@ -413,8 +413,8 @@ export const useManageProjectAllocation = ({ const queryClient = useQueryClient(); const { mutate, isPending, isSuccess, isError } = useMutation({ - mutationFn: async ({ items }) => { - const response = await ProjectRepository.manageProjectAllocation(items); + mutationFn: async ({payload}) => { + const response = await ProjectRepository.manageProjectAllocation(payload); return response.data; }, onSuccess: (data, variables, context) => { diff --git a/src/repositories/OrganizationRespository.jsx b/src/repositories/OrganizationRespository.jsx index 4b4ad1b0..28bbf3ce 100644 --- a/src/repositories/OrganizationRespository.jsx +++ b/src/repositories/OrganizationRespository.jsx @@ -10,11 +10,33 @@ const OrganizationRepository = { ); }, - getOrganizationBySPRID :(sprid)=>api.get(`/api/Organization/list?sprid=${sprid}`), + getOrganizationBySPRID: (sprid) => + api.get(`/api/Organization/list?sprid=${sprid}`), - assignOrganizationToProject:(data)=>api.post(`/api/Organization/assign/project`,data), + assignOrganizationToProject: (data) => + api.post(`/api/Organization/assign/project`, data), - assignOrganizationToTenanat:(organizationId)=>api.post(`/api/Organization/assign/tenant/${organizationId}`) + assignOrganizationToTenanat: (organizationId) => + api.post(`/api/Organization/assign/tenant/${organizationId}`), + + getOrganizationEmployees: (projectId, organizationId, searchString) => { + let url = `/api/Employee/list/organizations/${projectId}`; + const queryParams = []; + + if (organizationId) { + queryParams.push(`organizationId=${organizationId}`); + } + + if (searchString) { + queryParams.push(`searchString=${encodeURIComponent(searchString)}`); + } + + if (queryParams.length > 0) { + url += `?${queryParams.join("&")}`; + } + + return api.get(url); + }, }; export default OrganizationRepository; diff --git a/src/repositories/ProjectRepository.jsx b/src/repositories/ProjectRepository.jsx index b1b0727f..43a1ee66 100644 --- a/src/repositories/ProjectRepository.jsx +++ b/src/repositories/ProjectRepository.jsx @@ -5,19 +5,21 @@ const ProjectRepository = { getProjectByprojectId: (projetid) => api.get(`/api/project/details/${projetid}`), - getProjectAllocation: (projectId, organizationId, serviceId) => { - let url = `/api/project/allocation/${projectId}`; + getProjectAllocation: (projectId, serviceId, organizationId, employeeStatus) => { + let url = `/api/project/allocation/${projectId}`; - const params = []; - if (organizationId) params.push(`organizationId=${organizationId}`); - if (serviceId) params.push(`serviceId=${serviceId}`); + const params = []; + if (organizationId) params.push(`organizationId=${organizationId}`); + if (serviceId) params.push(`serviceId=${serviceId}`); + if (employeeStatus !== undefined) params.push(`includeInactive=${employeeStatus}`); - if (params.length > 0) { - url += `?${params.join("&")}`; - } + if (params.length > 0) { + url += `?${params.join("&")}`; + } + + return api.get(url); +}, - return api.get(url); - }, getEmployeesByProject: (projectId) => api.get(`/api/Project/employees/get/${projectId}`), From 265c74f07992f881803db4afe5c6031d70db51cf Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 10:12:34 +0530 Subject: [PATCH 3/4] added filter and sorted employee list - Team --- src/components/Project/Team/Teams.jsx | 313 ++++++++++++++------------ 1 file changed, 174 insertions(+), 139 deletions(-) diff --git a/src/components/Project/Team/Teams.jsx b/src/components/Project/Team/Teams.jsx index 0d5d501d..e9802765 100644 --- a/src/components/Project/Team/Teams.jsx +++ b/src/components/Project/Team/Teams.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback, useMemo } from "react"; import MapUsers from "../MapUsers"; import { Link, NavLink, useNavigate, useParams } from "react-router-dom"; @@ -28,16 +28,15 @@ const Teams = () => { const dispatch = useDispatch(); const [AssigTeam, setAssignTeam] = useState(false); const [employees, setEmployees] = useState([]); - const [filteredEmployees, setFilteredEmployees] = useState([]); const [selectedEmployee, setSelectedEmployee] = useState(null); const [deleteEmployee, setDeleteEmplyee] = useState(null); const [searchTerm, setSearchTerm] = useState(""); // State for search term const [selectedService, setSelectedService] = useState(null); - const [activeEmployee,setActiveEmployee] = useState(false) + const [activeEmployee, setActiveEmployee] = useState(false); const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(selectedProject); - const {data:empJobRoles,loading} = useMaster() + const { data: empJobRoles, loading } = useMaster(); const handleToggleActive = (e) => setActiveEmployee(e.target.checked); const handleServiceChange = (e) => { @@ -52,7 +51,12 @@ const Teams = () => { projectEmployees, loading: employeeLodaing, refetch, - } = useEmployeesByProjectAllocated(selectedProject, selectedService,null,activeEmployee); + } = useEmployeesByProjectAllocated( + selectedProject, + selectedService, + null, + activeEmployee + ); const { mutate: submitAllocations, isPending, @@ -80,9 +84,9 @@ const Teams = () => { }, ]; - submitAllocations({payload:payload}); + submitAllocations({ payload: payload }); }; - const getJobRole = (jobRoleId) => { + const getJobRole = (jobRoleId) => { if (loading) return "Loading..."; if (!Array.isArray(empJobRoles)) return "Unassigned"; if (!jobRoleId) return "Unassigned"; @@ -90,19 +94,36 @@ const Teams = () => { const role = empJobRoles.find((b) => b.id == jobRoleId); return role ? role.name : "Unassigned"; }; - // const employeeHandler = useCallback( - // (msg) => { - // if (filteredEmployees.some((emp) => emp.employeeId == msg.employeeId)) { - // refetch(); - // } - // }, - // [filteredEmployees, refetch] - // ); - // useEffect(() => { - // eventBus.on("employee", employeeHandler); - // return () => eventBus.off("employee", employeeHandler); - // }, [employeeHandler]); + const filteredEmployees = useMemo(() => { + if (!projectEmployees || !searchTerm?.trim()) return projectEmployees; + + const lower = searchTerm.toLowerCase(); + + return projectEmployees?.filter((emp) => { + const fullName = `${emp.firstName ?? ""} ${ + emp.lastName ?? "" + }`.toLowerCase(); + + const joberole = getJobRole(emp?.jobRoleId)?.toLowerCase(); + + return fullName?.includes(lower) || joberole?.includes(lower); + }); + }, [projectEmployees, searchTerm]); + const handleSearch = (e) => setSearchTerm(e.target.value); + const employeeHandler = useCallback( + (msg) => { + if (filteredEmployees.some((emp) => emp.employeeId == msg.employeeId)) { + refetch(); + } + }, + [filteredEmployees, refetch] + ); + + useEffect(() => { + eventBus.on("employee", employeeHandler); + return () => eventBus.off("employee", employeeHandler); + }, [employeeHandler]); return ( <> @@ -112,7 +133,7 @@ const Teams = () => { isOpen={AssigTeam} closeModal={() => setAssignTeam(false)} > - setAssignTeam(false)} /> + setAssignTeam(false)} /> )} @@ -128,79 +149,85 @@ const Teams = () => {
-
-
- {/*
- {!servicesLoading && - assignedServices?.length > 0 && - (assignedServices.length > 1 ? ( - - ) : ( -
{assignedServices[0].name}
- ))} -
*/} +
+
+
+
+ {!servicesLoading && ( + <> + {(!assignedServices || assignedServices.length === 0) && ( + + Not Service Assigned + + )} + + {assignedServices?.length === 1 && ( + + {assignedServices[0].name} + + )} + + {assignedServices?.length > 1 && ( + + )} + + )} +
+ +
+ + +
+
-
-
- - -
-
- -
+ +
+ + {HasAssignUserPermission && ( )}
+
{employeeLodaing &&

Loading..

} {projectEmployees && projectEmployees.length > 0 && ( @@ -213,72 +240,80 @@ const Teams = () => { Services Organization Assigned Date - Release Date + {activeEmployee && Release Date} Project Role Actions - {projectEmployees && - projectEmployees.map((emp) => ( - - -
- - - + - {emp.serviceName || "N/A"} - {emp.organizationName || "N/A"} + {emp.serviceName || "N/A"} + {emp.organizationName || "N/A"} - - {moment(emp.allocationDate).format("DD-MMM-YYYY")} - - - {emp.reAllocationDate - ? moment(emp.reAllocationDate).format("DD-MMM-YYYY") - : "Present"} - - - - {getJobRole(emp.jobRoleId)} - - - - {emp.isActive ? ( - - ) : ( - Not in project + + {moment(emp.allocationDate).format("DD-MMM-YYYY")} + + {activeEmployee && ( + + {emp.reAllocationDate + ? moment(emp.reAllocationDate).format( + "DD-MMM-YYYY" + ) + : "Present"} + )} - - - ))} + + + {getJobRole(emp.jobRoleId)} + + + + {emp.isActive ? ( + + ) : ( + Not in project + )} + + + ))} )} From ca8a41bb63cdb13c6845afe1b7b42201b8b4b953 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 11:21:41 +0530 Subject: [PATCH 4/4] added style classes for footer --- public/assets/vendor/css/core.css | 2 +- .../Project/Team/TeamAssignToProject.jsx | 2 +- .../Project/Team/TeamEmployeeList.jsx | 10 +++---- src/components/Project/Team/Teams.jsx | 30 +++++++++---------- src/hooks/useProjects.js | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index 5acc33c5..48163eb5 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -18613,7 +18613,7 @@ li:not(:first-child) .dropdown-item, min-height: 70vh !important; } -.modal-min-h-{ +.modal-min-h{ min-height: 60vh !important; } diff --git a/src/components/Project/Team/TeamAssignToProject.jsx b/src/components/Project/Team/TeamAssignToProject.jsx index 0bab7756..3a1bb856 100644 --- a/src/components/Project/Team/TeamAssignToProject.jsx +++ b/src/components/Project/Team/TeamAssignToProject.jsx @@ -66,7 +66,7 @@ const TeamAssignToProject = ({ closeModal }) => {
-
+
{ status: true, })); - handleAssignEmployee({ payload }); + handleAssignEmployee({ payload,actionType:"assign"} ); setEmployees((prev) => prev.map((emp) => ({ @@ -154,9 +154,9 @@ if (employees.length === 0) { return ( -
- - +
+
+ @@ -231,7 +231,7 @@ if (employees.length === 0) { ))}
Employee Service
-
+
-
+
{employeeLodaing &&

Loading..

} {projectEmployees && projectEmployees.length > 0 && ( @@ -317,13 +315,15 @@ const Teams = () => {
)} - {/* {!employeeLodaing && filteredEmployees.length === 0 && ( -
- {activeEmployee - ? "No active employees assigned to the project" - : "No inactive employees assigned to the project"} + {!employeeLodaing && filteredEmployees.length === 0 && ( +
+

+ {!activeEmployee + ? "No active employees assigned to the project" + : "No inactive employees assigned to the project"} +

- )} */} + )}
diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index 9225b0c4..a0116a62 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -422,7 +422,7 @@ export const useManageProjectAllocation = ({ queryKey: ["empListByProjectAllocated"], }); queryClient.removeQueries({ queryKey: ["projectEmployees"] }); - if (variables?.added) { + if (variables.actionType === "assign") { showToast("Employee Assigned Successfully", "success"); } else { showToast("Removed Employee Successfully", "success");