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 },
+ })
+ }
+ >
+
+

+
+ {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 && (
+
+ )}
+
+

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: },