diff --git a/src/pages/Gallary/ImageGallary.jsx b/src/pages/Gallary/ImageGallary.jsx index bfa30e79..70f3cf9c 100644 --- a/src/pages/Gallary/ImageGallary.jsx +++ b/src/pages/Gallary/ImageGallary.jsx @@ -1,4 +1,3 @@ -// ImageGallery.js import React, { useState, useEffect, useRef, useCallback } from "react"; import "./ImageGallery.css"; import { ImageGalleryAPI } from "./ImageGalleryAPI"; @@ -7,10 +6,17 @@ 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"; // Assuming this is the path to your DateRangePicker +import DateRangePicker from "../../components/common/DateRangePicker"; +import eventBus from "../../services/eventBus"; + +const PAGE_SIZE = 10; +const SCROLL_THRESHOLD = 5; const ImageGallery = () => { const [images, setImages] = useState([]); + const [allImagesData, setAllImagesData] = useState([]); + const [pageNumber, setPageNumber] = useState(1); + const [hasMore, setHasMore] = useState(true); const selectedProjectId = useSelector((store) => store.localVariables.projectId); const { openModal } = useModal(); @@ -51,8 +57,10 @@ const ImageGallery = () => { const [isFilterPanelOpen, setIsFilterPanelOpen] = useState(false); const [hoveredImage, setHoveredImage] = useState(null); const [loading, setLoading] = useState(true); + const [loadingMore, setLoadingMore] = useState(false); const imageGroupRefs = useRef({}); + const loaderRef = useRef(null); const filterPanelRef = useRef(null); const filterButtonRef = useRef(null); @@ -68,61 +76,159 @@ const ImageGallery = () => { } }; - document.addEventListener("mousedown", handleClickOutside); + if (isFilterPanelOpen) { + document.addEventListener("mousedown", handleClickOutside); + } else { + document.removeEventListener("mousedown", handleClickOutside); + } + return () => { document.removeEventListener("mousedown", handleClickOutside); }; - }, []); + }, [isFilterPanelOpen]); useEffect(() => { if (!selectedProjectId) { setImages([]); + setAllImagesData([]); setLoading(false); + setHasMore(false); return; } + setImages([]); + setPageNumber(1); + setHasMore(true); setLoading(true); - ImageGalleryAPI.ImagesGet(selectedProjectId, appliedFilters) - .then((res) => { - setImages(res.data); - }) - .catch((err) => { - console.error("Error fetching images:", err); - setImages([]); - }) - .finally(() => { - setLoading(false); - }); + setAllImagesData([]); + fetchImages(1, appliedFilters, true); }, [selectedProjectId, appliedFilters]); - const getUniqueValuesWithIds = useCallback( - (idKey, nameKey) => { - const uniqueMap = new Map(); - images.forEach((img) => { - if (img[idKey] && img[nameKey]) { - uniqueMap.set(img[idKey], img[nameKey]); - } + const fetchImages = useCallback(async (page, filters) => { + 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((prevImages) => { + const uniqueNewBatches = newBatches.filter( + (newBatch) => !prevImages.some((prevBatch) => prevBatch.batchId === newBatch.batchId) + ); + return [...prevImages, ...uniqueNewBatches]; }); - return Array.from(uniqueMap.entries()); - }, - [images] - ); + + setAllImagesData((prevAllImages) => { + const uniqueAllImages = newBatches.filter( + (newBatch) => !prevAllImages.some((prevBatch) => prevBatch.batchId === newBatch.batchId) + ); + return [...prevAllImages, ...uniqueAllImages]; + }); + + setHasMore(receivedCount === PAGE_SIZE); + } catch (err) { + console.error("Error fetching images:", err); + if (page === 1) { + setImages([]); + setAllImagesData([]); + } + setHasMore(false); + } finally { + setLoading(false); + setLoadingMore(false); + } + }, [selectedProjectId]); + + useEffect(() => { + const handleExternalEvent = (data) => { + if (selectedProjectId === data.projectId) { + setImages([]); + setAllImagesData([]); + setPageNumber(1); + setHasMore(true); + fetchImages(1, appliedFilters, true); + } + }; + + eventBus.on("image_gallery", handleExternalEvent); + + return () => { + eventBus.off("image_gallery", handleExternalEvent); + }; + }, [appliedFilters, fetchImages, selectedProjectId]); + + useEffect(() => { + if (!loaderRef.current) return; + + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting && hasMore && !loadingMore && !loading) { + setPageNumber((prevPageNumber) => prevPageNumber + 1); + } + }, + { + root: null, + rootMargin: "200px", + threshold: 0.1, + } + ); + + observer.observe(loaderRef.current); + + return () => { + if (loaderRef.current) { + observer.unobserve(loaderRef.current); + } + }; + }, [hasMore, loadingMore, loading]); + + useEffect(() => { + if (pageNumber > 1) { + fetchImages(pageNumber, appliedFilters); + } + }, [pageNumber, fetchImages, appliedFilters]); + + const getUniqueValuesWithIds = useCallback((idKey, nameKey) => { + const map = new Map(); + allImagesData.forEach(batch => { + 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); + } + }); + return Array.from(map.entries()); + }, [allImagesData]); const getUniqueUploadedByUsers = useCallback(() => { const uniqueUsersMap = new Map(); - images.forEach((img) => { - if (img.uploadedBy && img.uploadedBy.id) { - const fullName = `${img.uploadedBy.firstName || ""} ${ - img.uploadedBy.lastName || "" - }`.trim(); - if (fullName) { - uniqueUsersMap.set(img.uploadedBy.id, fullName); + allImagesData.forEach(batch => { + batch.documents.forEach(doc => { + if (doc.uploadedBy && doc.uploadedBy.id) { + const fullName = `${doc.uploadedBy.firstName || ""} ${doc.uploadedBy.lastName || ""}`.trim(); + if (fullName) { + uniqueUsersMap.set(doc.uploadedBy.id, fullName); + } } - } + }); }); return Array.from(uniqueUsersMap.entries()); - }, [images]); + }, [allImagesData]); const buildings = getUniqueValuesWithIds("buildingId", "buildingName"); const floors = getUniqueValuesWithIds("floorIds", "floorName"); @@ -191,9 +297,35 @@ const ImageGallery = () => { startDate: selectedFilters.startDate || null, endDate: selectedFilters.endDate || null, }; - setAppliedFilters(payload); - setIsFilterPanelOpen(false); - }, [selectedFilters]); + + const areFiltersChanged = Object.keys(payload).some(key => { + const oldVal = appliedFilters[key]; + const newVal = payload[key]; + + if (Array.isArray(oldVal) && Array.isArray(newVal)) { + if (oldVal.length !== newVal.length) return true; + const oldSet = new Set(oldVal); + const newSet = new Set(newVal); + if (oldSet.size !== newSet.size) return true; + for (const item of newSet) { + if (!oldSet.has(item)) return true; + } + return false; + } + if ((oldVal === null && newVal === "") || (oldVal === "" && newVal === null)) { + return false; + } + return oldVal !== newVal; + }); + + if (areFiltersChanged) { + setAppliedFilters(payload); + setImages([]); + setPageNumber(1); + setHasMore(true); + } + // Removed setIsFilterPanelOpen(false); to keep the drawer open + }, [selectedFilters, appliedFilters]); const handleClearAllFilters = useCallback(() => { const initialStateSelected = { @@ -219,49 +351,11 @@ const ImageGallery = () => { endDate: null, }; setAppliedFilters(initialStateApplied); + setImages([]); + setPageNumber(1); + setHasMore(true); }, []); - const filteredImages = images.filter((img) => { - const uploadedAtMoment = moment(img.uploadedAt); - const startDateMoment = appliedFilters.startDate - ? moment(appliedFilters.startDate) - : null; - const endDateMoment = appliedFilters.endDate - ? moment(appliedFilters.endDate) - : null; - - const isWithinDateRange = - (!startDateMoment || uploadedAtMoment.isSameOrAfter(startDateMoment, "day")) && - (!endDateMoment || uploadedAtMoment.isSameOrBefore(endDateMoment, "day")); - - const passesCategoryFilters = - (appliedFilters.buildingIds === null || - appliedFilters.buildingIds.includes(img.buildingId)) && - (appliedFilters.floorIds === null || - appliedFilters.floorIds.includes(img.floorIds)) && - (appliedFilters.activityIds === null || - appliedFilters.activityIds.includes(img.activityId)) && - (appliedFilters.workAreaIds === null || - appliedFilters.workAreaIds.includes(img.workAreaId)) && - (appliedFilters.uploadedByIds === null || - appliedFilters.uploadedByIds.includes(img.uploadedBy?.id)) && - (appliedFilters.workCategoryIds === null || - appliedFilters.workCategoryIds.includes(img.workCategoryId)); - - return isWithinDateRange && passesCategoryFilters; - }); - - const imagesByActivityUser = {}; - filteredImages.forEach((img) => { - const userName = `${img.uploadedBy?.firstName || ""} ${ - img.uploadedBy?.lastName || "" - }`.trim(); - const workArea = img.workAreaName || "Unknown"; - const key = `${img.activityName}__${userName}__${workArea}`; - if (!imagesByActivityUser[key]) imagesByActivityUser[key] = []; - imagesByActivityUser[key].push(img); - }); - const scrollLeft = useCallback((key) => { imageGroupRefs.current[key]?.scrollBy({ left: -200, behavior: "smooth" }); }, []); @@ -307,7 +401,15 @@ const ImageGallery = () => { {!collapsedFilters[type] && (
Date: {hoverDate} @@ -423,7 +542,7 @@ const ImageGallery = () => { Time: {hoverTime}
- Activity: {img.activityName} + Activity: {batch.activityName}
+ !loading &&
No images match the selected filters.
)} + +You've reached the end of the images.
+ )} +- 🏢 Location: {image.buildingName} >{" "} - {image.floorName} > {image.activityName} + 🏢 Location: {buildingName} > {floorName} >{" "} + {workAreaName || "Unknown"} > {activityName}
- 📝 Comments: {image.comment} + {/* Display the comment from the batch object */} + 📝 Comments: {batchComment || "N/A"}