Issues_July_2W: Project overview widgets #256
| @ -1,55 +1,71 @@ | |||||||
| import React from "react"; | import React from "react"; | ||||||
|  | import { useSelector } from "react-redux"; // Import useSelector to access Redux state | ||||||
| import { | import { | ||||||
|   useDashboardProjectsCardData, | useDashboardProjectsCardData, | ||||||
|   useDashboardTeamsCardData, | useDashboardTeamsCardData, | ||||||
|   useDashboardTasksCardData, | useDashboardTasksCardData, | ||||||
| } from "../../hooks/useDashboard_Data"; | } from "../../hooks/useDashboard_Data"; | ||||||
| import Projects from "./Projects"; | import Projects from "./Projects"; | ||||||
| import Teams from "./Teams"; | import Teams from "./Teams"; | ||||||
| import TasksCard from "./Tasks"; | import TasksCard from "./Tasks"; | ||||||
| import ProjectCompletionChart from "./ProjectCompletionChart"; | import ProjectCompletionChart from "./ProjectCompletionChart"; | ||||||
| import ProjectProgressChart from "./ProjectProgressChart"; | import ProjectProgressChart from "./ProjectProgressChart"; | ||||||
|  | import ProjectOverview from "../Project/ProjectOverview"; | ||||||
| // import Attendance from "./Attendance"; | // import Attendance from "./Attendance"; | ||||||
| 
 | 
 | ||||||
| const Dashboard = () => { | const Dashboard = () => { | ||||||
|   const { projectsCardData } = useDashboardProjectsCardData(); | const { projectsCardData } = useDashboardProjectsCardData(); | ||||||
|   const { teamsCardData } = useDashboardTeamsCardData(); | const { teamsCardData } = useDashboardTeamsCardData(); | ||||||
|   const { tasksCardData } = useDashboardTasksCardData(); | const { tasksCardData } = useDashboardTasksCardData(); | ||||||
| 
 | 
 | ||||||
|   return ( | // Get the selected project ID from Redux store | ||||||
|     <div className="container-fluid mt-3"> | const selectedProjectId = useSelector( | ||||||
|       <div className="row gy-4"> | (store) => store.localVariables.projectId | ||||||
|         {/* Projects Card */} | ); | ||||||
|         <div className="col-sm-6 col-lg-4"> |  | ||||||
|           <Projects projectsCardData={projectsCardData} /> |  | ||||||
|         </div> |  | ||||||
| 
 | 
 | ||||||
|         {/* Teams Card */} | // Determine if "All Projects" is selected | ||||||
|         <div className="col-sm-6 col-lg-4"> | // selectedProjectId will be null when "All Projects" is chosen | ||||||
|           <Teams teamsCardData={teamsCardData} /> | const isAllProjectsSelected = selectedProjectId === null; | ||||||
|         </div> |  | ||||||
| 
 | 
 | ||||||
|         {/* Tasks Card */} | return ( | ||||||
|         <div className="col-sm-6 col-lg-4"> | <div className="container-fluid mt-3"> | ||||||
|           <TasksCard tasksCardData={tasksCardData} /> | <div className="row gy-4"> | ||||||
|         </div> |  | ||||||
| 
 | 
 | ||||||
|         {/* Bar Chart (Project Completion) */} | {isAllProjectsSelected && ( | ||||||
|         <div className="col-xxl-6 col-lg-6"> |     <div className="col-sm-6 col-lg-4"> | ||||||
|           <ProjectCompletionChart /> | <Projects projectsCardData={projectsCardData} /> | ||||||
|         </div> | </div> | ||||||
|  | )} | ||||||
| 
 | 
 | ||||||
|         {/* Line Chart (Project Progress) */} |  | ||||||
|         <div className="col-xxl-6 col-lg-6"> |  | ||||||
|           <ProjectProgressChart /> |  | ||||||
|         </div> |  | ||||||
| 
 | 
 | ||||||
|         {/* <div className="col-xxl-6 col-lg-6"> | <div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6":"col-sm-6 col-lg-4"}`}> | ||||||
|           <Attendance /> | <Teams teamsCardData={teamsCardData} /> | ||||||
|         </div> */} | </div> | ||||||
|       </div> | 
 | ||||||
|     </div> | <div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6":"col-sm-6 col-lg-4"}`}> | ||||||
|   ); | <TasksCard tasksCardData={tasksCardData} /> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | {isAllProjectsSelected && ( | ||||||
|  |     <div className="col-xxl-6 col-lg-6"> | ||||||
|  | <ProjectCompletionChart /> | ||||||
|  | </div> | ||||||
|  | )} | ||||||
|  | 
 | ||||||
|  | {! isAllProjectsSelected && ( | ||||||
|  |     <div className="col-xxl-6 col-lg-6"> | ||||||
|  |     <ProjectOverview /> | ||||||
|  | </div> | ||||||
|  | )} | ||||||
|  | <div className="col-xxl-6 col-lg-6"> | ||||||
|  | <ProjectProgressChart /> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | </div> | ||||||
|  | </div> | ||||||
|  | ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default Dashboard; | export default Dashboard; | ||||||
| @ -27,9 +27,9 @@ const Header = () => { | |||||||
|   const { data, loading } = useMaster(); |   const { data, loading } = useMaster(); | ||||||
|   const navigate = useNavigate(); |   const navigate = useNavigate(); | ||||||
|   const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); |   const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); | ||||||
|     const isDashboard = location.pathname === "/dashboard"; |  | ||||||
|   // const isDirectoryPath = location.pathname === "/directory"; |  | ||||||
| 
 | 
 | ||||||
|  |     const isDirectoryPath = /^\/directory$/.test(location.pathname); | ||||||
|  |     const isDashboard = /^\/dashboard$/.test(location.pathname); | ||||||
|   const getRole = (roles, joRoleId) => { |   const getRole = (roles, joRoleId) => { | ||||||
|     if (!Array.isArray(roles)) return "User"; |     if (!Array.isArray(roles)) return "User"; | ||||||
|     let role = roles.find((role) => role.id === joRoleId); |     let role = roles.find((role) => role.id === joRoleId); | ||||||
| @ -37,7 +37,7 @@ const Header = () => { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   const handleLogout = (e) => { |   const handleLogout = (e) => { | ||||||
|     e.preventDefault(); // Prevent default anchor behavior (e.g., page reload) |     e.preventDefault();  | ||||||
|     logout(); |     logout(); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -105,14 +105,18 @@ const Header = () => { | |||||||
|       selectedProject === undefined && |       selectedProject === undefined && | ||||||
|       !getCachedData("hasReceived") |       !getCachedData("hasReceived") | ||||||
|     ) { |     ) { | ||||||
|       dispatch(setProjectId(null)); // Set to null for "All Projects" |       if(isDashboard){ | ||||||
|  |         dispatch(setProjectId(null)); | ||||||
|  |       }else{ | ||||||
|  |          dispatch(setProjectId(projectNames[0]?.id)); | ||||||
|  |       } | ||||||
|     } |     } | ||||||
|   }, [projectNames, selectedProject, dispatch]); |   }, [projectNames, selectedProject, dispatch]); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   /** Check if current page is project details page or directory page */ |   /** Check if current page is project details page or directory page */ | ||||||
|   // const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname); |   // const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname); | ||||||
|   const isDirectoryPath = /^\/directory$/.test(location.pathname); | 
 | ||||||
| 
 | 
 | ||||||
|   const handler = useCallback( |   const handler = useCallback( | ||||||
|     async (data) => { |     async (data) => { | ||||||
| @ -141,12 +145,10 @@ const Header = () => { | |||||||
|     [HasManageProjectPermission, projectNames, fetchData] |     [HasManageProjectPermission, projectNames, fetchData] | ||||||
|   ); |   ); | ||||||
| 
 | 
 | ||||||
|   // Correct way to dispatch an action on mount |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     dispatch(changeMaster("Job Role")); |     dispatch(changeMaster("Job Role")); | ||||||
|   }, [dispatch]); |   }, [dispatch]); | ||||||
| 
 | 
 | ||||||
|   // Event bus listeners for project changes |  | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     eventBus.on("assign_project_one", handler); |     eventBus.on("assign_project_one", handler); | ||||||
|     eventBus.on("project", newProjectHandler); |     eventBus.on("project", newProjectHandler); | ||||||
| @ -174,7 +176,6 @@ const Header = () => { | |||||||
|         className="navbar-nav-right d-flex align-items-center justify-content-between" |         className="navbar-nav-right d-flex align-items-center justify-content-between" | ||||||
|         id="navbar-collapse" |         id="navbar-collapse" | ||||||
|       > |       > | ||||||
|         {/* Project Selection Dropdown */} |  | ||||||
|         {projectNames && !isDirectoryPath && ( |         {projectNames && !isDirectoryPath && ( | ||||||
|           <div className="align-items-center"> |           <div className="align-items-center"> | ||||||
|             <i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i> |             <i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i> | ||||||
| @ -194,7 +195,7 @@ const Header = () => { | |||||||
|                   className="dropdown-menu" |                   className="dropdown-menu" | ||||||
|                   style={{ overflow: "auto", maxHeight: "300px" }} |                   style={{ overflow: "auto", maxHeight: "300px" }} | ||||||
|                 > |                 > | ||||||
|                   {/* Show "All Projects" only on dashboard */} |              | ||||||
|                   {isDashboard && ( |                   {isDashboard && ( | ||||||
|                     <li> |                     <li> | ||||||
|                       <button |                       <button | ||||||
| @ -228,10 +229,7 @@ const Header = () => { | |||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
| 
 | 
 | ||||||
|         {/* Display project name on project details or directory pages */} |  | ||||||
|         {/* { (<span className="fs-5 align-items-center"><i className="rounded-circle bx bx-building-house bx-sm-lg bx-md me-2"></i>{displayText}</span>)} */} |  | ||||||
| 
 | 
 | ||||||
|         {/* User Profile and Shortcuts */} |  | ||||||
|         <ul className="navbar-nav flex-row align-items-center ms-md-auto"> |         <ul className="navbar-nav flex-row align-items-center ms-md-auto"> | ||||||
|           <li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0"> |           <li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0"> | ||||||
|             <a |             <a | ||||||
|  | |||||||
| @ -25,7 +25,7 @@ import GlobalModel from "../common/GlobalModel"; | |||||||
| 
 | 
 | ||||||
| const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) => | const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) => | ||||||
| { | { | ||||||
|   const {projectId} = useParams() |   const projectId = useSelector((store)=>store.localVariables.projectId) | ||||||
|   const reloadedData = useSelector((store) => store.localVariables.reload); |   const reloadedData = useSelector((store) => store.localVariables.reload); | ||||||
|   const [ expandedBuildings, setExpandedBuildings ] = useState( [] ); |   const [ expandedBuildings, setExpandedBuildings ] = useState( [] ); | ||||||
|    const {projectInfra,isLoading,error} = useProjectInfra(projectId) |    const {projectInfra,isLoading,error} = useProjectInfra(projectId) | ||||||
|  | |||||||
| @ -60,7 +60,7 @@ const ProjectNav = ({ onPillClick, activePill }) => { | |||||||
|               onPillClick("imagegallary"); |               onPillClick("imagegallary"); | ||||||
|             }} |             }} | ||||||
|           > |           > | ||||||
|             <i className="bx bxs-file-image bx-sm me-1_5"></i> <span className="d-none d-md-inline">Image Gallary</span> |            <i className='bx bxs-cog bx-sm me-1_5'></i> <span className="d-none d-md-inline">project Setup</span> | ||||||
|           </a> |           </a> | ||||||
|         </li> |         </li> | ||||||
|         {(DirAdmin || DireManager || DirUser) && ( |         {(DirAdmin || DireManager || DirUser) && ( | ||||||
|  | |||||||
| @ -1,15 +1,168 @@ | |||||||
| import React from "react"; | import React from "react"; | ||||||
|  | import { useEffect, useState } from "react"; | ||||||
|  | import { useSelector } from "react-redux"; | ||||||
|  | 
 | ||||||
| import { | import { | ||||||
|   useEmployeesByProjectAllocated, |   useEmployeesByProjectAllocated, | ||||||
|   useProjects, |   useProjects, | ||||||
| } from "../../hooks/useProjects"; | } from "../../hooks/useProjects"; | ||||||
| import { formatNumber, getCompletionPercentage } from "../../utils/dateUtils"; | import ReactApexChart from "react-apexcharts"; | ||||||
| import ProgressBar from "../common/ProgressBar"; | import Chart from "react-apexcharts"; | ||||||
| 
 | 
 | ||||||
| const ProjectOverview = ({ project }) => { | const ProjectOverview = ({ project }) => { | ||||||
|   const { projects } = useProjects(); |   const { projects } = useProjects(); | ||||||
|  |   const [current_project, setCurrentProject] = useState( | ||||||
|  |     projects.find((pro) => pro.id == project) | ||||||
|  |   ); | ||||||
| 
 | 
 | ||||||
|   const project_detail = projects.find((pro) => pro.id == project); |   const selectedProject = useSelector( | ||||||
|  |     (store) => store.localVariables.projectId | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const getProgressInPercentage = (planned, completed) => { | ||||||
|  |     if (completed && planned) return (completed * 100) / planned; | ||||||
|  |     else return 0; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   //let project_detail = projects.find((pro) => pro.id == project); | ||||||
|  | 
 | ||||||
|  |   // Utility function to check if a number has a decimal part | ||||||
|  |   const hasDecimal = (num) => { | ||||||
|  |     // Convert to string and check for a decimal point | ||||||
|  |     // Or, check if the number is not equal to its integer part | ||||||
|  |     return num % 1 !== 0; | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   // FormattedNumber component to display numbers with conditional decimal places | ||||||
|  |   function FormattedNumber(value, locale = "en-US") { | ||||||
|  |     // Ensure the value is a number | ||||||
|  |     const numericValue = parseFloat(value); | ||||||
|  | 
 | ||||||
|  |     // Handle non-numeric values gracefully | ||||||
|  |     if (isNaN(numericValue)) { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     let options = {}; | ||||||
|  | 
 | ||||||
|  |     // Determine formatting options based on whether the number has a decimal part | ||||||
|  |     if (hasDecimal(numericValue)) { | ||||||
|  |       // If it has a decimal, format to exactly two decimal places | ||||||
|  |       options = { | ||||||
|  |         minimumFractionDigits: 2, | ||||||
|  |         maximumFractionDigits: 2, | ||||||
|  |       }; | ||||||
|  |     } else { | ||||||
|  |       // If it's a whole number, format to zero decimal places | ||||||
|  |       options = { | ||||||
|  |         minimumFractionDigits: 0, | ||||||
|  |         maximumFractionDigits: 0, | ||||||
|  |       }; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Use Intl.NumberFormat for robust and locale-aware formatting | ||||||
|  |     const formattedString = new Intl.NumberFormat(locale, options).format( | ||||||
|  |       numericValue | ||||||
|  |     ); | ||||||
|  | 
 | ||||||
|  |     return formattedString; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const getRadialBarOptions = (percentage) => { | ||||||
|  |     return { | ||||||
|  |       chart: { | ||||||
|  |         height: 350, | ||||||
|  |         type: "radialBar", | ||||||
|  |         sparkline: { | ||||||
|  |           // Often used with gauges for a minimalist look | ||||||
|  |           enabled: true, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       plotOptions: { | ||||||
|  |         radialBar: { | ||||||
|  |           startAngle: -90, // Start the gauge from the left (bottom-left) | ||||||
|  |           endAngle: 90, // End the gauge at the right (bottom-right) | ||||||
|  |           hollow: { | ||||||
|  |             size: "70%", // Size of the hollow part of the bar | ||||||
|  |           }, | ||||||
|  |           dataLabels: { | ||||||
|  |             show: true, | ||||||
|  |             name: { | ||||||
|  |               show: true, | ||||||
|  |               fontSize: "16px", | ||||||
|  |               fontFamily: "Inter, sans-serif", | ||||||
|  |               color: "#6B7280", // Tailwind gray-500 | ||||||
|  |               offsetY: -10, | ||||||
|  |             }, | ||||||
|  |             value: { | ||||||
|  |               show: true, | ||||||
|  |               fontSize: "28px", | ||||||
|  |               fontFamily: "Inter, sans-serif", | ||||||
|  |               color: "#374151", // Tailwind gray-700 | ||||||
|  |               offsetY: 20, | ||||||
|  |               formatter: function (val) { | ||||||
|  |                 return FormattedNumber(val) + "%"; // Format value as percentage | ||||||
|  |               }, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |           track: { | ||||||
|  |             background: "#E5E7EB", // Tailwind gray-200 for the track | ||||||
|  |             strokeWidth: "97%", | ||||||
|  |             margin: 5, // margin in between segments | ||||||
|  |             dropShadow: { | ||||||
|  |               enabled: true, | ||||||
|  |               top: 2, | ||||||
|  |               left: 0, | ||||||
|  |               blur: 4, | ||||||
|  |               opacity: 0.15, | ||||||
|  |             }, | ||||||
|  |           }, | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       fill: { | ||||||
|  |         type: "gradient", | ||||||
|  |         gradient: { | ||||||
|  |           shade: "dark", | ||||||
|  |           type: "horizontal", | ||||||
|  |           shadeIntensity: 0.5, | ||||||
|  |           gradientToColors: ["#6366F1"], // Tailwind indigo-500 | ||||||
|  |           inverseColors: true, | ||||||
|  |           opacityFrom: 1, | ||||||
|  |           opacityTo: 1, | ||||||
|  |           stops: [0, 100], | ||||||
|  |         }, | ||||||
|  |       }, | ||||||
|  |       stroke: { | ||||||
|  |         lineCap: "round",  | ||||||
|  |       }, | ||||||
|  |       labels: ["Progress"],  | ||||||
|  |       series: [percentage],  | ||||||
|  |     }; | ||||||
|  |   }; | ||||||
|  |   const [radialPercentage, setRadialPercentage] = useState(75); // Initial percentage | ||||||
|  | 
 | ||||||
|  |   const radialBarOptions = getRadialBarOptions(radialPercentage); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     if (current_project) { | ||||||
|  |       let val = getProgressInPercentage( | ||||||
|  |         current_project.plannedWork, | ||||||
|  |         current_project.completedWork | ||||||
|  |       ); | ||||||
|  |       setRadialPercentage(val); | ||||||
|  |     } else setRadialPercentage(0); | ||||||
|  |   }, [current_project]); | ||||||
|  | 
 | ||||||
|  |   useEffect(() => { | ||||||
|  |     setCurrentProject(projects.find((pro) => pro.id == selectedProject)); | ||||||
|  |     if (current_project) { | ||||||
|  |       let val = getProgressInPercentage( | ||||||
|  |         current_project.plannedWork, | ||||||
|  |         current_project.completedWork | ||||||
|  |       ); | ||||||
|  |       setRadialPercentage(val); | ||||||
|  |     } else setRadialPercentage(0); | ||||||
|  |   }, [selectedProject]); | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <div className="card mb-6"> |     <div className="card mb-6"> | ||||||
| @ -20,71 +173,78 @@ const ProjectOverview = ({ project }) => { | |||||||
|           <span className="ms-2">Project Statistics</span> |           <span className="ms-2">Project Statistics</span> | ||||||
|         </h6> |         </h6> | ||||||
|       </div> |       </div> | ||||||
|       <div className="card-body"> |      <div className="card-body"> | ||||||
|         <ul className="list-unstyled mb-0 mt-3 pt-1"> |   <ul className="list-unstyled m-0 p-0"> | ||||||
|           <li className="d-flex align-items-center mb-3"> |     <li className="d-flex flex-wrap"> | ||||||
|             <i className="bx bx-check"></i> |       <div className="w-100 d-flex flex-wrap"> | ||||||
|             <span className="fw-medium mx-2">Task Planned:</span>{" "} |         {/* Centered Chart */} | ||||||
|             <span>{formatNumber(project_detail?.plannedWork)}</span> |         <div className="w-100 d-flex justify-content-center mb-3"> | ||||||
|           </li> |           <div > | ||||||
|           <li className="d-flex align-items-center mb-3"> |             <Chart | ||||||
|             <i className="bx bx-star"></i> |               options={radialBarOptions} | ||||||
|             <span className="fw-medium mx-2">Task Completed:</span>{" "} |               series={radialBarOptions.series} | ||||||
|             <span>{formatNumber(project_detail?.completedWork)}</span> |               type="radialBar" | ||||||
|           </li> |               height="100%" | ||||||
|           <li className="d-flex align-items-center mb-3"> |             /> | ||||||
|             <i className="bx bx-user"></i> |           </div> | ||||||
|             <span className="fw-medium mx-2">Current team Size:</span>{" "} |         </div> | ||||||
|             <span>{project_detail?.teamSize}</span> | 
 | ||||||
|           </li> |         {/* Info Section */} | ||||||
|           <li className=" mb-3"> |         <div className="mb-2" style={{ flex: "1 1 auto" }}> | ||||||
|             {project_detail && ( |           <div> | ||||||
|               <> |             {/* Tasks Planned */} | ||||||
|                 <div className="d-flex  text-end mb-2  mt-5"> |             <div className="d-flex align-items-center mb-3"> | ||||||
|                   <small className="text-body text-muted "> |               <div className="avatar me-2"> | ||||||
|                     {/* {Math.floor( |                 <span className="avatar-initial rounded-2 bg-label-primary"> | ||||||
|                       getProgressInNumber( |                   <i className="bx bx-check text-primary fs-4"></i> | ||||||
|                          |                 </span> | ||||||
|                         project_detail.completedWork |               </div> | ||||||
|                       ) |               <div className="d-flex flex-column text-start"> | ||||||
|                     ) || 0}{" "} */} |                 <small className="fw-bold">Tasks Planned</small> | ||||||
|                     { |                 <h5 className="mb-0"> | ||||||
|                       getCompletionPercentage( formatNumber(project_detail.completedWork),formatNumber(project_detail.plannedWork)) |                   {FormattedNumber(current_project?.plannedWork)} | ||||||
|                     |                 </h5> | ||||||
|                     } |               </div> | ||||||
|                     % Completed |             </div> | ||||||
|                   </small> | 
 | ||||||
|                 </div> |             {/* Tasks Completed */} | ||||||
|                 {/* <div |             <div className="d-flex align-items-center mb-3"> | ||||||
|                   className="progress mb-4 rounded" |               <div className="avatar me-2"> | ||||||
|                   style={{ height: "8px" }} |                 <span className="avatar-initial rounded-2 bg-label-info"> | ||||||
|                 > |                   <i className="bx bx-star text-info fs-4"></i> | ||||||
|                   <div |                 </span> | ||||||
|                     className="progress-bar rounded" |               </div> | ||||||
|                     role="progressbar" |               <div className="d-flex flex-column text-start"> | ||||||
|                     style={{ |                 <small className="fw-bold">Tasks Completed</small> | ||||||
|                       width: getProgress( |                 <h5 className="mb-0"> | ||||||
|                         project_detail.plannedWork, |                   {FormattedNumber(current_project?.completedWork)} | ||||||
|                         project_detail.completedWork |                 </h5> | ||||||
|                       ), |               </div> | ||||||
|                     }} |             </div> | ||||||
|                     aria-valuenow={project_detail.completedWork} | 
 | ||||||
|                     aria-valuemin="0" |             {/* Team Size */} | ||||||
|                     aria-valuemax={project_detail.plannedWork} |             <div className="d-flex align-items-center"> | ||||||
|                   ></div> |               <div className="avatar me-2"> | ||||||
|                 </div> */} |                 <span className="avatar-initial rounded-2 bg-label-primary"> | ||||||
|                 <ProgressBar |                   <i className="bx bx-group text-primary fs-4"></i> | ||||||
|                   completedWork={formatNumber(project_detail?.completedWork)} |                 </span> | ||||||
|                   plannedWork={formatNumber(project_detail?.plannedWork)} |               </div> | ||||||
|                   className="m-0 text-info" |               <div className="d-flex flex-column text-start"> | ||||||
|                 /> |                 <small className="fw-bold">Current Team Size</small> | ||||||
|               </> |                 <h5 className="mb-0"> | ||||||
|             )} |                   {FormattedNumber(current_project?.teamSize)} | ||||||
|           </li> |                 </h5> | ||||||
|         </ul> |               </div> | ||||||
|  |             </div> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|       </div> |       </div> | ||||||
|  |     </li> | ||||||
|  |   </ul> | ||||||
|  | </div> | ||||||
|  | 
 | ||||||
|     </div> |     </div> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export default ProjectOverview; | export default ProjectOverview; | ||||||
| @ -21,13 +21,14 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | |||||||
| import { REGULARIZE_ATTENDANCE } from "../../utils/constants"; | import { REGULARIZE_ATTENDANCE } from "../../utils/constants"; | ||||||
| import eventBus from "../../services/eventBus"; | import eventBus from "../../services/eventBus"; | ||||||
| import AttendanceRepository from "../../repositories/AttendanceRepository"; | import AttendanceRepository from "../../repositories/AttendanceRepository"; | ||||||
|  | import { useProjectName } from "../../hooks/useProjects"; | ||||||
| 
 | 
 | ||||||
| const AttendancePage = () => { | const AttendancePage = () => { | ||||||
|   const [activeTab, setActiveTab] = useState("all"); |   const [activeTab, setActiveTab] = useState("all"); | ||||||
|   const [ShowPending, setShowPending] = useState(false); |   const [ShowPending, setShowPending] = useState(false); | ||||||
|   const loginUser = getCachedProfileData(); |   const loginUser = getCachedProfileData(); | ||||||
|   var selectedProject = useSelector((store) => store.localVariables.projectId); |   var selectedProject = useSelector((store) => store.localVariables.projectId); | ||||||
|   // const { projects, loading: projectLoading } = useProjects(); |   const dispatch = useDispatch() | ||||||
|   const { |   const { | ||||||
|     attendance, |     attendance, | ||||||
|     loading: attLoading, |     loading: attLoading, | ||||||
| @ -38,7 +39,8 @@ const AttendancePage = () => { | |||||||
|   const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); |   const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); | ||||||
|   const [modelConfig, setModelConfig] = useState(); |   const [modelConfig, setModelConfig] = useState(); | ||||||
|   const DoRegularized = useHasUserPermission(REGULARIZE_ATTENDANCE); |   const DoRegularized = useHasUserPermission(REGULARIZE_ATTENDANCE); | ||||||
|   const dispatch = useDispatch(); |   const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||||
|  |    | ||||||
| 
 | 
 | ||||||
|   const [formData, setFormData] = useState({ |   const [formData, setFormData] = useState({ | ||||||
|     markTime: "", |     markTime: "", | ||||||
| @ -130,7 +132,13 @@ const AttendancePage = () => { | |||||||
|   const handleToggle = (event) => { |   const handleToggle = (event) => { | ||||||
|     setShowOnlyCheckout(event.target.checked); |     setShowOnlyCheckout(event.target.checked); | ||||||
|   }; |   }; | ||||||
|  |   useEffect(() => { | ||||||
|  |    if(selectedProject == null){ | ||||||
|  |        dispatch(setProjectId(projectNames[0]?.id)); | ||||||
|  |    } | ||||||
|  |    },[]) | ||||||
| 
 | 
 | ||||||
|  |    | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|     if (modelConfig !== null) { |     if (modelConfig !== null) { | ||||||
|       openModel(); |       openModel(); | ||||||
| @ -140,18 +148,7 @@ const AttendancePage = () => { | |||||||
|     setAttendances(attendance); |     setAttendances(attendance); | ||||||
|   }, [attendance]); |   }, [attendance]); | ||||||
| 
 | 
 | ||||||
|   // useEffect(() => { |  | ||||||
|   //   if (selectedProject === 1 || selectedProject === undefined) { |  | ||||||
|   //     dispatch(setProjectId(loginUser?.projects[0])); |  | ||||||
|   //   } |  | ||||||
|   // }, [selectedProject, loginUser?.projects]); |  | ||||||
| 
 | 
 | ||||||
|   // Filter attendance data based on the toggle |  | ||||||
|   // const filteredAttendance = showOnlyCheckout |  | ||||||
|   //   ? attendances?.filter( |  | ||||||
|   //     (att) => att?.checkOutTime !== null && att?.checkInTime !== null |  | ||||||
|   //   ) |  | ||||||
|   //   : attendances; |  | ||||||
|   const filteredAttendance = ShowPending |   const filteredAttendance = ShowPending | ||||||
|     ? attendances?.filter( |     ? attendances?.filter( | ||||||
|         (att) => att?.checkInTime !== null && att?.checkOutTime === null |         (att) => att?.checkInTime !== null && att?.checkOutTime === null | ||||||
|  | |||||||
| @ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from "react"; | |||||||
| import { useDispatch, useSelector } from "react-redux"; | import { useDispatch, useSelector } from "react-redux"; | ||||||
| import Breadcrumb from "../../components/common/Breadcrumb"; | import Breadcrumb from "../../components/common/Breadcrumb"; | ||||||
| import { useTaskList } from "../../hooks/useTasks"; | import { useTaskList } from "../../hooks/useTasks"; | ||||||
| import { useProjects } from "../../hooks/useProjects"; | import { useProjectName, useProjects } from "../../hooks/useProjects"; | ||||||
| import { setProjectId } from "../../slices/localVariablesSlice"; | import { setProjectId } from "../../slices/localVariablesSlice"; | ||||||
| import { ReportTask } from "../../components/Activities/ReportTask"; | import { ReportTask } from "../../components/Activities/ReportTask"; | ||||||
| import ReportTaskComments from "../../components/Activities/ReportTaskComments"; | import ReportTaskComments from "../../components/Activities/ReportTaskComments"; | ||||||
| @ -18,11 +18,11 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | |||||||
| import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; | import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; | ||||||
| 
 | 
 | ||||||
| const DailyTask = () => { | const DailyTask = () => { | ||||||
|   const [searchParams] = useSearchParams(); |  | ||||||
|   const projectIdFromUrl = searchParams.get("project"); |  | ||||||
|   const selectedProject = useSelector( |   const selectedProject = useSelector( | ||||||
|     (store) => store.localVariables.projectId |     (store) => store.localVariables.projectId | ||||||
|   ); |   ); | ||||||
|  |   const dispatch = useDispatch() | ||||||
|  |     const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|   const [filters, setFilters] = useState({ |   const [filters, setFilters] = useState({ | ||||||
| @ -48,7 +48,11 @@ const DailyTask = () => { | |||||||
|     dateRange?.endDate || null |     dateRange?.endDate || null | ||||||
|     ); |     ); | ||||||
|    |    | ||||||
| 
 |   useEffect(() => { | ||||||
|  |    if(selectedProject == null){ | ||||||
|  |        dispatch(setProjectId(projectNames[0]?.id)); | ||||||
|  |    } | ||||||
|  |    },[]) | ||||||
|   const [TaskLists, setTaskLists] = useState([]); |   const [TaskLists, setTaskLists] = useState([]); | ||||||
|   const [dates, setDates] = useState([]); |   const [dates, setDates] = useState([]); | ||||||
|   const popoverRefs = useRef([]); |   const popoverRefs = useRef([]); | ||||||
|  | |||||||
| @ -1,9 +1,23 @@ | |||||||
| import React from "react"; | import React,{useEffect} from "react"; | ||||||
| import Breadcrumb from "../../components/common/Breadcrumb"; | import Breadcrumb from "../../components/common/Breadcrumb"; | ||||||
| import InfraPlanning from "../../components/Activities/InfraPlanning"; | import InfraPlanning from "../../components/Activities/InfraPlanning"; | ||||||
|  | import { useProjectName } from "../../hooks/useProjects"; | ||||||
|  | import { useDispatch, useSelector } from "react-redux"; | ||||||
|  | import { setProjectId } from "../../slices/localVariablesSlice"; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| const TaskPlannng = () => { | const TaskPlannng = () => { | ||||||
|  |   const selectedProject = useSelector( | ||||||
|  |     (store) => store.localVariables.projectId | ||||||
|  |   ); | ||||||
|  | 
 | ||||||
|  |   const dispatch = useDispatch() | ||||||
|  |     const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||||
|  |   useEffect(() => { | ||||||
|  |    if(selectedProject == null){ | ||||||
|  |        dispatch(setProjectId(projectNames[0]?.id)); | ||||||
|  |    } | ||||||
|  |    },[]) | ||||||
| 
 | 
 | ||||||
|   return ( |   return ( | ||||||
|     <> |     <> | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| import React, { useState, useEffect, useRef, useCallback } from "react"; | import React, { useState, useEffect, useRef, useCallback } from "react"; | ||||||
| import "./ImageGallery.css"; | import "./ImageGallery.css"; | ||||||
| import moment from "moment"; | import moment from "moment"; | ||||||
| import { useSelector } from "react-redux"; | import { useDispatch, useSelector } from "react-redux"; | ||||||
| import { useModal } from "./ModalContext"; | import { useModal } from "./ModalContext"; | ||||||
| import ImagePop from "./ImagePop"; | import ImagePop from "./ImagePop"; | ||||||
| import Avatar from "../../components/common/Avatar"; | import Avatar from "../../components/common/Avatar"; | ||||||
| @ -10,11 +10,22 @@ import eventBus from "../../services/eventBus"; | |||||||
| import Breadcrumb from "../../components/common/Breadcrumb"; | import Breadcrumb from "../../components/common/Breadcrumb"; | ||||||
| import { formatUTCToLocalTime } from "../../utils/dateUtils"; | import { formatUTCToLocalTime } from "../../utils/dateUtils"; | ||||||
| import useImageGallery from "../../hooks/useImageGallery"; | import useImageGallery from "../../hooks/useImageGallery"; | ||||||
|  | import { useProjectName } from "../../hooks/useProjects"; | ||||||
|  | import { setProjectId } from "../../slices/localVariablesSlice"; | ||||||
| 
 | 
 | ||||||
| const SCROLL_THRESHOLD = 5; | const SCROLL_THRESHOLD = 5; | ||||||
| 
 | 
 | ||||||
| const ImageGallery = () => { | const ImageGallery = () => { | ||||||
|   const selectedProjectId = useSelector((store) => store.localVariables.projectId); |   const selectedProjectId = useSelector((store) => store.localVariables.projectId); | ||||||
|  | 
 | ||||||
|  |   const dispatch = useDispatch() | ||||||
|  |     const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||||
|  |   useEffect(() => { | ||||||
|  |    if(selectedProjectId == null){ | ||||||
|  |        dispatch(setProjectId(projectNames[0]?.id)); | ||||||
|  |    } | ||||||
|  |    },[]) | ||||||
|  | 
 | ||||||
|   const { |   const { | ||||||
|     images, |     images, | ||||||
|     allImagesData, |     allImagesData, | ||||||
|  | |||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,11 +1,9 @@ | |||||||
| import { useSelector } from "react-redux"; // Import useSelector | import { useSelector } from "react-redux"; // Import useSelector | ||||||
| import React, { useState, useEffect, useCallback } from "react"; | import React, { useState, useEffect, useCallback } from "react"; | ||||||
| 
 | 
 | ||||||
| import ActivityTimeline from "../../components/Project/ActivityTimeline"; |  | ||||||
| import ProjectOverview from "../../components/Project/ProjectOverview"; | import ProjectOverview from "../../components/Project/ProjectOverview"; | ||||||
| import AboutProject from "../../components/Project/AboutProject"; | import AboutProject from "../../components/Project/AboutProject"; | ||||||
| import ProjectNav from "../../components/Project/ProjectNav"; | import ProjectNav from "../../components/Project/ProjectNav"; | ||||||
| import ProjectBanner from "../../components/Project/ProjectBanner"; |  | ||||||
| import Teams from "../../components/Project/Teams"; | import Teams from "../../components/Project/Teams"; | ||||||
| import ProjectInfra from "../../components/Project/ProjectInfra"; | import ProjectInfra from "../../components/Project/ProjectInfra"; | ||||||
| import Loader from "../../components/common/Loader"; | import Loader from "../../components/common/Loader"; | ||||||
| @ -16,25 +14,18 @@ import { | |||||||
|   clearCacheKey, |   clearCacheKey, | ||||||
|   getCachedData, |   getCachedData, | ||||||
| } from "../../slices/apiDataManager"; | } from "../../slices/apiDataManager"; | ||||||
| import ProjectRepository from "../../repositories/ProjectRepository"; |  | ||||||
| import { ActivityeRepository } from "../../repositories/MastersRepository"; |  | ||||||
| import "./ProjectDetails.css"; | import "./ProjectDetails.css"; | ||||||
| import { | import { | ||||||
|   useEmployeesByProjectAllocated, |  | ||||||
|   useProjectDetails, |   useProjectDetails, | ||||||
| } from "../../hooks/useProjects"; | } from "../../hooks/useProjects"; | ||||||
| import { useDispatch } from "react-redux"; |  | ||||||
| import { setProjectId } from "../../slices/localVariablesSlice"; |  | ||||||
| import { ComingSoonPage } from "../Misc/ComingSoonPage"; | import { ComingSoonPage } from "../Misc/ComingSoonPage"; | ||||||
| import Directory from "../Directory/Directory"; | import Directory from "../Directory/Directory"; | ||||||
| import eventBus from "../../services/eventBus"; | import eventBus from "../../services/eventBus"; | ||||||
| import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart"; | import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart"; | ||||||
| 
 | 
 | ||||||
| const ProjectDetails = () => { | const ProjectDetails = () => { | ||||||
|   // const { projectId } = useParams(); // REMOVE THIS LINE |  | ||||||
|   const dispatch = useDispatch(); |  | ||||||
| 
 | 
 | ||||||
|   // GET projectId FROM REDUX STORE | 
 | ||||||
|   const projectId = useSelector((store) => store.localVariables.projectId); |   const projectId = useSelector((store) => store.localVariables.projectId); | ||||||
| 
 | 
 | ||||||
|   const { |   const { | ||||||
| @ -46,10 +37,7 @@ const ProjectDetails = () => { | |||||||
| 
 | 
 | ||||||
|   const [activePill, setActivePill] = useState("profile"); |   const [activePill, setActivePill] = useState("profile"); | ||||||
| 
 | 
 | ||||||
|   // REMOVE THIS useEffect AS projectId IS NOW FROM REDUX | 
 | ||||||
|   // useEffect(() => { |  | ||||||
|   //   if (projectId) dispatch(setProjectId(projectId)); |  | ||||||
|   // }, [projectId, dispatch]); |  | ||||||
| 
 | 
 | ||||||
|   const handler = useCallback( |   const handler = useCallback( | ||||||
|     (msg) => { |     (msg) => { | ||||||
|  | |||||||
| @ -5,11 +5,6 @@ import Breadcrumb from "../../components/common/Breadcrumb"; | |||||||
| import ProjectRepository from "../../repositories/ProjectRepository"; | import ProjectRepository from "../../repositories/ProjectRepository"; | ||||||
| import { useProjects, useCreateProject } from "../../hooks/useProjects"; | import { useProjects, useCreateProject } from "../../hooks/useProjects"; | ||||||
| import showToast from "../../services/toastService"; | import showToast from "../../services/toastService"; | ||||||
| // import { |  | ||||||
| //   getCachedData, |  | ||||||
| //   cacheData, |  | ||||||
| //   clearCacheKey, |  | ||||||
| // } from "../../slices/apiDataManager"; |  | ||||||
| import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | ||||||
| import { useProfile } from "../../hooks/useProfile"; | import { useProfile } from "../../hooks/useProfile"; | ||||||
| import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants"; | import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants"; | ||||||
| @ -20,11 +15,15 @@ import { defaultCheckBoxAppearanceProvider } from "pdf-lib"; | |||||||
| import { useMutation } from "@tanstack/react-query"; | import { useMutation } from "@tanstack/react-query"; | ||||||
| import usePagination from "../../hooks/usePagination"; | import usePagination from "../../hooks/usePagination"; | ||||||
| import GlobalModel from "../../components/common/GlobalModel"; | import GlobalModel from "../../components/common/GlobalModel"; | ||||||
|  | import { useDispatch, useSelector } from "react-redux"; | ||||||
|  | import { setProjectId } from "../../slices/localVariablesSlice"; | ||||||
| 
 | 
 | ||||||
| const ProjectList = () => { | const ProjectList = () => { | ||||||
|   const { profile: loginUser } = useProfile(); |   const { profile: loginUser } = useProfile(); | ||||||
|   const [listView, setListView] = useState(false); |   const [listView, setListView] = useState(false); | ||||||
|   const [showModal, setShowModal] = useState(false); |   const [showModal, setShowModal] = useState(false); | ||||||
|  |   const selectedProject = useSelector((store)=>store.localVariables.projectId) | ||||||
|  |   const dispatch = useDispatch() | ||||||
| 
 | 
 | ||||||
|   const { projects, loading, error, refetch } = useProjects(); |   const { projects, loading, error, refetch } = useProjects(); | ||||||
|   const [projectList, setProjectList] = useState([]); |   const [projectList, setProjectList] = useState([]); | ||||||
| @ -75,6 +74,10 @@ const ProjectList = () => { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   useEffect(() => { |   useEffect(() => { | ||||||
|  |    if(selectedProject == null){ | ||||||
|  |        dispatch(setProjectId(projects[0]?.id)); | ||||||
|  |    } | ||||||
|  | 
 | ||||||
|     if (!loading && projects) { |     if (!loading && projects) { | ||||||
|       sortingProject(projects); |       sortingProject(projects); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -5,7 +5,7 @@ const localVariablesSlice = createSlice({ | |||||||
|   initialState: { |   initialState: { | ||||||
|     selectedMaster:"Application Role", |     selectedMaster:"Application Role", | ||||||
|     regularizationCount:0, |     regularizationCount:0, | ||||||
|     projectId: "", |     projectId: null, | ||||||
|     reload:false |     reload:false | ||||||
|    |    | ||||||
|   }, |   }, | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user