diff --git a/index.html b/index.html index f2e16b22..748027e9 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,6 @@ - + @@ -30,6 +25,7 @@ + diff --git a/public/assets/css/core-extend.css b/public/assets/css/core-extend.css new file mode 100644 index 00000000..c017c963 --- /dev/null +++ b/public/assets/css/core-extend.css @@ -0,0 +1,12 @@ +:root, +[data-bs-theme="light"] { + --bs-nav-link-font-size: 0.7375rem; +} + +.nav { + --bs-nav-link-font-size: 0.7375rem; +} + +.card-header { + padding: 0.5rem var(--bs-card-cap-padding-x); +} diff --git a/src/components/Dashboard/ProjectProgressChart.jsx b/src/components/Dashboard/ProjectProgressChart.jsx index 6159dbc9..20e0191a 100644 --- a/src/components/Dashboard/ProjectProgressChart.jsx +++ b/src/components/Dashboard/ProjectProgressChart.jsx @@ -4,12 +4,15 @@ import { useProjects } from "../../hooks/useProjects"; import { useDashboard_Data } from "../../hooks/useDashboard_Data"; import { useSelector } from "react-redux"; -const ProjectProgressChart = () => { +const ProjectProgressChart = ({ + ShowAllProject = true, + DefaultRange = "15D", +}) => { const selectedProject = useSelector( (store) => store.localVariables.projectId ); const { projects } = useProjects(); - const [range, setRange] = useState("15D"); + const [range, setRange] = useState(DefaultRange); const [showAllEmployees, setShowAllEmployees] = useState(false); const getDaysFromRange = (range) => { @@ -69,7 +72,7 @@ const ProjectProgressChart = () => { ); const lineChartCategoriesDates = sortedDashboardData.map((d) => new Date(d.date).toLocaleDateString("en-US", { - weekday:"short", + weekday: "short", month: "short", day: "numeric", year: "numeric", @@ -82,7 +85,7 @@ const ProjectProgressChart = () => { : selectedProjectData?.name; return ( -
+
{/* Left: Title */} @@ -93,22 +96,24 @@ const ProjectProgressChart = () => { {/* Right: Checkbox and Project Name */}
-
- setShowAllEmployees(e.target.checked)} - /> - -
+ {ShowAllProject == true && ( +
+ setShowAllEmployees(e.target.checked)} + /> + +
+ )} {!showAllEmployees && selectedProjectName && (

{selectedProjectName} diff --git a/src/components/Directory/NoteCardDirectory.jsx b/src/components/Directory/NoteCardDirectory.jsx index 30848855..fde21d4b 100644 --- a/src/components/Directory/NoteCardDirectory.jsx +++ b/src/components/Directory/NoteCardDirectory.jsx @@ -19,6 +19,10 @@ const NoteCardDirectory = ({ const [isLoading, setIsLoading] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [isActivProcess, setActiveProcessing] = useState(false); + + // State to manage hover status + const [isHovered, setIsHovered] = useState(false); + const handleUpdateNote = async () => { try { setIsLoading(true); @@ -127,6 +131,8 @@ const NoteCardDirectory = ({ background: `${noteItem.isActive ? "" : "#f8f6f6"}`, }} key={noteItem.id} + onMouseEnter={() => setIsHovered(true)} // Set hover state to true on mouse enter + onMouseLeave={() => setIsHovered(false)} // Set hover state to false on mouse leave >

@@ -149,11 +155,15 @@ const NoteCardDirectory = ({
-
+
{noteItem.isActive ? ( <> setEditing(true)} > diff --git a/src/components/Project/AboutProject.jsx b/src/components/Project/AboutProject.jsx index 5802e4d9..a64a09d1 100644 --- a/src/components/Project/AboutProject.jsx +++ b/src/components/Project/AboutProject.jsx @@ -1,23 +1,96 @@ import React, { useEffect, useState } from "react"; import moment from "moment"; import { getProjectStatusName } from "../../utils/projectStatus"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { MANAGE_PROJECT } from "../../utils/constants"; +import { cacheData, getCachedData } from "../../slices/apiDataManager"; +import ManageProjectInfo from "./ManageProjectInfo"; + const AboutProject = ({ data }) => { const [CurrentProject, setCurrentProject] = useState(data); + const [showModal, setShowModal] = useState(false); useEffect(() => { setCurrentProject(data); }, [data]); + const manageProject = useHasUserPermission(MANAGE_PROJECT); + + const handleShow = () => setShowModal(true); + const handleClose = () => setShowModal(false); + const handleFormSubmit = (updatedProject, setLoading) => { + if (CurrentProject?.id) { + ProjectRepository.updateProject(CurrentProject.id, updatedProject) + .then((response) => { + const updatedProjectData = { + ...CurrentProject, + ...response.data, + building: CurrentProject.building, + }; + setCurrentProject(updatedProject); + + cacheData(`projectinfo-${CurrentProject.id}`, updatedProjectData); + const projects_list = getCachedData("projectslist"); + if (projects_list) { + const updatedProjectsList = projects_list.map((project) => + project.id === CurrentProject.id + ? { + ...project, + ...response.data, + // tenant:project.tenant + } + : project + ); + + cacheData("projectslist", updatedProjectsList); + } + + showToast("Project updated successfully.", "success"); + setLoading(false); + setShowModal(false); + }) + .catch((error) => { + showToast(error.message, "error"); + }); + } + }; return ( <> +
+ +
{data && (
+
+
+ {" "} + + Project Profile +
+
- - Profile - -
    -
  • +
      +
    • + + Name:{" "} + {data.name} +
    • +
    • + + Nick Name:{" "} + {data.shortName} +
    • +
    • Start Date:{" "} @@ -26,7 +99,7 @@ const AboutProject = ({ data }) => { : "N/A"}
    • -
    • +
    • {" "} End Date:{" "} @@ -35,17 +108,17 @@ const AboutProject = ({ data }) => { : "N/A"}
    • -
    • +
    • Status:{" "} {getProjectStatusName(data.projectStatusId)}
    • -
    • +
    • Contact:{" "} {data.contactPerson}
    • -
    • +
    • Address: @@ -57,6 +130,22 @@ const AboutProject = ({ data }) => {
      {data.projectAddress}
      )}
    • + +
    • + {manageProject && ( + + )} +
diff --git a/src/components/Project/ProjectOverview.jsx b/src/components/Project/ProjectOverview.jsx index a15a8ae5..89b69886 100644 --- a/src/components/Project/ProjectOverview.jsx +++ b/src/components/Project/ProjectOverview.jsx @@ -1,33 +1,124 @@ import React from "react"; -import {useEmployeesByProjectAllocated, useProjects} from "../../hooks/useProjects"; +import { + useEmployeesByProjectAllocated, + useProjects, +} from "../../hooks/useProjects"; + +const ProjectOverview = ({ project }) => { + const { projects } = useProjects(); + const getProgress = (planned, completed) => { + return (completed * 100) / planned + "%"; + }; + const getProgressInNumber = (planned, completed) => { + var number = (completed * 100) / planned; + return FormattedNumber(number); + }; + + const 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 Invalid Number; + } + + 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 ProjectOverview = ({project}) => -{ -const {projects} = useProjects() - const project_detail = projects.find( ( pro ) => pro.id == project ) return (
+
+
+ {" "} + + Project Statistics +
+
- - Overview -
    -
  • +
  • Task Planned:{" "} - {project_detail?.plannedWork - } + {project_detail?.plannedWork}
  • -
  • +
  • Task Completed:{" "} - {project_detail?.completedWork } + {project_detail?.completedWork}
  • -
  • +
  • Current team Size:{" "} {project_detail?.teamSize}
  • +
  • + {project_detail && ( + <> +
    + + {Math.floor( + getProgressInNumber( + project_detail.plannedWork, + project_detail.completedWork + ) + ) || 0}{" "} + % Completed + +
    +
    +
    +
    + + )} +
diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index ff58e0e5..b52f6836 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -11,7 +11,11 @@ import ProjectInfra from "../../components/Project/ProjectInfra"; import Loader from "../../components/common/Loader"; import WorkPlan from "../../components/Project/WorkPlan"; import Breadcrumb from "../../components/common/Breadcrumb"; -import { cacheData, clearCacheKey, getCachedData } from "../../slices/apiDataManager"; +import { + cacheData, + clearCacheKey, + getCachedData, +} from "../../slices/apiDataManager"; import ProjectRepository from "../../repositories/ProjectRepository"; import { ActivityeRepository } from "../../repositories/MastersRepository"; import "./ProjectDetails.css"; @@ -24,6 +28,7 @@ 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 = () => { let { projectId } = useParams(); @@ -75,18 +80,31 @@ const ProjectDetails = () => { switch (activePill) { case "profile": { return ( -
-
- {/* About User */} - - {/* About User */} + <> +
+
+ {/* About User */} + + + {/* About User */} +
+
+ {/* Profile Overview */} + + {/* Profile Overview */} +
-
- {/* Profile Overview */} - - {/* Profile Overview */} +
+
+ {/* Profile Overview */} + {/* */} + {/* Profile Overview */} +
-
+ ); } case "teams": { @@ -141,22 +159,22 @@ const ProjectDetails = () => { const handler = useCallback( (msg) => { if (msg.keyword === "Update_Project" && project.id === msg.response.id) { - clearCacheKey("projectInfo") + clearCacheKey("projectInfo"); ProjectRepository.getProjectByprojectId(projectId) - .then((response) => { - setProjectDetails(response.data); - setProject(response.data); - cacheData("projectInfo", { projectId, data: response.data }); - setLoading(false); - }) - .catch((error) => { - console.error(error); - setError("Failed to fetch data."); - setLoading(false); - }); + .then((response) => { + setProjectDetails(response.data); + setProject(response.data); + cacheData("projectInfo", { projectId, data: response.data }); + setLoading(false); + }) + .catch((error) => { + console.error(error); + setError("Failed to fetch data."); + setLoading(false); + }); } }, - [project,handleDataChange] + [project, handleDataChange] ); useEffect(() => { eventBus.on("project", handler); @@ -171,15 +189,15 @@ const ProjectDetails = () => { data={[ { label: "Home", link: "/dashboard" }, { label: "Projects", link: "/projects" }, - { label: "Details", link: null }, + { label: `${project?.name ? project?.name : ""}`, link: null }, ]} >
{projectLoading &&

Loading....

} - {!projectLoading && project && ( + {/* {!projectLoading && project && ( - )} + )} */}