Issues_July_2W: Project overview widgets #256
| @ -1,4 +1,5 @@ | ||||
| import React from "react"; | ||||
| import { useSelector } from "react-redux"; // Import useSelector to access Redux state | ||||
| import { | ||||
| useDashboardProjectsCardData, | ||||
| useDashboardTeamsCardData, | ||||
| @ -9,6 +10,7 @@ import Teams from "./Teams"; | ||||
| import TasksCard from "./Tasks"; | ||||
| import ProjectCompletionChart from "./ProjectCompletionChart"; | ||||
| import ProjectProgressChart from "./ProjectProgressChart"; | ||||
| import ProjectOverview from "../Project/ProjectOverview"; | ||||
| // import Attendance from "./Attendance"; | ||||
| 
 | ||||
| const Dashboard = () => { | ||||
| @ -16,37 +18,51 @@ const Dashboard = () => { | ||||
| const { teamsCardData } = useDashboardTeamsCardData(); | ||||
| const { tasksCardData } = useDashboardTasksCardData(); | ||||
| 
 | ||||
| // Get the selected project ID from Redux store | ||||
| const selectedProjectId = useSelector( | ||||
| (store) => store.localVariables.projectId | ||||
| ); | ||||
| 
 | ||||
| // Determine if "All Projects" is selected | ||||
| // selectedProjectId will be null when "All Projects" is chosen | ||||
| const isAllProjectsSelected = selectedProjectId === null; | ||||
| 
 | ||||
| return ( | ||||
| <div className="container-fluid mt-3"> | ||||
| <div className="row gy-4"> | ||||
|         {/* Projects Card */} | ||||
| 
 | ||||
| {isAllProjectsSelected && ( | ||||
|     <div className="col-sm-6 col-lg-4"> | ||||
| <Projects projectsCardData={projectsCardData} /> | ||||
| </div> | ||||
| )} | ||||
| 
 | ||||
|         {/* Teams Card */} | ||||
|         <div className="col-sm-6 col-lg-4"> | ||||
| 
 | ||||
| <div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6":"col-sm-6 col-lg-4"}`}> | ||||
| <Teams teamsCardData={teamsCardData} /> | ||||
| </div> | ||||
| 
 | ||||
|         {/* Tasks Card */} | ||||
|         <div className="col-sm-6 col-lg-4"> | ||||
| <div className={`${!isAllProjectsSelected ? "col-sm-6 col-lg-6":"col-sm-6 col-lg-4"}`}> | ||||
| <TasksCard tasksCardData={tasksCardData} /> | ||||
| </div> | ||||
| 
 | ||||
|         {/* Bar Chart (Project Completion) */} | ||||
| 
 | ||||
| {isAllProjectsSelected && ( | ||||
|     <div className="col-xxl-6 col-lg-6"> | ||||
| <ProjectCompletionChart /> | ||||
| </div> | ||||
| )} | ||||
| 
 | ||||
|         {/* Line Chart (Project Progress) */} | ||||
| {! isAllProjectsSelected && ( | ||||
|     <div className="col-xxl-6 col-lg-6"> | ||||
|     <ProjectOverview /> | ||||
| </div> | ||||
| )} | ||||
| <div className="col-xxl-6 col-lg-6"> | ||||
| <ProjectProgressChart /> | ||||
| </div> | ||||
| 
 | ||||
|         {/* <div className="col-xxl-6 col-lg-6"> | ||||
|           <Attendance /> | ||||
|         </div> */} | ||||
| 
 | ||||
| </div> | ||||
| </div> | ||||
| ); | ||||
|  | ||||
| @ -27,9 +27,9 @@ const Header = () => { | ||||
|   const { data, loading } = useMaster(); | ||||
|   const navigate = useNavigate(); | ||||
|   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) => { | ||||
|     if (!Array.isArray(roles)) return "User"; | ||||
|     let role = roles.find((role) => role.id === joRoleId); | ||||
| @ -37,7 +37,7 @@ const Header = () => { | ||||
|   }; | ||||
| 
 | ||||
|   const handleLogout = (e) => { | ||||
|     e.preventDefault(); // Prevent default anchor behavior (e.g., page reload) | ||||
|     e.preventDefault();  | ||||
|     logout(); | ||||
|   }; | ||||
| 
 | ||||
| @ -105,14 +105,18 @@ const Header = () => { | ||||
|       selectedProject === undefined && | ||||
|       !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]); | ||||
| 
 | ||||
| 
 | ||||
|   /** 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( | ||||
|     async (data) => { | ||||
| @ -141,12 +145,10 @@ const Header = () => { | ||||
|     [HasManageProjectPermission, projectNames, fetchData] | ||||
|   ); | ||||
| 
 | ||||
|   // Correct way to dispatch an action on mount | ||||
|   useEffect(() => { | ||||
|     dispatch(changeMaster("Job Role")); | ||||
|   }, [dispatch]); | ||||
| 
 | ||||
|   // Event bus listeners for project changes | ||||
|   useEffect(() => { | ||||
|     eventBus.on("assign_project_one", handler); | ||||
|     eventBus.on("project", newProjectHandler); | ||||
| @ -174,7 +176,6 @@ const Header = () => { | ||||
|         className="navbar-nav-right d-flex align-items-center justify-content-between" | ||||
|         id="navbar-collapse" | ||||
|       > | ||||
|         {/* Project Selection Dropdown */} | ||||
|         {projectNames && !isDirectoryPath && ( | ||||
|           <div className="align-items-center"> | ||||
|             <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" | ||||
|                   style={{ overflow: "auto", maxHeight: "300px" }} | ||||
|                 > | ||||
|                   {/* Show "All Projects" only on dashboard */} | ||||
|              | ||||
|                   {isDashboard && ( | ||||
|                     <li> | ||||
|                       <button | ||||
| @ -228,10 +229,7 @@ const Header = () => { | ||||
|           </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"> | ||||
|           <li className="nav-item dropdown-shortcuts navbar-dropdown dropdown me-2 me-xl-0"> | ||||
|             <a | ||||
|  | ||||
| @ -25,7 +25,7 @@ import GlobalModel from "../common/GlobalModel"; | ||||
| 
 | ||||
| const ProjectInfra = ( {data, onDataChange, eachSiteEngineer} ) => | ||||
| { | ||||
|   const {projectId} = useParams() | ||||
|   const projectId = useSelector((store)=>store.localVariables.projectId) | ||||
|   const reloadedData = useSelector((store) => store.localVariables.reload); | ||||
|   const [ expandedBuildings, setExpandedBuildings ] = useState( [] ); | ||||
|    const {projectInfra,isLoading,error} = useProjectInfra(projectId) | ||||
|  | ||||
| @ -60,7 +60,7 @@ const ProjectNav = ({ onPillClick, activePill }) => { | ||||
|               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> | ||||
|         </li> | ||||
|         {(DirAdmin || DireManager || DirUser) && ( | ||||
|  | ||||
| @ -1,15 +1,168 @@ | ||||
| import React from "react"; | ||||
| import { useEffect, useState } from "react"; | ||||
| import { useSelector } from "react-redux"; | ||||
| 
 | ||||
| import { | ||||
|   useEmployeesByProjectAllocated, | ||||
|   useProjects, | ||||
| } from "../../hooks/useProjects"; | ||||
| import { formatNumber, getCompletionPercentage } from "../../utils/dateUtils"; | ||||
| import ProgressBar from "../common/ProgressBar"; | ||||
| import ReactApexChart from "react-apexcharts"; | ||||
| import Chart from "react-apexcharts"; | ||||
| 
 | ||||
| const ProjectOverview = ({ project }) => { | ||||
|   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 ( | ||||
|     <div className="card mb-6"> | ||||
| @ -21,68 +174,75 @@ const ProjectOverview = ({ project }) => { | ||||
|         </h6> | ||||
|       </div> | ||||
|      <div className="card-body"> | ||||
|         <ul className="list-unstyled mb-0 mt-3 pt-1"> | ||||
|           <li className="d-flex align-items-center mb-3"> | ||||
|             <i className="bx bx-check"></i> | ||||
|             <span className="fw-medium mx-2">Task Planned:</span>{" "} | ||||
|             <span>{formatNumber(project_detail?.plannedWork)}</span> | ||||
|           </li> | ||||
|           <li className="d-flex align-items-center mb-3"> | ||||
|             <i className="bx bx-star"></i> | ||||
|             <span className="fw-medium mx-2">Task Completed:</span>{" "} | ||||
|             <span>{formatNumber(project_detail?.completedWork)}</span> | ||||
|           </li> | ||||
|           <li className="d-flex align-items-center mb-3"> | ||||
|             <i className="bx bx-user"></i> | ||||
|             <span className="fw-medium mx-2">Current team Size:</span>{" "} | ||||
|             <span>{project_detail?.teamSize}</span> | ||||
|           </li> | ||||
|           <li className=" mb-3"> | ||||
|             {project_detail && ( | ||||
|               <> | ||||
|                 <div className="d-flex  text-end mb-2  mt-5"> | ||||
|                   <small className="text-body text-muted "> | ||||
|                     {/* {Math.floor( | ||||
|                       getProgressInNumber( | ||||
|                          | ||||
|                         project_detail.completedWork | ||||
|                       ) | ||||
|                     ) || 0}{" "} */} | ||||
|                     { | ||||
|                       getCompletionPercentage( formatNumber(project_detail.completedWork),formatNumber(project_detail.plannedWork)) | ||||
|                     | ||||
|                     } | ||||
|                     % Completed | ||||
|                   </small> | ||||
|                 </div> | ||||
|                 {/* <div | ||||
|                   className="progress mb-4 rounded" | ||||
|                   style={{ height: "8px" }} | ||||
|                 > | ||||
|                   <div | ||||
|                     className="progress-bar rounded" | ||||
|                     role="progressbar" | ||||
|                     style={{ | ||||
|                       width: getProgress( | ||||
|                         project_detail.plannedWork, | ||||
|                         project_detail.completedWork | ||||
|                       ), | ||||
|                     }} | ||||
|                     aria-valuenow={project_detail.completedWork} | ||||
|                     aria-valuemin="0" | ||||
|                     aria-valuemax={project_detail.plannedWork} | ||||
|                   ></div> | ||||
|                 </div> */} | ||||
|                 <ProgressBar | ||||
|                   completedWork={formatNumber(project_detail?.completedWork)} | ||||
|                   plannedWork={formatNumber(project_detail?.plannedWork)} | ||||
|                   className="m-0 text-info" | ||||
|   <ul className="list-unstyled m-0 p-0"> | ||||
|     <li className="d-flex flex-wrap"> | ||||
|       <div className="w-100 d-flex flex-wrap"> | ||||
|         {/* Centered Chart */} | ||||
|         <div className="w-100 d-flex justify-content-center mb-3"> | ||||
|           <div > | ||||
|             <Chart | ||||
|               options={radialBarOptions} | ||||
|               series={radialBarOptions.series} | ||||
|               type="radialBar" | ||||
|               height="100%" | ||||
|             /> | ||||
|               </> | ||||
|             )} | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         {/* Info Section */} | ||||
|         <div className="mb-2" style={{ flex: "1 1 auto" }}> | ||||
|           <div> | ||||
|             {/* Tasks Planned */} | ||||
|             <div className="d-flex align-items-center mb-3"> | ||||
|               <div className="avatar me-2"> | ||||
|                 <span className="avatar-initial rounded-2 bg-label-primary"> | ||||
|                   <i className="bx bx-check text-primary fs-4"></i> | ||||
|                 </span> | ||||
|               </div> | ||||
|               <div className="d-flex flex-column text-start"> | ||||
|                 <small className="fw-bold">Tasks Planned</small> | ||||
|                 <h5 className="mb-0"> | ||||
|                   {FormattedNumber(current_project?.plannedWork)} | ||||
|                 </h5> | ||||
|               </div> | ||||
|             </div> | ||||
| 
 | ||||
|             {/* Tasks Completed */} | ||||
|             <div className="d-flex align-items-center mb-3"> | ||||
|               <div className="avatar me-2"> | ||||
|                 <span className="avatar-initial rounded-2 bg-label-info"> | ||||
|                   <i className="bx bx-star text-info fs-4"></i> | ||||
|                 </span> | ||||
|               </div> | ||||
|               <div className="d-flex flex-column text-start"> | ||||
|                 <small className="fw-bold">Tasks Completed</small> | ||||
|                 <h5 className="mb-0"> | ||||
|                   {FormattedNumber(current_project?.completedWork)} | ||||
|                 </h5> | ||||
|               </div> | ||||
|             </div> | ||||
| 
 | ||||
|             {/* Team Size */} | ||||
|             <div className="d-flex align-items-center"> | ||||
|               <div className="avatar me-2"> | ||||
|                 <span className="avatar-initial rounded-2 bg-label-primary"> | ||||
|                   <i className="bx bx-group text-primary fs-4"></i> | ||||
|                 </span> | ||||
|               </div> | ||||
|               <div className="d-flex flex-column text-start"> | ||||
|                 <small className="fw-bold">Current Team Size</small> | ||||
|                 <h5 className="mb-0"> | ||||
|                   {FormattedNumber(current_project?.teamSize)} | ||||
|                 </h5> | ||||
|               </div> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|     </li> | ||||
|   </ul> | ||||
| </div> | ||||
| 
 | ||||
|     </div> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -21,13 +21,14 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | ||||
| import { REGULARIZE_ATTENDANCE } from "../../utils/constants"; | ||||
| import eventBus from "../../services/eventBus"; | ||||
| import AttendanceRepository from "../../repositories/AttendanceRepository"; | ||||
| import { useProjectName } from "../../hooks/useProjects"; | ||||
| 
 | ||||
| const AttendancePage = () => { | ||||
|   const [activeTab, setActiveTab] = useState("all"); | ||||
|   const [ShowPending, setShowPending] = useState(false); | ||||
|   const loginUser = getCachedProfileData(); | ||||
|   var selectedProject = useSelector((store) => store.localVariables.projectId); | ||||
|   // const { projects, loading: projectLoading } = useProjects(); | ||||
|   const dispatch = useDispatch() | ||||
|   const { | ||||
|     attendance, | ||||
|     loading: attLoading, | ||||
| @ -38,7 +39,8 @@ const AttendancePage = () => { | ||||
|   const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); | ||||
|   const [modelConfig, setModelConfig] = useState(); | ||||
|   const DoRegularized = useHasUserPermission(REGULARIZE_ATTENDANCE); | ||||
|   const dispatch = useDispatch(); | ||||
|   const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||
|    | ||||
| 
 | ||||
|   const [formData, setFormData] = useState({ | ||||
|     markTime: "", | ||||
| @ -130,6 +132,12 @@ const AttendancePage = () => { | ||||
|   const handleToggle = (event) => { | ||||
|     setShowOnlyCheckout(event.target.checked); | ||||
|   }; | ||||
|   useEffect(() => { | ||||
|    if(selectedProject == null){ | ||||
|        dispatch(setProjectId(projectNames[0]?.id)); | ||||
|    } | ||||
|    },[]) | ||||
| 
 | ||||
|    | ||||
|   useEffect(() => { | ||||
|     if (modelConfig !== null) { | ||||
| @ -140,18 +148,7 @@ const AttendancePage = () => { | ||||
|     setAttendances(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 | ||||
|     ? attendances?.filter( | ||||
|         (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 Breadcrumb from "../../components/common/Breadcrumb"; | ||||
| import { useTaskList } from "../../hooks/useTasks"; | ||||
| import { useProjects } from "../../hooks/useProjects"; | ||||
| import { useProjectName, useProjects } from "../../hooks/useProjects"; | ||||
| import { setProjectId } from "../../slices/localVariablesSlice"; | ||||
| import { ReportTask } from "../../components/Activities/ReportTask"; | ||||
| import ReportTaskComments from "../../components/Activities/ReportTaskComments"; | ||||
| @ -18,11 +18,11 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | ||||
| import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; | ||||
| 
 | ||||
| const DailyTask = () => { | ||||
|   const [searchParams] = useSearchParams(); | ||||
|   const projectIdFromUrl = searchParams.get("project"); | ||||
|   const selectedProject = useSelector( | ||||
|     (store) => store.localVariables.projectId | ||||
|   ); | ||||
|   const dispatch = useDispatch() | ||||
|     const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||
| 
 | ||||
| 
 | ||||
|   const [filters, setFilters] = useState({ | ||||
| @ -48,7 +48,11 @@ const DailyTask = () => { | ||||
|     dateRange?.endDate || null | ||||
|     ); | ||||
|    | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|    if(selectedProject == null){ | ||||
|        dispatch(setProjectId(projectNames[0]?.id)); | ||||
|    } | ||||
|    },[]) | ||||
|   const [TaskLists, setTaskLists] = useState([]); | ||||
|   const [dates, setDates] = useState([]); | ||||
|   const popoverRefs = useRef([]); | ||||
|  | ||||
| @ -1,9 +1,23 @@ | ||||
| import React from "react"; | ||||
| import React,{useEffect} from "react"; | ||||
| import Breadcrumb from "../../components/common/Breadcrumb"; | ||||
| 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 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 ( | ||||
|     <> | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| import React, { useState, useEffect, useRef, useCallback } from "react"; | ||||
| import "./ImageGallery.css"; | ||||
| import moment from "moment"; | ||||
| import { useSelector } from "react-redux"; | ||||
| import { useDispatch, useSelector } from "react-redux"; | ||||
| import { useModal } from "./ModalContext"; | ||||
| import ImagePop from "./ImagePop"; | ||||
| import Avatar from "../../components/common/Avatar"; | ||||
| @ -10,11 +10,22 @@ import eventBus from "../../services/eventBus"; | ||||
| import Breadcrumb from "../../components/common/Breadcrumb"; | ||||
| import { formatUTCToLocalTime } from "../../utils/dateUtils"; | ||||
| import useImageGallery from "../../hooks/useImageGallery"; | ||||
| import { useProjectName } from "../../hooks/useProjects"; | ||||
| import { setProjectId } from "../../slices/localVariablesSlice"; | ||||
| 
 | ||||
| const SCROLL_THRESHOLD = 5; | ||||
| 
 | ||||
| const ImageGallery = () => { | ||||
|   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 { | ||||
|     images, | ||||
|     allImagesData, | ||||
|  | ||||
| @ -5,11 +5,19 @@ import { Link, NavLink, useNavigate } from "react-router-dom"; | ||||
| import Avatar from "../../components/common/Avatar"; | ||||
| import Breadcrumb from "../../components/common/Breadcrumb"; | ||||
| import ManageEmp from "../../components/Employee/ManageRole"; | ||||
| import { useEmployeesAllOrByProjectId, useSuspendEmployee } from "../../hooks/useEmployees"; | ||||
| import { useProjects } from "../../hooks/useProjects"; | ||||
| import { | ||||
|   useEmployeesAllOrByProjectId, | ||||
|   useSuspendEmployee, | ||||
| } from "../../hooks/useEmployees"; | ||||
| import { useProjectName, useProjects } from "../../hooks/useProjects"; | ||||
| import { useProfile } from "../../hooks/useProfile"; | ||||
| import { hasUserPermission } from "../../utils/authUtils"; | ||||
| import { ITEMS_PER_PAGE, MANAGE_EMPLOYEES, VIEW_ALL_EMPLOYEES, VIEW_TEAM_MEMBERS } from "../../utils/constants"; | ||||
| import { | ||||
|   ITEMS_PER_PAGE, | ||||
|   MANAGE_EMPLOYEES, | ||||
|   VIEW_ALL_EMPLOYEES, | ||||
|   VIEW_TEAM_MEMBERS, | ||||
| } from "../../utils/constants"; | ||||
| import { clearCacheKey } from "../../slices/apiDataManager"; | ||||
| import { useHasUserPermission } from "../../hooks/useHasUserPermission"; | ||||
| import SuspendEmp from "../../components/Employee/SuspendEmp"; // Keep if you use SuspendEmp | ||||
| @ -22,16 +30,20 @@ import { | ||||
| import EmployeeRepository from "../../repositories/EmployeeRepository"; | ||||
| import ManageEmployee from "../../components/Employee/ManageEmployee"; | ||||
| import ConfirmModal from "../../components/common/ConfirmModal"; | ||||
| import { useSelector } from "react-redux"; | ||||
| import { useDispatch, useSelector } from "react-redux"; | ||||
| import eventBus from "../../services/eventBus"; | ||||
| import { newlineChars } from "pdf-lib"; | ||||
| import GlobalModel from "../../components/common/GlobalModel"; | ||||
| import usePagination from "../../hooks/usePagination"; | ||||
| import { setProjectId } from "../../slices/localVariablesSlice"; | ||||
| 
 | ||||
| const EmployeeList = () => { | ||||
|   const selectedProjectId = useSelector( | ||||
|     (store) => store.localVariables.projectId | ||||
|   ); | ||||
|   const { projectNames, loading: projectLoading, fetchData } = useProjectName(); | ||||
| 
 | ||||
|   const dispatch = useDispatch(); | ||||
| 
 | ||||
|   const [showInactive, setShowInactive] = useState(false); | ||||
|   const [showAllEmployees, setShowAllEmployees] = useState(false); | ||||
| @ -45,7 +57,7 @@ const EmployeeList = () => { | ||||
| 
 | ||||
|   const [employeeList, setEmployeeList] = useState([]); | ||||
|   const [modelConfig, setModelConfig] = useState(); | ||||
|   const [EmpForManageRole,setEmpForManageRole] = useState(null) | ||||
|   const [EmpForManageRole, setEmpForManageRole] = useState(null); | ||||
|   // const [currentPage, setCurrentPage] = useState(1); | ||||
|   // const [itemsPerPage] = useState(ITEMS_PER_PAGE); | ||||
|   const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); | ||||
| @ -56,21 +68,22 @@ const EmployeeList = () => { | ||||
|   const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false); | ||||
|   const [selectedEmpFordelete, setSelectedEmpFordelete] = useState(null); | ||||
|   const [employeeLodaing, setemployeeLodaing] = useState(false); | ||||
|   const ViewTeamMember = useHasUserPermission(VIEW_TEAM_MEMBERS) | ||||
|   const ViewAllEmployee = useHasUserPermission(VIEW_ALL_EMPLOYEES) | ||||
|   const { | ||||
|   mutate: suspendEmployee, | ||||
|   isPending: empLodaing | ||||
| } = useSuspendEmployee({ | ||||
|   const ViewTeamMember = useHasUserPermission(VIEW_TEAM_MEMBERS); | ||||
|   const ViewAllEmployee = useHasUserPermission(VIEW_ALL_EMPLOYEES); | ||||
|   const { mutate: suspendEmployee, isPending: empLodaing } = useSuspendEmployee( | ||||
|     { | ||||
|       setIsDeleteModalOpen, | ||||
|   setemployeeLodaing | ||||
| } ); | ||||
|   | ||||
| 
 | ||||
|       setemployeeLodaing, | ||||
|     } | ||||
|   ); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (selectedProjectId === null) { | ||||
|       dispatch(setProjectId(projectNames[0]?.id)); | ||||
|     } | ||||
|   }, [selectedProjectId]); | ||||
|   const navigate = useNavigate(); | ||||
| 
 | ||||
|    | ||||
|   const applySearchFilter = (data, text) => { | ||||
|     if (!text) { | ||||
|       return data; | ||||
| @ -101,71 +114,23 @@ const EmployeeList = () => { | ||||
|     }); | ||||
|   }; | ||||
| 
 | ||||
| 
 | ||||
|   const handleSearch = (e) => { | ||||
|     const value = e.target.value; | ||||
|     setSearchText(value); | ||||
|     setCurrentPage(1); | ||||
|   }; | ||||
| useEffect(() => { | ||||
|   const filtered = applySearchFilter(employeeList, searchText); | ||||
|   setFilteredData(filtered); | ||||
| }, [searchText, employeeList]); | ||||
| 
 | ||||
| 
 | ||||
|   const displayData = searchText ? filteredData : employeeList; | ||||
|    const { currentPage, totalPages, currentItems, paginate,setCurrentPage } = usePagination( | ||||
|             displayData, | ||||
|            ITEMS_PER_PAGE | ||||
|           ); | ||||
|   const { currentPage, totalPages, currentItems, paginate, setCurrentPage } = | ||||
|     usePagination(displayData, ITEMS_PER_PAGE); | ||||
|   const openModal = () => { | ||||
|     setIsCreateModalOpen(true); | ||||
|   }; | ||||
| 
 | ||||
| useEffect(() => { | ||||
|   if (!loading && Array.isArray(employees)) { | ||||
|     const sorted = [...employees].sort((a, b) => { | ||||
|       const nameA = `${a.firstName || ""}${a.middleName || ""}${a.lastName || ""}`.toLowerCase(); | ||||
|       const nameB = `${b.firstName || ""}${b.middleName || ""}${b.lastName || ""}`.toLowerCase(); | ||||
|       return nameA?.localeCompare(nameB); | ||||
|     }); | ||||
| 
 | ||||
|     setEmployeeList((prevList) => { | ||||
|       const prevJSON = JSON.stringify(prevList); | ||||
|       const nextJSON = JSON.stringify(sorted); | ||||
|       if (prevJSON !== nextJSON) { | ||||
|         return sorted; | ||||
|       } | ||||
|       return prevList; | ||||
|     }); | ||||
| 
 | ||||
|     setFilteredData((prev) => { | ||||
|       const prevJSON = JSON.stringify(prev); | ||||
|       const nextJSON = JSON.stringify(sorted); | ||||
|       if (prevJSON !== nextJSON) { | ||||
|         return sorted; | ||||
|       } | ||||
|       return prev; | ||||
|     }); | ||||
| 
 | ||||
|     // set currentPage to 1 only if needed | ||||
|     setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage)); | ||||
|   } | ||||
| }, [loading, employees, selectedProjectId, showAllEmployees]); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|   const handleConfigData = (config) => { | ||||
|     setModelConfig(config); | ||||
|   }; | ||||
| 
 | ||||
|   // useEffect(() => { | ||||
|   //   if (modelConfig !== null) { | ||||
|   //     openModal(); | ||||
|   //   } | ||||
|   // }, [modelConfig, isCreateModalOpen]); | ||||
| 
 | ||||
|   const tableRef = useRef(null); | ||||
|   const handleExport = (type) => { | ||||
|     if (!currentItems || currentItems.length === 0) return; | ||||
| @ -190,14 +155,16 @@ useEffect(() => { | ||||
| 
 | ||||
|   const handleToggle = (e) => { | ||||
|     setShowInactive(e.target.checked); | ||||
|     recallEmployeeData(e.target.checked, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here | ||||
|     recallEmployeeData( | ||||
|       e.target.checked, | ||||
|       showAllEmployees ? null : selectedProjectId | ||||
|     ); // Use selectedProjectId here | ||||
|   }; | ||||
| 
 | ||||
|   const handleAllEmployeesToggle = (e) => { | ||||
|     const isChecked = e.target.checked; | ||||
|     setShowInactive(false); | ||||
|     setShowAllEmployees(isChecked); | ||||
| 
 | ||||
|   }; | ||||
| 
 | ||||
|   const handleEmployeeModel = (id) => { | ||||
| @ -209,6 +176,43 @@ const handleAllEmployeesToggle = (e) => { | ||||
|     setSelectedEmpFordelete(employee); | ||||
|     setIsDeleteModalOpen(true); | ||||
|   }; | ||||
|   useEffect(() => { | ||||
|     const filtered = applySearchFilter(employeeList, searchText); | ||||
|     setFilteredData(filtered); | ||||
|   }, [searchText, employeeList]); | ||||
|   useEffect(() => { | ||||
|     if (!loading && Array.isArray(employees)) { | ||||
|       const sorted = [...employees].sort((a, b) => { | ||||
|         const nameA = `${a.firstName || ""}${a.middleName || ""}${ | ||||
|           a.lastName || "" | ||||
|         }`.toLowerCase(); | ||||
|         const nameB = `${b.firstName || ""}${b.middleName || ""}${ | ||||
|           b.lastName || "" | ||||
|         }`.toLowerCase(); | ||||
|         return nameA?.localeCompare(nameB); | ||||
|       }); | ||||
| 
 | ||||
|       setEmployeeList((prevList) => { | ||||
|         const prevJSON = JSON.stringify(prevList); | ||||
|         const nextJSON = JSON.stringify(sorted); | ||||
|         if (prevJSON !== nextJSON) { | ||||
|           return sorted; | ||||
|         } | ||||
|         return prevList; | ||||
|       }); | ||||
| 
 | ||||
|       setFilteredData((prev) => { | ||||
|         const prevJSON = JSON.stringify(prev); | ||||
|         const nextJSON = JSON.stringify(sorted); | ||||
|         if (prevJSON !== nextJSON) { | ||||
|           return sorted; | ||||
|         } | ||||
|         return prev; | ||||
|       }); | ||||
| 
 | ||||
|       setCurrentPage((prevPage) => (prevPage !== 1 ? 1 : prevPage)); | ||||
|     } | ||||
|   }, [loading, employees, selectedProjectId, showAllEmployees]); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     if (!showAllEmployees) { | ||||
| @ -220,27 +224,40 @@ const handleAllEmployeesToggle = (e) => { | ||||
|     (msg) => { | ||||
|       if (employees.some((item) => item.id == msg.employeeId)) { | ||||
|         setEmployeeList([]); | ||||
|         recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here | ||||
|         recallEmployeeData( | ||||
|           showInactive, | ||||
|           showAllEmployees ? null : selectedProjectId | ||||
|         ); // Use selectedProjectId here | ||||
|       } | ||||
|     },[employees, showInactive, showAllEmployees, selectedProjectId] // Add all relevant dependencies | ||||
|     }, | ||||
|     [employees, showInactive, showAllEmployees, selectedProjectId] // Add all relevant dependencies | ||||
|   ); | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|     eventBus.on("employee", handler); | ||||
|     return () => eventBus.off("employee",handler) | ||||
|   },[handler]) | ||||
| 
 | ||||
|     return () => eventBus.off("employee", handler); | ||||
|   }, [handler]); | ||||
| 
 | ||||
|   return ( | ||||
|     <> | ||||
|       {EmpForManageRole && ( | ||||
|         <GlobalModel isOpen={EmpForManageRole} closeModal={() => setEmpForManageRole( null )}> | ||||
|             <ManageEmp employeeId={EmpForManageRole} onClosed={()=>setEmpForManageRole(null)} /> | ||||
|         <GlobalModel | ||||
|           isOpen={EmpForManageRole} | ||||
|           closeModal={() => setEmpForManageRole(null)} | ||||
|         > | ||||
|           <ManageEmp | ||||
|             employeeId={EmpForManageRole} | ||||
|             onClosed={() => setEmpForManageRole(null)} | ||||
|           /> | ||||
|         </GlobalModel> | ||||
|       )} | ||||
| 
 | ||||
|       {showModal && ( | ||||
|         <GlobalModel isOpen={showModal} size="lg" closeModal={()=>setShowModal(false)}> | ||||
|         <GlobalModel | ||||
|           isOpen={showModal} | ||||
|           size="lg" | ||||
|           closeModal={() => setShowModal(false)} | ||||
|         > | ||||
|           <ManageEmployee | ||||
|             employeeId={selectedEmployeeId} | ||||
|             onClosed={() => setShowModal(false)} | ||||
| @ -282,11 +299,10 @@ const handleAllEmployeesToggle = (e) => { | ||||
|           ]} | ||||
|         ></Breadcrumb> | ||||
| 
 | ||||
|        | ||||
|         {ViewTeamMember ? (<div className="row"> | ||||
|         {ViewTeamMember ? ( | ||||
|           <div className="row"> | ||||
|             <div className="card "> | ||||
|               <div className="card-datatable table-responsive pt-2"> | ||||
| 
 | ||||
|                 <div | ||||
|                   id="DataTables_Table_0_wrapper" | ||||
|                   className="dataTables_wrapper dt-bootstrap5 no-footer" | ||||
| @ -306,7 +322,10 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                             checked={showAllEmployees} | ||||
|                             onChange={handleAllEmployeesToggle} | ||||
|                           /> | ||||
|                       <label className="form-check-label ms-0" htmlFor="allEmployeesCheckbox"> | ||||
|                           <label | ||||
|                             className="form-check-label ms-0" | ||||
|                             htmlFor="allEmployeesCheckbox" | ||||
|                           > | ||||
|                             All Employees | ||||
|                           </label> | ||||
|                         </div> | ||||
| @ -323,12 +342,14 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                             checked={showInactive} | ||||
|                             onChange={handleToggle} | ||||
|                           /> | ||||
|                         <label className="form-check-label ms-0" htmlFor="inactiveEmployeesCheckbox"> | ||||
|                           <label | ||||
|                             className="form-check-label ms-0" | ||||
|                             htmlFor="inactiveEmployeesCheckbox" | ||||
|                           > | ||||
|                             Show Inactive Employees | ||||
|                           </label> | ||||
|                         </div> | ||||
|                       )} | ||||
| 
 | ||||
|                     </div> | ||||
| 
 | ||||
|                     {/* Right side: Search + Export + Add Employee */} | ||||
| @ -360,22 +381,38 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                         </button> | ||||
|                         <ul className="dropdown-menu"> | ||||
|                           <li> | ||||
|                           <a className="dropdown-item" href="#" onClick={() => handleExport("print")}> | ||||
|                             <a | ||||
|                               className="dropdown-item" | ||||
|                               href="#" | ||||
|                               onClick={() => handleExport("print")} | ||||
|                             > | ||||
|                               <i className="bx bx-printer me-1"></i> Print | ||||
|                             </a> | ||||
|                           </li> | ||||
|                           <li> | ||||
|                           <a className="dropdown-item" href="#" onClick={() => handleExport("csv")}> | ||||
|                             <a | ||||
|                               className="dropdown-item" | ||||
|                               href="#" | ||||
|                               onClick={() => handleExport("csv")} | ||||
|                             > | ||||
|                               <i className="bx bx-file me-1"></i> CSV | ||||
|                             </a> | ||||
|                           </li> | ||||
|                           <li> | ||||
|                           <a className="dropdown-item" href="#" onClick={() => handleExport("excel")}> | ||||
|                             <a | ||||
|                               className="dropdown-item" | ||||
|                               href="#" | ||||
|                               onClick={() => handleExport("excel")} | ||||
|                             > | ||||
|                               <i className="bx bxs-file-export me-1"></i> Excel | ||||
|                             </a> | ||||
|                           </li> | ||||
|                           <li> | ||||
|                           <a className="dropdown-item" href="#" onClick={() => handleExport("pdf")}> | ||||
|                             <a | ||||
|                               className="dropdown-item" | ||||
|                               href="#" | ||||
|                               onClick={() => handleExport("pdf")} | ||||
|                             > | ||||
|                               <i className="bx bxs-file-pdf me-1"></i> PDF | ||||
|                             </a> | ||||
|                           </li> | ||||
| @ -390,7 +427,9 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                           onClick={() => handleEmployeeModel(null)} | ||||
|                         > | ||||
|                           <i className="bx bx-plus-circle me-2"></i> | ||||
|                         <span className="d-none d-md-inline-block">Add New Employee</span> | ||||
|                           <span className="d-none d-md-inline-block"> | ||||
|                             Add New Employee | ||||
|                           </span> | ||||
|                         </button> | ||||
|                       )} | ||||
|                     </div> | ||||
| @ -471,7 +510,8 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                           Status | ||||
|                         </th> | ||||
|                         <th | ||||
|                         className={`sorting_disabled  ${!Manage_Employee && "d-none" | ||||
|                           className={`sorting_disabled  ${ | ||||
|                             !Manage_Employee && "d-none" | ||||
|                           }`} | ||||
|                           rowSpan="1" | ||||
|                           colSpan="1" | ||||
| @ -491,7 +531,10 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                         </tr> | ||||
|                       )} | ||||
|                       {/* Conditional messages for no data or no search results */} | ||||
|                     {!loading && displayData?.length === 0 && searchText && !showAllEmployees ? ( | ||||
|                       {!loading && | ||||
|                       displayData?.length === 0 && | ||||
|                       searchText && | ||||
|                       !showAllEmployees ? ( | ||||
|                         <tr> | ||||
|                           <td colSpan={8}> | ||||
|                             <small className="muted"> | ||||
| @ -500,7 +543,9 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                           </td> | ||||
|                         </tr> | ||||
|                       ) : null} | ||||
|                     {!loading && displayData?.length === 0 && (!searchText || showAllEmployees) ? ( | ||||
|                       {!loading && | ||||
|                       displayData?.length === 0 && | ||||
|                       (!searchText || showAllEmployees) ? ( | ||||
|                         <tr> | ||||
|                           <td | ||||
|                             colSpan={8} | ||||
| @ -512,7 +557,9 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                       ) : null} | ||||
| 
 | ||||
|                       {/* Render current items */} | ||||
|                     {currentItems && !loading && currentItems.map((item) => ( | ||||
|                       {currentItems && | ||||
|                         !loading && | ||||
|                         currentItems.map((item) => ( | ||||
|                           <tr className="odd" key={item.id}> | ||||
|                             <td className="sorting_1" colSpan={2}> | ||||
|                               <div className="d-flex justify-content-start align-items-center user-name"> | ||||
| @ -598,7 +645,8 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                                       } | ||||
|                                       className="dropdown-item py-1" | ||||
|                                     > | ||||
|                                     <i className="bx bx-detail bx-sm"></i> View | ||||
|                                       <i className="bx bx-detail bx-sm"></i>{" "} | ||||
|                                       View | ||||
|                                     </button> | ||||
|                                     <button | ||||
|                                       className="dropdown-item py-1" | ||||
| @ -649,7 +697,9 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                     <nav aria-label="Page"> | ||||
|                       <ul className="pagination pagination-sm justify-content-end py-1"> | ||||
|                         <li | ||||
|                         className={`page-item ${currentPage === 1 ? "disabled" : ""}`} | ||||
|                           className={`page-item ${ | ||||
|                             currentPage === 1 ? "disabled" : "" | ||||
|                           }`} | ||||
|                         > | ||||
|                           <button | ||||
|                             className="page-link btn-xs" | ||||
| @ -662,7 +712,8 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                         {[...Array(totalPages)]?.map((_, index) => ( | ||||
|                           <li | ||||
|                             key={index} | ||||
|                           className={`page-item ${currentPage === index + 1 ? "active" : "" | ||||
|                             className={`page-item ${ | ||||
|                               currentPage === index + 1 ? "active" : "" | ||||
|                             }`} | ||||
|                           > | ||||
|                             <button | ||||
| @ -675,7 +726,8 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                         ))} | ||||
| 
 | ||||
|                         <li | ||||
|                         className={`page-item ${currentPage === totalPages ? "disabled" : "" | ||||
|                           className={`page-item ${ | ||||
|                             currentPage === totalPages ? "disabled" : "" | ||||
|                           }`} | ||||
|                         > | ||||
|                           <button | ||||
| @ -691,11 +743,15 @@ const handleAllEmployeesToggle = (e) => { | ||||
|                 </div> | ||||
|               </div> | ||||
|             </div> | ||||
|         </div>):( | ||||
|           </div> | ||||
|         ) : ( | ||||
|           <div className="card"> | ||||
|             <div className="text-center"> | ||||
|               <i className="fa-solid fa-triangle-exclamation fs-5"></i> | ||||
|                 <p>Access Denied: You don't have permission to perform this action. !</p> | ||||
|               <p> | ||||
|                 Access Denied: You don't have permission to perform this action. | ||||
|                 ! | ||||
|               </p> | ||||
|             </div> | ||||
|           </div> | ||||
|         )} | ||||
|  | ||||
| @ -1,11 +1,9 @@ | ||||
| import { useSelector } from "react-redux"; // Import useSelector | ||||
| import React, { useState, useEffect, useCallback } from "react"; | ||||
| 
 | ||||
| import ActivityTimeline from "../../components/Project/ActivityTimeline"; | ||||
| import ProjectOverview from "../../components/Project/ProjectOverview"; | ||||
| import AboutProject from "../../components/Project/AboutProject"; | ||||
| import ProjectNav from "../../components/Project/ProjectNav"; | ||||
| import ProjectBanner from "../../components/Project/ProjectBanner"; | ||||
| import Teams from "../../components/Project/Teams"; | ||||
| import ProjectInfra from "../../components/Project/ProjectInfra"; | ||||
| import Loader from "../../components/common/Loader"; | ||||
| @ -16,25 +14,18 @@ import { | ||||
|   clearCacheKey, | ||||
|   getCachedData, | ||||
| } from "../../slices/apiDataManager"; | ||||
| import ProjectRepository from "../../repositories/ProjectRepository"; | ||||
| import { ActivityeRepository } from "../../repositories/MastersRepository"; | ||||
| import "./ProjectDetails.css"; | ||||
| import { | ||||
|   useEmployeesByProjectAllocated, | ||||
|   useProjectDetails, | ||||
| } from "../../hooks/useProjects"; | ||||
| import { useDispatch } from "react-redux"; | ||||
| import { setProjectId } from "../../slices/localVariablesSlice"; | ||||
| import { ComingSoonPage } from "../Misc/ComingSoonPage"; | ||||
| import Directory from "../Directory/Directory"; | ||||
| import eventBus from "../../services/eventBus"; | ||||
| import ProjectProgressChart from "../../components/Dashboard/ProjectProgressChart"; | ||||
| 
 | ||||
| const ProjectDetails = () => { | ||||
|   // const { projectId } = useParams(); // REMOVE THIS LINE | ||||
|   const dispatch = useDispatch(); | ||||
| 
 | ||||
|   // GET projectId FROM REDUX STORE | ||||
| 
 | ||||
|   const projectId = useSelector((store) => store.localVariables.projectId); | ||||
| 
 | ||||
|   const { | ||||
| @ -46,10 +37,7 @@ const ProjectDetails = () => { | ||||
| 
 | ||||
|   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( | ||||
|     (msg) => { | ||||
|  | ||||
| @ -5,11 +5,6 @@ import Breadcrumb from "../../components/common/Breadcrumb"; | ||||
| import ProjectRepository from "../../repositories/ProjectRepository"; | ||||
| import { useProjects, useCreateProject } from "../../hooks/useProjects"; | ||||
| import showToast from "../../services/toastService"; | ||||
| // 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"; | ||||
| @ -20,11 +15,15 @@ import { defaultCheckBoxAppearanceProvider } from "pdf-lib"; | ||||
| import { useMutation } from "@tanstack/react-query"; | ||||
| import usePagination from "../../hooks/usePagination"; | ||||
| import GlobalModel from "../../components/common/GlobalModel"; | ||||
| import { useDispatch, useSelector } from "react-redux"; | ||||
| import { setProjectId } from "../../slices/localVariablesSlice"; | ||||
| 
 | ||||
| const ProjectList = () => { | ||||
|   const { profile: loginUser } = useProfile(); | ||||
|   const [listView, setListView] = useState(false); | ||||
|   const [showModal, setShowModal] = useState(false); | ||||
|   const selectedProject = useSelector((store)=>store.localVariables.projectId) | ||||
|   const dispatch = useDispatch() | ||||
| 
 | ||||
|   const { projects, loading, error, refetch } = useProjects(); | ||||
|   const [projectList, setProjectList] = useState([]); | ||||
| @ -75,6 +74,10 @@ const ProjectList = () => { | ||||
|   }; | ||||
| 
 | ||||
|   useEffect(() => { | ||||
|    if(selectedProject == null){ | ||||
|        dispatch(setProjectId(projects[0]?.id)); | ||||
|    } | ||||
| 
 | ||||
|     if (!loading && projects) { | ||||
|       sortingProject(projects); | ||||
|     } | ||||
|  | ||||
| @ -5,7 +5,7 @@ const localVariablesSlice = createSlice({ | ||||
|   initialState: { | ||||
|     selectedMaster:"Application Role", | ||||
|     regularizationCount:0, | ||||
|     projectId: "", | ||||
|     projectId: null, | ||||
|     reload:false | ||||
|    | ||||
|   }, | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user