diff --git a/src/components/Dashboard/ProjectProgressChart.jsx b/src/components/Dashboard/ProjectProgressChart.jsx index 20e0191a..f80bb0d5 100644 --- a/src/components/Dashboard/ProjectProgressChart.jsx +++ b/src/components/Dashboard/ProjectProgressChart.jsx @@ -93,33 +93,6 @@ const ProjectProgressChart = ({
Project Progress

Progress Overview by Project

- - {/* Right: Checkbox and Project Name */} -
- {ShowAllProject == true && ( -
- setShowAllEmployees(e.target.checked)} - /> - -
- )} - {!showAllEmployees && selectedProjectName && ( -

- {selectedProjectName} -

- )} -
{/* Row 2: Time Range Buttons */} diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 9c1b4b17..9023d007 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -1,3 +1,4 @@ + import getGreetingMessage from "../../utils/greetingHandler"; import { cacheData, @@ -20,18 +21,21 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { MANAGE_PROJECT } from "../../utils/constants"; const Header = () => { - const {profile} = useProfile(); + const { profile } = useProfile(); const location = useLocation(); const dispatch = useDispatch(); const { data, loading } = useMaster(); const navigate = useNavigate(); const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); + const isDashboard = location.pathname === "/dashboard"; + // const isDirectoryPath = location.pathname === "/directory"; const getRole = (roles, joRoleId) => { if (!Array.isArray(roles)) return "User"; let role = roles.find((role) => role.id === joRoleId); return role ? role.name : "User"; }; + const handleLogout = (e) => { e.preventDefault(); // Prevent default anchor behavior (e.g., page reload) logout(); @@ -39,7 +43,6 @@ const Header = () => { const logout = async () => { try { - // Notify server about the logout (optional) let data = { refreshToken: localStorage.getItem("refreshToken"), }; @@ -54,6 +57,7 @@ const Header = () => { window.location.href = "/auth/login"; }) .catch((error) => { + // Even if logout API fails, clear local storage and redirect localStorage.removeItem("jwtToken"); localStorage.removeItem("refreshToken"); localStorage.removeItem("user"); @@ -71,39 +75,43 @@ const Header = () => { const handleProfilePage = () => { navigate(`/employee/${profile?.employeeInfo?.id}?for=attendance`); }; - // const { projects, loading: projectLoading } = useProjects(); + const { projectNames, loading: projectLoading, fetchData } = useProjectName(); const selectedProject = useSelector( (store) => store.localVariables.projectId ); - const selectedProjectName = projectNames?.find( - (p) => p?.id === selectedProject - )?.name; - - let displayText = ""; - if (selectedProjectName) { - displayText = selectedProjectName; - } else if (projectLoading && selectedProject) { - displayText = selectedProject; + // Determine the display text for the project dropdown + let displayText = "All Projects"; + if (selectedProject === null) { + displayText = "All Projects"; + } else if (selectedProject) { + const selectedProjectObj = projectNames?.find( + (p) => p?.id === selectedProject + ); + // Fallback to selectedProject ID if name not found during loading or mismatch + displayText = selectedProjectObj ? selectedProjectObj.name : selectedProject; } else if (projectLoading) { displayText = "Loading..."; } const { openChangePassword } = useChangePassword(); + useEffect(() => { if ( projectNames && - selectedProject !== " " && + projectNames.length > 0 && + selectedProject === undefined && !getCachedData("hasReceived") ) { - dispatch(setProjectId(projectNames[0]?.id)); + dispatch(setProjectId(null)); // Set to null for "All Projects" } - }, [projectNames]); + }, [projectNames, selectedProject, dispatch]); - /** Check if current page id project details page */ - const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname) + + /** Check if current page is project details page or directory page */ + // const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname); const isDirectoryPath = /^\/directory$/.test(location.pathname); const handler = useCallback( @@ -111,58 +119,47 @@ const Header = () => { if (!HasManageProjectPermission) { await fetchData(); const projectExist = data.projectIds.some( - (item) => item == selectedProject + (item) => item === selectedProject ); if (projectExist) { cacheData("hasReceived", false); } } }, - [fetchData,projectNames,selectedProject] + [fetchData, selectedProject, HasManageProjectPermission] ); - // useEffect(() => { - // eventBus.on("assign_project_one", handler); - // return () => eventBus.off("assign_project_one", handler); - // }, [handler]); - const newProjectHandler = useCallback( async (msg) => { - if (HasManageProjectPermission && msg.keyword === "Create_Project") { await fetchData(); - } else if (projectNames.some((item) => item.id == msg.response.id)) { - console.log((projectNames.some((item) => item.id == msg.response.id))) + } else if (projectNames?.some((item) => item.id === msg.response.id)) { await fetchData(); } cacheData("hasReceived", false); }, - [HasManageProjectPermission,projectNames] + [HasManageProjectPermission, projectNames, fetchData] ); - // useEffect(() => { - // eventBus.on("project", newProjectHandler); - // return () => eventBus.off("project", newProjectHandler); - // }, [handler]); - - useDispatch( () => - { - dispatch(changeMaster("Job Role")) -},[]) + // Correct way to dispatch an action on mount useEffect(() => { - eventBus.on("assign_project_one", handler); - eventBus.on("project", newProjectHandler); + dispatch(changeMaster("Job Role")); + }, [dispatch]); - return () => { - eventBus.off("assign_project_one", handler); - eventBus.off("project", newProjectHandler); - }; -}, [handler, newProjectHandler]); + // Event bus listeners for project changes + useEffect(() => { + eventBus.on("assign_project_one", handler); + eventBus.on("project", newProjectHandler); + return () => { + eventBus.off("assign_project_one", handler); + eventBus.off("project", newProjectHandler); + }; + }, [handler, newProjectHandler]); return ( ); }; -export default Header; +export default Header; \ No newline at end of file diff --git a/src/components/Project/AboutProject.jsx b/src/components/Project/AboutProject.jsx index 30c42e03..e5196eac 100644 --- a/src/components/Project/AboutProject.jsx +++ b/src/components/Project/AboutProject.jsx @@ -2,48 +2,53 @@ import React, { useEffect, useState } from "react"; import moment from "moment"; import { getProjectStatusName } from "../../utils/projectStatus"; import {useProjectDetails, useUpdateProject} from "../../hooks/useProjects"; -import {useParams} from "react-router-dom"; +import { useSelector } from "react-redux"; // Import useSelector import {useHasUserPermission} from "../../hooks/useHasUserPermission"; import {MANAGE_PROJECT} from "../../utils/constants"; import GlobalModel from "../common/GlobalModel"; import ManageProjectInfo from "./ManageProjectInfo"; import {useQueryClient} from "@tanstack/react-query"; -const AboutProject = () => -{ - const [ IsOpenModal, setIsOpenModal ] = useState( false ) - const {mutate: UpdateProjectDetails, isPending} = useUpdateProject( { - onSuccessCallback: () => - { - setIsOpenModal(false) + +const AboutProject = () => { + const [IsOpenModal, setIsOpenModal] = useState(false); + const {mutate: UpdateProjectDetails, isPending} = useUpdateProject({ + onSuccessCallback: () => { + setIsOpenModal(false); } - } ) - const ClientQuery = useQueryClient() - const {projectId} = useParams(); + }); + const ClientQuery = useQueryClient(); + + // *** MODIFIED LINE: Get projectId from Redux store using useSelector *** + const projectId = useSelector((store) => store.localVariables.projectId); + const manageProject = useHasUserPermission(MANAGE_PROJECT); - const {projects_Details, isLoading, error,refetch} = useProjectDetails( projectId ) - const handleFormSubmit = ( updatedProject ) => - { - if ( projects_Details?.id ) - { - UpdateProjectDetails({ projectId: projects_Details?.id,updatedData: updatedProject, - } ); - refetch() - } -}; + const {projects_Details, isLoading, error,refetch} = useProjectDetails( projectId ); // Pass projectId from useSelector + + const handleFormSubmit = ( updatedProject ) => { + if ( projects_Details?.id ) { + UpdateProjectDetails({ projectId: projects_Details?.id,updatedData: updatedProject }); + // The refetch here might be redundant or could be handled by react-query's invalidateQueries + // if UpdateProjectDetails properly invalidates the 'projectDetails' query key. + // If refetch is still needed, consider adding a delay or using onSuccess of UpdateProjectDetails. + // For now, keeping it as is based on your original code. + refetch(); + } + }; + return ( <> {IsOpenModal && ( - setIsOpenModal(false)}> + setIsOpenModal(false)}> setIsOpenModal( false )} isPending={isPending} /> - - )} + + )} {projects_Details && ( - <> + <>
@@ -126,10 +131,9 @@ const AboutProject = () => )} - {isLoading && loading...} ); }; -export default AboutProject; +export default AboutProject; \ No newline at end of file diff --git a/src/components/Project/ProjectCard.jsx b/src/components/Project/ProjectCard.jsx index 37e47cfa..17b19572 100644 --- a/src/components/Project/ProjectCard.jsx +++ b/src/components/Project/ProjectCard.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import moment from "moment"; -import { formatNumber, getCompletionPercentage, getDateDifferenceInDays } from "../../utils/dateUtils"; +import { formatNumber, getDateDifferenceInDays } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; import { useProjectDetails, useUpdateProject } from "../../hooks/useProjects"; import ManageProjectInfo from "./ManageProjectInfo"; @@ -57,7 +57,7 @@ const ProjectCard = ({ projectData, recall }) => { const handleClose = () => setShowModal(false); const handleViewProject = () => { - navigate(`/projects/${projectData.id}`); + navigate(`/projects/details`); }; const handleFormSubmit = (updatedProject) => { @@ -87,19 +87,18 @@ const ProjectCard = ({ projectData, recall }) => {
-
+
-
+
+ className="mb-0 stretched-link text-heading text-start" + onClick={handleViewProject} + > {projectInfo.shortName ? projectInfo.shortName : projectInfo.name} @@ -228,7 +227,12 @@ const ProjectCard = ({ projectData, recall }) => { Task: {formatNumber(projectInfo.completedWork)} / {formatNumber(projectInfo.plannedWork)} - {getCompletionPercentage(projectInfo.completedWork, projectInfo.plannedWork)} + {Math.floor( + getProgressInNumber( + projectInfo.plannedWork, + projectInfo.completedWork + ) + ) || 0}{" "} % Completed
@@ -270,4 +274,4 @@ const ProjectCard = ({ projectData, recall }) => { ); }; -export default ProjectCard; +export default ProjectCard; \ No newline at end of file diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index f34586aa..ec94d8ae 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -1,4 +1,4 @@ -import { useParams } from "react-router-dom"; +import { useSelector } from "react-redux"; // Import useSelector import React, { useState, useEffect, useCallback } from "react"; import ActivityTimeline from "../../components/Project/ActivityTimeline"; @@ -31,157 +31,111 @@ import eventBus from "../../services/eventBus"; import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart"; const ProjectDetails = () => { - let { projectId } = useParams(); - const { - projects_Details, - loading: projectLoading, - error: ProjectError, - refetch - } = useProjectDetails(projectId); + // const { projectId } = useParams(); // REMOVE THIS LINE const dispatch = useDispatch(); - const [project, setProject] = 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); - // } - // }; + // GET projectId FROM REDUX STORE + const projectId = useSelector((store) => store.localVariables.projectId); + + const { + projects_Details, + loading: projectLoading, + error: projectError, + refetch, + } = useProjectDetails(projectId); const [activePill, setActivePill] = useState("profile"); - const handlePillClick = (pillKey) => { - setActivePill(pillKey); - }; - - const handleDataChange = (data) => { - fetchData(); - }; - - const renderContent = () => { - if (projectLoading) return ; - switch (activePill) { - case "profile": { - return ( -
-
- -
-
- -
-
- ); - } - case "teams": { - return ( -
-
- {/* Teams */} - - {/* Teams */} -
-
- ); - break; - } - case "infra": { - return ( - - ); - break; - } - case "workplan": { - return ( - - ); - break; - } - case "directory": { - return ( -
- -
- ); - } - - default: - return ; - } - }; - - useEffect(() => { - dispatch(setProjectId(projectId)); - - }, [projects_Details, projectId]); + // REMOVE THIS useEffect AS projectId IS NOW FROM REDUX + // useEffect(() => { + // if (projectId) dispatch(setProjectId(projectId)); + // }, [projectId, dispatch]); const handler = useCallback( (msg) => { - if (msg.keyword === "Update_Project" && projects_Details.id === msg.response.id) { - refetch() + if (msg.keyword === "Update_Project" && projects_Details?.id === msg.response.id) { + refetch(); } }, - [projects_Details, handleDataChange] + [projects_Details, refetch] ); + useEffect(() => { eventBus.on("project", handler); return () => eventBus.off("project", handler); }, [handler]); + const handlePillClick = (pillKey) => { + setActivePill(pillKey); + }; + + const renderContent = () => { + if (projectLoading || !projects_Details) return ; + + switch (activePill) { + case "profile": + return ( + <> +
+
+ + +
+
+ +
+
+ + ); + + case "teams": + return ( +
+
+ +
+
+ ); + + case "infra": + return ( + + ); + + case "workplan": + return ( + + ); + + case "directory": + return ( +
+ +
+ ); + + default: + return ; + } + }; + return ( - <> - {} -
- +
+ -
- {projectLoading &&

Loading....

} - {/* {!projectLoading && project && ( - - )} */} - - -
- -
- - {renderContent()} +
+
- + + {renderContent()} +
); }; -export default ProjectDetails; +export default ProjectDetails; \ No newline at end of file diff --git a/src/pages/project/ProjectListView.jsx b/src/pages/project/ProjectListView.jsx index 9deead71..a9ffc60d 100644 --- a/src/pages/project/ProjectListView.jsx +++ b/src/pages/project/ProjectListView.jsx @@ -62,7 +62,7 @@ const ProjectListView = ({ projectData, recall }) => { const handleClose = () => setShowModal(false); const handleViewProject = () => { - navigate(`/projects/${projectData.id}`); + navigate(`/projects/details`); }; const handleFormSubmit = (updatedProject) => { @@ -89,7 +89,7 @@ const ProjectListView = ({ projectData, recall }) => { navigate(`/projects/${projectInfo.id}`)} + onClick={() => navigate(`/projects/details`)} > {projectInfo.shortName ? `${projectInfo.name} (${projectInfo.shortName})` @@ -162,7 +162,7 @@ const ProjectListView = ({ projectData, recall }) => { navigate(`/projects/${projectInfo.id}`)} + onClick={() => navigate(`/projects/details`)} > View details @@ -193,4 +193,4 @@ const ProjectListView = ({ projectData, recall }) => { ); }; -export default ProjectListView; +export default ProjectListView; \ No newline at end of file diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 7c364ca9..5ce5b6d4 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -63,7 +63,7 @@ const router = createBrowserRouter( { path: "/", element: }, { path: "/dashboard", element: }, { path: "/projects", element: }, - { path: "/projects/:projectId", element: }, + { path: "/projects/details", element: }, { path: "/project/manage/:projectId", element: }, { path: "/employees", element: }, { path: "/employee/:employeeId", element: },