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/JobComments.jsx b/src/components/ServiceProject/JobComments.jsx index ff86577c..84d31665 100644 --- a/src/components/ServiceProject/JobComments.jsx +++ b/src/components/ServiceProject/JobComments.jsx @@ -22,7 +22,7 @@ const JobComments = ({ data }) => { formState: { errors }, } = useAppForm({ resolver: zodResolver(JobCommentSchema), - defaultValues: { comment: "", attachments: [] } + defaultValues: { comment: "", attachments: [] }, }); const { @@ -118,50 +118,41 @@ const JobComments = ({ data }) => { )}
- -
- {/* LEFT SIDE → Uploaded Files */} -
- {files?.length > 0 && ( - - )} -
- - {/* RIGHT SIDE → Add Attachment + Submit */} -
-
document.getElementById("attachments").click()} - className="cursor-pointer" - style={{ whiteSpace: 'nowrap' }} - > - { - onFileChange(e); - e.target.value = ""; - }} - /> - - Add Attachment -
- - - -
+
+ {files?.length > 0 && ( + + )}
+
+
document.getElementById("attachments").click()} + className="cursor-pointer" + style={{ whiteSpace: "nowrap" }} + > + { + onFileChange(e); + e.target.value = ""; + }} + /> + + Add Attachment +
+ +
diff --git a/src/components/ServiceProject/JobList.jsx b/src/components/ServiceProject/JobList.jsx index fd0122ef..8a7f1639 100644 --- a/src/components/ServiceProject/JobList.jsx +++ b/src/components/ServiceProject/JobList.jsx @@ -1,5 +1,9 @@ import React, { useState } from "react"; -import { daysLeft, getNextBadgeColor } from "../../utils/appUtils"; +import { + daysLeft, + getJobStatusBadge, + getNextBadgeColor, +} from "../../utils/appUtils"; import { useServiceProjectJobs } from "../../hooks/useServiceProject"; import { ITEMS_PER_PAGE } from "../../utils/constants"; import EmployeeAvatarGroup from "../common/EmployeeAvatarGroup"; @@ -23,7 +27,7 @@ const JobList = () => { { key: "jobTicketUId", label: "Job Id", - getValue: (e) => e?.jobTicketUId || "N/A", + getValue: (e) => {e?.jobTicketUId || "N/A"}, align: "text-start", }, { @@ -56,22 +60,9 @@ const JobList = () => { key: "status", label: "Status", getValue: (e) => { - const statusName = e?.status?.displayName || "N/A"; - const statusColorMap = { - Assigned: "label-primary", - Pending: "label-warning", - Completed: "label-success", - Cancelled: "label-danger", - }; - - const badgeColor = statusColorMap[statusName] || "label-secondary"; - return ( - - {statusName} + + {e?.status?.displayName} ); }, @@ -85,7 +76,7 @@ const JobList = () => { const { days, color } = daysLeft(e.startDate, e.dueDate); return ( - + {days !== null ? `${days} days` : "N/A"} ); @@ -96,7 +87,7 @@ const JobList = () => { ]; return ( -
+
{ {Array.isArray(data?.data) && data.data.length > 0 ? ( data.data.map((row, i) => ( - + {jobGrid.map((col) => ( - ))} diff --git a/src/components/ServiceProject/JobStatusLog.jsx b/src/components/ServiceProject/JobStatusLog.jsx index 6e97f9e9..88ff3562 100644 --- a/src/components/ServiceProject/JobStatusLog.jsx +++ b/src/components/ServiceProject/JobStatusLog.jsx @@ -1,5 +1,6 @@ import React from "react"; import Avatar from "../common/Avatar"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; const JobStatusLog = ({ data }) => { return ( @@ -20,9 +21,9 @@ const JobStatusLog = ({ data }) => { - {/* - Level {item.nextStatus?.level ?? item.status?.level} - */} + + {formatUTCToLocalTime(item?.updatedAt,true)} +
{ path: "/assignees", value: updatedEmployees, }, + { + op: "replace", + path: "/statusId", + value: formData.statusId, + }, ]; UpdateJob({ id: Job, payload }); } else { @@ -159,7 +164,6 @@ const ManageJob = ({ Job }) => { statusId: JobData.status.id, }); }, [JobData, Job, projectId]); - return (
@@ -202,30 +206,32 @@ const ManageJob = ({ Job }) => { placeholder="Select employee" />
-
- ( - - )} - /> + {Job && ( +
+ ( + + )} + /> - {errors.statusId && ( - {errors.statusId.message} - )} -
+ {errors.statusId && ( + {errors.statusId.message} + )} +
+ )}
{ const { projectId } = useParams(); @@ -45,54 +47,45 @@ const ManageJobTicket = ({ Job }) => { return (
-
{data?.title}
-
+
{data?.title}
+

Job Id : {data?.jobTicketUId || "N/A"}

- {data?.dueDate && - (() => { - const { days, color } = daysLeft( - data?.startDate, - data?.dueDate - ); - return ( - - Days Left: - - {days !== null ? `${days} days` : "N/A"} - - - ); - })()}
- - {data?.status?.name} + + {data?.status?.displayName} - + } + > + + } /> - } - > - - + + )}
- -

{data?.description || "N/A"}

@@ -106,7 +99,7 @@ const ManageJobTicket = ({ Job }) => {
-
+
Start Date :{" "} @@ -118,47 +111,74 @@ const ManageJobTicket = ({ Job }) => { {formatUTCToLocalTime(data?.startDate)}
+ {data?.dueDate && + (() => { + const { days, color } = daysLeft(data?.startDate, data?.dueDate); + return ( + + Days Left: + + {days !== null ? `${days} days` : "N/A"} + + + ); + })()}
-
- Created By{" "} - {" "} -
-

{`${data?.createdBy?.firstName} ${data?.createdBy?.lastName}`}

- - ({data?.createdBy?.jobRoleName}) - +
+
+ Created By +
+
+ {" "} +
+

{`${data?.createdBy?.firstName} ${data?.createdBy?.lastName}`}

+ + ({data?.createdBy?.jobRoleName}) + +
-
- Assigned By -
- {data?.assignees?.map((emp) => ( -
-
- -
- - {emp.firstName} {emp.lastName} - - - {emp.jobRoleName} - + {data?.assignees?.length > 0 && ( +
+
+ Assigned To +
+ +
+
+ {data?.assignees?.map((emp) => ( +
+
+ + +
+ + {emp.firstName} {emp.lastName} + + + {emp.jobRoleName} + +
+
-
+ ))}
- ))} +
-
+ )}
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/ServiceProjectSchema.jsx b/src/components/ServiceProject/ServiceProjectSchema.jsx index 793dc722..5a5727b7 100644 --- a/src/components/ServiceProject/ServiceProjectSchema.jsx +++ b/src/components/ServiceProject/ServiceProjectSchema.jsx @@ -62,14 +62,14 @@ export const jobSchema = z.object({ description: z.string().min(1, "Description is required"), projectId: z.string().min(1, "Project is required"), - assignees: z.array(z.string()).nonempty("At least one assignee is required"), + assignees: z.array(z.string()).optional(), startDate: z.string(), dueDate: z.string(), tags: z.array(TagSchema).optional().default([]), - statusId: z.string().optional(), + statusId: z.string().optional().nullable(), }); const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB 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/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx index 50b834ac..37005288 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx @@ -123,12 +123,14 @@ const ServiceProjectTeamAllocation = () => {
- {!isAddEmployee && } + {!isAddEmployee && ( + + )}
{isAddEmployee && ( @@ -166,18 +168,18 @@ const ServiceProjectTeamAllocation = () => { )}
- { isAddEmployee && ( -
+ {isAddEmployee && ( +
{" "} - +
)}
-
- setSelectedJob({ showCanvas: true, job: row?.id }) - }> + + setSelectedJob({ showCanvas: true, job: row?.id }) + } + > {col.getValue(row)}
+
@@ -202,29 +204,30 @@ const ServiceProjectTeamAllocation = () => { Team?.map((emp) => ( + - @@ -233,18 +236,12 @@ const ServiceProjectTeamAllocation = () => { diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx index ee98305a..786ef198 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamList.jsx @@ -2,7 +2,7 @@ import React from "react"; import Avatar from "../../common/Avatar"; import { formatUTCToLocalTime } from "../../../utils/dateUtils"; import { useServiceProjectTeam } from "../../../hooks/useServiceProject"; -import { useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { SpinnerLoader } from "../../common/Loader"; const ServiceProjectTeamList = () => { @@ -11,18 +11,25 @@ const ServiceProjectTeamList = () => { projectId, true ); + + const navigate = useNavigate(); + const servceProjectColmen = [ { key: "employeName", label: "Name", - getValue: (e) => { - return ( -
- - {`${e.employee.firstName} ${e.employee.lastName}`} -
- ); - }, + getValue: (e) => ( +
navigate(`/employee/${e.employee.id}`)}> + {" "} + + {`${e.employee.firstName} ${e.employee.lastName}`} +
+ ), + align: "text-start", }, { key: "teamRole", @@ -30,66 +37,51 @@ const ServiceProjectTeamList = () => { getValue: (e) => { return (
- {`${e.teamRole.firstName}`} + {`${e.teamRole.name}`}
); }, + align: "text-start", }, { key: "assignedAt", label: "assigned Date", - getValue: (e) => { - return ( -
- {formatUTCToLocalTime(e.assignedAT)} -
- ); - }, + getValue: (e) => + formatUTCToLocalTime(e.assignedAT) + , + align: "text-center", }, ]; return ( -
-
Name
-
- {" "} +
- {`${emp?.employee?.firstName} ${emp?.employee?.lastName}`} + + {emp?.employee?.firstName} {emp?.employee?.lastName} +
{emp?.teamRole?.name} - {deletingEmp?.emplyee?.id === emp.id && isPending ? ( -
+ +
+ {deletingEmp?.employee?.id === emp.id && isPending ? ( +
) : ( - - - setSeletingEmp({ employee: emp, isOpen: true }) - } - > - + + setSeletingEmp({ employee: emp, isOpen: true }) + } + > )}
{isTeamLoading ? ( ) : ( -
- -

- {" "} - Please Add a Employee{" "} -

-
+

No Records Found

)}
+
+
{servceProjectColmen.map((col) => ( - + ))} {data?.length > 0 ? ( - data.map((emp) => ( - - - - - - + data.map((row) => ( + + {servceProjectColmen.map((col) => ( + + ))} )) ) : ( - diff --git a/src/components/common/Forms/InputFieldSuggesstion.jsx b/src/components/common/Forms/InputFieldSuggesstion.jsx new file mode 100644 index 00000000..881eb16c --- /dev/null +++ b/src/components/common/Forms/InputFieldSuggesstion.jsx @@ -0,0 +1,56 @@ +import React from 'react' + +const InputFieldSuggesstion = () => { + return ( +
+ setTimeout(() => setShowSuggestions(false), 150)} + onFocus={() => { + if (value) setShowSuggestions(true); + }} + disabled={disabled} + /> + {showSuggestions && filteredList.length > 0 && ( +
    + {filteredList.map((org) => ( +
  • handleSelectSuggestion(org)} + onMouseEnter={(e) => + (e.currentTarget.style.backgroundColor = "#f8f9fa") + } + onMouseLeave={(e) => + (e.currentTarget.style.backgroundColor = "transparent") + } + > + {org} +
  • + ))} +
+ )} + + {error && {error}} +
+ ) +} + +export default InputFieldSuggesstion diff --git a/src/components/common/Forms/SelectFieldServerSide.jsx b/src/components/common/Forms/SelectFieldServerSide.jsx index 5d398932..f7aa70eb 100644 --- a/src/components/common/Forms/SelectFieldServerSide.jsx +++ b/src/components/common/Forms/SelectFieldServerSide.jsx @@ -181,5 +181,173 @@ const SelectEmployeeServerSide = ({ ); }; - export default SelectEmployeeServerSide; + + +export const SelectProjectField = ()=>{ + const [searchText, setSearchText] = useState(""); + const debounce = useDebounce(searchText, 300); + + const { data, isLoading } = useEmployeesName( + projectId, + debounce, + isAllEmployee + ); + + const options = data?.data ?? []; + const [open, setOpen] = useState(false); + const dropdownRef = useRef(null); + + const getDisplayName = (emp) => { + if (!emp) return ""; + return `${emp.firstName || ""} ${emp.lastName || ""}`.trim(); + }; + + /** ----------------------------- + * SELECTED OPTION (SINGLE) + * ----------------------------- */ + let selectedSingle = null; + + if (!isMultiple) { + if (isFullObject && value) selectedSingle = value; + else if (!isFullObject && value) + selectedSingle = options.find((o) => o[valueKey] === value); + } + + /** ----------------------------- + * SELECTED OPTION (MULTIPLE) + * ----------------------------- */ + let selectedList = []; + + if (isMultiple && Array.isArray(value)) { + if (isFullObject) selectedList = value; + else { + selectedList = options.filter((opt) => value.includes(opt[valueKey])); + } + } + + /** Main button label */ + const displayText = !isMultiple + ? getDisplayName(selectedSingle) || placeholder + : selectedList.length > 0 + ? selectedList.map((e) => getDisplayName(e)).join(", ") + : placeholder; + + /** ----------------------------- + * HANDLE OUTSIDE CLICK + * ----------------------------- */ + useEffect(() => { + const handleClickOutside = (e) => { + if (dropdownRef.current && !dropdownRef.current.contains(e.target)) { + setOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + /** ----------------------------- + * HANDLE SELECT + * ----------------------------- */ + const handleSelect = (option) => { + if (!isMultiple) { + // SINGLE SELECT + if (isFullObject) onChange(option); + else onChange(option[valueKey]); + } else { + // MULTIPLE SELECT + let updated = []; + + const exists = selectedList.some((e) => e[valueKey] === option[valueKey]); + + if (exists) { + // remove + updated = selectedList.filter((e) => e[valueKey] !== option[valueKey]); + } else { + // add + updated = [...selectedList, option]; + } + + if (isFullObject) onChange(updated); + else onChange(updated.map((x) => x[valueKey])); + } + }; + + return ( +
+ {label && ( + + )} + + {/* MAIN BUTTON */} + + + {/* DROPDOWN */} + {open && ( +
    +
    + setSearchText(e.target.value)} + className="form-control form-control-sm" + placeholder="Search..." + /> +
    + + {isLoading && ( +
  • Loading...
  • + )} + + {!isLoading && options.length === 0 && ( +
  • No results found
  • + )} + + {!isLoading && + options.map((option) => { + const isActive = isMultiple + ? selectedList.some((x) => x[valueKey] === option[valueKey]) + : selectedSingle && + selectedSingle[valueKey] === option[valueKey]; + + return ( +
  • + +
  • + ); + })} +
+ )} +
+ ); +}; \ No newline at end of file diff --git a/src/components/common/OffcanvasComponent.jsx b/src/components/common/OffcanvasComponent.jsx index f9e2d1c1..88d3ba57 100644 --- a/src/components/common/OffcanvasComponent.jsx +++ b/src/components/common/OffcanvasComponent.jsx @@ -66,7 +66,7 @@ const OffcanvasComponent = ({ > -
{children}
+
{children}
); 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 }) => { /> ) : ( { } } - return null; // + return null; }; +export const getJobStatusBadge = (statusId) => { + if (!statusId) return "bg-label-secondary"; + + const map = { + "32d76a02-8f44-4aa0-9b66-c3716c45a918": "bg-label-primary", // New + "cfa1886d-055f-4ded-84c6-42a2a8a14a66": "bg-label-info", // Assigned + "5a6873a5-fed7-4745-a52f-8f61bf3bd72d": "bg-label-warning", // In Progress + "aab71020-2fb8-44d9-9430-c9a7e9bf33b0": "bg-label-label-dark", // Review + "ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7": "bg-label-success", // Done + "3ddeefb5-ae3c-4e10-a922-35e0a452bb69": "bg-label-secondary", // Closed + "75a0c8b8-9c6a-41af-80bf-b35bab722eb2": "bg-label-danger", // On Hold + }; + + return map[statusId] || "bg-label-secondary"; +}; + export const useDebounce = (value, delay = 500) => { const [debouncedValue, setDebouncedValue] = useState(value); diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx index 434a833b..24121cb0 100644 --- a/src/utils/constants.jsx +++ b/src/utils/constants.jsx @@ -205,3 +205,9 @@ export const PAYEE_RECURRING_EXPENSE = [ label: "Paused", }, ]; + + +//#region Service Project and Jobs +export const STATUS_JOB_DONE = "ed10ab57-dbaa-4ca5-8ecd-56745dcbdbd7" + +//#endregion \ No newline at end of file
{col.label}{col.label}
-
- - - {`${emp?.employee?.firstName} ${emp?.employee?.lastName}`} - -
-
{emp?.teamRole?.name}{formatUTCToLocalTime(emp.assignedAt)}
{col.getValue(row)}
+ {isLoading ? ( ) : ( -
- -

Please Add an Employee

-
+
No Records Available
)}