From 2ef1fcfd1db611f9545fe98ed5525aea38778af3 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 01:15:07 +0530 Subject: [PATCH] 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}`),