integrate React Query for project details and real-time updates

This commit is contained in:
Pramod Mahajan 2025-06-29 00:55:02 +05:30
parent 22e65c167e
commit 467d5d4b13
7 changed files with 789 additions and 507 deletions

View File

@ -122,18 +122,9 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
};
return (
<div
className="modal-dialog modal-lg modal-simple mx-sm-auto mx-1 edit-project-modal"
role="document"
>
<div className="modal-content">
<div className="modal-body p-sm-4 p-0">
<button
type="button"
className="btn-close"
onClick={handleCancel}
aria-label="Close"
></button>
<div className="p-sm-2 p-2">
<div className="text-center mb-2">
<h5 className="mb-2">
{project?.id ? "Edit Project" : "Create Project"}
@ -148,7 +139,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
type="text"
id="name"
name="name"
className="form-control"
className="form-control form-control-sm"
placeholder="Project Name"
{...register("name")}
/>
@ -169,7 +160,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
type="text"
id="shortName"
name="shortName"
className="form-control"
className="form-control form-control-sm"
placeholder="Short Name"
{...register("shortName")}
/>
@ -190,7 +181,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
type="text"
id="contactPerson"
name="contactPerson"
className="form-control"
className="form-control form-control-sm"
placeholder="Contact Person"
maxLength={50}
{...register("contactPerson")}
@ -252,7 +243,7 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
<select
id="modalEditUserStatus"
name="modalEditUserStatus"
className="select2 form-select"
className="select2 form-select form-select-sm"
aria-label="Default select example"
{...register("projectStatusId", {
required: "Status is required",
@ -321,8 +312,6 @@ const ManageProjectInfo = ({ project, handleSubmitForm, onClose }) => {
</div>
</form>
</div>
</div>
</div>
);
};

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react";
import moment from "moment";
import { getDateDifferenceInDays } from "../../utils/dateUtils";
import { useNavigate } from "react-router-dom";
import { useProjectDetails } from "../../hooks/useProjects";
import { useProjectDetails, useUpdateProject } from "../../hooks/useProjects";
import ManageProjectInfo from "./ManageProjectInfo";
import ProjectRepository from "../../repositories/ProjectRepository";
import { cacheData, getCachedData } from "../../slices/apiDataManager";
@ -13,29 +13,36 @@ import {
getProjectStatusColor,
getProjectStatusName,
} from "../../utils/projectStatus";
import GlobalModel from "../common/GlobalModel";
const ProjectCard = ({ projectData, recall }) => {
const [projectInfo, setProjectInfo] = useState(projectData);
const [projectDetails, setProjectDetails] = useState(null);
const [ projectInfo, setProjectInfo ] = useState( projectData );
const { projects_Details, loading, error, refetch } = useProjectDetails(
projectInfo?.id,false
);
const [showModal, setShowModal] = useState(false);
const navigate = useNavigate();
const ManageProject = useHasUserPermission(MANAGE_PROJECT);
const [modifyProjectLoading, setMdifyProjectLoading] = useState(false);
const {
mutate: updateProject,
isPending,
isSuccess,
isError,
} = useUpdateProject({
onSuccessCallback: () => {
setShowModal(false);
},
})
useEffect(()=>{
setProjectInfo(projectData);
},[projectData])
// console.log("in card view",projectInfo);
const handleShow = async () => {
}, [ projectData ] )
const handleShow = async () => {
try {
setMdifyProjectLoading(true);
const response = await ProjectRepository.getProjectByprojectId(
projectInfo.id
);
setProjectDetails(response.data);
setMdifyProjectLoading(false);
const { data } = await refetch();
setShowModal(true);
} catch (error) {
} catch (err) {
showToast("Failed to load project details", "error");
}
};
@ -54,64 +61,68 @@ const ProjectCard = ({ projectData, recall }) => {
};
const handleFormSubmit = (updatedProject) => {
if (projectInfo?.id) {
ProjectRepository.updateProject(projectInfo.id, updatedProject)
.then((response) => {
const updatedProjectData = {
...projectInfo,
...response.data,
building: projectDetails?.building,
};
if (projectInfo?.id) {
updateProject({
projectId: projectInfo.id,
updatedData: updatedProject,
});
}
};
setProjectInfo(updatedProject);
// const handleFormSubmit = (updatedProject) => {
// if (projectInfo?.id) {
// ProjectRepository.updateProject(projectInfo.id, updatedProject)
// .then((response) => {
// const updatedProjectData = {
// ...projectInfo,
// ...response.data,
// building: projectDetails?.building,
// };
if (getCachedData(`projectinfo-${projectInfo.id}`)) {
cacheData(`projectinfo-${projectInfo.id}`, updatedProjectData);
}
// setProjectInfo(updatedProject);
const projects_list = getCachedData("projectslist");
if (projects_list) {
const updatedProjectsList = projects_list.map((project) =>
project.id === projectInfo.id
? {
...project,
...response.data,
// tenant: project.tenant
}
: project
);
cacheData("projectslist", updatedProjectsList);
}
recall(getCachedData("projectslist"));
showToast("Project updated successfully.", "success");
setShowModal(false);
})
.catch((error) => {
showToast(error.message, "error");
});
}
};
// if (getCachedData(`projectinfo-${projectInfo.id}`)) {
// cacheData(`projectinfo-${projectInfo.id}`, updatedProjectData);
// }
// const projects_list = getCachedData("projectslist");
// if (projects_list) {
// const updatedProjectsList = projects_list.map((project) =>
// project.id === projectInfo.id
// ? {
// ...project,
// ...response.data,
// // tenant: project.tenant
// }
// : project
// );
// cacheData("projectslist", updatedProjectsList);
// }
// recall(getCachedData("projectslist"));
// showToast("Project updated successfully.", "success");
// setShowModal(false);
// })
// .catch((error) => {
// showToast(error.message, "error");
// });
// }
// };
return (
<>
{showModal && projectDetails && (
<div
className="modal fade show"
tabIndex="-1"
role="dialog"
style={{ display: "block" }}
aria-hidden="false"
>
<ManageProjectInfo
project={projectDetails}
{showModal && projects_Details && (
<GlobalModel isOpen={showModal} closeModal={handleClose}>
<ManageProjectInfo
project={projects_Details}
handleSubmitForm={handleFormSubmit}
onClose={handleClose}
/>
</div>
</GlobalModel>
)}
<div className="col-md-6 col-lg-4 col-xl-4 order-0 mb-4">
<div className="card cursor-pointer">
<div className={`card cursor-pointer ${isPending ? "bg-light opacity-50 pointer-events-none" : ""}`}>
<div className="card-header pb-4">
<div className="d-flex align-items-start">
<div className="d-flex align-items-center">
@ -143,23 +154,23 @@ const ProjectCard = ({ projectData, recall }) => {
data-bs-toggle="dropdown"
aria-expanded="false"
>
{modifyProjectLoading ? (
<div
className="spinner-border spinner-border-sm text-secondary"
role="status"
>
<span className="visually-hidden">Loading...</span>
</div>
) : (
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
)}
{loading ? (
<div
className="spinner-border spinner-border-sm text-secondary"
role="status"
>
<span className="visually-hidden">Loading...</span>
</div>
) : (
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
)}
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>

View File

@ -14,6 +14,7 @@ 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} from "../../hooks/useProjects";
const Teams = ({ project }) => {
const dispatch = useDispatch();
@ -26,69 +27,104 @@ const Teams = ({ project }) => {
const [filteredEmployees, setFilteredEmployees] = useState([]);
const [removingEmployeeId, setRemovingEmployeeId] = useState(null);
const [assignedLoading, setAssignedLoading] = useState(false);
const [ employeeLodaing, setEmployeeLoading ] = 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 [ IsDeleteModal, setIsDeleteModal ] = useState( false )
const {projectEmployees, loading:employeeLodaing, refetch} = useEmployeesByProjectAllocated( project.id )
const {
mutate: submitAllocations,
isPending,
isSuccess,
isError,
} = useManageProjectAllocation({
onSuccessCallback: () => {
// Example: UI reset
setRemovingEmployeeId(null);
setAssignedLoading(false);
setDeleteEmplyee(null);
closeDeleteModal();
},
onErrorCallback: () => {
closeDeleteModal();
},
});
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 fetchEmployees = async () => {
// try {
// setEmployeeLoading(true);
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()
});
};
// // 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 removeAllocation = (item) => {
setRemovingEmployeeId(item.id);
submitAllocations([
setRemovingEmployeeId(item.id);
submitAllocations({
items: [
{
empID: item.employeeId,
jobRoleId: item.jobRoleId,
projectId: project.id,
status: false,
},
] ,false);
};
],
added: false,
});
};
const handleEmpAlicationFormSubmit = (allocaionObj) => {
let items = allocaionObj.map((item) => {
@ -100,7 +136,7 @@ const Teams = ({ project }) => {
};
});
submitAllocations(items, true);
submitAllocations({ items, added: true });
// Force switch to active view after assignment
setActiveEmployee(true);
@ -146,8 +182,12 @@ const Teams = ({ project }) => {
useEffect(() => {
fetchEmployees();
}, []);
if ( projectEmployees )
{
setEmployees(projectEmployees);
setFilteredEmployees( projectEmployees?.filter( ( emp ) => emp.isActive ) );
}
}, [projectEmployees,employeeLodaing]);
useEffect(() => {
if (data) {
@ -177,10 +217,10 @@ const Teams = ({ project }) => {
const handler = useCallback(
(msg) => {
if (msg.projectIds.some((item) => item === project.id)) {
fetchEmployees();
refetch();
}
},
[]
[project.id, refetch]
);
useEffect(() => {
@ -191,9 +231,9 @@ const Teams = ({ project }) => {
const employeeHandler = useCallback(
(msg) => {
if(filteredEmployees.some((item) => item.employeeId == msg.employeeId)){
fetchEmployees();
refetch();
}
},[filteredEmployees]
},[filteredEmployees, refetch]
);
useEffect(() => {
@ -240,7 +280,7 @@ const Teams = ({ project }) => {
message={"Are you sure you want delete?"}
onSubmit={removeAllocation}
onClose={closeDeleteModal}
loading={employeeLodaing}
loading={isPending}
paramData={deleteEmployee}
/>
</div>

View File

@ -6,194 +6,430 @@ import { useDispatch, useSelector } from "react-redux";
import { setProjectId } from "../slices/localVariablesSlice";
import EmployeeList from "../components/Directory/EmployeeList";
import eventBus from "../services/eventBus";
import {Mutation, useMutation, useQuery, useQueryClient} from "@tanstack/react-query";
import showToast from "../services/toastService";
// export const useProjects = () => {
// const loggedUser = useSelector((store) => store.globalVariables.loginUser);
// const [projects, setProjects] = useState([]);
// const [loading, setLoading] = useState(true);
// const [error, setError] = useState("");
// const fetchData = async () => {
// const projectIds = loggedUser?.projects || [];
// const filterProjects = (projectsList) => {
// return projectsList
// .filter((proj) => projectIds.includes(String(proj.id)))
// .sort((a, b) => a?.name?.localeCompare(b.name));
// };
// const projects_cache = getCachedData("projectslist");
// if (!projects_cache) {
// setLoading(true);
// try {
// const response = await ProjectRepository.getProjectList();
// const allProjects = response.data;
// const filtered = filterProjects(allProjects);
// setProjects(filtered);
// cacheData("projectslist", allProjects);
// } catch (err) {
// setError("Failed to fetch data.");
// } finally {
// setLoading(false);
// }
// } else {
// if (!projects.length) {
// const filtered = filterProjects(projects_cache);
// setProjects(filtered);
// setLoading(false);
// }
// }
// };
// useEffect(() => {
// if (loggedUser) {
// fetchData();
// }
// }, [loggedUser]);
// return { projects, loading, error, refetch: fetchData };
// };
// export const useEmployeesByProjectAllocated = (selectedProject) => {
// const [projectEmployees, setEmployeeList] = useState([]);
// const [loading, setLoading] = useState(true);
// const [projects, setProjects] = useState([]);
// const fetchData = async (projectid) => {
// try {
// let EmployeeByProject_Cache = getCachedData("empListByProjectAllocated");
// if (
// !EmployeeByProject_Cache ||
// !EmployeeByProject_Cache.projectId === projectid
// ) {
// let response = await ProjectRepository.getProjectAllocation(projectid);
// setEmployeeList(response.data);
// cacheData("empListByProjectAllocated", {
// data: response.data,
// projectId: projectid,
// });
// setLoading(false);
// } else {
// setEmployeeList(EmployeeByProject_Cache.data);
// setLoading(false);
// }
// } catch (err) {
// setError("Failed to fetch data.");
// setLoading(false);
// }
// };
// useEffect(() => {
// if (selectedProject) {
// fetchData(selectedProject);
// }
// }, [selectedProject]);
// return { projectEmployees, loading, projects };
// };
// export const useProjectDetails = (projectId) => {
// const { profile } = useProfile();
// const [projects_Details, setProject_Details] = useState(null);
// const [loading, setLoading] = useState(true);
// const [error, setError] = useState("");
// const fetchData = async () => {
// setLoading(true);
// const project_cache = getCachedData("projectInfo");
// if (!project_cache || project_cache?.projectId != projectId) {
// ProjectRepository.getProjectByprojectId(projectId)
// .then((response) => {
// setProject_Details(response.data);
// cacheData("projectInfo", {
// projectId: projectId,
// data: response.data,
// });
// setLoading(false);
// })
// .catch((error) => {
// console.error(error);
// setError("Failed to fetch data.");
// setLoading(false);
// });
// } else {
// setProject_Details(project_cache.data);
// setLoading(false);
// }
// };
// useEffect(() => {
// if (profile && projectId != undefined) {
// fetchData();
// }
// }, [projectId, profile]);
// return { projects_Details, loading, error, refetch: fetchData };
// };
// export const useProjectsByEmployee = (employeeId) => {
// const [projectList, setProjectList] = useState([]);
// const [loading, setLoading] = useState(false);
// const [error, setError] = useState("");
// const fetchProjects = async (id) => {
// try {
// setLoading(true);
// setError(""); // clear previous error
// const res = await ProjectRepository.getProjectsByEmployee(id);
// setProjectList(res.data);
// cacheData("ProjectsByEmployee", { data: res.data, employeeId: id });
// setLoading(false);
// } catch (err) {
// setError(err?.message || "Failed to fetch projects");
// setLoading(false);
// }
// };
// useEffect(() => {
// if (!employeeId) return;
// const cache_project = getCachedData("ProjectsByEmployee");
// if (!cache_project?.data || cache_project?.employeeId !== employeeId) {
// fetchProjects(employeeId);
// } else {
// setProjectList(cache_project.data);
// }
// }, [employeeId]);
// return {
// projectList,
// loading,
// error,
// refetch: fetchProjects,
// };
// };
// export const useProjectName = () => {
// const [loading, setLoading] = useState(true);
// const [projectNames, setProjectName] = useState([]);
// const [Error, setError] = useState();
// const dispatch = useDispatch();
// const fetchData = async () => {
// try {
// let response = await ProjectRepository.projectNameList();
// setProjectName(response.data);
// cacheData("basicProjectNameList", response.data);
// setLoading(false);
// if(response.data.length === 1){
// dispatch(setProjectId(response.data[0]?.id));
// }
// } catch (err) {
// setError("Failed to fetch data.");
// setLoading(false);
// }
// };
// useEffect(() => {
// fetchData();
// }, []);
// return { projectNames, loading, Error, fetchData };
// };
// ------------------------------Query-------------------
export const useProjects = () => {
const loggedUser = useSelector((store) => store.globalVariables.loginUser);
const [projects, setProjects] = useState([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const fetchData = async () => {
const projectIds = loggedUser?.projects || [];
const filterProjects = (projectsList) => {
return projectsList
.filter((proj) => projectIds.includes(String(proj.id)))
.sort((a, b) => a?.name?.localeCompare(b.name));
};
const projects_cache = getCachedData("projectslist");
if (!projects_cache) {
setLoading(true);
try {
const response = await ProjectRepository.getProjectList();
const allProjects = response.data;
const filtered = filterProjects(allProjects);
setProjects(filtered);
cacheData("projectslist", allProjects);
} catch (err) {
setError("Failed to fetch data.");
} finally {
setLoading(false);
}
} else {
if (!projects.length) {
const filtered = filterProjects(projects_cache);
setProjects(filtered);
setLoading(false);
}
}
};
useEffect(() => {
if (loggedUser) {
fetchData();
}
}, [loggedUser]);
return { projects, loading, error, refetch: fetchData };
};
export const useEmployeesByProjectAllocated = (selectedProject) => {
const [projectEmployees, setEmployeeList] = useState([]);
const [loading, setLoading] = useState(true);
const [projects, setProjects] = useState([]);
const fetchData = async (projectid) => {
try {
let EmployeeByProject_Cache = getCachedData("empListByProjectAllocated");
if (
!EmployeeByProject_Cache ||
!EmployeeByProject_Cache.projectId === projectid
) {
let response = await ProjectRepository.getProjectAllocation(projectid);
setEmployeeList(response.data);
cacheData("empListByProjectAllocated", {
data: response.data,
projectId: projectid,
});
setLoading(false);
} else {
setEmployeeList(EmployeeByProject_Cache.data);
setLoading(false);
}
} catch (err) {
setError("Failed to fetch data.");
setLoading(false);
}
};
useEffect(() => {
if (selectedProject) {
fetchData(selectedProject);
}
}, [selectedProject]);
return { projectEmployees, loading, projects };
};
export const useProjectDetails = (projectId) => {
const { profile } = useProfile();
const [projects_Details, setProject_Details] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const fetchData = async () => {
setLoading(true);
const project_cache = getCachedData("projectInfo");
if (!project_cache || project_cache?.projectId != projectId) {
ProjectRepository.getProjectByprojectId(projectId)
.then((response) => {
setProject_Details(response.data);
cacheData("projectInfo", {
projectId: projectId,
data: response.data,
});
setLoading(false);
})
.catch((error) => {
console.error(error);
setError("Failed to fetch data.");
setLoading(false);
});
} else {
setProject_Details(project_cache.data);
setLoading(false);
}
};
useEffect(() => {
if (profile && projectId != undefined) {
fetchData();
}
}, [projectId, profile]);
return { projects_Details, loading, error, refetch: fetchData };
};
export const useProjectsByEmployee = (employeeId) => {
const [projectList, setProjectList] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const fetchProjects = async (id) => {
try {
setLoading(true);
setError(""); // clear previous error
const res = await ProjectRepository.getProjectsByEmployee(id);
setProjectList(res.data);
cacheData("ProjectsByEmployee", { data: res.data, employeeId: id });
setLoading(false);
} catch (err) {
setError(err?.message || "Failed to fetch projects");
setLoading(false);
}
};
useEffect(() => {
if (!employeeId) return;
const cache_project = getCachedData("ProjectsByEmployee");
if (!cache_project?.data || cache_project?.employeeId !== employeeId) {
fetchProjects(employeeId);
} else {
setProjectList(cache_project.data);
}
}, [employeeId]);
const {
data: projects = [],
isLoading: loading,
error,
refetch,
} = useQuery({
queryKey: ['ProjectsList'],
queryFn: async () => {
const response = await ProjectRepository.getProjectList();
return response.data;
},
enabled: !!loggedUser,
});
return {
projectList,
projects,
loading,
error,
refetch: fetchProjects,
refetch,
};
};
export const useProjectName = () => {
const [loading, setLoading] = useState(true);
const [projectNames, setProjectName] = useState([]);
const [Error, setError] = useState();
const dispatch = useDispatch();
export const useEmployeesByProjectAllocated = (selectedProject) =>
{
const {data = [], isLoading, refetch, error} = useQuery( {
queryKey: ["empListByProjectAllocated", selectedProject ],
queryFn: async () =>
{
const res = await ProjectRepository.getProjectAllocation( selectedProject );
return res.data || res
},
enabled:!!selectedProject
} )
return {
projectEmployees: data,
loading:isLoading,
error,
refetch
}
}
const fetchData = async () => {
try {
let response = await ProjectRepository.projectNameList();
setProjectName(response.data);
cacheData("basicProjectNameList", response.data);
setLoading(false);
if(response.data.length === 1){
dispatch(setProjectId(response.data[0]?.id));
export const useProjectDetails = ( projectId,isAuto = true ) =>
{
const {data: projects_Details, isLoading, error, refetch} = useQuery( {
queryKey: [ "projectInfo", projectId ],
queryFn: async () =>
{
const res = await ProjectRepository.getProjectByprojectId( projectId );
return res.data || res;
},
enabled:!!projectId && isAuto
} )
return { projects_Details, loading:isLoading, error, refetch };
}
export const useProjectsByEmployee = (employeeId) =>
{
const { data:projectNameList =[],isLoading,error,refetch} = useQuery( {
queryKey: [ "ProjectsByEmployee", employeeId ],
queryFn: async () =>
{
const res = await ProjectRepository.getProjectsByEmployee( employeeId );
return res.data || res;
},
enabled: !!employeeId
})
return {projectList, loading:isLoading,error,refetch }
}
export const useProjectName = () =>
{
const {data = [],isLoading,error,refetch} = useQuery( {
queryFn: [ "basicProjectNameList" ],
queryFn: async () =>
{
const res = await ProjectRepository.projectNameList();
return res.data || res;
},
} )
return {projectNames:data,loading:isLoading,Error:error,refetch}
}
// -- Mutation-------------------------------
export const useCreateProject = ({ onSuccessCallback }) => {
const queryClient = useQueryClient();
return useMutation({
mutationFn: async (newProject) => {
const res = await ProjectRepository.manageProject(newProject);
return res.data;
},
onSuccess: (data) => {
// Invalidate the cache
queryClient.invalidateQueries(['ProjectsList']);
// Emit event for consumers (like useProjects or others)
eventBus.emit("project", {
keyword: "Create_Project",
response: data,
});
showToast("Project Created successfully.", "success");
if (onSuccessCallback) {
onSuccessCallback(data);
}
} catch (err) {
setError("Failed to fetch data.");
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return { projectNames, loading, Error, fetchData };
},
onError: (error) => {
showToast(error.message || "Error while creating project", "error");
},
});
};
export const useUpdateProject = ({ onSuccessCallback }) => {
const queryClient = useQueryClient();
const {
mutate,
isPending,
isSuccess,
isError,
} = useMutation({
mutationFn: async ({ projectId, updatedData }) => {
return await ProjectRepository.updateProject(projectId, updatedData);
},
onSuccess: (data, variables) => {
const { projectId } = variables;
// Invalidate queries
queryClient.invalidateQueries(["ProjectsList"]);
queryClient.invalidateQueries(["projectinfo", projectId]);
// Emit update event
eventBus.emit("project", {
keyword: "Update_Project",
response: data,
});
showToast("Project updated successfully.", "success");
if (onSuccessCallback) {
onSuccessCallback(data);
}
},
onError: (error) => {
showToast(error?.message || "Error while updating project", "error");
},
});
return {
mutate,
isPending,
isSuccess,
isError,
};
};
export const useManageProjectInfra = () => {
return useMutation({
mutationFn: async ({infraObject,projectId}) => {
return await ProjectRepository.manageProjectInfra(infraObject);
},
onSuccess: ( response, variables ) =>
{
const { projectId } = variables;
showToast( "Details updated successfully.", "success" );
queryClient.invalidateQueries(["projectinfo", projectId]);
},
onError: (error) => {
showToast(error.message || "Failed to update task details", "error");
},
});
};
export const useManageProjectAllocation = ({
onSuccessCallback,
onErrorCallback,
}) => {
const queryClient = useQueryClient();
const {
mutate,
isPending,
isSuccess,
isError,
} = useMutation({
mutationFn: async ({ items }) => {
const response = await ProjectRepository.manageProjectAllocation(items);
return response.data;
},
onSuccess: (data, variables, context) => {
queryClient.invalidateQueries(['empListByProjectAllocated']);
if (context?.added) {
showToast('Employee Assigned Successfully', 'success');
} else {
showToast('Removed Employee Successfully', 'success');
}
if (onSuccessCallback) onSuccessCallback(data, context);
},
onError: (error) => {
const message =
error?.response?.data?.message || error.message || 'Error occurred during API call';
showToast(message, 'error');
if (onErrorCallback) onErrorCallback(error);
},
});
return {
mutate,
isPending,
isSuccess,
isError,
};
};

View File

@ -31,34 +31,35 @@ const ProjectDetails = () => {
projects_Details,
loading: projectLoading,
error: ProjectError,
refetch
} = useProjectDetails(projectId);
const dispatch = useDispatch();
const [project, setProject] = useState(null);
const [projectDetails, setProjectDetails] = useState(null);
// const [projectDetails, setProjectDetails] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState("");
const fetchData = async () => {
const project_cache = getCachedData("projectInfo");
if (!project_cache || project_cache?.projectId !== projectId) {
ProjectRepository.getProjectByprojectId(projectId)
.then((response) => {
setProjectDetails(response.data);
setProject(response.data);
cacheData("projectInfo", { projectId, data: response.data });
setLoading(false);
})
.catch((error) => {
console.error(error);
setError("Failed to fetch data.");
setLoading(false);
});
} else {
setProjectDetails(project_cache.data);
setProject(project_cache.data);
setLoading(false);
}
};
// const fetchData = async () => {
// const project_cache = getCachedData("projectInfo");
// if (!project_cache || project_cache?.projectId !== projectId) {
// ProjectRepository.getProjectByprojectId(projectId)
// .then((response) => {
// setProjectDetails(response.data);
// setProject(response.data);
// cacheData("projectInfo", { projectId, data: response.data });
// setLoading(false);
// })
// .catch((error) => {
// console.error(error);
// setError("Failed to fetch data.");
// setLoading(false);
// });
// } else {
// setProjectDetails(project_cache.data);
// setProject(project_cache.data);
// setLoading(false);
// }
// };
const [activePill, setActivePill] = useState("profile");
@ -78,7 +79,7 @@ const ProjectDetails = () => {
<div className="row">
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
{/* About User */}
<AboutProject data={projectDetails}></AboutProject>
<AboutProject data={projects_Details}></AboutProject>
{/* About User */}
</div>
<div className="col-xl-4 col-lg-5 col-md-5 mt-5">
@ -94,7 +95,7 @@ const ProjectDetails = () => {
<div className="row">
<div className="col-lg-12 col-xl-12">
{/* Teams */}
<Teams project={projectDetails}></Teams>
<Teams project={projects_Details}></Teams>
{/* Teams */}
</div>
</div>
@ -104,7 +105,7 @@ const ProjectDetails = () => {
case "infra": {
return (
<ProjectInfra
data={projectDetails}
data={projects_Details}
onDataChange={handleDataChange}
></ProjectInfra>
);
@ -113,7 +114,7 @@ const ProjectDetails = () => {
case "workplan": {
return (
<WorkPlan
data={projectDetails}
data={projects_Details}
onDataChange={handleDataChange}
></WorkPlan>
);
@ -122,7 +123,7 @@ const ProjectDetails = () => {
case "directory": {
return (
<div className="row">
<Directory IsPage={false} prefernceContacts={projectDetails.id} />
<Directory IsPage={false} prefernceContacts={projects_Details.id} />
</div>
);
}
@ -134,26 +135,27 @@ const ProjectDetails = () => {
useEffect(() => {
dispatch(setProjectId(projectId));
setProject(projects_Details);
setProjectDetails(projects_Details);
// setProject(projects_Details);
// setProjectDetails(projects_Details);
}, [projects_Details, projectId]);
const handler = useCallback(
(msg) => {
if (msg.keyword === "Update_Project" && project.id === msg.response.id) {
clearCacheKey("projectInfo")
ProjectRepository.getProjectByprojectId(projectId)
.then((response) => {
setProjectDetails(response.data);
setProject(response.data);
cacheData("projectInfo", { projectId, data: response.data });
setLoading(false);
})
.catch((error) => {
console.error(error);
setError("Failed to fetch data.");
setLoading(false);
});
// clearCacheKey("projectInfo")
// ProjectRepository.getProjectByprojectId(projectId)
// .then((response) => {
// setProjectDetails(response.data);
// setProject(response.data);
// cacheData("projectInfo", { projectId, data: response.data });
// setLoading(false);
// })
// .catch((error) => {
// console.error(error);
// setError("Failed to fetch data.");
// setLoading(false);
// });
refetch()
}
},
[project,handleDataChange]

View File

@ -3,10 +3,14 @@ import ProjectCard from "../../components/Project/ProjectCard";
import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
import Breadcrumb from "../../components/common/Breadcrumb";
import ProjectRepository from "../../repositories/ProjectRepository";
import { useProjects } from "../../hooks/useProjects";
import { useProjects, useCreateProject } from "../../hooks/useProjects";
import { useDispatch } from "react-redux";
import showToast from "../../services/toastService";
import { getCachedData, cacheData, clearCacheKey } from "../../slices/apiDataManager";
// import {
// getCachedData,
// cacheData,
// clearCacheKey,
// } from "../../slices/apiDataManager";
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { useProfile } from "../../hooks/useProfile";
import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants";
@ -14,6 +18,9 @@ import ProjectListView from "./ProjectListView";
import eventBus from "../../services/eventBus";
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
import { defaultCheckBoxAppearanceProvider } from "pdf-lib";
import { useMutation } from "@tanstack/react-query";
import usePagination from "../../hooks/usePagination";
import GlobalModel from "../../components/common/GlobalModel";
const ProjectList = () => {
const { profile: loginUser } = useProfile();
@ -26,9 +33,12 @@ const ProjectList = () => {
HasManageProjectPermission
);
const dispatch = useDispatch();
const { mutate: createProject } = useCreateProject({
onSuccessCallback: () => {
setShowModal(false);
},
});
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage] = useState(ITEMS_PER_PAGE);
const [searchTerm, setSearchTerm] = useState("");
const [selectedStatuses, setSelectedStatuses] = useState([
"b74da4c2-d07e-46f2-9919-e75e49b12731",
@ -72,29 +82,33 @@ const ProjectList = () => {
}
}, [loginUser, HasManageProjectPermission]);
// const handleSubmitForm = (newProject, setloading, reset) => {
// ProjectRepository.manageProject(newProject)
// .then((response) => {
// const cachedProjects = getCachedData("projectslist") || [];
// const updatedProjects = [...cachedProjects, response.data];
// cacheData("projectslist", updatedProjects);
// setProjectList((prev) => [...prev, response.data]);
// setloading(false);
// reset();
// sortingProject(getCachedData("projectslist"));
// showToast("Project Created successfully.", "success");
// setShowModal(false);
// })
// .catch((error) => {
// showToast(error.message, "error");
// setShowModal(false);
// });
// };
const handleSubmitForm = (newProject, setloading, reset) => {
ProjectRepository.manageProject(newProject)
.then((response) => {
const cachedProjects = getCachedData("projectslist") || [];
const updatedProjects = [...cachedProjects, response.data];
cacheData("projectslist", updatedProjects);
setProjectList((prev) => [...prev, response.data]);
setloading(true);
createProject(newProject, {
onSettled: () => {
setloading(false);
reset();
sortingProject(getCachedData("projectslist"));
showToast("Project Created successfully.", "success");
setShowModal(false);
})
.catch((error) => {
showToast(error.message, "error");
setShowModal(false);
});
};
const handleReFresh = () => {
if (!projects || projects.length === 0) {
refetch();
}
},
});
};
const handleStatusChange = (statusId) => {
@ -118,13 +132,11 @@ const ProjectList = () => {
return matchesStatus && matchesSearch;
});
const indexOfLastItem = currentPage * itemsPerPage;
const indexOfFirstItem = indexOfLastItem - itemsPerPage;
const currentItems = filteredProjects.slice(
indexOfFirstItem,
indexOfLastItem
);
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
const totalPages = Math.ceil( filteredProjects.length / ITEMS_PER_PAGE );
const {currentItems,currentPage,paginate,setCurrentPage} = usePagination(filteredProjects, ITEMS_PER_PAGE)
useEffect(() => {
const tooltipTriggerList = Array.from(
document.querySelectorAll('[data-bs-toggle="tooltip"]')
@ -132,67 +144,20 @@ const ProjectList = () => {
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el));
}, []);
const handler = useCallback(
async (msg) => {
if (HasManageProject && msg.keyword === "Create_Project") {
const updatedProjects = [...projectList, msg.response];
cacheData("projectslist", updatedProjects);
setProjectList(updatedProjects);
sortingProject(updatedProjects);
}
if (
msg.keyword === "Update_Project" &&
projectList.some((item) => item.id === msg.response.id)
) {
ProjectRepository.getProjectList()
.then((response) => {
cacheData("projectslist", response?.data);
sortingProject(response?.data);
})
.catch((e) => {
console.error(e)
});
}
},
[HasManageProject, projectList, sortingProject]
);
useEffect(() => {
eventBus.on("project", handler);
return () => eventBus.off("project", handler);
}, [handler]);
const assignProjectHandler = useCallback(
async (data) => {
clearCacheKey("projectslist");
await refetch();
sortingProject(projects);
},
[refetch]
);
useEffect(() => {
eventBus.on("assign_project_one", assignProjectHandler);
return () => eventBus.off("assign_project_one", assignProjectHandler);
}, [handler]);
return (
<>
<div
className={`modal fade ${showModal ? "show" : ""}`}
tabIndex="-1"
role="dialog"
style={{ display: showModal ? "block" : "none" }}
aria-hidden={!showModal}
>
<ManageProjectInfo
{showModal && (
<GlobalModel isOpen={showModal} closeModal={handleClose}>
<ManageProjectInfo
project={null}
handleSubmitForm={handleSubmitForm}
onClose={handleClose}
/>
</div>
</GlobalModel>
)}
<div className="container-xxl flex-grow-1 container-p-y">
<Breadcrumb

View File

@ -1,6 +1,10 @@
import React, { useState, useEffect } from "react";
import moment from "moment";
import { useProjects } from "../../hooks/useProjects";
import {
useProjectDetails,
useProjects,
useUpdateProject,
} from "../../hooks/useProjects";
import {
getProjectStatusName,
getProjectStatusColor,
@ -14,25 +18,35 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
import showToast from "../../services/toastService";
import { getCachedData, cacheData } from "../../slices/apiDataManager";
import GlobalModel from "../../components/common/GlobalModel";
const ProjectListView = ({ projectData, recall }) => {
const [projectInfo, setProjectInfo] = useState(projectData);
const [projectDetails, setProjectDetails] = useState(null);
const { projects_Details, loading, error, refetch } = useProjectDetails(
projectInfo?.id,false
);
const [showModal, setShowModal] = useState(false);
const navigate = useNavigate();
const ManageProject = useHasUserPermission(MANAGE_PROJECT);
useEffect(()=>{
setProjectInfo(projectData);
},[projectData])
useEffect(() => {
setProjectInfo(projectData);
}, [projectData]);
const {
mutate: updateProject,
isPending,
isSuccess,
isError,
} = useUpdateProject({
onSuccessCallback: () => {
setShowModal(false);
},
})
const handleShow = async () => {
try {
const response = await ProjectRepository.getProjectByprojectId(
projectInfo.id
);
setProjectDetails(response.data);
const { data } = await refetch();
setShowModal(true);
} catch (error) {
} catch (err) {
showToast("Failed to load project details", "error");
}
};
@ -52,45 +66,54 @@ const ProjectListView = ({ projectData, recall }) => {
const handleFormSubmit = (updatedProject) => {
if (projectInfo?.id) {
ProjectRepository.updateProject(projectInfo.id, updatedProject)
.then((response) => {
const updatedProjectData = {
...projectInfo,
...response.data,
building: projectDetails?.building,
};
setProjectInfo(updatedProjectData);
if (getCachedData(`projectinfo-${projectInfo.id}`)) {
cacheData(`projectinfo-${projectInfo.id}`, updatedProjectData);
}
const projects_list = getCachedData("projectslist");
if (projects_list) {
const updatedProjectsList = projects_list.map((project) =>
project.id === projectInfo.id
? {
...project,
...response.data,
// tenant: project.tenant
}
: project
);
cacheData("projectslist", updatedProjectsList);
}
recall(getCachedData("projectslist"));
showToast("Project updated successfully.", "success");
setShowModal(false);
})
.catch((error) => {
showToast(error.message, "error");
});
updateProject({
projectId: projectInfo.id,
updatedData: updatedProject,
});
}
};
// const handleFormSubmit = (updatedProject) => {
// if (projectInfo?.id) {
// ProjectRepository.updateProject(projectInfo.id, updatedProject)
// .then((response) => {
// const updatedProjectData = {
// ...projectInfo,
// ...response.data,
// building: projectDetails?.building,
// };
// setProjectInfo(updatedProjectData);
// if (getCachedData(`projectinfo-${projectInfo.id}`)) {
// cacheData(`projectinfo-${projectInfo.id}`, updatedProjectData);
// }
// const projects_list = getCachedData("projectslist");
// if (projects_list) {
// const updatedProjectsList = projects_list.map((project) =>
// project.id === projectInfo.id
// ? {
// ...project,
// ...response.data,
// // tenant: project.tenant
// }
// : project
// );
// cacheData("projectslist", updatedProjectsList);
// }
// recall(getCachedData("projectslist"));
// showToast("Project updated successfully.", "success");
// setShowModal(false);
// })
// .catch((error) => {
// showToast(error.message, "error");
// });
// }
// };
return (
<>
{showModal && projectDetails && (
{/* {showModal && projects_Details && (
<tr>
<td
className="modal fade show"
@ -100,15 +123,22 @@ const ProjectListView = ({ projectData, recall }) => {
aria-hidden="false"
>
<ManageProjectInfo
project={projectDetails}
project={projects_Details}
handleSubmitForm={handleFormSubmit}
onClose={handleClose}
/>
</td>
</tr>
)} */}
{showModal && projects_Details && (
<GlobalModel isOpen={showModal} closeModal={handleClose}> <ManageProjectInfo
project={projects_Details}
handleSubmitForm={handleFormSubmit}
onClose={handleClose}
/></GlobalModel>
)}
<tr className="py-8">
<tr className={`py-8 ${isPending ? "bg-light opacity-50 pointer-events-none" : ""} `}>
<td className="text-start" colSpan={5}>
<strong
className="text-primary cursor-pointer"
@ -162,14 +192,23 @@ const ProjectListView = ({ projectData, recall }) => {
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
{loading ? (
<div
class="spinner-border spinner-border-sm text-secondary"
role="status"
>
<span class="visually-hidden">Loading...</span>
</div>
) : (
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
)}
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>