From ad1db0f366951e8f86723046af4002cfe152a883 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Mon, 7 Jul 2025 15:07:43 +0530 Subject: [PATCH 001/120] Image Popup Expands Page Width when add lengthy comment on Click in Image Gallery. --- src/pages/Gallary/ImagePop.css | 98 +++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 26 deletions(-) diff --git a/src/pages/Gallary/ImagePop.css b/src/pages/Gallary/ImagePop.css index dfdb549c..367758f6 100644 --- a/src/pages/Gallary/ImagePop.css +++ b/src/pages/Gallary/ImagePop.css @@ -1,103 +1,149 @@ +/* Image Modal Overlay */ .image-modal-overlay { position: fixed; top: 0; left: 0; - z-index: 9999; /* High z-index to ensure it's on top */ + z-index: 9999; width: 100%; height: 100%; - background-color: rgba(0, 0, 0, 0.85); /* Dark semi-transparent background */ + background-color: rgba(0, 0, 0, 0.85); display: flex; justify-content: center; align-items: center; } +/* Main Modal Content Box */ .image-modal-content { background: #fff; padding: 24px; - max-width: 90%; /* Responsive max-width */ - max-height: 100%; /* Responsive max-height */ + max-width: 50%; + max-height: 95vh; /* Limits the modal's height to 95% of viewport height */ border-radius: 12px; position: relative; box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4); text-align: center; - display: flex; /* Use flexbox for internal layout */ + display: flex; flex-direction: column; align-items: center; justify-content: center; + overflow-y: auto; /* Enables vertical scrolling */ + + /* --- HIDE SCROLLBAR FOR MAIN MODAL CONTENT --- */ + /* For Webkit browsers (Chrome, Safari, Edge) */ + &::-webkit-scrollbar { + width: 0px; /* Hide vertical scrollbar */ + height: 0px; /* Hide horizontal scrollbar, though unlikely needed here */ + } + /* For Firefox */ + scrollbar-width: none; /* Hide scrollbar in Firefox */ + /* For Internet Explorer and Edge (legacy) */ + -ms-overflow-style: none; + /* --- END HIDE SCROLLBAR --- */ } +/* Image Styles */ .modal-image { max-width: 100%; - max-height: 70vh; /* Limits image height to 70% of viewport height */ + max-height: 70vh; + width: auto; border-radius: 10px; - object-fit: contain; /* Ensures the whole image is visible without cropping */ + object-fit: contain; margin-bottom: 20px; - flex-shrink: 0; /* Prevent image from shrinking if content is too large */ + flex-shrink: 0; } -.image-details { +/* Scrollable Container for Text Details */ +.image-details-scroll-container { + width: 100%; + flex-grow: 1; + max-height: calc(95vh - 70vh - (24px * 2) - 20px); /* Approximate calculation for text area height */ + overflow-y: auto; /* Enables vertical scrolling for details */ text-align: left; + padding-right: 5px; /* Add some padding so text doesn't touch the hidden scrollbar area */ + + /* --- HIDE SCROLLBAR FOR TEXT DETAILS SECTION --- */ + /* For Webkit browsers (Chrome, Safari, Edge) */ + &::-webkit-scrollbar { + width: 0px; /* Hide vertical scrollbar */ + height: 0px; /* Hide horizontal scrollbar */ + } + /* For Firefox */ + scrollbar-width: none; /* Hide scrollbar in Firefox */ + /* For Internet Explorer and Edge (legacy) */ + -ms-overflow-style: none; + /* --- END HIDE SCROLLBAR --- */ +} + +/* Image Details Section (inside the scroll container) */ +.image-details { color: #444; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; - width: 100%; /* Ensure details section takes full width */ + width: 100%; + text-align: left; } .image-details p { - margin: 4px 0; /* Reduce vertical space between lines in details */ + margin: 4px 0; + white-space: normal; + word-wrap: break-word; + overflow-wrap: break-word; + text-overflow: initial; + text-align: left; } +/* Close Button */ .close-button { position: absolute; - top: 1px; /* Position relative to the modal content */ + top: 1px; right: 8px; font-size: 30px; background: none; border: none; - color: black; /* White color for visibility on dark overlay */ + color: black; cursor: pointer; padding: 0; line-height: 1; - z-index: 10000; /* Ensure it's above everything else */ + z-index: 10000; } -/* Styles for the navigation buttons */ +/* Navigation Buttons */ .nav-button { position: absolute; - top: 50%; /* Vertically center them */ - transform: translateY(-50%); /* Adjust for perfect vertical centering */ - background-color: rgba(0, 0, 0, 0.5); /* Semi-transparent background */ + top: 50%; + transform: translateY(-50%); + background-color: rgba(0, 0, 0, 0.5); color: white; border: none; padding: 10px 15px; font-size: 30px; cursor: pointer; - z-index: 1000; /* Ensure buttons are above the image */ - border-radius: 50%; /* Make them circular */ + z-index: 1000; + border-radius: 50%; width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; - transition: background-color 0.3s ease; /* Smooth hover effect */ + transition: background-color 0.3s ease; } .nav-button:hover { - background-color: rgba(0, 0, 0, 0.8); /* Darker on hover */ + background-color: rgba(0, 0, 0, 0.8); } .nav-button.prev-button { - left: 0px; /* Position left arrow */ + left: 0px; } .nav-button.next-button { - right: 0px; /* Position right arrow */ + right: 0px; } -/* Style for disabled buttons (optional) */ +/* Disabled Button Style */ .nav-button:disabled { opacity: 0.5; cursor: not-allowed; -} +} \ No newline at end of file From 9b63b2000ed18603bbc4eed8b5fa71c2e60f22e1 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Tue, 8 Jul 2025 12:35:02 +0530 Subject: [PATCH 002/120] Adding pagination 20 in Profile Attendance page and adding condtion to show more then 20 records to show paging. --- .../employee/AttendancesEmployeeRecords.jsx | 21 ++++++++----------- 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/pages/employee/AttendancesEmployeeRecords.jsx b/src/pages/employee/AttendancesEmployeeRecords.jsx index 1908ef21..bac57a09 100644 --- a/src/pages/employee/AttendancesEmployeeRecords.jsx +++ b/src/pages/employee/AttendancesEmployeeRecords.jsx @@ -85,7 +85,7 @@ const AttendancesEmployeeRecords = ({ employee }) => { const currentDate = new Date().toLocaleDateString("en-CA"); const { currentPage, totalPages, currentItems, paginate } = usePagination( sortedFinalList, - 10 + 20 ); useEffect(() => { @@ -141,13 +141,12 @@ const AttendancesEmployeeRecords = ({ employee }) => { id="DataTables_Table_0_length" >
- +
setIsRefreshing(!isRefreshing)} @@ -224,7 +223,7 @@ const AttendancesEmployeeRecords = ({ employee }) => { )}
- {!loading && data.length > 5 && ( + {!loading && sortedFinalList.length > 20 && ( )} + ); }; -export default Directory; \ No newline at end of file +export default Directory; + From e1e4ba2b541e8bb329b614186596867614cbacf0 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Tue, 8 Jul 2025 12:45:48 +0530 Subject: [PATCH 004/120] Navigate to "Projects" Page Should Display Only Message When No Projects Are Assigned. --- src/pages/project/ProjectList.jsx | 162 +++++++++++++++--------------- 1 file changed, 79 insertions(+), 83 deletions(-) diff --git a/src/pages/project/ProjectList.jsx b/src/pages/project/ProjectList.jsx index 24e87ca1..a72276ed 100644 --- a/src/pages/project/ProjectList.jsx +++ b/src/pages/project/ProjectList.jsx @@ -315,96 +315,91 @@ const ProjectList = () => {

No projects found.

)} - {listView ? ( -
-
-
- - - - - - - - - - - - - - - {currentItems.length === 0 ? ( + {listView ? ( + currentItems.length === 0 ? ( +
+ No projects found. +
+ ) : ( +
+
+
+
- Project Name - Contact PersonSTART DATEDEADLINETaskProgress -
- -
    - {[ - { - id: "b74da4c2-d07e-46f2-9919-e75e49b12731", - label: "Active", - }, - { - id: "603e994b-a27f-4e5d-a251-f3d69b0498ba", - label: "On Hold", - }, - { - id: "ef1c356e-0fe0-42df-a5d3-8daee355492d", - label: "Inactive", - }, - { - id: "33deaef9-9af1-4f2a-b443-681ea0d04f81", - label: "Completed", - }, - ].map(({ id, label }) => ( -
  • -
    - handleStatusChange(id)} - /> - -
    -
  • - ))} -
-
-
- Action -
+ - + + + + + + + + - ) : ( - currentItems.map((project) => ( + + + {currentItems.map((project) => ( - )) - )} - -
- No projects found - + Project Name + Contact PersonSTART DATEDEADLINETaskProgress +
+ +
    + {[ + { + id: "b74da4c2-d07e-46f2-9919-e75e49b12731", + label: "Active", + }, + { + id: "603e994b-a27f-4e5d-a251-f3d69b0498ba", + label: "On Hold", + }, + { + id: "ef1c356e-0fe0-42df-a5d3-8daee355492d", + label: "Inactive", + }, + { + id: "33deaef9-9af1-4f2a-b443-681ea0d04f81", + label: "Completed", + }, + ].map(({ id, label }) => ( +
  • +
    + handleStatusChange(id)} + /> + +
    +
  • + ))} +
+
+
+ Action +
-
{" "} -
{" "} -
+ ))} + + + + + + ) ) : (
{currentItems.map((project) => ( @@ -417,6 +412,7 @@ const ProjectList = () => {
)} + {!loading && totalPages > 1 && ( + )} ); From e3d7d6005b5ba713aea891c96b62a827a7fb5a3f Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Wed, 9 Jul 2025 15:15:53 +0530 Subject: [PATCH 087/120] Nevigate to "Projects" Page Should Display Only Message When No Projects Are Assigned. --- src/pages/project/ProjectList.jsx | 278 ++++++++++++++++-------------- 1 file changed, 144 insertions(+), 134 deletions(-) diff --git a/src/pages/project/ProjectList.jsx b/src/pages/project/ProjectList.jsx index 50c60643..6213a786 100644 --- a/src/pages/project/ProjectList.jsx +++ b/src/pages/project/ProjectList.jsx @@ -264,144 +264,154 @@ const ProjectList = () => { )} {listView ? ( - currentItems.length === 0 ? ( -
- No projects found. -
- ) : ( -
-
-
- - - - - - - - - - - - - - - {currentItems.map((project) => ( - - ))} - -
- Project Name - Contact PersonSTART DATEDEADLINETaskProgress -
- -
    - {[ - { - id: "b74da4c2-d07e-46f2-9919-e75e49b12731", - label: "Active", - }, - { - id: "603e994b-a27f-4e5d-a251-f3d69b0498ba", - label: "On Hold", - }, - { - id: "ef1c356e-0fe0-42df-a5d3-8daee355492d", - label: "Inactive", - }, - { - id: "33deaef9-9af1-4f2a-b443-681ea0d04f81", - label: "Completed", - }, - ].map(({ id, label }) => ( -
  • -
    - handleStatusChange(id)} - /> - -
    -
  • - ))} -
-
-
- Action -
-
-
-
- ) - ) : ( -
- {currentItems.map((project) => ( - - ))} -
- )} - - - {!loading && totalPages > 1 && ( - +)} + ); From f8abcc8aa89a95c14de20c0350f267e2c6278a1e Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Thu, 10 Jul 2025 16:58:25 +0530 Subject: [PATCH 088/120] In ProjectNav.jsx and AttendancePage.jsx, remove inline CSS font-size styles and replace them with Bootstrap classes. --- src/components/Project/ProjectNav.jsx | 5 ----- src/pages/Activities/AttendancePage.jsx | 3 --- 2 files changed, 8 deletions(-) diff --git a/src/components/Project/ProjectNav.jsx b/src/components/Project/ProjectNav.jsx index ffaf65dd..9fa89cdb 100644 --- a/src/components/Project/ProjectNav.jsx +++ b/src/components/Project/ProjectNav.jsx @@ -20,7 +20,6 @@ const ProjectNav = ({ onPillClick, activePill }) => { e.preventDefault(); onPillClick("profile"); }} - style={{ fontSize: '1rem' }} > Profile @@ -33,7 +32,6 @@ const ProjectNav = ({ onPillClick, activePill }) => { e.preventDefault(); onPillClick("teams"); }} - style={{ fontSize: '1rem' }} > Teams @@ -46,7 +44,6 @@ const ProjectNav = ({ onPillClick, activePill }) => { e.preventDefault(); onPillClick("infra"); }} - style={{ fontSize: '1rem' }} > Infrastructure @@ -62,7 +59,6 @@ const ProjectNav = ({ onPillClick, activePill }) => { e.preventDefault(); // Prevent page reload onPillClick("imagegallary"); }} - style={{ fontSize: '1rem' }} > Image Gallary @@ -76,7 +72,6 @@ const ProjectNav = ({ onPillClick, activePill }) => { e.preventDefault(); // Prevent page reload onPillClick("directory"); }} - style={{ fontSize: '1rem' }} > Directory diff --git a/src/pages/Activities/AttendancePage.jsx b/src/pages/Activities/AttendancePage.jsx index aabc7ee2..9f309197 100644 --- a/src/pages/Activities/AttendancePage.jsx +++ b/src/pages/Activities/AttendancePage.jsx @@ -237,7 +237,6 @@ const AttendancePage = () => { onClick={() => setActiveTab("all")} data-bs-toggle="tab" data-bs-target="#navs-top-home" - style={{ fontSize: '1rem' }} > Today's @@ -249,7 +248,6 @@ const AttendancePage = () => { onClick={() => setActiveTab("logs")} data-bs-toggle="tab" data-bs-target="#navs-top-profile" - style={{ fontSize: '1rem' }} > Logs @@ -263,7 +261,6 @@ const AttendancePage = () => { onClick={() => setActiveTab("regularization")} data-bs-toggle="tab" data-bs-target="#navs-top-messages" - style={{ fontSize: '1rem' }} > Regularization From b8949b3b71aa04594f874d4c9438632a8fc59020 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Tue, 8 Jul 2025 13:09:10 +0530 Subject: [PATCH 089/120] In filter DatePicker should not display dates in Image Gallery. --- .../Gallary/DateRangePickerNoDefault.jsx | 55 +++++++++++++++++++ src/pages/Gallary/ImageGallary.jsx | 6 +- 2 files changed, 58 insertions(+), 3 deletions(-) create mode 100644 src/pages/Gallary/DateRangePickerNoDefault.jsx diff --git a/src/pages/Gallary/DateRangePickerNoDefault.jsx b/src/pages/Gallary/DateRangePickerNoDefault.jsx new file mode 100644 index 00000000..6c793b86 --- /dev/null +++ b/src/pages/Gallary/DateRangePickerNoDefault.jsx @@ -0,0 +1,55 @@ +import React, { useEffect, useRef } from "react"; + +const DateRangePickerNoDefault = ({ + onRangeChange, + startDate, + endDate, +}) => { + const inputRef = useRef(null); + + useEffect(() => { + const fp = flatpickr(inputRef.current, { + mode: "range", + dateFormat: "Y-m-d", + altInput: true, + altFormat: "d-m-Y", + static: true, + clickOpens: true, + onChange: (selectedDates, dateStr) => { + if (selectedDates.length === 2) { + const [startDateString, endDateString] = dateStr.split(" to "); + onRangeChange?.({ startDate: startDateString, endDate: endDateString }); + } else if (selectedDates.length === 0) { + onRangeChange?.({ startDate: "", endDate: "" }); + } + }, + onReady: (selectedDates, dateStr, instance) => { + if (startDate && endDate) { + instance.setDate([startDate, endDate], true); + } + }, + }); + + if (startDate && endDate) { + fp.setDate([startDate, endDate], false); + } else if (!startDate && !endDate) { + fp.clear(); + } + + return () => { + fp.destroy(); + }; + }, [onRangeChange, startDate, endDate]); + + return ( + + ); +}; + +export default DateRangePickerNoDefault; diff --git a/src/pages/Gallary/ImageGallary.jsx b/src/pages/Gallary/ImageGallary.jsx index 8716e398..96236cba 100644 --- a/src/pages/Gallary/ImageGallary.jsx +++ b/src/pages/Gallary/ImageGallary.jsx @@ -8,8 +8,8 @@ import Avatar from "../../components/common/Avatar"; import DateRangePicker from "../../components/common/DateRangePicker"; import eventBus from "../../services/eventBus"; import Breadcrumb from "../../components/common/Breadcrumb"; -import { formatUTCToLocalTime } from "../../utils/dateUtils"; -import useImageGallery from "../../hooks/useImageGallery"; +import {formatUTCToLocalTime} from "../../utils/dateUtils"; +import DateRangePickerNoDefault from "./DateRangePickerNoDefault"; const SCROLL_THRESHOLD = 5; @@ -319,7 +319,7 @@ const ImageGallery = () => {
{type === "dateRange" ? (
- Date: Wed, 9 Jul 2025 13:09:44 +0530 Subject: [PATCH 090/120] In the Image Gallery, the filter drawer's date picker should default to the last 7 days (today minus 7 days). --- .../Gallary/DateRangePickerNoDefault.jsx | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/pages/Gallary/DateRangePickerNoDefault.jsx b/src/pages/Gallary/DateRangePickerNoDefault.jsx index 6c793b86..dd364daf 100644 --- a/src/pages/Gallary/DateRangePickerNoDefault.jsx +++ b/src/pages/Gallary/DateRangePickerNoDefault.jsx @@ -8,6 +8,15 @@ const DateRangePickerNoDefault = ({ const inputRef = useRef(null); useEffect(() => { + const today = new Date(); + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(today.getDate() - 7); + + const formatDate = (date) => date.toISOString().split('T')[0]; + + const defaultStartDate = formatDate(sevenDaysAgo); + const defaultEndDate = formatDate(today); + const fp = flatpickr(inputRef.current, { mode: "range", dateFormat: "Y-m-d", @@ -24,7 +33,10 @@ const DateRangePickerNoDefault = ({ } }, onReady: (selectedDates, dateStr, instance) => { - if (startDate && endDate) { + if (!startDate && !endDate) { + instance.setDate([defaultStartDate, defaultEndDate], true); + onRangeChange?.({ startDate: defaultStartDate, endDate: defaultEndDate }); + } else if (startDate && endDate) { instance.setDate([startDate, endDate], true); } }, @@ -32,8 +44,12 @@ const DateRangePickerNoDefault = ({ if (startDate && endDate) { fp.setDate([startDate, endDate], false); - } else if (!startDate && !endDate) { + } else if (!startDate && !endDate && fp.selectedDates.length > 0) { fp.clear(); + onRangeChange?.({ startDate: defaultStartDate, endDate: defaultEndDate }); + } else if (!startDate && !endDate && fp.selectedDates.length === 0) { + fp.setDate([defaultStartDate, defaultEndDate], false); + onRangeChange?.({ startDate: defaultStartDate, endDate: defaultEndDate }); } return () => { @@ -52,4 +68,4 @@ const DateRangePickerNoDefault = ({ ); }; -export default DateRangePickerNoDefault; +export default DateRangePickerNoDefault; \ No newline at end of file From 94358c7aa277861bb18318d636a4d41072dccb56 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Fri, 11 Jul 2025 11:40:09 +0530 Subject: [PATCH 091/120] Changes in Date picker . --- .../Gallary/DateRangePickerNoDefault.jsx | 71 ------------------- src/pages/Gallary/ImageGallary.jsx | 55 ++++++++++---- 2 files changed, 40 insertions(+), 86 deletions(-) delete mode 100644 src/pages/Gallary/DateRangePickerNoDefault.jsx diff --git a/src/pages/Gallary/DateRangePickerNoDefault.jsx b/src/pages/Gallary/DateRangePickerNoDefault.jsx deleted file mode 100644 index dd364daf..00000000 --- a/src/pages/Gallary/DateRangePickerNoDefault.jsx +++ /dev/null @@ -1,71 +0,0 @@ -import React, { useEffect, useRef } from "react"; - -const DateRangePickerNoDefault = ({ - onRangeChange, - startDate, - endDate, -}) => { - const inputRef = useRef(null); - - useEffect(() => { - const today = new Date(); - const sevenDaysAgo = new Date(); - sevenDaysAgo.setDate(today.getDate() - 7); - - const formatDate = (date) => date.toISOString().split('T')[0]; - - const defaultStartDate = formatDate(sevenDaysAgo); - const defaultEndDate = formatDate(today); - - const fp = flatpickr(inputRef.current, { - mode: "range", - dateFormat: "Y-m-d", - altInput: true, - altFormat: "d-m-Y", - static: true, - clickOpens: true, - onChange: (selectedDates, dateStr) => { - if (selectedDates.length === 2) { - const [startDateString, endDateString] = dateStr.split(" to "); - onRangeChange?.({ startDate: startDateString, endDate: endDateString }); - } else if (selectedDates.length === 0) { - onRangeChange?.({ startDate: "", endDate: "" }); - } - }, - onReady: (selectedDates, dateStr, instance) => { - if (!startDate && !endDate) { - instance.setDate([defaultStartDate, defaultEndDate], true); - onRangeChange?.({ startDate: defaultStartDate, endDate: defaultEndDate }); - } else if (startDate && endDate) { - instance.setDate([startDate, endDate], true); - } - }, - }); - - if (startDate && endDate) { - fp.setDate([startDate, endDate], false); - } else if (!startDate && !endDate && fp.selectedDates.length > 0) { - fp.clear(); - onRangeChange?.({ startDate: defaultStartDate, endDate: defaultEndDate }); - } else if (!startDate && !endDate && fp.selectedDates.length === 0) { - fp.setDate([defaultStartDate, defaultEndDate], false); - onRangeChange?.({ startDate: defaultStartDate, endDate: defaultEndDate }); - } - - return () => { - fp.destroy(); - }; - }, [onRangeChange, startDate, endDate]); - - return ( - - ); -}; - -export default DateRangePickerNoDefault; \ No newline at end of file diff --git a/src/pages/Gallary/ImageGallary.jsx b/src/pages/Gallary/ImageGallary.jsx index 96236cba..aeac34bd 100644 --- a/src/pages/Gallary/ImageGallary.jsx +++ b/src/pages/Gallary/ImageGallary.jsx @@ -5,11 +5,10 @@ import { useSelector } from "react-redux"; import { useModal } from "./ModalContext"; import ImagePop from "./ImagePop"; import Avatar from "../../components/common/Avatar"; -import DateRangePicker from "../../components/common/DateRangePicker"; import eventBus from "../../services/eventBus"; import Breadcrumb from "../../components/common/Breadcrumb"; -import {formatUTCToLocalTime} from "../../utils/dateUtils"; -import DateRangePickerNoDefault from "./DateRangePickerNoDefault"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import DateRangePicker from "../../components/common/DateRangePicker"; const SCROLL_THRESHOLD = 5; @@ -153,8 +152,15 @@ const ImageGallery = () => { const getUniqueValuesWithIds = useCallback((idKey, nameKey) => { const map = new Map(); allImagesData.forEach(batch => { - let id = idKey === "floorIds" ? batch.floorIds : batch[idKey]; + let id; + if (idKey === "floorIds") { + id = batch.floorIds; + } else { + id = batch[idKey]; + } + const name = batch[nameKey]; + if (id && name && !map.has(id)) { map.set(id, name); } @@ -175,7 +181,6 @@ const ImageGallery = () => { }); }); return Array.from(uniqueUsersMap.entries()).sort((a, b) => a[1].localeCompare(b[1])); - return Array.from(uniqueUsersMap.entries()).sort((a, b) => a[1].localeCompare(b[1])); }, [allImagesData]); const buildings = getUniqueValuesWithIds("buildingId", "buildingName"); @@ -240,7 +245,9 @@ const ImageGallery = () => { } return false; } - if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) return false; + if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) { + return false; + } return oldVal !== newVal; }); @@ -319,10 +326,9 @@ const ImageGallery = () => {
{type === "dateRange" ? (
- @@ -346,7 +352,12 @@ const ImageGallery = () => { return (
- +
@@ -433,8 +452,12 @@ const ImageGallery = () => {
- - + +
{renderFilterCategory("Date Range", [], "dateRange")} @@ -444,6 +467,8 @@ const ImageGallery = () => { {renderFilterCategory("Activity", activities, "activity")} {renderFilterCategory("Uploaded By (User)", uploadedByUsers, "uploadedBy")} {renderFilterCategory("Work Category", workCategories, "workCategory")} + +
From 85d10e3b1782f88eb3130f221c6602dd9ebf410d Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Fri, 11 Jul 2025 13:13:44 +0530 Subject: [PATCH 092/120] Creating a custom hook for ImageGallery to call an API. --- src/pages/Gallary/ImageGallary.jsx | 49 +++++++----------------------- 1 file changed, 11 insertions(+), 38 deletions(-) diff --git a/src/pages/Gallary/ImageGallary.jsx b/src/pages/Gallary/ImageGallary.jsx index aeac34bd..eb7bd8ad 100644 --- a/src/pages/Gallary/ImageGallary.jsx +++ b/src/pages/Gallary/ImageGallary.jsx @@ -5,10 +5,11 @@ import { useSelector } from "react-redux"; import { useModal } from "./ModalContext"; import ImagePop from "./ImagePop"; import Avatar from "../../components/common/Avatar"; +import DateRangePicker from "../../components/common/DateRangePicker"; import eventBus from "../../services/eventBus"; import Breadcrumb from "../../components/common/Breadcrumb"; import { formatUTCToLocalTime } from "../../utils/dateUtils"; -import DateRangePicker from "../../components/common/DateRangePicker"; +import useImageGallery from "../../hooks/useImageGallery"; const SCROLL_THRESHOLD = 5; @@ -152,15 +153,8 @@ const ImageGallery = () => { const getUniqueValuesWithIds = useCallback((idKey, nameKey) => { const map = new Map(); allImagesData.forEach(batch => { - let id; - if (idKey === "floorIds") { - id = batch.floorIds; - } else { - id = batch[idKey]; - } - + let id = idKey === "floorIds" ? batch.floorIds : batch[idKey]; const name = batch[nameKey]; - if (id && name && !map.has(id)) { map.set(id, name); } @@ -245,9 +239,7 @@ const ImageGallery = () => { } return false; } - if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) { - return false; - } + if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) return false; return oldVal !== newVal; }); @@ -352,12 +344,7 @@ const ImageGallery = () => { return (
- +
@@ -452,12 +431,8 @@ const ImageGallery = () => {
- - + +
{renderFilterCategory("Date Range", [], "dateRange")} @@ -467,8 +442,6 @@ const ImageGallery = () => { {renderFilterCategory("Activity", activities, "activity")} {renderFilterCategory("Uploaded By (User)", uploadedByUsers, "uploadedBy")} {renderFilterCategory("Work Category", workCategories, "workCategory")} - -
From 50585e1fbdbfcfe7df05e3bc06edb494719fe9de Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 14:10:25 +0530 Subject: [PATCH 093/120] rebase from main --- src/pages/Directory/Directory.jsx | 118 +++++++++++------------------- 1 file changed, 42 insertions(+), 76 deletions(-) diff --git a/src/pages/Directory/Directory.jsx b/src/pages/Directory/Directory.jsx index c80ec0f2..ca95181f 100644 --- a/src/pages/Directory/Directory.jsx +++ b/src/pages/Directory/Directory.jsx @@ -353,7 +353,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { setOpenBucketModal={setOpenBucketModal} contactsToExport={contacts} notesToExport={notes} - selectedNoteNames={selectedNoteNames} + selectedNoteNames={selectedNoteNames} setSelectedNoteNames={setSelectedNoteNames} notesForFilter={notes} setFilterAppliedNotes={setFilterAppliedNotes} @@ -361,35 +361,25 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
- {/* LIST VIEW */} + {(viewType === "card" || viewType === "list" || viewType === "notes") && ( +
+ {!loading && (viewType === "card" || viewType === "list") && contacts?.length === 0 && ( +

No contact found

+ )} + {!loading && + (viewType === "card" || viewType === "list") && + contacts?.length > 0 && + currentItems.length === 0 && ( +

No matching contact found

+ )} +
+ )} + {viewType === "list" && ( -
-
+
+
- {!loading && contacts?.length === 0 ? ( - - -
-

No contact found

-
- - - ) : !loading && contacts?.length > 0 && currentItems.length === 0 ? ( - - -
-

No matching contact found

-
- - - ) : ( - !loading && + {!loading && currentItems.map((contact) => ( { IsDeleted={setDeleteContact} restore={handleDeleteContact} /> - )) - )} + ))}
)} - {/* CARD VIEW */} {viewType === "card" && ( - <> - {contacts?.length === 0 && !loading ? ( -
-

No contact found

-
- ) : currentItems.length === 0 && !loading ? ( -
-

No matching contact found

-
- ) : ( -
- {currentItems.map((contact) => ( -
- -
- ))} -
- )} - +
+ {!loading && + currentItems.map((contact) => ( +
+ +
+ ))} +
)} - {/* NOTES VIEW */} {viewType === "notes" && (
{ setNotesForFilter={setNotes} searchText={searchText} setIsOpenModalNote={setIsOpenModalNote} - filterAppliedNotes={filterAppliedNotes} + filterAppliedNotes={filterAppliedNotes} />
)} - {/* PAGINATION */} + {/* Pagination */} {!loading && viewType !== "notes" && contacts?.length > 0 && @@ -494,10 +464,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { ))} -
  • +
  • -
    ); }; From 39f0908fc052424b5a065bf7aa6d94f004abea21 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 14:13:58 +0530 Subject: [PATCH 094/120] fixed error during rebase --- src/pages/project/ProjectDetails.jsx | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index 732255b8..f34586aa 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -81,22 +81,9 @@ const ProjectDetails = () => { switch (activePill) { case "profile": { return ( - <> -
    -
    - {/* About User */} - - - {/* About User */} -
    -
    - {/* Profile Overview */} - - {/* Profile Overview */} -
    +
    +
    +
    @@ -136,8 +123,8 @@ const ProjectDetails = () => { } case "directory": { return ( -
    - +
    +
    ); } @@ -177,7 +164,7 @@ const ProjectDetails = () => { ]} > -
    +
    {projectLoading &&

    Loading....

    } {/* {!projectLoading && project && ( From 0ccd00d3f703f175ff500bbadb8fde7c52c15270 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 14:30:49 +0530 Subject: [PATCH 095/120] error fixed during occured rebase --- src/components/Activities/InfraPlanning.jsx | 46 +++++---------------- 1 file changed, 11 insertions(+), 35 deletions(-) diff --git a/src/components/Activities/InfraPlanning.jsx b/src/components/Activities/InfraPlanning.jsx index a0ee3ac3..da0a9a33 100644 --- a/src/components/Activities/InfraPlanning.jsx +++ b/src/components/Activities/InfraPlanning.jsx @@ -40,48 +40,24 @@ const InfraPlanning = () => },[reloadedData]) - // Show only "No Result Found" message if no data is present - if (!project_deatilsLoader && projects_Details?.buildings?.length === 0) { - return
    No Result Found
    ; - } - - // Optionally show loading indicator - if (project_deatilsLoader) { - return
    Loading...
    ; - } - return (
    -
    - {/*
    -
    - -
    - -
    */} + {ManageInfra ? ( +
    - {project_deatilsLoader && (

    Loading...

    )} - {( !project_deatilsLoader && projects_Details?.buildings.length === 0 ) && (

    No Result Found

    )} - - - - {(!project_deatilsLoader && projects_Details?.buildings?.length > 0) && ()} + {isLoading && (

    Loading...

    )} + {( !isLoading && projectInfra?.length === 0 ) && (

    No Result Found

    )} + {(!isLoading && projectInfra?.length > 0) && ()}
    + ) : ( +
    + +

    Access Denied: You don't have permission to perform this action. !

    +
    + )}
    From 11dc196ae8b6d81144be5af23024ce6e2798262c Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Mon, 14 Jul 2025 16:16:13 +0530 Subject: [PATCH 096/120] Merge branch 'main' of https://git.marcoaiot.com/admin/marco.pms.web into Kartik_Task#734 From 918300c64ced5d592d8198934a0ace93617b2ee7 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 16:59:31 +0530 Subject: [PATCH 097/120] fixed css bug --- public/assets/vendor/css/core.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index e73f6706..5001fa1a 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -836,7 +836,7 @@ progress { } .row { - --bs-gutter-x: 3.625rem; + --bs-gutter-x: 0.500rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; From 1718638a4b6d64a81a3006e4729230935640f6ee Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 18:17:07 +0530 Subject: [PATCH 098/120] fixed card width in core.css --- public/assets/vendor/css/core.css | 2 +- src/pages/Directory/Directory.jsx | 2 +- src/pages/project/ProjectList.jsx | 288 +++++++++++++++--------------- 3 files changed, 145 insertions(+), 147 deletions(-) diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index 5001fa1a..aca2dc05 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -2553,7 +2553,7 @@ progress { } .table-responsive { - /* overflow-x: auto; */ + overflow-x: auto; -webkit-overflow-scrolling: touch; } diff --git a/src/pages/Directory/Directory.jsx b/src/pages/Directory/Directory.jsx index ca95181f..8f72ac81 100644 --- a/src/pages/Directory/Directory.jsx +++ b/src/pages/Directory/Directory.jsx @@ -260,7 +260,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { }, [prefernceContacts]); return ( -
    +
    {IsPage && ( {

    No projects found.

    )} - {listView ? ( -
    -
    -
    - - - - - - - - - - - - - - {currentItems.length === 0 ? ( - - - - ) : ( - currentItems.map((project) => ( - - )) - )} - -
    - Project Name - Contact PersonSTART DATEDEADLINETaskProgress -
    - -
      - {[ - { - id: "b74da4c2-d07e-46f2-9919-e75e49b12731", - label: "Active", - }, - { - id: "603e994b-a27f-4e5d-a251-f3d69b0498ba", - label: "On Hold", - }, - { - id: "ef1c356e-0fe0-42df-a5d3-8daee355492d", - label: "Inactive", - }, - { - id: "33deaef9-9af1-4f2a-b443-681ea0d04f81", - label: "Completed", - }, - ].map(({ id, label }) => ( -
    • -
      - handleStatusChange(id)} - /> - + {listView ? ( +
      +
      +
      + + + + + + + + + + - + + + + {currentItems.length === 0 ? ( + + + + ) : ( + currentItems.map((project) => ( + + )) + )} + +
      + Project Name + Contact PersonSTART DATEDEADLINETaskProgress +
      + +
        + {[ + { + id: "b74da4c2-d07e-46f2-9919-e75e49b12731", + label: "Active", + }, + { + id: "cdad86aa-8a56-4ff4-b633-9c629057dfef", + label:"In Progress" + }, + { + id: "603e994b-a27f-4e5d-a251-f3d69b0498ba", + label: "On Hold", + }, + { + id: "ef1c356e-0fe0-42df-a5d3-8daee355492d", + label: "Inactive", + }, + { + id: "33deaef9-9af1-4f2a-b443-681ea0d04f81", + label: "Completed", + }, + ].map(({ id, label }) => ( +
      • +
        + handleStatusChange(id)} + /> + +
        +
      • + ))} +
      - - ))} - - -
      + + Action +
      + No projects found +
      +
      {" "} +
      {" "} +
      + ) : ( +
      + {currentItems.map((project) => ( + + ))} +
      + )} + + {!loading && totalPages > 1 && ( +
    + setCurrentPage((p) => Math.min(totalPages, p + 1)) + } > -
    -

    No projects found

    -
    -
    -
    -
    -
    -) : ( -
    - {currentItems.map((project) => ( - - ))} -
    -)} - -{!loading && totalPages > 1 && ( - -)} - + » + + + + + )}
    ); From 788f0baee42f6bff85f4d6f9eae7c4c6d21d3c17 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 20:18:36 +0530 Subject: [PATCH 099/120] dynamically update planned and completed work in ProjectInfra and project list card on activity create/edit/delete --- .../Infrastructure/EditActivityModal.jsx | 12 +- .../Project/Infrastructure/TaskModel.jsx | 13 +- .../Project/Infrastructure/WorkItem.jsx | 77 +-- src/hooks/useProjects.js | 478 ++++++++++++------ 4 files changed, 376 insertions(+), 204 deletions(-) diff --git a/src/components/Project/Infrastructure/EditActivityModal.jsx b/src/components/Project/Infrastructure/EditActivityModal.jsx index 06a6e41e..14b64b53 100644 --- a/src/components/Project/Infrastructure/EditActivityModal.jsx +++ b/src/components/Project/Infrastructure/EditActivityModal.jsx @@ -116,7 +116,17 @@ useEffect(() => { floorId: floor?.id, workAreaId: workArea?.id, }; - UpdateTask([payload]) + let plannedTask = + workItem?.workItem?.plannedWork || workItem?.plannedWork || 0; + let completedTask = workItem?.workItem?.completedWork || workItem?.completedWork || 0 + UpdateTask({ + payload: [payload], + PreviousPlannedWork: plannedTask, + buildingId: building?.id, + floorId: floor?.id, + workAreaId: workArea?.id, + previousCompletedWork:completedTask + }); } return (
    diff --git a/src/components/Project/Infrastructure/TaskModel.jsx b/src/components/Project/Infrastructure/TaskModel.jsx index 12cf8c87..06261603 100644 --- a/src/components/Project/Infrastructure/TaskModel.jsx +++ b/src/components/Project/Infrastructure/TaskModel.jsx @@ -69,10 +69,9 @@ const TaskModel = ({ project, onSubmit, onClose }) => { const selectedCategory = categoryData?.find((c) => c.id === watchCategoryId); const { mutate: CreateTask, isPending } = useManageTask({ - onSuccessCallback: ( response ) => - { - showToast( response?.message, "success" ) - onClose?.() + onSuccessCallback: (response) => { + showToast(response?.message, "success"); + onClose?.(); }, }); useEffect(() => { @@ -96,8 +95,10 @@ const TaskModel = ({ project, onSubmit, onClose }) => { }, [categories]); const onSubmitForm = async (data) => { - const payload = [data]; - CreateTask(payload); + const payload = [data]; + CreateTask({payload:payload,buildingId: data.buildingID, + floorId: data.floorId, + workAreaId: data.workAreaId, PreviousPlannedWork:0,previousCompletedWork:0}); }; return ( diff --git a/src/components/Project/Infrastructure/WorkItem.jsx b/src/components/Project/Infrastructure/WorkItem.jsx index 1116991c..df1f2bc1 100644 --- a/src/components/Project/Infrastructure/WorkItem.jsx +++ b/src/components/Project/Infrastructure/WorkItem.jsx @@ -10,7 +10,10 @@ import { } from "../../../utils/constants"; import ConfirmModal from "../../common/ConfirmModal"; import ProjectRepository from "../../../repositories/ProjectRepository"; -import { useDeleteProjectTask, useProjectDetails } from "../../../hooks/useProjects"; +import { + useDeleteProjectTask, + useProjectDetails, +} from "../../../hooks/useProjects"; import showToast from "../../../services/toastService"; import { cacheData, @@ -19,7 +22,7 @@ import { } from "../../../slices/apiDataManager"; import { refreshData } from "../../../slices/localVariablesSlice"; import GlobalModel from "../../common/GlobalModel"; -import {useDeleteMasterItem} from "../../../hooks/masterHook/useMaster"; +import { useDeleteMasterItem } from "../../../hooks/masterHook/useMaster"; const WorkItem = ({ workItem, @@ -40,19 +43,18 @@ const WorkItem = ({ const [loadingDelete, setLoadingDelete] = useState(false); const project = getCachedData("projectInfo"); - const openModal = () => setIsModalOpen(true); - const closeModal = () => setIsModalOpen( false ); - - const showModalDelete = () => setShowModal2(true); + const closeModal = () => setIsModalOpen(false); + + const showModalDelete = () => setShowModal2(true); const closeModalDelete = () => setShowModal2(false); const getProgress = (planned, completed) => { return (completed * 100) / planned + "%"; }; - - const {mutate:DeleteTask,isPending } = useDeleteProjectTask(() => { - closeModalDelete?.(); - }); + + const { mutate: DeleteTask, isPending } = useDeleteProjectTask(() => { + closeModalDelete?.(); + }); const handleAssignTask = () => { setItemName(""); @@ -61,15 +63,15 @@ const WorkItem = ({ setNewWorkItem(workItem); }, [workItem]); - const refreshWorkItem = (plannedTask) =>{ + const refreshWorkItem = (plannedTask) => { if (workItem) { - const updated = { - ...workItem, - todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask, - }; - setNewWorkItem(updated); - } - } + const updated = { + ...workItem, + todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask, + }; + setNewWorkItem(updated); + } + }; let assigndata = { building: forBuilding, floor: forFloor, @@ -85,15 +87,17 @@ const WorkItem = ({ tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); }, []); - - - - - const handleSubmit = async () => { let WorkItemId = workItem.workItemId || workItem.id; - DeleteTask({workItemId:WorkItemId,workAreaId:forWorkArea?.id}) - + debugger + DeleteTask({ + workItemId: WorkItemId, + workAreaId: forWorkArea?.id, + completedTask: workItem?.completedWork, + plannedTask: workItem?.plannedWork, + buildingId: forBuilding?.id, + floorId: forFloor?.id, + }); }; const PlannedWork = @@ -104,18 +108,26 @@ const WorkItem = ({ <> {isModalOpen && ( - + )} {showModal && ( - setShowModal(false)}> + setShowModal(false)} + > setShowModal(false)} + onClose={() => setShowModal(false)} /> )} @@ -167,7 +179,6 @@ const WorkItem = ({ : "NA"} - {hasWorkItem @@ -195,9 +206,7 @@ const WorkItem = ({ {hasWorkItem ? `${ - NewWorkItem?.todaysAssigned ?? - workItem?.todaysAssigned ?? - "0" + NewWorkItem?.todaysAssigned ?? workItem?.todaysAssigned ?? "0" }` : "NA"} @@ -251,7 +260,7 @@ const WorkItem = ({ setShowModal(true)} + onClick={() => setShowModal(true)} role="button" > setShowModal(true) } + onClick={() => setShowModal(true)} > Edit diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index 2cc5ee1d..daf705fa 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -6,7 +6,12 @@ import { useDispatch, useSelector } from "react-redux"; import { setProjectId } from "../slices/localVariablesSlice"; import EmployeeList from "../components/Directory/EmployeeList"; import eventBus from "../services/eventBus"; -import {Mutation, useMutation, useQuery, useQueryClient} from "@tanstack/react-query"; +import { + Mutation, + useMutation, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import showToast from "../services/toastService"; // export const useProjects = () => { @@ -211,7 +216,7 @@ export const useProjects = () => { error, refetch, } = useQuery({ - queryKey: ['ProjectsList'], + queryKey: ["ProjectsList"], queryFn: async () => { const response = await ProjectRepository.getProjectList(); return response.data; @@ -227,97 +232,112 @@ export const useProjects = () => { }; }; -export const useEmployeesByProjectAllocated = (selectedProject) => -{ - const {data = [], isLoading, refetch, error} = useQuery( { - queryKey: ["empListByProjectAllocated", selectedProject ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectAllocation( selectedProject ); - return res.data || res +export const useEmployeesByProjectAllocated = (selectedProject) => { + const { + data = [], + isLoading, + refetch, + error, + } = useQuery({ + queryKey: ["empListByProjectAllocated", selectedProject], + queryFn: async () => { + const res = await ProjectRepository.getProjectAllocation(selectedProject); + return res.data || res; }, enabled: !!selectedProject, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Allocated Employees", "error"); - } - } ) - + onError: (error) => { + showToast( + error.message || "Error while Fetching project Allocated Employees", + "error" + ); + }, + }); + return { projectEmployees: data, - loading:isLoading, + loading: isLoading, error, - refetch - } -} + refetch, + }; +}; -export const useProjectDetails = ( projectId,isAuto = true ) => -{ - const {data: projects_Details, isLoading, error, refetch} = useQuery( { - queryKey: [ "projectInfo", projectId ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectByprojectId( projectId ); +export const useProjectDetails = (projectId, isAuto = true) => { + const { + data: projects_Details, + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["projectInfo", projectId], + queryFn: async () => { + const res = await ProjectRepository.getProjectByprojectId(projectId); return res.data || res; }, enabled: !!projectId && isAuto, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Details", "error"); - } - } ) - return { projects_Details, loading:isLoading, error, refetch }; -} + onError: (error) => { + showToast( + error.message || "Error while Fetching project Details", + "error" + ); + }, + }); + return { projects_Details, loading: isLoading, error, refetch }; +}; -export const useProjectsByEmployee = (employeeId) => -{ - const { data:projectNameList =[],isLoading,error,refetch} = useQuery( { - queryKey: [ "ProjectsByEmployee", employeeId ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectsByEmployee( employeeId ); +export const useProjectsByEmployee = (employeeId) => { + const { + data: projectNameList = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["ProjectsByEmployee", employeeId], + queryFn: async () => { + const res = await ProjectRepository.getProjectsByEmployee(employeeId); return res.data || res; }, enabled: !!employeeId, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Employee", "error"); - } - }) - return {projectList, loading:isLoading,error,refetch } -} + onError: (error) => { + showToast( + error.message || "Error while Fetching project Employee", + "error" + ); + }, + }); + return { projectList, loading: isLoading, error, refetch }; +}; -export const useProjectName = () => -{ - const {data = [],isLoading,error,refetch} = useQuery( { - queryKey: [ "basicProjectNameList" ], - queryFn: async () => - { +export const useProjectName = () => { + const { + data = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["basicProjectNameList"], + queryFn: async () => { const res = await ProjectRepository.projectNameList(); return res.data || res; }, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Name", "error"); - } - } ) - return {projectNames:data,loading:isLoading,Error:error,refetch} -} + onError: (error) => { + showToast(error.message || "Error while Fetching project Name", "error"); + }, + }); + return { projectNames: data, loading: isLoading, Error: error, refetch }; +}; - - export const useProjectInfra = (projectId) => { const { data: projectInfra, isLoading, error, } = useQuery({ - queryKey: ["ProjectInfra", projectId], + queryKey: ["ProjectInfra", projectId], queryFn: async () => { const res = await ProjectRepository.getProjectInfraByproject(projectId); return res.data; }, - enabled: !!projectId , + enabled: !!projectId, onError: (error) => { showToast(error.message || "Error while fetching project infra", "error"); }, @@ -326,30 +346,27 @@ export const useProjectInfra = (projectId) => { return { projectInfra, isLoading, error }; }; - -export const useProjectTasks = (workAreaId,IsExpandedArea=false) => -{ - const { data:ProjectTaskList,isLoading,error } = useQuery( { - queryKey: [ "WorkItems",workAreaId ], - queryFn: async () => - { +export const useProjectTasks = (workAreaId, IsExpandedArea = false) => { + const { + data: ProjectTaskList, + isLoading, + error, + } = useQuery({ + queryKey: ["WorkItems", workAreaId], + queryFn: async () => { const res = await ProjectRepository.getProjectTasksByWorkArea(workAreaId); return res.data; }, enabled: !!workAreaId && !!IsExpandedArea, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Tasks", "error"); - } - } ) - return {ProjectTaskList,isLoading,error} -} - + onError: (error) => { + showToast(error.message || "Error while Fetching project Tasks", "error"); + }, + }); + return { ProjectTaskList, isLoading, error }; +}; // -- -------------Mutation------------------------------- - - export const useCreateProject = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); @@ -360,8 +377,8 @@ export const useCreateProject = ({ onSuccessCallback }) => { }, onSuccess: (data) => { // Invalidate the cache - queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} ); - queryClient.invalidateQueries({queryKey:['basicProjectNameList']}); + queryClient.invalidateQueries({ queryKey: ["ProjectsList"] }); + queryClient.invalidateQueries({ queryKey: ["basicProjectNameList"] }); // Emit event for consumers (like useProjects or others) eventBus.emit("project", { @@ -381,27 +398,19 @@ export const useCreateProject = ({ onSuccessCallback }) => { }); }; - export const useUpdateProject = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - } = useMutation({ - mutationFn: async ( {projectId, updatedData} ) => - { + const { mutate, isPending, isSuccess, isError } = useMutation({ + mutationFn: async ({ projectId, updatedData }) => { return await ProjectRepository.updateProject(projectId, updatedData); }, - onSuccess: ( data, variables ) => - { + onSuccess: (data, variables) => { const { projectId } = variables; - - queryClient.invalidateQueries({queryKey:["ProjectsList"]}); - queryClient.invalidateQueries( {queryKey: [ "projectInfo", projectId ]} ); - queryClient.invalidateQueries({queryKey:['basicProjectNameList']}); + + queryClient.invalidateQueries({ queryKey: ["ProjectsList"] }); + queryClient.invalidateQueries({ queryKey: ["projectInfo", projectId] }); + queryClient.invalidateQueries({ queryKey: ["basicProjectNameList"] }); eventBus.emit("project", { keyword: "Update_Project", @@ -415,9 +424,8 @@ export const useUpdateProject = ({ onSuccessCallback }) => { } }, - onError: ( error ) => - { - console.log(error) + onError: (error) => { + console.log(error); showToast(error?.message || "Error while updating project", "error"); }, }); @@ -430,20 +438,16 @@ export const useUpdateProject = ({ onSuccessCallback }) => { }; }; - -export const useManageProjectInfra = ( {onSuccessCallback} ) => -{ +export const useManageProjectInfra = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {infraObject, projectId} ) => - { + mutationFn: async ({ infraObject, projectId }) => { return await ProjectRepository.manageProjectInfra(infraObject); }, - onSuccess: ( data, variables ) => - { - const { projectId } = variables; - queryClient.invalidateQueries({queryKey:["ProjectInfra", projectId]}); - if (onSuccessCallback) onSuccessCallback(data,variables); + onSuccess: (data, variables) => { + const { projectId } = variables; + queryClient.invalidateQueries({ queryKey: ["ProjectInfra", projectId] }); + if (onSuccessCallback) onSuccessCallback(data, variables); }, onError: (error) => { showToast(error.message || "Failed to update Project Infra", "error"); @@ -451,39 +455,36 @@ export const useManageProjectInfra = ( {onSuccessCallback} ) => }); }; - export const useManageProjectAllocation = ({ onSuccessCallback, onErrorCallback, }) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - } = useMutation({ - mutationFn: async ( {items} ) => - { + const { mutate, isPending, isSuccess, isError } = useMutation({ + mutationFn: async ({ items }) => { const response = await ProjectRepository.manageProjectAllocation(items); return response.data; }, onSuccess: (data, variables, context) => { - queryClient.invalidateQueries({queryKey:['empListByProjectAllocated']}); - queryClient.removeQueries({queryKey:["projectEmployees"]}) + queryClient.invalidateQueries({ + queryKey: ["empListByProjectAllocated"], + }); + queryClient.removeQueries({ queryKey: ["projectEmployees"] }); if (variables?.added) { - showToast('Employee Assigned Successfully', 'success'); + showToast("Employee Assigned Successfully", "success"); } else { - showToast('Removed Employee Successfully', 'success'); + showToast("Removed Employee Successfully", "success"); } if (onSuccessCallback) onSuccessCallback(data, context); }, onError: (error) => { const message = - error?.response?.data?.message || error.message || 'Error occurred during API call'; - showToast(message, 'error'); + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(message, "error"); if (onErrorCallback) onErrorCallback(error); }, }); @@ -496,48 +497,199 @@ export const useManageProjectAllocation = ({ }; }; -export const useManageTask = ({onSuccessCallback}) => -{ +export const useManageTask = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + return useMutation({ + mutationFn: async ({ + payload, + PreviousPlannedWork, + previousCompletedWork, + buildingId, + floorId, + workAreaId, + }) => await ProjectRepository.manageProjectTasks(payload), + onSuccess: (data, variables) => { + const { + workAreaId, + buildingId, + floorId, + PreviousPlannedWork, + previousCompletedWork, + } = variables; + queryClient.invalidateQueries({ queryKey: ["WorkItems"] }); + const getPlannedDelta = (previous, current) => current - previous; + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + const { workAreaId, floorId, buildingId } = variables; + const updatedData = JSON.parse(JSON.stringify(oldData)); + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; - return useMutation( { - mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ), - onSuccess: ( data, variables ) => - { - queryClient.invalidateQueries({ queryKey: ["WorkItems"] }) + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + + const previousPlanned = PreviousPlannedWork ?? 0; + const currentPlanned = + data?.data[0]?.workItem.plannedWork ?? 0; + + const plannedWorkDelta = getPlannedDelta( + previousPlanned, + currentPlanned + ); + const updatedPlannedWork = + (area.plannedWork ?? 0) + plannedWorkDelta; + const previousCompleted = previousCompletedWork; + + const currentCompleted = + data?.data[0]?.workItem.completedWork ?? 0; + const completedWorkDelta = getPlannedDelta( + previousCompleted, + currentCompleted + ); + const updatedCompletedWork = + (area.completedWork ?? 0) + completedWorkDelta; + return { + ...area, + plannedWork: updatedPlannedWork, + completedWork: updatedCompletedWork, + }; + }), + }; + }), + }; + }); + }); + + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + + return projects.map((project) => { + if (project.id !== selectedProject) return project; + + const previousPlanned = PreviousPlannedWork ?? 0; + const currentPlanned = data?.data[0]?.workItem.plannedWork ?? 0; + + const plannedWorkDelta = getPlannedDelta( + previousPlanned, + currentPlanned + ); + const updatedPlannedWork = + (project.plannedWork ?? 0) + plannedWorkDelta; + const previousCompleted = previousCompletedWork; + const currentCompleted = data?.data[0]?.workItem.completedWork ?? 0; + const completedWorkDelta = getPlannedDelta( + previousCompleted, + currentCompleted + ); + const updatedCompletedWork = + (project.completedWork ?? 0) + completedWorkDelta; + return { + ...project, + plannedWork: updatedPlannedWork, + completedWork: updatedCompletedWork, + }; + }); + }); if (onSuccessCallback) onSuccessCallback(data); }, - onError: (error) => - { - const message = - error?.response?.data?.message || error.message || 'Error occurred during API call'; - showToast(message, 'error'); - } - - }) -} - -export const useDeleteProjectTask = (onSuccessCallback) => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async ( {workItemId, workAreaId} ) => - { - return await ProjectRepository.deleteProjectTask(workItemId); - }, - onSuccess: ( _, variables ) => - { - showToast("Task deleted successfully", "success"); - queryClient.invalidateQueries({queryKey:[ "WorkItems",variables.workAreaId]}); - if (onSuccessCallback) onSuccessCallback(); - }, onError: (error) => { - showToast( - error?.response?.data?.message || error.message || "Failed to delete task", - "error" - ); - if (onSuccessCallback) onSuccessCallback(); + const message = + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(message, "error"); }, }); }; +export const useDeleteProjectTask = (onSuccessCallback) => { + const queryClient = useQueryClient(); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + return useMutation({ + mutationFn: async ({ + workItemId, + workAreaId, + completedTask, + plannedTask, + }) => { + return await ProjectRepository.deleteProjectTask(workItemId); + }, + onSuccess: (_, variables) => { + const { completedTask, plannedTask, workAreaId, buildingId, floorId } = + variables; + + queryClient.invalidateQueries({ + queryKey: ["WorkItems", variables.workAreaId], + }); + + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + + const { workAreaId, floorId, buildingId, plannedTask, completedTask } = + variables; + + const updatedData = JSON.parse(JSON.stringify(oldData)); + + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; + + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + + return { + ...area, + plannedWork: (area.plannedWork ?? 0) - plannedTask, + completedWork: (area.completedWork ?? 0) - completedTask, + }; + }), + }; + }), + }; + }); + }); + + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + + return projects.map((project) => { + if (project.id !== selectedProject) return project; + + return { + ...project, + plannedWork: (project.plannedWork ?? 0) - plannedTask, + completedWork: (project.completedWork ?? 0) - completedTask, + }; + }); + }); + showToast("Task deleted successfully", "success"); + if (onSuccessCallback) onSuccessCallback(); + }, + onError: (error) => { + showToast( + error?.response?.data?.message || + error.message || + "Failed to delete task", + "error" + ); + if (onSuccessCallback) onSuccessCallback(); + }, + }); +}; From d2b80b4f02f02fcac7d4493a6f80176e8ba6ef6f Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 20:19:25 +0530 Subject: [PATCH 100/120] dynamically update planned and completed work in ProjectInfra and project list card on Task reported --- src/components/Activities/ReportTask.jsx | 7 +- src/hooks/useTasks.js | 131 ++++++++++++++--------- 2 files changed, 86 insertions(+), 52 deletions(-) diff --git a/src/components/Activities/ReportTask.jsx b/src/components/Activities/ReportTask.jsx index 8194a5cc..a3550683 100644 --- a/src/components/Activities/ReportTask.jsx +++ b/src/components/Activities/ReportTask.jsx @@ -62,7 +62,12 @@ export const ReportTask = ({ report, closeModal }) => { checkList: [], }; - reportTask({ reportData, workAreaId: report?.workItem?.workArea?.id }); + reportTask({ + reportData, + workAreaId: report?.workItem?.workArea?.id, + buildingId: report?.workItem?.workArea?.floor?.building.id, + floorId: report?.workItem?.workArea?.floor?.id, + }); }; const handleClose = () => { closeModal(); diff --git a/src/hooks/useTasks.js b/src/hooks/useTasks.js index 1cf531c0..b793ced4 100644 --- a/src/hooks/useTasks.js +++ b/src/hooks/useTasks.js @@ -1,11 +1,10 @@ import { useEffect, useState } from "react"; import { TasksRepository } from "../repositories/TaskRepository"; import { cacheData, getCachedData } from "../slices/apiDataManager"; -import {MasterRespository} from "../repositories/MastersRepository"; +import { MasterRespository } from "../repositories/MastersRepository"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import showToast from "../services/toastService"; -import {useSelector} from "react-redux"; - +import { useSelector } from "react-redux"; // ---------Query--------------------------------- @@ -28,7 +27,6 @@ export const useTaskList = (projectId, dateFrom, toDate) => { return response.data; }, enabled, - }); return { TaskList, loading, error, refetch }; @@ -46,7 +44,7 @@ export const useTaskById = (TaskId) => { const res = await TasksRepository.getTaskById(TaskId); return res.data; }, - enabled: !!TaskId, + enabled: !!TaskId, }); return { Task, loading, error, refetch }; @@ -75,47 +73,80 @@ export const useAuditStatus = () => { const res = await MasterRespository.getAuditStatus(); return res.data; }, - }); return { status, loading, error, refetch }; }; - // -----------------------Mutation------------------------ -const toDate = new Date().toISOString().split('T')[0]; -const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; +const toDate = new Date().toISOString().split("T")[0]; +const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000) + .toISOString() + .split("T")[0]; - - -export const useReportTask = ( {onSuccessCallback, onErrorCallback} = {} ) => -{ +export const useReportTask = ({ onSuccessCallback, onErrorCallback } = {}) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - error, - } = useMutation({ - mutationFn: async ( {reportData,workAreaId} ) => - { - debugger + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + const { mutate, isPending, isSuccess, isError, error } = useMutation({ + mutationFn: async ({ reportData, workAreaId }) => { return await TasksRepository.reportTask(reportData); }, - onSuccess: ( data, variables ) => - { - const {workAreaId} = variables; - queryClient.invalidateQueries( {queryKey: [ "taskList" ]} ); - queryClient.invalidateQueries( {queryKey: [ "WorkItems", workAreaId ]} ); - queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} ); - showToast( "Task Reported Successfully.", "success" ); + onSuccess: (data, variables) => { + const { workAreaId, buildingId, floorId } = variables; + queryClient.invalidateQueries({ queryKey: ["taskList"] }); + queryClient.invalidateQueries({ queryKey: ["WorkItems", workAreaId] }); + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + const { workAreaId, floorId, buildingId } = variables; + const updatedData = JSON.parse(JSON.stringify(oldData)); + + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; + + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + return { + ...area, + completedWork: + (area.completedWork ?? 0) + + (data?.data?.completedTask ?? 0), + }; + }), + }; + }), + }; + }); + }); + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + const updatedProject = JSON.parse(JSON.stringify(projects)); + return updatedProject.map((project) => { + if (project.id !== selectedProject) return project; + + return { + ...project, + completedWork: + (project.completedWork ?? 0) + (data?.data?.completedTask ?? 0), + }; + }); + }); + showToast("Task Reported Successfully.", "success"); if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { const msg = - error?.response?.data?.message || error.message || "Error occurred during API call"; - showToast( msg, "error" ); + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(msg, "error"); }, }); @@ -145,16 +176,12 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { return response.data; }, - onSuccess: ( data,variables ) => - { - + onSuccess: (data, variables) => { const workAreaId = variables?.commentsData?.workItem?.workArea?.id; queryClient.invalidateQueries({ queryKey: ["taskList"] }); if (actionAllow) { - showToast( "Review submitted successfully.", "success" ); - - } else - { + showToast("Review submitted successfully.", "success"); + } else { showToast("Comment sent successfully.", "success"); } @@ -162,7 +189,10 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { }, onError: (error) => { - const msg = error?.response?.data?.message || error.message || "Error during API call"; + const msg = + error?.response?.data?.message || + error.message || + "Error during API call"; showToast(msg, "error"); }, }); @@ -170,24 +200,23 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { return { submitComment: mutate, isPending }; }; -export const useCreateTask = ( {onSuccessCallback, onErrorCallback} = {} ) => -{ +export const useCreateTask = ({ onSuccessCallback, onErrorCallback } = {}) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({payload,workAreaId}) => { + mutationFn: async ({ payload, workAreaId }) => { return await TasksRepository.assignTask(payload); }, - onSuccess: ( _, variables ) => - { - queryClient.invalidateQueries( {queryKey: [ "taskList" ]} ); - queryClient.invalidateQueries( {queryKey: [ "WorkItems", variables?.workAreaId ]} ); - showToast( "Task Assigned Successfully.", "success" ); + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ queryKey: ["taskList"] }); + queryClient.invalidateQueries({ + queryKey: ["WorkItems", variables?.workAreaId], + }); + showToast("Task Assigned Successfully.", "success"); if (onSuccessCallback) onSuccessCallback(variables); }, - onError: ( error ) => - { + onError: (error) => { showToast("Something went wrong. Please try again.", "error"); if (onErrorCallback) onErrorCallback(error); }, }); -}; \ No newline at end of file +}; From 01ccd2e75869b6e40382cc8bf1268405d9b718a2 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 20:30:53 +0530 Subject: [PATCH 101/120] add utility to calculate and format completion percentage from planned and completed work --- src/components/Project/ProjectCard.jsx | 9 ++------- src/components/Project/ProjectOverview.jsx | 7 +++---- src/utils/dateUtils.jsx | 9 +++++++++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/Project/ProjectCard.jsx b/src/components/Project/ProjectCard.jsx index 015312fe..7ff4350b 100644 --- a/src/components/Project/ProjectCard.jsx +++ b/src/components/Project/ProjectCard.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import moment from "moment"; -import { formatNumber, getDateDifferenceInDays } from "../../utils/dateUtils"; +import { formatNumber, getCompletionPercentage, getDateDifferenceInDays } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; import { useProjectDetails, useUpdateProject } from "../../hooks/useProjects"; import ManageProjectInfo from "./ManageProjectInfo"; @@ -227,12 +227,7 @@ const ProjectCard = ({ projectData, recall }) => { Task: {formatNumber(projectInfo.completedWork)} / {formatNumber(projectInfo.plannedWork)} - {Math.floor( - getProgressInNumber( - projectInfo.plannedWork, - projectInfo.completedWork - ) - ) || 0}{" "} + {getCompletionPercentage(projectInfo.completedWork, projectInfo.plannedWork)} % Completed
    diff --git a/src/components/Project/ProjectOverview.jsx b/src/components/Project/ProjectOverview.jsx index c489c339..f258ce8b 100644 --- a/src/components/Project/ProjectOverview.jsx +++ b/src/components/Project/ProjectOverview.jsx @@ -3,7 +3,7 @@ import { useEmployeesByProjectAllocated, useProjects, } from "../../hooks/useProjects"; -import { formatNumber } from "../../utils/dateUtils"; +import { formatNumber, getCompletionPercentage } from "../../utils/dateUtils"; import ProgressBar from "../common/ProgressBar"; const ProjectOverview = ({ project }) => { @@ -49,9 +49,8 @@ const ProjectOverview = ({ project }) => { ) ) || 0}{" "} */} { - (formatNumber(project_detail.plannedWork), - "/", - formatNumber(project_detail.completedWork)) + getCompletionPercentage( formatNumber(project_detail.completedWork),formatNumber(project_detail.plannedWork)) + } % Completed diff --git a/src/utils/dateUtils.jsx b/src/utils/dateUtils.jsx index 0105d4aa..4d6643b6 100644 --- a/src/utils/dateUtils.jsx +++ b/src/utils/dateUtils.jsx @@ -69,4 +69,13 @@ export const formatNumber = (num) => { }; export const formatUTCToLocalTime = (datetime) =>{ return moment.utc(datetime).local().format("MMMM DD, YYYY [at] hh:mm A"); +} + +export const getCompletionPercentage = (completedWork, plannedWork)=> { + if (!plannedWork || plannedWork === 0) return 0; + + const percentage = (completedWork / plannedWork) * 100; + const clamped = Math.min(Math.max(percentage, 0), 100); + + return clamped.toFixed(2); } \ No newline at end of file From 679d5d167f450479c4f09b2731eaa3b6c8f3c6d8 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 23:41:51 +0530 Subject: [PATCH 102/120] dynamically updated completed planned work at project lis and workarea when going to create new sub task --- src/components/Activities/SubTask.jsx | 46 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/components/Activities/SubTask.jsx b/src/components/Activities/SubTask.jsx index 43a3c049..4014673b 100644 --- a/src/components/Activities/SubTask.jsx +++ b/src/components/Activities/SubTask.jsx @@ -9,7 +9,7 @@ import { import showToast from "../../services/toastService"; import ProjectRepository from "../../repositories/ProjectRepository"; import { useTaskById } from "../../hooks/useTasks"; -import {useManageTask} from "../../hooks/useProjects"; +import { useManageTask } from "../../hooks/useProjects"; const subTaskSchema = z.object({ activityId: z.string().min(1, "Activity is required"), @@ -37,14 +37,13 @@ const SubTask = ({ activity, onClose }) => { }); const selectedActivityId = watch("activityId"); const selectedActivity = activities?.find((a) => a.id === selectedActivityId); - const {mutate:createSubTask,isPending } = useManageTask( { - onSuccessCallback: () => - { - showToast("Sub Task Created Successfully","success") + const { mutate: createSubTask, isPending } = useManageTask({ + onSuccessCallback: () => { + showToast("Sub Task Created Successfully", "success"); reset(); onClose(); - } - } ) + }, + }); useEffect(() => { setCategoryData(categories); @@ -73,7 +72,7 @@ const SubTask = ({ activity, onClose }) => { }; const onSubmitForm = async (formData) => { - let payload = { + let data = { workAreaID: Task.workItem.workAreaId, workCategoryId: formData.workCategoryId, activityID: formData.activityId, @@ -82,8 +81,19 @@ const SubTask = ({ activity, onClose }) => { parentTaskId: activity?.id, comment: formData.comment, }; - - createSubTask([payload]) + + const payload = [data]; + let buildingId = activity.workItem.workArea.floor.building.id; + let floorId = activity.workItem.workArea.floor.id; + let workAreaId = activity.workItem.workArea.id; + createSubTask({ + payload: payload, + buildingId: buildingId, + floorId: floorId, + workAreaId: workAreaId, + PreviousPlannedWork:0, + previousCompletedWork:0 + }); }; return (
    @@ -147,15 +157,15 @@ const SubTask = ({ activity, onClose }) => { disabled > + {loading ? "Loading..." : "-- Select Activity --"} + - {!loading && - activities?.map((activity) => ( - - ))} + {!loading && + activities?.map((activity) => ( + + ))} {errors.activityId && (
    {errors.activityId.message}
    From 5ec982778d0ef232a9c44b3ba8e110b04b5c05ae Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Tue, 15 Jul 2025 10:21:17 +0530 Subject: [PATCH 103/120] added view team memeber and all employee --- src/pages/employee/EmployeeList.jsx | 36 +++++++++++++---------------- src/utils/constants.jsx | 5 ++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/pages/employee/EmployeeList.jsx b/src/pages/employee/EmployeeList.jsx index ef470979..002d77d4 100644 --- a/src/pages/employee/EmployeeList.jsx +++ b/src/pages/employee/EmployeeList.jsx @@ -9,7 +9,7 @@ import { useEmployeesAllOrByProjectId, useSuspendEmployee } from "../../hooks/us import { useProjects } from "../../hooks/useProjects"; import { useProfile } from "../../hooks/useProfile"; import { hasUserPermission } from "../../utils/authUtils"; -import { ITEMS_PER_PAGE, MANAGE_EMPLOYEES } 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 @@ -56,6 +56,8 @@ 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 @@ -120,23 +122,6 @@ useEffect(() => { setIsCreateModalOpen(true); }; - // const closeModal = () => { - // setIsCreateModalOpen(false); - - // const modalElement = document.getElementById("managerole-modal"); - // if (modalElement && !showModal) { - // modalElement.classList.remove("show"); - // modalElement.style.display = "none"; - // document.body.classList.remove("modal-open"); - // document.querySelector(".modal-backdrop")?.remove(); - // } - // setShowModal(false); - // clearCacheKey("employeeProfile"); - // recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here - // }; - // const handleShow = () => setShowModal(true); - // const handleClose = () => setShowModal( false ); - useEffect(() => { if (!loading && Array.isArray(employees)) { const sorted = [...employees].sort((a, b) => { @@ -296,7 +281,9 @@ const handleAllEmployeesToggle = (e) => { { label: "Employees", link: null }, ]} > -
    + + + {ViewTeamMember ? (
    @@ -309,6 +296,7 @@ const handleAllEmployeesToggle = (e) => { {/* Switches: All Employees + Inactive */}
    {/* All Employees Switch */} + {ViewAllEmployee && (
    { All Employees
    + ) } {/* Show Inactive Employees Switch */} {showAllEmployees && ( @@ -702,7 +691,14 @@ const handleAllEmployeesToggle = (e) => {
    -
    +
    ):( +
    +
    + +

    Access Denied: You don't have permission to perform this action. !

    +
    +
    + )}
    ); diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx index fca560af..f8d14a30 100644 --- a/src/utils/constants.jsx +++ b/src/utils/constants.jsx @@ -11,6 +11,11 @@ export const VIEW_PROJECTS = "6ea44136-987e-44ba-9e5d-1cf8f5837ebc" export const MANAGE_EMPLOYEES = "a97d366a-c2bb-448d-be93-402bd2324566" +export const VIEW_ALL_EMPLOYEES = "60611762-7f8a-4fb5-b53f-b1139918796b" + +export const VIEW_TEAM_MEMBERS = "b82d2b7e-0d52-45f3-997b-c008ea460e7f" + + export const MANAGE_PROJECT_INFRA = "cf2825ad-453b-46aa-91d9-27c124d63373" export const VIEW_PROJECT_INFRA = "8d7cc6e3-9147-41f7-aaa7-fa507e450bd4" From 999b8bfb9927d9d4ad6b76437983081b428ae622 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Tue, 15 Jul 2025 10:34:35 +0530 Subject: [PATCH 104/120] added permission rights in daily task report --- src/pages/Activities/DailyTask.jsx | 8 +++++++- src/utils/constants.jsx | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/Activities/DailyTask.jsx b/src/pages/Activities/DailyTask.jsx index dba18d18..fb121a89 100644 --- a/src/pages/Activities/DailyTask.jsx +++ b/src/pages/Activities/DailyTask.jsx @@ -14,6 +14,8 @@ import GlobalModel from "../../components/common/GlobalModel"; import AssignTask from "../../components/Project/AssignTask"; import SubTask from "../../components/Activities/SubTask"; import {formatNumber} from "../../utils/dateUtils"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; const DailyTask = () => { const [searchParams] = useSearchParams(); @@ -32,6 +34,8 @@ const DailyTask = () => { const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); + const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK) + const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK) const { TaskList, @@ -386,6 +390,7 @@ const DailyTask = () => {
    + { ReportTaskRights && - {task.reportedDate && ( + } + {(ApprovedTaskRights && task.reportedDate ) && ( - - {[...Array(totalPages)].map((_, i) => ( -
  • - -
  • - ))} -
  • - -
  • - - -)} - + » + + + + + )}
    ); From 4fc6a5c9f3c55f129660ab9f86f96adf254f67f4 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 20:18:36 +0530 Subject: [PATCH 107/120] dynamically update planned and completed work in ProjectInfra and project list card on activity create/edit/delete --- .../Infrastructure/EditActivityModal.jsx | 12 +- .../Project/Infrastructure/TaskModel.jsx | 13 +- .../Project/Infrastructure/WorkItem.jsx | 77 +-- src/hooks/useProjects.js | 478 ++++++++++++------ 4 files changed, 376 insertions(+), 204 deletions(-) diff --git a/src/components/Project/Infrastructure/EditActivityModal.jsx b/src/components/Project/Infrastructure/EditActivityModal.jsx index 06a6e41e..14b64b53 100644 --- a/src/components/Project/Infrastructure/EditActivityModal.jsx +++ b/src/components/Project/Infrastructure/EditActivityModal.jsx @@ -116,7 +116,17 @@ useEffect(() => { floorId: floor?.id, workAreaId: workArea?.id, }; - UpdateTask([payload]) + let plannedTask = + workItem?.workItem?.plannedWork || workItem?.plannedWork || 0; + let completedTask = workItem?.workItem?.completedWork || workItem?.completedWork || 0 + UpdateTask({ + payload: [payload], + PreviousPlannedWork: plannedTask, + buildingId: building?.id, + floorId: floor?.id, + workAreaId: workArea?.id, + previousCompletedWork:completedTask + }); } return ( diff --git a/src/components/Project/Infrastructure/TaskModel.jsx b/src/components/Project/Infrastructure/TaskModel.jsx index 12cf8c87..06261603 100644 --- a/src/components/Project/Infrastructure/TaskModel.jsx +++ b/src/components/Project/Infrastructure/TaskModel.jsx @@ -69,10 +69,9 @@ const TaskModel = ({ project, onSubmit, onClose }) => { const selectedCategory = categoryData?.find((c) => c.id === watchCategoryId); const { mutate: CreateTask, isPending } = useManageTask({ - onSuccessCallback: ( response ) => - { - showToast( response?.message, "success" ) - onClose?.() + onSuccessCallback: (response) => { + showToast(response?.message, "success"); + onClose?.(); }, }); useEffect(() => { @@ -96,8 +95,10 @@ const TaskModel = ({ project, onSubmit, onClose }) => { }, [categories]); const onSubmitForm = async (data) => { - const payload = [data]; - CreateTask(payload); + const payload = [data]; + CreateTask({payload:payload,buildingId: data.buildingID, + floorId: data.floorId, + workAreaId: data.workAreaId, PreviousPlannedWork:0,previousCompletedWork:0}); }; return ( diff --git a/src/components/Project/Infrastructure/WorkItem.jsx b/src/components/Project/Infrastructure/WorkItem.jsx index 1116991c..df1f2bc1 100644 --- a/src/components/Project/Infrastructure/WorkItem.jsx +++ b/src/components/Project/Infrastructure/WorkItem.jsx @@ -10,7 +10,10 @@ import { } from "../../../utils/constants"; import ConfirmModal from "../../common/ConfirmModal"; import ProjectRepository from "../../../repositories/ProjectRepository"; -import { useDeleteProjectTask, useProjectDetails } from "../../../hooks/useProjects"; +import { + useDeleteProjectTask, + useProjectDetails, +} from "../../../hooks/useProjects"; import showToast from "../../../services/toastService"; import { cacheData, @@ -19,7 +22,7 @@ import { } from "../../../slices/apiDataManager"; import { refreshData } from "../../../slices/localVariablesSlice"; import GlobalModel from "../../common/GlobalModel"; -import {useDeleteMasterItem} from "../../../hooks/masterHook/useMaster"; +import { useDeleteMasterItem } from "../../../hooks/masterHook/useMaster"; const WorkItem = ({ workItem, @@ -40,19 +43,18 @@ const WorkItem = ({ const [loadingDelete, setLoadingDelete] = useState(false); const project = getCachedData("projectInfo"); - const openModal = () => setIsModalOpen(true); - const closeModal = () => setIsModalOpen( false ); - - const showModalDelete = () => setShowModal2(true); + const closeModal = () => setIsModalOpen(false); + + const showModalDelete = () => setShowModal2(true); const closeModalDelete = () => setShowModal2(false); const getProgress = (planned, completed) => { return (completed * 100) / planned + "%"; }; - - const {mutate:DeleteTask,isPending } = useDeleteProjectTask(() => { - closeModalDelete?.(); - }); + + const { mutate: DeleteTask, isPending } = useDeleteProjectTask(() => { + closeModalDelete?.(); + }); const handleAssignTask = () => { setItemName(""); @@ -61,15 +63,15 @@ const WorkItem = ({ setNewWorkItem(workItem); }, [workItem]); - const refreshWorkItem = (plannedTask) =>{ + const refreshWorkItem = (plannedTask) => { if (workItem) { - const updated = { - ...workItem, - todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask, - }; - setNewWorkItem(updated); - } - } + const updated = { + ...workItem, + todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask, + }; + setNewWorkItem(updated); + } + }; let assigndata = { building: forBuilding, floor: forFloor, @@ -85,15 +87,17 @@ const WorkItem = ({ tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); }, []); - - - - - const handleSubmit = async () => { let WorkItemId = workItem.workItemId || workItem.id; - DeleteTask({workItemId:WorkItemId,workAreaId:forWorkArea?.id}) - + debugger + DeleteTask({ + workItemId: WorkItemId, + workAreaId: forWorkArea?.id, + completedTask: workItem?.completedWork, + plannedTask: workItem?.plannedWork, + buildingId: forBuilding?.id, + floorId: forFloor?.id, + }); }; const PlannedWork = @@ -104,18 +108,26 @@ const WorkItem = ({ <> {isModalOpen && ( - + )} {showModal && ( - setShowModal(false)}> + setShowModal(false)} + > setShowModal(false)} + onClose={() => setShowModal(false)} /> )} @@ -167,7 +179,6 @@ const WorkItem = ({ : "NA"} - {hasWorkItem @@ -195,9 +206,7 @@ const WorkItem = ({ {hasWorkItem ? `${ - NewWorkItem?.todaysAssigned ?? - workItem?.todaysAssigned ?? - "0" + NewWorkItem?.todaysAssigned ?? workItem?.todaysAssigned ?? "0" }` : "NA"} @@ -251,7 +260,7 @@ const WorkItem = ({ setShowModal(true)} + onClick={() => setShowModal(true)} role="button" > setShowModal(true) } + onClick={() => setShowModal(true)} > Edit diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index 2cc5ee1d..daf705fa 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -6,7 +6,12 @@ import { useDispatch, useSelector } from "react-redux"; import { setProjectId } from "../slices/localVariablesSlice"; import EmployeeList from "../components/Directory/EmployeeList"; import eventBus from "../services/eventBus"; -import {Mutation, useMutation, useQuery, useQueryClient} from "@tanstack/react-query"; +import { + Mutation, + useMutation, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import showToast from "../services/toastService"; // export const useProjects = () => { @@ -211,7 +216,7 @@ export const useProjects = () => { error, refetch, } = useQuery({ - queryKey: ['ProjectsList'], + queryKey: ["ProjectsList"], queryFn: async () => { const response = await ProjectRepository.getProjectList(); return response.data; @@ -227,97 +232,112 @@ export const useProjects = () => { }; }; -export const useEmployeesByProjectAllocated = (selectedProject) => -{ - const {data = [], isLoading, refetch, error} = useQuery( { - queryKey: ["empListByProjectAllocated", selectedProject ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectAllocation( selectedProject ); - return res.data || res +export const useEmployeesByProjectAllocated = (selectedProject) => { + const { + data = [], + isLoading, + refetch, + error, + } = useQuery({ + queryKey: ["empListByProjectAllocated", selectedProject], + queryFn: async () => { + const res = await ProjectRepository.getProjectAllocation(selectedProject); + return res.data || res; }, enabled: !!selectedProject, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Allocated Employees", "error"); - } - } ) - + onError: (error) => { + showToast( + error.message || "Error while Fetching project Allocated Employees", + "error" + ); + }, + }); + return { projectEmployees: data, - loading:isLoading, + loading: isLoading, error, - refetch - } -} + refetch, + }; +}; -export const useProjectDetails = ( projectId,isAuto = true ) => -{ - const {data: projects_Details, isLoading, error, refetch} = useQuery( { - queryKey: [ "projectInfo", projectId ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectByprojectId( projectId ); +export const useProjectDetails = (projectId, isAuto = true) => { + const { + data: projects_Details, + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["projectInfo", projectId], + queryFn: async () => { + const res = await ProjectRepository.getProjectByprojectId(projectId); return res.data || res; }, enabled: !!projectId && isAuto, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Details", "error"); - } - } ) - return { projects_Details, loading:isLoading, error, refetch }; -} + onError: (error) => { + showToast( + error.message || "Error while Fetching project Details", + "error" + ); + }, + }); + return { projects_Details, loading: isLoading, error, refetch }; +}; -export const useProjectsByEmployee = (employeeId) => -{ - const { data:projectNameList =[],isLoading,error,refetch} = useQuery( { - queryKey: [ "ProjectsByEmployee", employeeId ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectsByEmployee( employeeId ); +export const useProjectsByEmployee = (employeeId) => { + const { + data: projectNameList = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["ProjectsByEmployee", employeeId], + queryFn: async () => { + const res = await ProjectRepository.getProjectsByEmployee(employeeId); return res.data || res; }, enabled: !!employeeId, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Employee", "error"); - } - }) - return {projectList, loading:isLoading,error,refetch } -} + onError: (error) => { + showToast( + error.message || "Error while Fetching project Employee", + "error" + ); + }, + }); + return { projectList, loading: isLoading, error, refetch }; +}; -export const useProjectName = () => -{ - const {data = [],isLoading,error,refetch} = useQuery( { - queryKey: [ "basicProjectNameList" ], - queryFn: async () => - { +export const useProjectName = () => { + const { + data = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["basicProjectNameList"], + queryFn: async () => { const res = await ProjectRepository.projectNameList(); return res.data || res; }, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Name", "error"); - } - } ) - return {projectNames:data,loading:isLoading,Error:error,refetch} -} + onError: (error) => { + showToast(error.message || "Error while Fetching project Name", "error"); + }, + }); + return { projectNames: data, loading: isLoading, Error: error, refetch }; +}; - - export const useProjectInfra = (projectId) => { const { data: projectInfra, isLoading, error, } = useQuery({ - queryKey: ["ProjectInfra", projectId], + queryKey: ["ProjectInfra", projectId], queryFn: async () => { const res = await ProjectRepository.getProjectInfraByproject(projectId); return res.data; }, - enabled: !!projectId , + enabled: !!projectId, onError: (error) => { showToast(error.message || "Error while fetching project infra", "error"); }, @@ -326,30 +346,27 @@ export const useProjectInfra = (projectId) => { return { projectInfra, isLoading, error }; }; - -export const useProjectTasks = (workAreaId,IsExpandedArea=false) => -{ - const { data:ProjectTaskList,isLoading,error } = useQuery( { - queryKey: [ "WorkItems",workAreaId ], - queryFn: async () => - { +export const useProjectTasks = (workAreaId, IsExpandedArea = false) => { + const { + data: ProjectTaskList, + isLoading, + error, + } = useQuery({ + queryKey: ["WorkItems", workAreaId], + queryFn: async () => { const res = await ProjectRepository.getProjectTasksByWorkArea(workAreaId); return res.data; }, enabled: !!workAreaId && !!IsExpandedArea, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Tasks", "error"); - } - } ) - return {ProjectTaskList,isLoading,error} -} - + onError: (error) => { + showToast(error.message || "Error while Fetching project Tasks", "error"); + }, + }); + return { ProjectTaskList, isLoading, error }; +}; // -- -------------Mutation------------------------------- - - export const useCreateProject = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); @@ -360,8 +377,8 @@ export const useCreateProject = ({ onSuccessCallback }) => { }, onSuccess: (data) => { // Invalidate the cache - queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} ); - queryClient.invalidateQueries({queryKey:['basicProjectNameList']}); + queryClient.invalidateQueries({ queryKey: ["ProjectsList"] }); + queryClient.invalidateQueries({ queryKey: ["basicProjectNameList"] }); // Emit event for consumers (like useProjects or others) eventBus.emit("project", { @@ -381,27 +398,19 @@ export const useCreateProject = ({ onSuccessCallback }) => { }); }; - export const useUpdateProject = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - } = useMutation({ - mutationFn: async ( {projectId, updatedData} ) => - { + const { mutate, isPending, isSuccess, isError } = useMutation({ + mutationFn: async ({ projectId, updatedData }) => { return await ProjectRepository.updateProject(projectId, updatedData); }, - onSuccess: ( data, variables ) => - { + onSuccess: (data, variables) => { const { projectId } = variables; - - queryClient.invalidateQueries({queryKey:["ProjectsList"]}); - queryClient.invalidateQueries( {queryKey: [ "projectInfo", projectId ]} ); - queryClient.invalidateQueries({queryKey:['basicProjectNameList']}); + + queryClient.invalidateQueries({ queryKey: ["ProjectsList"] }); + queryClient.invalidateQueries({ queryKey: ["projectInfo", projectId] }); + queryClient.invalidateQueries({ queryKey: ["basicProjectNameList"] }); eventBus.emit("project", { keyword: "Update_Project", @@ -415,9 +424,8 @@ export const useUpdateProject = ({ onSuccessCallback }) => { } }, - onError: ( error ) => - { - console.log(error) + onError: (error) => { + console.log(error); showToast(error?.message || "Error while updating project", "error"); }, }); @@ -430,20 +438,16 @@ export const useUpdateProject = ({ onSuccessCallback }) => { }; }; - -export const useManageProjectInfra = ( {onSuccessCallback} ) => -{ +export const useManageProjectInfra = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {infraObject, projectId} ) => - { + mutationFn: async ({ infraObject, projectId }) => { return await ProjectRepository.manageProjectInfra(infraObject); }, - onSuccess: ( data, variables ) => - { - const { projectId } = variables; - queryClient.invalidateQueries({queryKey:["ProjectInfra", projectId]}); - if (onSuccessCallback) onSuccessCallback(data,variables); + onSuccess: (data, variables) => { + const { projectId } = variables; + queryClient.invalidateQueries({ queryKey: ["ProjectInfra", projectId] }); + if (onSuccessCallback) onSuccessCallback(data, variables); }, onError: (error) => { showToast(error.message || "Failed to update Project Infra", "error"); @@ -451,39 +455,36 @@ export const useManageProjectInfra = ( {onSuccessCallback} ) => }); }; - export const useManageProjectAllocation = ({ onSuccessCallback, onErrorCallback, }) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - } = useMutation({ - mutationFn: async ( {items} ) => - { + const { mutate, isPending, isSuccess, isError } = useMutation({ + mutationFn: async ({ items }) => { const response = await ProjectRepository.manageProjectAllocation(items); return response.data; }, onSuccess: (data, variables, context) => { - queryClient.invalidateQueries({queryKey:['empListByProjectAllocated']}); - queryClient.removeQueries({queryKey:["projectEmployees"]}) + queryClient.invalidateQueries({ + queryKey: ["empListByProjectAllocated"], + }); + queryClient.removeQueries({ queryKey: ["projectEmployees"] }); if (variables?.added) { - showToast('Employee Assigned Successfully', 'success'); + showToast("Employee Assigned Successfully", "success"); } else { - showToast('Removed Employee Successfully', 'success'); + showToast("Removed Employee Successfully", "success"); } if (onSuccessCallback) onSuccessCallback(data, context); }, onError: (error) => { const message = - error?.response?.data?.message || error.message || 'Error occurred during API call'; - showToast(message, 'error'); + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(message, "error"); if (onErrorCallback) onErrorCallback(error); }, }); @@ -496,48 +497,199 @@ export const useManageProjectAllocation = ({ }; }; -export const useManageTask = ({onSuccessCallback}) => -{ +export const useManageTask = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + return useMutation({ + mutationFn: async ({ + payload, + PreviousPlannedWork, + previousCompletedWork, + buildingId, + floorId, + workAreaId, + }) => await ProjectRepository.manageProjectTasks(payload), + onSuccess: (data, variables) => { + const { + workAreaId, + buildingId, + floorId, + PreviousPlannedWork, + previousCompletedWork, + } = variables; + queryClient.invalidateQueries({ queryKey: ["WorkItems"] }); + const getPlannedDelta = (previous, current) => current - previous; + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + const { workAreaId, floorId, buildingId } = variables; + const updatedData = JSON.parse(JSON.stringify(oldData)); + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; - return useMutation( { - mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ), - onSuccess: ( data, variables ) => - { - queryClient.invalidateQueries({ queryKey: ["WorkItems"] }) + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + + const previousPlanned = PreviousPlannedWork ?? 0; + const currentPlanned = + data?.data[0]?.workItem.plannedWork ?? 0; + + const plannedWorkDelta = getPlannedDelta( + previousPlanned, + currentPlanned + ); + const updatedPlannedWork = + (area.plannedWork ?? 0) + plannedWorkDelta; + const previousCompleted = previousCompletedWork; + + const currentCompleted = + data?.data[0]?.workItem.completedWork ?? 0; + const completedWorkDelta = getPlannedDelta( + previousCompleted, + currentCompleted + ); + const updatedCompletedWork = + (area.completedWork ?? 0) + completedWorkDelta; + return { + ...area, + plannedWork: updatedPlannedWork, + completedWork: updatedCompletedWork, + }; + }), + }; + }), + }; + }); + }); + + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + + return projects.map((project) => { + if (project.id !== selectedProject) return project; + + const previousPlanned = PreviousPlannedWork ?? 0; + const currentPlanned = data?.data[0]?.workItem.plannedWork ?? 0; + + const plannedWorkDelta = getPlannedDelta( + previousPlanned, + currentPlanned + ); + const updatedPlannedWork = + (project.plannedWork ?? 0) + plannedWorkDelta; + const previousCompleted = previousCompletedWork; + const currentCompleted = data?.data[0]?.workItem.completedWork ?? 0; + const completedWorkDelta = getPlannedDelta( + previousCompleted, + currentCompleted + ); + const updatedCompletedWork = + (project.completedWork ?? 0) + completedWorkDelta; + return { + ...project, + plannedWork: updatedPlannedWork, + completedWork: updatedCompletedWork, + }; + }); + }); if (onSuccessCallback) onSuccessCallback(data); }, - onError: (error) => - { - const message = - error?.response?.data?.message || error.message || 'Error occurred during API call'; - showToast(message, 'error'); - } - - }) -} - -export const useDeleteProjectTask = (onSuccessCallback) => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async ( {workItemId, workAreaId} ) => - { - return await ProjectRepository.deleteProjectTask(workItemId); - }, - onSuccess: ( _, variables ) => - { - showToast("Task deleted successfully", "success"); - queryClient.invalidateQueries({queryKey:[ "WorkItems",variables.workAreaId]}); - if (onSuccessCallback) onSuccessCallback(); - }, onError: (error) => { - showToast( - error?.response?.data?.message || error.message || "Failed to delete task", - "error" - ); - if (onSuccessCallback) onSuccessCallback(); + const message = + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(message, "error"); }, }); }; +export const useDeleteProjectTask = (onSuccessCallback) => { + const queryClient = useQueryClient(); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + return useMutation({ + mutationFn: async ({ + workItemId, + workAreaId, + completedTask, + plannedTask, + }) => { + return await ProjectRepository.deleteProjectTask(workItemId); + }, + onSuccess: (_, variables) => { + const { completedTask, plannedTask, workAreaId, buildingId, floorId } = + variables; + + queryClient.invalidateQueries({ + queryKey: ["WorkItems", variables.workAreaId], + }); + + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + + const { workAreaId, floorId, buildingId, plannedTask, completedTask } = + variables; + + const updatedData = JSON.parse(JSON.stringify(oldData)); + + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; + + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + + return { + ...area, + plannedWork: (area.plannedWork ?? 0) - plannedTask, + completedWork: (area.completedWork ?? 0) - completedTask, + }; + }), + }; + }), + }; + }); + }); + + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + + return projects.map((project) => { + if (project.id !== selectedProject) return project; + + return { + ...project, + plannedWork: (project.plannedWork ?? 0) - plannedTask, + completedWork: (project.completedWork ?? 0) - completedTask, + }; + }); + }); + showToast("Task deleted successfully", "success"); + if (onSuccessCallback) onSuccessCallback(); + }, + onError: (error) => { + showToast( + error?.response?.data?.message || + error.message || + "Failed to delete task", + "error" + ); + if (onSuccessCallback) onSuccessCallback(); + }, + }); +}; From d7d759732b7802eabca905d2d1fb68e1b66b712d Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 20:19:25 +0530 Subject: [PATCH 108/120] dynamically update planned and completed work in ProjectInfra and project list card on Task reported --- src/components/Activities/ReportTask.jsx | 7 +- src/hooks/useTasks.js | 131 ++++++++++++++--------- 2 files changed, 86 insertions(+), 52 deletions(-) diff --git a/src/components/Activities/ReportTask.jsx b/src/components/Activities/ReportTask.jsx index 8194a5cc..a3550683 100644 --- a/src/components/Activities/ReportTask.jsx +++ b/src/components/Activities/ReportTask.jsx @@ -62,7 +62,12 @@ export const ReportTask = ({ report, closeModal }) => { checkList: [], }; - reportTask({ reportData, workAreaId: report?.workItem?.workArea?.id }); + reportTask({ + reportData, + workAreaId: report?.workItem?.workArea?.id, + buildingId: report?.workItem?.workArea?.floor?.building.id, + floorId: report?.workItem?.workArea?.floor?.id, + }); }; const handleClose = () => { closeModal(); diff --git a/src/hooks/useTasks.js b/src/hooks/useTasks.js index 1cf531c0..b793ced4 100644 --- a/src/hooks/useTasks.js +++ b/src/hooks/useTasks.js @@ -1,11 +1,10 @@ import { useEffect, useState } from "react"; import { TasksRepository } from "../repositories/TaskRepository"; import { cacheData, getCachedData } from "../slices/apiDataManager"; -import {MasterRespository} from "../repositories/MastersRepository"; +import { MasterRespository } from "../repositories/MastersRepository"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import showToast from "../services/toastService"; -import {useSelector} from "react-redux"; - +import { useSelector } from "react-redux"; // ---------Query--------------------------------- @@ -28,7 +27,6 @@ export const useTaskList = (projectId, dateFrom, toDate) => { return response.data; }, enabled, - }); return { TaskList, loading, error, refetch }; @@ -46,7 +44,7 @@ export const useTaskById = (TaskId) => { const res = await TasksRepository.getTaskById(TaskId); return res.data; }, - enabled: !!TaskId, + enabled: !!TaskId, }); return { Task, loading, error, refetch }; @@ -75,47 +73,80 @@ export const useAuditStatus = () => { const res = await MasterRespository.getAuditStatus(); return res.data; }, - }); return { status, loading, error, refetch }; }; - // -----------------------Mutation------------------------ -const toDate = new Date().toISOString().split('T')[0]; -const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; +const toDate = new Date().toISOString().split("T")[0]; +const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000) + .toISOString() + .split("T")[0]; - - -export const useReportTask = ( {onSuccessCallback, onErrorCallback} = {} ) => -{ +export const useReportTask = ({ onSuccessCallback, onErrorCallback } = {}) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - error, - } = useMutation({ - mutationFn: async ( {reportData,workAreaId} ) => - { - debugger + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + const { mutate, isPending, isSuccess, isError, error } = useMutation({ + mutationFn: async ({ reportData, workAreaId }) => { return await TasksRepository.reportTask(reportData); }, - onSuccess: ( data, variables ) => - { - const {workAreaId} = variables; - queryClient.invalidateQueries( {queryKey: [ "taskList" ]} ); - queryClient.invalidateQueries( {queryKey: [ "WorkItems", workAreaId ]} ); - queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} ); - showToast( "Task Reported Successfully.", "success" ); + onSuccess: (data, variables) => { + const { workAreaId, buildingId, floorId } = variables; + queryClient.invalidateQueries({ queryKey: ["taskList"] }); + queryClient.invalidateQueries({ queryKey: ["WorkItems", workAreaId] }); + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + const { workAreaId, floorId, buildingId } = variables; + const updatedData = JSON.parse(JSON.stringify(oldData)); + + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; + + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + return { + ...area, + completedWork: + (area.completedWork ?? 0) + + (data?.data?.completedTask ?? 0), + }; + }), + }; + }), + }; + }); + }); + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + const updatedProject = JSON.parse(JSON.stringify(projects)); + return updatedProject.map((project) => { + if (project.id !== selectedProject) return project; + + return { + ...project, + completedWork: + (project.completedWork ?? 0) + (data?.data?.completedTask ?? 0), + }; + }); + }); + showToast("Task Reported Successfully.", "success"); if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { const msg = - error?.response?.data?.message || error.message || "Error occurred during API call"; - showToast( msg, "error" ); + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(msg, "error"); }, }); @@ -145,16 +176,12 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { return response.data; }, - onSuccess: ( data,variables ) => - { - + onSuccess: (data, variables) => { const workAreaId = variables?.commentsData?.workItem?.workArea?.id; queryClient.invalidateQueries({ queryKey: ["taskList"] }); if (actionAllow) { - showToast( "Review submitted successfully.", "success" ); - - } else - { + showToast("Review submitted successfully.", "success"); + } else { showToast("Comment sent successfully.", "success"); } @@ -162,7 +189,10 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { }, onError: (error) => { - const msg = error?.response?.data?.message || error.message || "Error during API call"; + const msg = + error?.response?.data?.message || + error.message || + "Error during API call"; showToast(msg, "error"); }, }); @@ -170,24 +200,23 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { return { submitComment: mutate, isPending }; }; -export const useCreateTask = ( {onSuccessCallback, onErrorCallback} = {} ) => -{ +export const useCreateTask = ({ onSuccessCallback, onErrorCallback } = {}) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({payload,workAreaId}) => { + mutationFn: async ({ payload, workAreaId }) => { return await TasksRepository.assignTask(payload); }, - onSuccess: ( _, variables ) => - { - queryClient.invalidateQueries( {queryKey: [ "taskList" ]} ); - queryClient.invalidateQueries( {queryKey: [ "WorkItems", variables?.workAreaId ]} ); - showToast( "Task Assigned Successfully.", "success" ); + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ queryKey: ["taskList"] }); + queryClient.invalidateQueries({ + queryKey: ["WorkItems", variables?.workAreaId], + }); + showToast("Task Assigned Successfully.", "success"); if (onSuccessCallback) onSuccessCallback(variables); }, - onError: ( error ) => - { + onError: (error) => { showToast("Something went wrong. Please try again.", "error"); if (onErrorCallback) onErrorCallback(error); }, }); -}; \ No newline at end of file +}; From 1c2b6eaac8d994b7c6a7946564e98fd73661e9c7 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 20:30:53 +0530 Subject: [PATCH 109/120] add utility to calculate and format completion percentage from planned and completed work --- src/components/Project/ProjectCard.jsx | 9 ++------- src/components/Project/ProjectOverview.jsx | 7 +++---- src/utils/dateUtils.jsx | 9 +++++++++ 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/Project/ProjectCard.jsx b/src/components/Project/ProjectCard.jsx index 015312fe..7ff4350b 100644 --- a/src/components/Project/ProjectCard.jsx +++ b/src/components/Project/ProjectCard.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import moment from "moment"; -import { formatNumber, getDateDifferenceInDays } from "../../utils/dateUtils"; +import { formatNumber, getCompletionPercentage, getDateDifferenceInDays } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; import { useProjectDetails, useUpdateProject } from "../../hooks/useProjects"; import ManageProjectInfo from "./ManageProjectInfo"; @@ -227,12 +227,7 @@ const ProjectCard = ({ projectData, recall }) => { Task: {formatNumber(projectInfo.completedWork)} / {formatNumber(projectInfo.plannedWork)} - {Math.floor( - getProgressInNumber( - projectInfo.plannedWork, - projectInfo.completedWork - ) - ) || 0}{" "} + {getCompletionPercentage(projectInfo.completedWork, projectInfo.plannedWork)} % Completed
    diff --git a/src/components/Project/ProjectOverview.jsx b/src/components/Project/ProjectOverview.jsx index c489c339..f258ce8b 100644 --- a/src/components/Project/ProjectOverview.jsx +++ b/src/components/Project/ProjectOverview.jsx @@ -3,7 +3,7 @@ import { useEmployeesByProjectAllocated, useProjects, } from "../../hooks/useProjects"; -import { formatNumber } from "../../utils/dateUtils"; +import { formatNumber, getCompletionPercentage } from "../../utils/dateUtils"; import ProgressBar from "../common/ProgressBar"; const ProjectOverview = ({ project }) => { @@ -49,9 +49,8 @@ const ProjectOverview = ({ project }) => { ) ) || 0}{" "} */} { - (formatNumber(project_detail.plannedWork), - "/", - formatNumber(project_detail.completedWork)) + getCompletionPercentage( formatNumber(project_detail.completedWork),formatNumber(project_detail.plannedWork)) + } % Completed diff --git a/src/utils/dateUtils.jsx b/src/utils/dateUtils.jsx index 0105d4aa..4d6643b6 100644 --- a/src/utils/dateUtils.jsx +++ b/src/utils/dateUtils.jsx @@ -69,4 +69,13 @@ export const formatNumber = (num) => { }; export const formatUTCToLocalTime = (datetime) =>{ return moment.utc(datetime).local().format("MMMM DD, YYYY [at] hh:mm A"); +} + +export const getCompletionPercentage = (completedWork, plannedWork)=> { + if (!plannedWork || plannedWork === 0) return 0; + + const percentage = (completedWork / plannedWork) * 100; + const clamped = Math.min(Math.max(percentage, 0), 100); + + return clamped.toFixed(2); } \ No newline at end of file From 6afe1e6700d0a1ed69f8a6ce127378a64a8b2d28 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Mon, 14 Jul 2025 23:41:51 +0530 Subject: [PATCH 110/120] dynamically updated completed planned work at project lis and workarea when going to create new sub task --- src/components/Activities/SubTask.jsx | 46 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/src/components/Activities/SubTask.jsx b/src/components/Activities/SubTask.jsx index 43a3c049..4014673b 100644 --- a/src/components/Activities/SubTask.jsx +++ b/src/components/Activities/SubTask.jsx @@ -9,7 +9,7 @@ import { import showToast from "../../services/toastService"; import ProjectRepository from "../../repositories/ProjectRepository"; import { useTaskById } from "../../hooks/useTasks"; -import {useManageTask} from "../../hooks/useProjects"; +import { useManageTask } from "../../hooks/useProjects"; const subTaskSchema = z.object({ activityId: z.string().min(1, "Activity is required"), @@ -37,14 +37,13 @@ const SubTask = ({ activity, onClose }) => { }); const selectedActivityId = watch("activityId"); const selectedActivity = activities?.find((a) => a.id === selectedActivityId); - const {mutate:createSubTask,isPending } = useManageTask( { - onSuccessCallback: () => - { - showToast("Sub Task Created Successfully","success") + const { mutate: createSubTask, isPending } = useManageTask({ + onSuccessCallback: () => { + showToast("Sub Task Created Successfully", "success"); reset(); onClose(); - } - } ) + }, + }); useEffect(() => { setCategoryData(categories); @@ -73,7 +72,7 @@ const SubTask = ({ activity, onClose }) => { }; const onSubmitForm = async (formData) => { - let payload = { + let data = { workAreaID: Task.workItem.workAreaId, workCategoryId: formData.workCategoryId, activityID: formData.activityId, @@ -82,8 +81,19 @@ const SubTask = ({ activity, onClose }) => { parentTaskId: activity?.id, comment: formData.comment, }; - - createSubTask([payload]) + + const payload = [data]; + let buildingId = activity.workItem.workArea.floor.building.id; + let floorId = activity.workItem.workArea.floor.id; + let workAreaId = activity.workItem.workArea.id; + createSubTask({ + payload: payload, + buildingId: buildingId, + floorId: floorId, + workAreaId: workAreaId, + PreviousPlannedWork:0, + previousCompletedWork:0 + }); }; return (
    @@ -147,15 +157,15 @@ const SubTask = ({ activity, onClose }) => { disabled > + {loading ? "Loading..." : "-- Select Activity --"} + - {!loading && - activities?.map((activity) => ( - - ))} + {!loading && + activities?.map((activity) => ( + + ))} {errors.activityId && (
    {errors.activityId.message}
    From e4638139f5f046e9dea6fe93239e58129549d000 Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Tue, 15 Jul 2025 10:21:17 +0530 Subject: [PATCH 111/120] added view team memeber and all employee --- src/pages/employee/EmployeeList.jsx | 36 +++++++++++++---------------- src/utils/constants.jsx | 5 ++++ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/src/pages/employee/EmployeeList.jsx b/src/pages/employee/EmployeeList.jsx index ef470979..002d77d4 100644 --- a/src/pages/employee/EmployeeList.jsx +++ b/src/pages/employee/EmployeeList.jsx @@ -9,7 +9,7 @@ import { useEmployeesAllOrByProjectId, useSuspendEmployee } from "../../hooks/us import { useProjects } from "../../hooks/useProjects"; import { useProfile } from "../../hooks/useProfile"; import { hasUserPermission } from "../../utils/authUtils"; -import { ITEMS_PER_PAGE, MANAGE_EMPLOYEES } 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 @@ -56,6 +56,8 @@ 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 @@ -120,23 +122,6 @@ useEffect(() => { setIsCreateModalOpen(true); }; - // const closeModal = () => { - // setIsCreateModalOpen(false); - - // const modalElement = document.getElementById("managerole-modal"); - // if (modalElement && !showModal) { - // modalElement.classList.remove("show"); - // modalElement.style.display = "none"; - // document.body.classList.remove("modal-open"); - // document.querySelector(".modal-backdrop")?.remove(); - // } - // setShowModal(false); - // clearCacheKey("employeeProfile"); - // recallEmployeeData(showInactive, showAllEmployees ? null : selectedProjectId); // Use selectedProjectId here - // }; - // const handleShow = () => setShowModal(true); - // const handleClose = () => setShowModal( false ); - useEffect(() => { if (!loading && Array.isArray(employees)) { const sorted = [...employees].sort((a, b) => { @@ -296,7 +281,9 @@ const handleAllEmployeesToggle = (e) => { { label: "Employees", link: null }, ]} > -
    + + + {ViewTeamMember ? (
    @@ -309,6 +296,7 @@ const handleAllEmployeesToggle = (e) => { {/* Switches: All Employees + Inactive */}
    {/* All Employees Switch */} + {ViewAllEmployee && (
    { All Employees
    + ) } {/* Show Inactive Employees Switch */} {showAllEmployees && ( @@ -702,7 +691,14 @@ const handleAllEmployeesToggle = (e) => {
    -
    +
    ):( +
    +
    + +

    Access Denied: You don't have permission to perform this action. !

    +
    +
    + )}
    ); diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx index fca560af..f8d14a30 100644 --- a/src/utils/constants.jsx +++ b/src/utils/constants.jsx @@ -11,6 +11,11 @@ export const VIEW_PROJECTS = "6ea44136-987e-44ba-9e5d-1cf8f5837ebc" export const MANAGE_EMPLOYEES = "a97d366a-c2bb-448d-be93-402bd2324566" +export const VIEW_ALL_EMPLOYEES = "60611762-7f8a-4fb5-b53f-b1139918796b" + +export const VIEW_TEAM_MEMBERS = "b82d2b7e-0d52-45f3-997b-c008ea460e7f" + + export const MANAGE_PROJECT_INFRA = "cf2825ad-453b-46aa-91d9-27c124d63373" export const VIEW_PROJECT_INFRA = "8d7cc6e3-9147-41f7-aaa7-fa507e450bd4" From f55cf309a41ad5478b2c66a6c058bb7cd55429eb Mon Sep 17 00:00:00 2001 From: pramod mahajan Date: Tue, 15 Jul 2025 10:34:35 +0530 Subject: [PATCH 112/120] added permission rights in daily task report --- src/pages/Activities/DailyTask.jsx | 8 +++++++- src/utils/constants.jsx | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/pages/Activities/DailyTask.jsx b/src/pages/Activities/DailyTask.jsx index dba18d18..fb121a89 100644 --- a/src/pages/Activities/DailyTask.jsx +++ b/src/pages/Activities/DailyTask.jsx @@ -14,6 +14,8 @@ import GlobalModel from "../../components/common/GlobalModel"; import AssignTask from "../../components/Project/AssignTask"; import SubTask from "../../components/Activities/SubTask"; import {formatNumber} from "../../utils/dateUtils"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; const DailyTask = () => { const [searchParams] = useSearchParams(); @@ -32,6 +34,8 @@ const DailyTask = () => { const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); + const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK) + const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK) const { TaskList, @@ -386,6 +390,7 @@ const DailyTask = () => {
    + { ReportTaskRights && - {task.reportedDate && ( + } + {(ApprovedTaskRights && task.reportedDate ) && ( - {projectNames?.length > 1 && ( + {/* Render dropdown menu only if there are projects or the "All Projects" option is relevant */} + {projectNames.length > 0 && (
      + {/* "All Projects" option */} +
    • + +
    • {[...projectNames] .sort((a, b) => a?.name?.localeCompare(b.name)) .map((project) => ( @@ -214,7 +217,6 @@ const Header = () => { {project?.name}{" "} {project?.shortName ? ( - {" "} ({project?.shortName}) ) : ( @@ -230,8 +232,11 @@ const Header = () => { )}
    )} - {isProjectPath && ({displayText})} + {/* Display project name on project details or directory pages */} + {isProjectPath && ({displayText})} + + {/* User Profile and Shortcuts */}
    @@ -282,7 +286,6 @@ const Header = () => { Projects - Projects List
    @@ -323,7 +326,6 @@ const Header = () => { Allocate Work - Work Allocations
    @@ -336,321 +338,12 @@ const Header = () => { Daily Work Log - Daily Work Allocations
    - - {/*
  • - -
      -
    • -
      -
      Notification
      -
      - 8 New - - - -
      -
      -
    • -
    • -
        -
      • -
        -
        -
        - -
        -
        -
        -
        Congratulation Lettie 🎉
        - - Won the monthly best seller gold badge - - 1h ago -
        - -
        -
      • -
      • -
        -
        -
        - - CF - -
        -
        -
        -
        Charles Franklin
        - - Accepted your connection - - 12hr ago -
        - -
        -
      • -
      • -
        -
        -
        - -
        -
        -
        -
        New Message ✉️
        - - You have new message from Natalie - - 1h ago -
        - -
        -
      • -
      • -
        -
        -
        - - - -
        -
        -
        -
        - Whoo! You have new order 🛒 -
        - - ACME Inc. made new order $1,154 - - 1 day ago -
        - -
        -
      • -
      • -
        -
        -
        - -
        -
        -
        -
        - Application has been approved 🚀 -
        - - Your ABC project application has been approved. - - - 2 days ago - -
        - -
        -
      • -
      • -
        -
        -
        - - - -
        -
        -
        -
        - Monthly report is generated -
        - - July monthly financial report is generated{" "} - - - 3 days ago - -
        - -
        -
      • -
      • -
        -
        -
        - -
        -
        -
        -
        Send connection request
        - - Peter sent you connection request - - - 4 days ago - -
        - -
        -
      • -
      • -
        -
        -
        - -
        -
        -
        -
        New message from Jane
        - - Your have new message from Jane - - - 5 days ago - -
        - -
        -
      • -
      • -
        -
        -
        - - - -
        -
        -
        -
        CPU is running high
        - - CPU Utilization Percent is currently at 88.63%, - - - 5 days ago - -
        - -
        -
      • -
      - -
    • -
    • - -
    • -
    -
  • */}
  • { Settings
  • - {/*
  • - - - - - Billing - - - 4 - - - -
  • */}
  • {" "} - {/* Use the function from the context */} { @@ -757,4 +433,4 @@ const Header = () => { ); }; -export default Header; +export default Header; \ No newline at end of file diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index f34586aa..206ea20f 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -88,7 +88,7 @@ const ProjectDetails = () => {
    -
  • + ); } case "teams": { From 5f8e5d16f8078e141a667d4422058e8af36bdc56 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Mon, 14 Jul 2025 15:35:45 +0530 Subject: [PATCH 115/120] Changes in Project Selection. --- src/components/Layout/Header.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index e4bf4e83..9769e26b 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -80,7 +80,7 @@ const Header = () => { ); // Determine the display text for the project dropdown - let displayText = ""; + let displayText = "All Projects"; if (selectedProject === null) { displayText = "All Projects"; } else if (selectedProject) { From 6ca72c7e5c465902e70908201a6340b8014873f9 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Tue, 15 Jul 2025 14:52:23 +0530 Subject: [PATCH 116/120] added needed closed tag --- src/pages/project/ProjectDetails.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index 206ea20f..f34586aa 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -88,7 +88,7 @@ const ProjectDetails = () => {
    - + ); } case "teams": { From 33684031888169d1a859fb48b63e8381f8b41974 Mon Sep 17 00:00:00 2001 From: Kartik sharma Date: Tue, 15 Jul 2025 15:00:49 +0530 Subject: [PATCH 117/120] Changes in Dashboard dropdown adding All Projects Selection. --- src/components/Layout/Header.jsx | 103 ++++++----- src/components/Project/AboutProject.jsx | 58 ++++--- src/components/Project/ProjectCard.jsx | 24 +-- src/pages/project/ProjectDetails.jsx | 220 ++++++++++-------------- src/pages/project/ProjectListView.jsx | 8 +- src/router/AppRoutes.jsx | 2 +- 6 files changed, 186 insertions(+), 229 deletions(-) diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 9769e26b..9023d007 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -1,3 +1,4 @@ + import getGreetingMessage from "../../utils/greetingHandler"; import { cacheData, @@ -26,6 +27,8 @@ 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 getRole = (roles, joRoleId) => { if (!Array.isArray(roles)) return "User"; @@ -99,7 +102,7 @@ const Header = () => { if ( projectNames && projectNames.length > 0 && - selectedProject === undefined && + selectedProject === undefined && !getCachedData("hasReceived") ) { dispatch(setProjectId(null)); // Set to null for "All Projects" @@ -108,7 +111,7 @@ const Header = () => { /** Check if current page is project details page or directory page */ - const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname); + // const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname); const isDirectoryPath = /^\/directory$/.test(location.pathname); const handler = useCallback( @@ -172,69 +175,61 @@ const Header = () => { id="navbar-collapse" > {/* Project Selection Dropdown */} - {projectNames && ( // Ensure projectNames is not null before rendering dropdown + {projectNames && !isDirectoryPath && (
    - {(!isProjectPath && !isDirectoryPath) && ( - <> - -
    - + +
    + - {/* Render dropdown menu only if there are projects or the "All Projects" option is relevant */} - {projectNames.length > 0 && ( -
      - {/* "All Projects" option */} -
    • + {projectNames.length > 0 && ( +
        + {/* Show "All Projects" only on dashboard */} + {isDashboard && ( +
      • + +
      • + )} + {[...projectNames] + .sort((a, b) => a?.name?.localeCompare(b.name)) + .map((project) => ( +
      • - {[...projectNames] - .sort((a, b) => a?.name?.localeCompare(b.name)) - .map((project) => ( -
      • - -
      • - ))} -
      - )} -
    - - )} + ))} + + )} +
    )} {/* Display project name on project details or directory pages */} - {isProjectPath && ({displayText})} + {/* { ({displayText})} */} {/* User Profile and Shortcuts */}