diff --git a/src/components/DailyProgressRport/TaskReportFilterPanel.jsx b/src/components/DailyProgressRport/TaskReportFilterPanel.jsx index 2af3636d..8394a158 100644 --- a/src/components/DailyProgressRport/TaskReportFilterPanel.jsx +++ b/src/components/DailyProgressRport/TaskReportFilterPanel.jsx @@ -54,6 +54,7 @@ const TaskReportFilterPanel = ({ handleFilter }) => {
{ Total Pending{" "} This shows the total pending tasks for each activity on that date.

} > @@ -213,6 +214,7 @@ const TaskReportList = () => { Reported/Planned{" "} This shows the reported versus planned tasks for each activity on that date.

} > diff --git a/src/components/RecurringExpense/ManageRecurringExpense.jsx b/src/components/RecurringExpense/ManageRecurringExpense.jsx index 49fb3675..aad3c468 100644 --- a/src/components/RecurringExpense/ManageRecurringExpense.jsx +++ b/src/components/RecurringExpense/ManageRecurringExpense.jsx @@ -241,6 +241,7 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => { Choose whether the payment amount varies or remains fixed each cycle. @@ -387,6 +388,7 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => { Defines how often payments or billing occur, such as monthly, quarterly, or annually. @@ -450,6 +452,7 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => { Number of extra days allowed after the due date before payment is considered late. @@ -485,6 +488,7 @@ const ManageRecurringExpense = ({ closeModal, requestToEdit = null }) => { The date when the last payment in the recurrence occurs. diff --git a/src/components/ServiceProject/ServiceProjectProfile.jsx b/src/components/ServiceProject/ServiceProjectProfile.jsx index 54af1442..2963c6e1 100644 --- a/src/components/ServiceProject/ServiceProjectProfile.jsx +++ b/src/components/ServiceProject/ServiceProjectProfile.jsx @@ -1,96 +1,123 @@ -import React from "react"; +import React, { useState } from "react"; import { useParams } from "react-router-dom"; import { useServiceProject } from "../../hooks/useServiceProject"; import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import ManageServiceProject from "./ManageServiceProject"; +import GlobalModel from "../common/GlobalModel"; const ServiceProjectProfile = () => { const { projectId } = useParams(); + const [IsOpenModal, setIsOpenModal] = useState(false); const { data, isLoading, isError, error } = useServiceProject(projectId); if (isLoading) { return
Loadng.
; } return ( -
+ <> + {IsOpenModal && ( + setIsOpenModal(false)}> + setIsOpenModal(false)} + /> + + )} + +
-
-
-
- {" "} - - Project Profile -
-
-
-
    - -
  • -
    - - Name: -
    - - {/* Content section that wraps nicely */} -
    - {data.name} -
    -
  • -
  • -
    - - Nick Name: -
    - {data.shortName} -
  • -
  • -
    - - Assign Date: -
    - - {data.assignedDate ? formatUTCToLocalTime(data.assignedDate) : "NA"} - -
  • - -
  • -
    - - Status: -
    - {data?.status.status} -
  • -
  • -
    - - Contact: -
    - {data.contactName} -
  • -
  • - {/* Label section with icon */} -
    - - Address: -
    - - {/* Content section that wraps nicely */} -
    - {data.address} -
    -
  • - - - -
  • {/* Added mt-4 for some top margin */} - -
  • -
- -
-
+
+
+
+ {" "} + + Project Profile +
+
+
+
    + +
  • +
    + + Name: +
    + + {/* Content section that wraps nicely */} +
    + {data.name} +
    +
  • +
  • +
    + + Nick Name: +
    + {data.shortName} +
  • +
  • +
    + + Assign Date: +
    + + {data.assignedDate ? formatUTCToLocalTime(data.assignedDate) : "NA"} + +
  • + +
  • +
    + + Status: +
    + {data?.status.status} +
  • +
  • +
    + + Contact: +
    + {data.contactName} +
  • +
  • + {/* Label section with icon */} +
    + + Address: +
    + + {/* Content section that wraps nicely */} +
    + {data.address} +
    +
  • + + + +
  • {/* Added mt-4 for some top margin */} + +
  • {/* Added mt-4 for some top margin */} + + + +
  • + +
+ +
+
- - -
+ + +
+ ); }; diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx new file mode 100644 index 00000000..e6f39b36 --- /dev/null +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectCard.jsx @@ -0,0 +1,246 @@ +import React, { useEffect, useState } from "react"; +import moment from "moment"; +import { formatNumber, formatUTCToLocalTime, getDateDifferenceInDays } from "../../../utils/dateUtils"; +import { useNavigate } from "react-router-dom"; +import ManageProjectInfo from "../../Project/ManageProjectInfo"; +import ProjectRepository from "../../../repositories/ProjectRepository"; +import { cacheData, getCachedData } from "../../../slices/apiDataManager"; +import showToast from "../../../services/toastService"; +import { useHasUserPermission } from "../../../hooks/useHasUserPermission"; +import { MANAGE_PROJECT } from "../../../utils/constants"; +import GlobalModel from "../../common/GlobalModel"; +import { useDispatch } from "react-redux"; +import { setProjectId } from "../../../slices/localVariablesSlice"; +import { useProjectContext } from "../../../pages/project/ProjectPage"; +import { useActiveInActiveServiceProject } from "../../../hooks/useServiceProject"; +import ConfirmModal from "../../common/ConfirmModal"; +import { getProjectStatusColor, getProjectStatusName } from "../../../utils/projectStatus"; + +const ServiceProjectCard = ({ project, isCore = true }) => { + const [deleteProject, setDeleteProject] = useState({ + project: null, + isOpen: false, + }); + const dispatch = useDispatch(); + const navigate = useNavigate(); + const ManageProject = useHasUserPermission(MANAGE_PROJECT); + const { setMangeProject, setManageServiceProject } = useProjectContext(); + + const getProgress = (planned, completed) => { + return (completed * 100) / planned + "%"; + }; + const getProgressInNumber = (planned, completed) => { + return (completed * 100) / planned; + }; + + const handleClose = () => setShowModal(false); + + const handleViewProject = () => { + if (isCore) { + dispatch(setProjectId(project.id)); + navigate(`/projects/details`); + } else { + navigate(`/service-projects/${project.id}`); + } + }; + const handleViewActivities = () => { + dispatch(setProjectId(project.id)); + navigate(`/activities/records?project=${project.id}`); + }; + const handleManage = () => { + if (isCore) { + setMangeProject({ + isOpen: true, + Project: project.id, + }); + } else { + setManageServiceProject({ + isOpen: true, + project: project.id, + }); + } + }; + + const { mutate: DeleteProject, isPending } = useActiveInActiveServiceProject( + () => setDeleteProject({ project: null, isOpen: false }) + ); + const handleActiveInactive = (projectId) => { + DeleteProject(projectId, false); + }; + + return ( + <> + setDeleteProject({ project: null, isOpen: false })} + loading={isPending} + paramData={project.id} + isOpen={deleteProject.isOpen} + /> +
+
+
+
+
+
+ +
+
+
+ {project?.shortName ? project?.shortName : project?.name} +
+
+ {project?.shortName ? project?.name : ""} +
+
+
+
+
+ + +
+
+
+
+ + {/* Card View */} + +
+
+
+
+

+ + {getProjectStatusName(project?.status?.id)} + +

+
+ +

+ Contact Person: + {project?.contactName} +

+ +

+ Assigned Date: + {formatUTCToLocalTime(project?.assignedDate)} +

+ +

+ Address: + {project?.address} +

+ + +

{project?.projectAddress}

+
+
+
+
+
+

+ + {project?.teamMemberCount} Employees +

+
+ + {/* Heading */} +
+
Jobs
+ + {/* Job details */} +
+

+ + {project?.assignedJobsCount} Assigned Jobs +

+ +

+ + {project?.activeJobsCount} Active Jobs +

+ +

+ + {project?.jobsPassedDueDateCount} Job Passes Due Date +

+

+ + {project?.onHoldJobsCount} On Hold Jobs +

+
+
+
+
+
+ + ); +}; + +export default ServiceProjectCard; \ No newline at end of file diff --git a/src/pages/ServiceProject/ServiceProjectDisplay.jsx b/src/pages/ServiceProject/ServiceProjectDisplay.jsx index 1a3eeb24..96a9ddb4 100644 --- a/src/pages/ServiceProject/ServiceProjectDisplay.jsx +++ b/src/pages/ServiceProject/ServiceProjectDisplay.jsx @@ -10,8 +10,9 @@ import Pagination from "../../components/common/Pagination"; import GlobalModel from "../../components/common/GlobalModel"; import ManageServiceProject from "../../components/ServiceProject/ManageServiceProject"; import { SpinnerLoader } from "../../components/common/Loader"; +import ServiceProjectCard from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectCard"; -const ServiceProjectDisplay = ({ listView }) => { +const ServiceProjectDisplay = ({ listView ,selectedStatuses }) => { const [currentPage, setCurrentPage] = useState(1); const { manageServiceProject, setManageServiceProject } = useProjectContext(); @@ -25,6 +26,9 @@ const ServiceProjectDisplay = ({ listView }) => { } }; + const filteredProjects = data?.data?.filter(project => + selectedStatuses.includes(project?.status?.id) + ); if (isLoading) return ( @@ -47,9 +51,10 @@ const ServiceProjectDisplay = ({ listView }) => { {listView ? (

List

) : ( - data?.data?.map((project) => ( - + filteredProjects?.map((project) => ( + )) + )}
diff --git a/src/pages/project/ProjectPage.jsx b/src/pages/project/ProjectPage.jsx index 0838e1a4..ad2c3d80 100644 --- a/src/pages/project/ProjectPage.jsx +++ b/src/pages/project/ProjectPage.jsx @@ -49,6 +49,13 @@ const ProjectPage = () => { const [selectedStatuses, setSelectedStatuses] = useState( PROJECT_STATUS.map((s) => s.id) ); + const handleStatusChange = (statusId) => { + setSelectedStatuses((prev) => + prev.includes(statusId) + ? prev.filter((id) => id !== statusId) + : [...prev, statusId] + ); + }; const contextDispatcher = { setMangeProject, @@ -75,7 +82,7 @@ const ProjectPage = () => { />
-
+
{/* LEFT SIDE — DATE TOGGLE BUTTONS */}
@@ -183,16 +190,16 @@ const ProjectPage = () => { New Project )} - -
- -
- {coreProjects ? : } + {coreProjects ? : }
); diff --git a/src/pages/project/ProjectsDisplay.jsx b/src/pages/project/ProjectsDisplay.jsx index 9fa84029..20c6d064 100644 --- a/src/pages/project/ProjectsDisplay.jsx +++ b/src/pages/project/ProjectsDisplay.jsx @@ -11,7 +11,7 @@ import { ITEMS_PER_PAGE, PROJECT_STATUS } from "../../utils/constants"; import usePagination from "../../hooks/usePagination"; import ManageProjectInfo from "../../components/Project/ManageProjectInfo"; -const ProjectsDisplay = ({ listView, searchTerm }) => { +const ProjectsDisplay = ({ listView, searchTerm, selectedStatuses, handleStatusChange }) => { const [currentPage, setCurrentPage] = useState(1); const { manageProject, @@ -21,35 +21,34 @@ const ProjectsDisplay = ({ listView, searchTerm }) => { } = useProjectContext(); const [projectList, setProjectList] = useState([]); - const [selectedStatuses, setSelectedStatuses] = useState( - PROJECT_STATUS.map((s) => s.id) - ); + const { data, isLoading, isError, error } = useProjects(ITEMS_PER_PAGE, 1); const filteredProjects = data?.data?.filter((project) => { - const matchesStatus = selectedStatuses.includes(project.projectStatusId); + const statusId = + project.projectStatusId ?? + project?.status?.id ?? + project?.statusId; + + const matchesStatus = selectedStatuses.includes(statusId); + const matchesSearch = project?.name ?.toLowerCase() ?.includes(searchTerm?.toLowerCase()); + return matchesStatus && matchesSearch; }) ?? []; + const paginate = (page) => { if (page >= 1 && page <= (data?.totalPages ?? 1)) { setCurrentPage(page); } }; - const handleStatusChange = (statusId) => { - setCurrentPage(1); - setSelectedStatuses((prev) => - prev.includes(statusId) - ? prev.filter((id) => id !== statusId) - : [...prev, statusId] - ); - }; + const sortingProject = (projects) => { if (!isLoading && Array.isArray(projects)) { @@ -98,6 +97,7 @@ const ProjectsDisplay = ({ listView, searchTerm }) => {

{error.message}

); + return (
{listView ? ( @@ -111,7 +111,7 @@ const ProjectsDisplay = ({ listView, searchTerm }) => { /> ) : (