From ad1bef4f7b6d3cd44015aaeb42c71c9334b7e4b2 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Mon, 6 Oct 2025 16:05:22 +0530 Subject: [PATCH 1/2] added gallery and gallery filter --- src/components/gallary/GalleryFilterPanel.jsx | 128 +++++++++++++ src/components/gallary/GallerySchema.jsx | 25 +++ .../gallary}/ImageGallery.css | 0 .../gallary/ImageGalleryListView.jsx | 169 ++++++++++++++++++ src/components/gallary/ViewGallery.jsx | 111 ++++++++++++ src/hooks/useImageGallery.js | 117 +++--------- src/pages/Gallary/ImageGallary.jsx | 2 +- src/pages/Gallary/ImageGallaryPage.jsx | 124 +++++++++++++ src/repositories/ImageGalleryAPI.jsx | 10 +- src/router/AppRoutes.jsx | 3 +- 10 files changed, 590 insertions(+), 99 deletions(-) create mode 100644 src/components/gallary/GalleryFilterPanel.jsx create mode 100644 src/components/gallary/GallerySchema.jsx rename src/{pages/Gallary => components/gallary}/ImageGallery.css (100%) create mode 100644 src/components/gallary/ImageGalleryListView.jsx create mode 100644 src/components/gallary/ViewGallery.jsx create mode 100644 src/pages/Gallary/ImageGallaryPage.jsx diff --git a/src/components/gallary/GalleryFilterPanel.jsx b/src/components/gallary/GalleryFilterPanel.jsx new file mode 100644 index 00000000..f6e1c6c2 --- /dev/null +++ b/src/components/gallary/GalleryFilterPanel.jsx @@ -0,0 +1,128 @@ +import React, { useState } from "react"; +import { useImageGalleryFilter } from "../../hooks/useImageGallery"; +import { useSelectedProject } from "../../slices/apiDataManager"; +import { FormProvider, useForm } from "react-hook-form"; +import Label from "../common/Label"; +import { DateRangePicker1 } from "../common/DateRangePicker"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { defaultGalleryFilterValue, gallerySchema } from "./GallerySchema"; +import SelectMultiple from "../common/SelectMultiple"; + +const GalleryFilterPanel = () => { + const selectedProject = useSelectedProject(); + const [resetKey, setResetKey] = useState(0); + const { data, isLoading, isError, error } = + useImageGalleryFilter(selectedProject); + + const methods = useForm({ + resolver: zodResolver(gallerySchema), + defaultValues: defaultGalleryFilterValue, + }); + + const { + handleSubmit, + register, + setValue, + formState: { errors }, + } = methods; + + const onSubmit = (formData) => { + console.log(formData); + }; + + if (isLoading) return
Loading....
; + if (isError) return
{error.message}
; + return ( +
+ +
+
+ + +
+
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ + +
+
+
+
+ ); +}; + +export default GalleryFilterPanel; diff --git a/src/components/gallary/GallerySchema.jsx b/src/components/gallary/GallerySchema.jsx new file mode 100644 index 00000000..1ea5b8b4 --- /dev/null +++ b/src/components/gallary/GallerySchema.jsx @@ -0,0 +1,25 @@ +import { z } from "zod"; + +export const gallerySchema = z.object({ + buildingIds: z.array(z.string()).optional(), + floorIds: z.array(z.string()).optional(), + workAreaIds: z.array(z.string()).optional(), + activityIds: z.array(z.string()).optional(), + workCategoryIds: z.array(z.string()).optional(), + startDate: z.string().optional(), + endDate: z.string().optional(), + uploadedByIds: z.array(z.string()).optional(), + serviceIds: z.array(z.string()).optional(), +}); + +export const defaultGalleryFilterValue = { + buildingIds: [], + floorIds: [], + workAreaIds: [], + activityIds: [], + workCategoryIds:[], + startDate: null, + endDate: null, + uploadedByIds:[], + serviceIds: [], +}; diff --git a/src/pages/Gallary/ImageGallery.css b/src/components/gallary/ImageGallery.css similarity index 100% rename from src/pages/Gallary/ImageGallery.css rename to src/components/gallary/ImageGallery.css diff --git a/src/components/gallary/ImageGalleryListView.jsx b/src/components/gallary/ImageGalleryListView.jsx new file mode 100644 index 00000000..32960681 --- /dev/null +++ b/src/components/gallary/ImageGalleryListView.jsx @@ -0,0 +1,169 @@ +import React, { useRef, useState, useCallback, useEffect } from "react"; +import moment from "moment"; +import Avatar from "../../components/common/Avatar"; +import { useGalleryContext } from "../../pages/Gallary/ImageGallaryPage"; +import useImageGallery from "../../hooks/useImageGallery"; +import { useSelectedProject } from "../../slices/apiDataManager"; +import { ITEMS_PER_PAGE } from "../../utils/constants"; +import Pagination from "../common/Pagination"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import Loader from "../common/Loader"; + +const ImageGalleryListView = () => { + const [hoveredImage, setHoveredImage] = useState(null); + const selectedProject = useSelectedProject(); + const [currentPage, setCurrentPage] = useState(1); + const { setOpenGallery } = useGalleryContext(); + + const { data, isLoading, isError, error } = useImageGallery( + selectedProject, + currentPage, + 10, + {} + ); + + const paginate = (page) => { + if (page >= 1 && page <= (data?.totalPages ?? 1)) { + setCurrentPage(page); + } + }; + + if (!data?.data?.length && !isLoading) { + return ( +

+ {selectedProject ? " No images match the selected filters.":"Please Select Project!"} +

+ ); + } + + if (isLoading) { + return ( +
+ +
+ ); + } + + return ( +
+
+ {data?.data?.map((batch) => { + if (!batch.documents?.length) return null; + + const doc = batch.documents[0]; + const userName = `${doc.uploadedBy?.firstName || ""} ${ + doc.uploadedBy?.lastName || "" + }`.trim(); + const date = formatUTCToLocalTime(doc.uploadedAt); + // const hasArrows = batch.documents.length > scrollThreshold; + + return ( +
+
+
+ +
+ {userName} + {date} +
+
+ +
+
+ + {batch.buildingName} + + + + {batch.floorName} + + + + {batch.workAreaName || "Unknown"} + + {batch.activityName} + +
+ {batch.workCategoryName && ( + + {batch.workCategoryName} + + )} +
+
+ +
+ {/* {hasArrows && ( + + )} */} +
(imageGroupRefs.current[batch.batchId] = el)} + > + {batch.documents.map((d, i) => { + const hoverDate = moment().format("DD MMMM, YYYY"); + const hoverTime = moment(d.uploadedAt).format("hh:mm A"); + + return ( +
setHoveredImage(d)} + onMouseLeave={() => setHoveredImage(null)} + onClick={() => + setOpenGallery({ + isOpen: true, + data: { data: batch, index: i }, + }) + } + > +
+ {`Image +
+ {hoveredImage === d && ( +
+

+ Date: {hoverDate} +

+

+ Time: {hoverTime} +

+

+ Activity: {batch.activityName} +

+
+ )} +
+ ); + })} +
+ {/* {hasArrows && ( + + )} */} +
+
+ ); + })} +
+ + {data?.data?.length > 0 && ( + + )} +
+ ); +}; + +export default ImageGalleryListView; diff --git a/src/components/gallary/ViewGallery.jsx b/src/components/gallary/ViewGallery.jsx new file mode 100644 index 00000000..869284e8 --- /dev/null +++ b/src/components/gallary/ViewGallery.jsx @@ -0,0 +1,111 @@ +import React, { useState, useEffect } from "react"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; + +const ViewGallery = ({ batch, index }) => { + const [loading, setLoading] = useState(true); + const [currentIndex, setCurrentIndex] = useState(index); + console.log(batch); + useEffect(() => { + setCurrentIndex(index); + }, [index, batch]); + + if (!batch || !batch.documents || batch.documents.length === 0) return null; + + const image = batch.documents[currentIndex]; + if (!image) return null; + + const fullName = `${image.uploadedBy?.firstName || ""} ${ + image.uploadedBy?.lastName || "" + }`.trim(); + const date = formatUTCToLocalTime(image.uploadedAt); + + const buildingName = batch.buildingName; + const floorName = batch.floorName; + const workAreaName = batch.workAreaName; + const activityName = batch.activityName; + const batchComment = batch.comment; + + const handlePrev = () => { + setCurrentIndex((prevIndex) => Math.max(0, prevIndex - 1)); + }; + + const handleNext = () => { + setCurrentIndex((prevIndex) => + Math.min(batch.documents.length - 1, prevIndex + 1) + ); + }; + + const hasPrev = currentIndex > 0; + const hasNext = currentIndex < batch.documents.length - 1; + + return ( +
+ + {loading &&

Loading...

} +
+ {hasPrev && ( + + )} + + Preview setLoading(false)} + /> + + {hasNext && ( + + )} +
+ + {/* Details */} +
+

+ + Uploaded By: + {fullName} +

+ +

+ + Date: + {date} +

+ +

+ + Location: + + {buildingName} {floorName}{" "} + {workAreaName || "Unknown"}{" "} + {activityName} + +

+ +

+ + Comment: + {batchComment} +

+
+
+ ); +}; + +export default ViewGallery; diff --git a/src/hooks/useImageGallery.js b/src/hooks/useImageGallery.js index b1511d93..85f2c41b 100644 --- a/src/hooks/useImageGallery.js +++ b/src/hooks/useImageGallery.js @@ -1,116 +1,45 @@ import { useState, useCallback } from "react"; // import { ImageGalleryAPI } from "../repositories/ImageGalleyRepository"; -import { ImageGalleryAPI } from "../repositories/ImageGalleryAPI"; +import { ImageGalleryRepository } from "../repositories/ImageGalleryAPI"; -// const PAGE_SIZE = 10; -// const useImageGallery = (selectedProjectId) => { -// const [images, setImages] = useState([]); -// const [allImagesData, setAllImagesData] = useState([]); -// const [pageNumber, setPageNumber] = useState(1); -// const [hasMore, setHasMore] = useState(true); -// const [loading, setLoading] = useState(false); -// const [loadingMore, setLoadingMore] = useState(false); - -// const fetchImages = useCallback(async (page = 1, filters = {}, reset = false) => { -// if (!selectedProjectId) return; - -// try { -// if (page === 1) { -// setLoading(true); -// } else { -// setLoadingMore(true); -// } - -// const res = await ImageGalleryAPI.ImagesGet( -// selectedProjectId, -// filters, -// page, -// PAGE_SIZE -// ); - -// const newBatches = res.data || []; -// const receivedCount = newBatches.length; - -// setImages((prev) => { -// if (page === 1 || reset) return newBatches; -// const uniqueNew = newBatches.filter( -// (batch) => !prev.some((b) => b.batchId === batch.batchId) -// ); -// return [...prev, ...uniqueNew]; -// }); - -// setAllImagesData((prev) => { -// if (page === 1 || reset) return newBatches; -// const uniqueAll = newBatches.filter( -// (batch) => !prev.some((b) => b.batchId === batch.batchId) -// ); -// return [...prev, ...uniqueAll]; -// }); - -// setHasMore(receivedCount === PAGE_SIZE); -// } catch (error) { -// console.error("Error fetching images:", error); -// if (page === 1) { -// setImages([]); -// setAllImagesData([]); -// } -// setHasMore(false); -// } finally { -// setLoading(false); -// setLoadingMore(false); -// } -// }, [selectedProjectId]); - -// const resetGallery = useCallback(() => { -// setImages([]); -// setAllImagesData([]); -// setPageNumber(1); -// setHasMore(true); -// }, []); - -// return { -// images, -// allImagesData, -// pageNumber, -// setPageNumber, -// hasMore, -// loading, -// loadingMore, -// fetchImages, -// resetGallery, -// }; -// }; - -// export default useImageGallery; -import { useInfiniteQuery } from "@tanstack/react-query"; +import { useInfiniteQuery, useQuery } from "@tanstack/react-query"; const PAGE_SIZE = 10; -const useImageGallery = (selectedProjectId, filters) => { +const useImageGallery = (selectedProjectId,pageNumber, pageSize, filters) => { const hasFilters = filters && Object.values(filters).some( value => Array.isArray(value) ? value.length > 0 : value !== null && value !== "" ); - return useInfiniteQuery({ - queryKey: ["imageGallery", selectedProjectId, hasFilters ? filters : null], + return useQuery({ + queryKey: ["imageGallery", selectedProjectId, pageNumber, pageSize, hasFilters ? filters : null], enabled: !!selectedProjectId, - getNextPageParam: (lastPage, allPages) => { - if (!lastPage?.data?.length) return undefined; - return allPages.length + 1; - }, - queryFn: async ({ pageParam = 1 }) => { - const res = await ImageGalleryAPI.ImagesGet( + + queryFn: async () => { + const res = await ImageGalleryRepository.ImagesGet( selectedProjectId, + pageNumber, + pageSize, hasFilters ? filters : undefined, - pageParam, - PAGE_SIZE ); - return res; + return res.data; }, }); }; export default useImageGallery; + + +export const useImageGalleryFilter = (project)=>{ + return useQuery({ + queryKey:["imageGalleryFlter",project], + queryFn:async() => { + const response = await ImageGalleryRepository.getImageGalleryFilter(project); + return response.data; + }, + }) +} + diff --git a/src/pages/Gallary/ImageGallary.jsx b/src/pages/Gallary/ImageGallary.jsx index d92ce8ca..75bbc2b6 100644 --- a/src/pages/Gallary/ImageGallary.jsx +++ b/src/pages/Gallary/ImageGallary.jsx @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef, useCallback } from "react"; -import "./ImageGallery.css"; +// import "./ImageGallery.css"; import moment from "moment"; import { useDispatch, useSelector } from "react-redux"; import { useModal } from "./ModalContext"; diff --git a/src/pages/Gallary/ImageGallaryPage.jsx b/src/pages/Gallary/ImageGallaryPage.jsx new file mode 100644 index 00000000..a230a44b --- /dev/null +++ b/src/pages/Gallary/ImageGallaryPage.jsx @@ -0,0 +1,124 @@ +import React, { + useState, + useEffect, + useRef, + useContext, + createContext, +} from "react"; +import { useDispatch, useSelector } from "react-redux"; +import moment from "moment"; +import eventBus from "../../services/eventBus"; +import Breadcrumb from "../../components/common/Breadcrumb"; +import { formatUTCToLocalTime } from "../../utils/dateUtils"; +import useImageGallery from "../../hooks/useImageGallery"; +import { + useProjectAssignedServices, + useProjectName, +} from "../../hooks/useProjects"; +import { setProjectId } from "../../slices/localVariablesSlice"; +import ImageGalleryListView from "../../components/gallary/ImageGalleryListView"; +import "../../components/gallary/ImageGallery.css"; +import { useSelectedProject } from "../../slices/apiDataManager"; +import GlobalModel from "../../components/common/GlobalModel"; +import ViewGallery from "../../components/gallary/ViewGallery"; +import { useFab } from "../../Context/FabContext"; +import GalleryFilterPanel from "../../components/gallary/GalleryFilterPanel"; + +const GalleryContext = createContext(); + +export const useGalleryContext = () => { + let context = useContext(GalleryContext); + + if (!context) { + throw new Error("Error"); + } + return context; +}; + +const ImageGalleryPage = () => { + const selectedProjectId = useSelectedProject(); + const { projectNames } = useProjectName(); + + const [openGallery, setOpenGallery] = useState({ isOpen: false, data: null }); + + const { data: assignedServices = [], isLoading } = + useProjectAssignedServices(selectedProjectId); + + const [selectedService, setSelectedService] = useState(""); + + const handleServiceChange = (e) => { + setSelectedService(e.target.value); + }; + + const contextMessager = { + setOpenGallery, + }; + + const { setOffcanvasContent, setShowTrigger } = useFab(); + + useEffect(()=>{ + setShowTrigger(true); + setOffcanvasContent("Gallery Filter",); + + return ()=>{ + setOffcanvasContent("",null) + setShowTrigger(false); + } + },[]) + + + return ( + +
+ + +
+ {selectedProjectId && ( +
+ {!isLoading && assignedServices?.length === 0 ? ( + + Not service assiged yet + + ) : ( + + )} +
+ )} + + +
+ + {openGallery?.isOpen && ( + setOpenGallery({ isOpen: false, data: null })} + > + + + )} +
+
+ ); +}; + +export default ImageGalleryPage; diff --git a/src/repositories/ImageGalleryAPI.jsx b/src/repositories/ImageGalleryAPI.jsx index 5f75dbed..aa1062c5 100644 --- a/src/repositories/ImageGalleryAPI.jsx +++ b/src/repositories/ImageGalleryAPI.jsx @@ -1,8 +1,12 @@ import { api } from "../utils/axiosClient"; -export const ImageGalleryAPI = { - ImagesGet: (projectId, filter, pageNumber, pageSize) => { +export const ImageGalleryRepository = { + ImagesGet: (projectId, pageNumber, pageSize,filter) => { const payloadJsonString = JSON.stringify(filter); - return api.get(`/api/image/images/${projectId}?filter=${payloadJsonString}&pageNumber=${pageNumber}&pageSize=${pageSize}`); + return api.get(`/api/image/images/${projectId}?pageNumber=${pageNumber}&pageSize=${pageSize}&filter=${payloadJsonString}`); }, + + + getImageGalleryFilter:(projectId)=>api.get(`/api/Image/filter/${projectId}`) + }; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 61ed40c8..48dd5dd2 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -53,6 +53,7 @@ import TenantSelectionPage from "../pages/authentication/TenantSelectionPage"; import DailyProgrssReport from "../pages/DailyProgressReport/DailyProgrssReport"; import ProjectPage from "../pages/project/ProjectPage"; import { ComingSoonPage } from "../pages/Misc/ComingSoonPage"; +import ImageGalleryPage from "../pages/Gallary/ImageGallaryPage"; const router = createBrowserRouter( [ { @@ -93,7 +94,7 @@ const router = createBrowserRouter( { path: "/activities/records/:projectId?", element: }, { path: "/activities/task", element: }, { path: "/activities/reports", element: }, - { path: "/gallary", element: }, + { path: "/gallary", element: }, { path: "/expenses", element: }, { path: "/masters", element: }, { path: "/tenants", element: }, From 8bcfcc5718bc9342a34d00ee9add1ed9a8eb4479 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Tue, 7 Oct 2025 12:17:27 +0530 Subject: [PATCH 2/2] fixed attendance - check In -out and persisted date range from Redux store for attendance logs --- src/components/Activities/Attendance.jsx | 4 +- src/components/Activities/AttendcesLogs.jsx | 78 +++++------ .../Activities/CheckCheckOutForm.jsx | 12 +- src/components/common/DateRangePicker.jsx | 48 ++++--- src/hooks/useAttendance.js | 127 +++++++++++------- src/pages/Activities/AttendancePage.jsx | 36 ++--- src/slices/localVariablesSlice.jsx | 30 +++-- 7 files changed, 188 insertions(+), 147 deletions(-) diff --git a/src/components/Activities/Attendance.jsx b/src/components/Activities/Attendance.jsx index a4443dc9..9a082976 100644 --- a/src/components/Activities/Attendance.jsx +++ b/src/components/Activities/Attendance.jsx @@ -12,7 +12,7 @@ import { useQueryClient } from "@tanstack/react-query"; import eventBus from "../../services/eventBus"; import { useSelectedProject } from "../../slices/apiDataManager"; -const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizationId, includeInactive, date }) => { +const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizationId, }) => { const queryClient = useQueryClient(); const [loading, setLoading] = useState(false); const navigate = useNavigate(); @@ -24,7 +24,7 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat loading: attLoading, recall: attrecall, isFetching - } = useAttendance(selectedProject, organizationId, includeInactive, date); + } = useAttendance(selectedProject, organizationId); const filteredAttendance = ShowPending ? attendance?.filter( (att) => att?.checkInTime !== null && att?.checkOutTime === null diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 71c7328a..267bec1d 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -5,7 +5,11 @@ import { convertShortTime } from "../../utils/dateUtils"; import RenderAttendanceStatus from "./RenderAttendanceStatus"; import { useSelector, useDispatch } from "react-redux"; import DateRangePicker from "../common/DateRangePicker"; -import { clearCacheKey, getCachedData, useSelectedProject } from "../../slices/apiDataManager"; +import { + clearCacheKey, + getCachedData, + useSelectedProject, +} from "../../slices/apiDataManager"; import eventBus from "../../services/eventBus"; import AttendanceRepository from "../../repositories/AttendanceRepository"; import { useAttendancesLogs } from "../../hooks/useAttendance"; @@ -33,7 +37,7 @@ const usePagination = (data, itemsPerPage) => { }; }; -const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { +const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { // const selectedProject = useSelector( // (store) => store.localVariables.projectId // ); @@ -41,7 +45,7 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); const dispatch = useDispatch(); const [loading, setLoading] = useState(false); - const [showPending, setShowPending] = useState(false) + const [showPending, setShowPending] = useState(false); const [isRefreshing, setIsRefreshing] = useState(false); const [processedData, setProcessedData] = useState([]); @@ -151,33 +155,6 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { }); }, [processedData, searchTerm]); -// const filteredSearchData = useMemo(() => { -// let tempData = processedData; - -// if (searchTerm) { -// const lowercasedSearchTerm = searchTerm.toLowerCase(); -// tempData = tempData.filter((item) => { -// const fullName = `${item.firstName} ${item.lastName}`.toLowerCase(); -// return fullName.includes(lowercasedSearchTerm); -// }); -// } - -// if (filters?.selectedOrganization) { -// tempData = tempData.filter( -// (item) => item.organization?.name === filters.selectedOrganization -// ); -// } - -// if (filters?.selectedServices?.length > 0) { -// tempData = tempData.filter((item) => -// filters.selectedServices.includes(item.service?.name) -// ); -// } - -// return tempData; -// }, [processedData, searchTerm, filters]); - - const { currentPage, totalPages, @@ -235,7 +212,7 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { // }) // ); - refetch() + refetch(); } }, [selectedProject, dateRange, data, refetch] @@ -270,11 +247,16 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { - -
+
{isLoading ? ( -
+

Loading...

) : filteredSearchData?.length > 0 ? ( @@ -287,7 +269,8 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { Date Organization - Check-In + {" "} + Check-In Check-Out @@ -303,9 +286,9 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { const previousAttendance = arr[index - 1]; const previousDate = previousAttendance ? moment( - previousAttendance.checkInTime || - previousAttendance.checkOutTime - ).format("YYYY-MM-DD") + previousAttendance.checkInTime || + previousAttendance.checkOutTime + ).format("YYYY-MM-DD") : null; if (!previousDate || currentDate !== previousDate) { @@ -344,7 +327,7 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { attendance.checkInTime || attendance.checkOutTime ).format("DD-MMM-YYYY")} - {attendance.organizationName || "--"} + {attendance.organizationName || "--"} {convertShortTime(attendance.checkInTime)} {attendance.checkOutTime @@ -366,7 +349,12 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { ) : ( -
No data available for the selected date range. Please Select another date.
+
+ + No data available for the selected date range. Please Select + another date. + +
)}
{paginatedAttendances?.length == 0 && filteredSearchData?.length > 0 && ( @@ -392,8 +380,9 @@ const AttendanceLog = ({ handleModalData, searchTerm ,organizationId}) => { (pageNumber) => (
  • ); }; @@ -76,6 +87,7 @@ export default DateRangePicker; + export const DateRangePicker1 = ({ startField = "startDate", endField = "endDate", diff --git a/src/hooks/useAttendance.js b/src/hooks/useAttendance.js index 7929223d..70c06cdd 100644 --- a/src/hooks/useAttendance.js +++ b/src/hooks/useAttendance.js @@ -1,5 +1,9 @@ import { useEffect, useState } from "react"; -import { cacheData, getCachedData, useSelectedProject } from "../slices/apiDataManager"; +import { + cacheData, + getCachedData, + useSelectedProject, +} from "../slices/apiDataManager"; import AttendanceRepository from "../repositories/AttendanceRepository"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import showToast from "../services/toastService"; @@ -7,11 +11,8 @@ import { useDispatch, useSelector } from "react-redux"; import { store } from "../store/store"; import { setDefaultDateRange } from "../slices/localVariablesSlice"; - - // ----------------------------Query----------------------------- - // export const useAttendance = (projectId) => { // const dispatch = useDispatch() // const { @@ -41,7 +42,10 @@ import { setDefaultDateRange } from "../slices/localVariablesSlice"; // }; // }; -export const useAttendance = (projectId, organizationId, includeInactive = false, date = null) => { +export const useAttendance = ( + projectId, + organizationId, +) => { const dispatch = useDispatch(); const { @@ -51,13 +55,11 @@ export const useAttendance = (projectId, organizationId, includeInactive = false refetch: recall, isFetching, } = useQuery({ - queryKey: ["attendance", projectId, organizationId, includeInactive, date], // include filters in cache key + queryKey: ["attendance", projectId, organizationId], // include filters in cache key queryFn: async () => { const response = await AttendanceRepository.getAttendance( projectId, organizationId, - includeInactive, - date ); return response.data; }, @@ -70,12 +72,17 @@ export const useAttendance = (projectId, organizationId, includeInactive = false return { attendance, loading, error, recall, isFetching }; }; -export const useAttendancesLogs = (projectId, fromDate, toDate,organizationId) => { +export const useAttendancesLogs = ( + projectId, + fromDate, + toDate, + organizationId +) => { const dispatch = useDispatch(); const enabled = !!projectId && !!fromDate && !!toDate; const query = useQuery({ - queryKey: ['attendanceLogs', projectId, fromDate, toDate,organizationId], + queryKey: ["attendanceLogs", projectId, fromDate, toDate, organizationId], queryFn: async () => { const res = await AttendanceRepository.getAttendanceFilteredByDate( projectId, @@ -87,7 +94,7 @@ export const useAttendancesLogs = (projectId, fromDate, toDate,organizationId) = }, enabled, }); - + useEffect(() => { if (query.data && fromDate && toDate) { dispatch( @@ -101,8 +108,6 @@ export const useAttendancesLogs = (projectId, fromDate, toDate,organizationId) = return query; }; - - export const useEmployeeAttendacesLog = (id) => { const { data: logs = [], @@ -117,7 +122,10 @@ export const useEmployeeAttendacesLog = (id) => { }, enabled: !!id, onError: (error) => { - showToast(error.message || "Error while fetching Attendance Logs", "error"); + showToast( + error.message || "Error while fetching Attendance Logs", + "error" + ); }, }); @@ -135,16 +143,22 @@ export const useAttendanceByEmployee = (employeeId, fromDate, toDate) => { return useQuery({ queryKey: ["employeeAttendance", employeeId, fromDate, toDate], queryFn: async () => { - const res = await AttendanceRepository.getAttendanceByEmployee(employeeId, fromDate, toDate); + const res = await AttendanceRepository.getAttendanceByEmployee( + employeeId, + fromDate, + toDate + ); return res.data; }, - enabled + enabled, }); }; - - -export const useRegularizationRequests = (projectId, organizationId, IncludeInActive = false) => { +export const useRegularizationRequests = ( + projectId, + organizationId, + IncludeInActive = false +) => { const dispatch = useDispatch(); const { @@ -159,7 +173,7 @@ export const useRegularizationRequests = (projectId, organizationId, IncludeInAc const response = await AttendanceRepository.getRegularizeList( projectId, organizationId, - IncludeInActive, + IncludeInActive ); return response.data; }, @@ -172,48 +186,61 @@ export const useRegularizationRequests = (projectId, organizationId, IncludeInAc return { regularizes, loading, error, recall, isFetching }; }; - // -------------------Mutation-------------------------------------- export const useMarkAttendance = () => { const queryClient = useQueryClient(); const selectedProject = useSelectedProject(); - const selectedDateRange = useSelector((store)=>store.localVariables.defaultDateRange) + const selectedDateRange = useSelector( + (store) => store.localVariables.attendance.defaultDateRange + ); + const selectedOrganization = useSelector( + (store) => store.localVariables.attendance.SelectedOrg + ); return useMutation({ - mutationFn: async ({payload,forWhichTab}) => { + mutationFn: async ({ payload, forWhichTab }) => { const res = await AttendanceRepository.markAttendance(payload); return res.data; }, - onSuccess: (data,variables) => { - if(variables.forWhichTab == 1){ - queryClient.setQueryData(["attendance",selectedProject], (oldData) => { - if (!oldData) return oldData; - return oldData.map((emp) => - emp.employeeId === data.employeeId ? { ...emp, ...data } : emp - ); - }); - }else if(variables.forWhichTab == 2){ - // queryClient.invalidateQueries({ - // queryKey: ["attendanceLogs"], - // }); - queryClient.setQueryData(["attendanceLogs",selectedProject,selectedDateRange.startDate,selectedDateRange.endDate], (oldData) => { - if (!oldData) return oldData; - return oldData.map((record) => - record.id === data.id ? { ...record, ...data } : record - ); - }); - queryClient.invalidateQueries({queryKey:["regularizedList"]}) - }else( - queryClient.setQueryData(["regularizedList",selectedProject], (oldData) => { - if (!oldData) return oldData; - return oldData.filter((record) => record.id !== data.id) - }), - queryClient.invalidateQueries({queryKey:["attendanceLogs"]}) - ) + onSuccess: (data, variables) => { + if (variables.forWhichTab == 1) { + queryClient.setQueryData(["attendance", selectedProject,selectedOrganization], (oldData) => { - if(variables.forWhichTab !== 3) showToast("Attendance marked successfully", "success"); + if (!oldData) return oldData; + return oldData.map((emp) => + emp.employeeId === data.employeeId ? { ...emp, ...data } : emp + ); + }); + } else if (variables.forWhichTab == 2) { + queryClient.setQueryData( + [ + "attendanceLogs", + selectedProject, + selectedDateRange.startDate, + selectedDateRange.endDate,selectedOrganization + ], + (oldData) => { + if (!oldData) return oldData; + return oldData.map((record) => + record.id === data.id ? { ...record, ...data } : record + ); + } + ); + queryClient.invalidateQueries({ queryKey: ["regularizedList"] }); + } else + queryClient.setQueryData( + ["regularizedList", selectedProject], + (oldData) => { + if (!oldData) return oldData; + return oldData.filter((record) => record.id !== data.id); + } + ), + queryClient.invalidateQueries({ queryKey: ["attendanceLogs"] }); + + if (variables.forWhichTab !== 3) + showToast("Attendance marked successfully", "success"); }, onError: (error) => { showToast(error.message || "Failed to mark attendance", "error"); diff --git a/src/pages/Activities/AttendancePage.jsx b/src/pages/Activities/AttendancePage.jsx index 4cd5956a..fcf8c9b4 100644 --- a/src/pages/Activities/AttendancePage.jsx +++ b/src/pages/Activities/AttendancePage.jsx @@ -16,7 +16,10 @@ import { setProjectId } from "../../slices/localVariablesSlice"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { REGULARIZE_ATTENDANCE } from "../../utils/constants"; import eventBus from "../../services/eventBus"; -import { useProjectAssignedOrganizations, useProjectName } from "../../hooks/useProjects"; +import { + useProjectAssignedOrganizations, + useProjectName, +} from "../../hooks/useProjects"; import GlobalModel from "../../components/common/GlobalModel"; import CheckCheckOutmodel from "../../components/Activities/CheckCheckOutForm"; import AttendLogs from "../../components/Activities/AttendLogs"; @@ -101,11 +104,11 @@ const AttendancePage = () => { {(modelConfig?.action === 0 || modelConfig?.action === 1 || modelConfig?.action === 2) && ( - - )} + + )} {/* For view logs */} {modelConfig?.action === 6 && ( @@ -134,8 +137,9 @@ const AttendancePage = () => {
  • - - diff --git a/src/slices/localVariablesSlice.jsx b/src/slices/localVariablesSlice.jsx index 784100e0..b7885cf7 100644 --- a/src/slices/localVariablesSlice.jsx +++ b/src/slices/localVariablesSlice.jsx @@ -4,10 +4,11 @@ const localVariablesSlice = createSlice({ name: "localVariables", initialState: { selectedMaster: "Application Role", - regularizationCount: 0, - defaultDateRange: { - startDate: null, - endDate: null, + // Attendances + attendance: { + regularizationCount: 0, + defaultDateRange: { startDate: null, endDate: null }, + SelectedOrg:null, }, projectId: null, reload: false, @@ -28,9 +29,20 @@ const localVariablesSlice = createSlice({ changeMaster: (state, action) => { state.selectedMaster = action.payload; }, + + + // ─── ATTENDANCE ───────────────────────── updateRegularizationCount: (state, action) => { - state.regularizationCount = action.payload; + state.attendance.regularizationCount = action.payload; }, + setDefaultDateRange: (state, action) => { + state.attendance.defaultDateRange = action.payload; + }, + setOrganization:(state,action)=>{ + state.attendance.SelectedOrg = action.payload; + }, + + setProjectId: (state, action) => { localStorage.setItem("project", null); state.projectId = action.payload; @@ -39,10 +51,6 @@ const localVariablesSlice = createSlice({ refreshData: (state, action) => { state.reload = action.payload; }, - setDefaultDateRange: (state, action) => { - state.defaultDateRange = action.payload; - }, - openOrgModal: (state, action) => { state.OrganizationModal.isOpen = true; state.OrganizationModal.orgData = action.payload?.orgData || null; @@ -83,6 +91,6 @@ export const { closeOrgModal, toggleOrgModal, openAuthModal, - closeAuthModal, + closeAuthModal,setOrganization } = localVariablesSlice.actions; -export default localVariablesSlice.reducer; \ No newline at end of file +export default localVariablesSlice.reducer;