diff --git a/src/pages/Gallary/ImageGallary.jsx b/src/pages/Gallary/ImageGallary.jsx
index 5b3fca0d..ba023adb 100644
--- a/src/pages/Gallary/ImageGallary.jsx
+++ b/src/pages/Gallary/ImageGallary.jsx
@@ -1,11 +1,11 @@
-import React, { useState, useEffect, useRef } from "react";
+import React, { useState, useEffect, useRef, useCallback } from "react";
import "./ImageGallery.css";
-import { ImageGalleryAPI } from "./ImageGalleryAPI"; // Assuming this exists
+import { ImageGalleryAPI } from "./ImageGalleryAPI";
import moment from "moment";
-import { useSelector } from "react-redux"; // Assuming Redux is set up
-import { useModal } from "./ModalContext"; // Assuming ModalContext exists
-import ImagePop from "./ImagePop"; // Assuming ImagePop component exists
-import Avatar from "../../components/common/Avatar"; // Assuming Avatar component exists
+import { useSelector } from "react-redux";
+import { useModal } from "./ModalContext";
+import ImagePop from "./ImagePop";
+import Avatar from "../../components/common/Avatar";
const ImageGallery = () => {
const [images, setImages] = useState([]);
@@ -19,20 +19,41 @@ const ImageGallery = () => {
uploadedBy: [],
workCategory: [],
workArea: [],
+ startDate: "",
+ endDate: "",
});
- const [isFilterPanelOpen, setIsFilterPanelOpen] = useState(false); // Controls filter drawer
- const [hoveredImage, setHoveredImage] = useState(null); // For image hover description
+ const [appliedFilters, setAppliedFilters] = useState({
+ buildingIds: null,
+ floorIds: null,
+ activityIds: null,
+ uploadedByIds: null,
+ workCategoryIds: null,
+ workAreaIds: null,
+ startDate: null,
+ endDate: null,
+ });
+
+ const [collapsedFilters, setCollapsedFilters] = useState({
+ dateRange: false,
+ building: false,
+ floor: false,
+ activity: false,
+ uploadedBy: false,
+ workCategory: false,
+ workArea: false,
+ });
+
+ const [isFilterPanelOpen, setIsFilterPanelOpen] = useState(false);
+ const [hoveredImage, setHoveredImage] = useState(null);
const [loading, setLoading] = useState(true);
- const imageGroupRefs = useRef({}); // To reference horizontal scroll containers
- const filterPanelRef = useRef(null); // Ref for the filter panel for click-outside
- const filterButtonRef = useRef(null); // Ref for the filter button for click-outside
+ const imageGroupRefs = useRef({});
+ const filterPanelRef = useRef(null);
+ const filterButtonRef = useRef(null);
- // Click outside handler for the filter panel
useEffect(() => {
const handleClickOutside = (event) => {
- // Close filter panel if click is outside the panel and not on the toggle button itself
if (
filterPanelRef.current &&
!filterPanelRef.current.contains(event.target) &&
@@ -49,136 +70,250 @@ const ImageGallery = () => {
};
}, []);
- // Fetch images when the selectedProjectId changes
useEffect(() => {
- if (!selectedProjectId) return;
+ if (!selectedProjectId) {
+ setImages([]);
+ setLoading(false);
+ return;
+ }
setLoading(true);
- ImageGalleryAPI.ImagesGet(selectedProjectId)
+
+ ImageGalleryAPI.ImagesGet(selectedProjectId, appliedFilters)
.then((res) => {
setImages(res.data);
})
.catch((err) => {
console.error("Error fetching images:", err);
+ setImages([]);
})
.finally(() => {
setLoading(false);
});
- }, [selectedProjectId]);
+ }, [selectedProjectId, appliedFilters]);
- // Helper functions to get unique filter values
- const getUniqueValues = (key) => [
- ...new Set(images.map((img) => img[key]).filter(Boolean)),
- ];
-
- const getUniqueUploadedByUsers = () => [
- ...new Set(
- images
- .map((img) => {
- const firstName = img.uploadedBy?.firstName || "";
- const lastName = img.uploadedBy?.lastName || "";
- return `${firstName} ${lastName}`.trim();
- })
- .filter(Boolean)
- ),
- ];
-
- const getUniqueWorkCategories = () => [
- ...new Set(images.map((img) => img.workCategoryName).filter(Boolean)),
- ];
-
- // Derive filter options
- const buildings = getUniqueValues("buildingName");
- const floors = getUniqueValues("floorName");
- const activities = getUniqueValues("activityName");
- const workAreas = getUniqueValues("workAreaName");
- const uploadedByUsers = getUniqueUploadedByUsers();
- const workCategories = getUniqueWorkCategories();
-
- // Toggle selected filters
- const toggleFilter = (type, value) => {
- setSelectedFilters((prev) => {
- const current = prev[type];
- return {
- ...prev,
- [type]: current.includes(value)
- ? current.filter((v) => v !== value)
- : [...current, value],
- };
- });
- };
-
- // Filter images based on selected filters
- const filteredImages = images.filter(
- (img) =>
- (selectedFilters.building.length === 0 ||
- selectedFilters.building.includes(img.buildingName)) &&
- (selectedFilters.floor.length === 0 ||
- selectedFilters.floor.includes(img.floorName)) &&
- (selectedFilters.activity.length === 0 ||
- selectedFilters.activity.includes(img.activityName)) &&
- (selectedFilters.workArea.length === 0 ||
- selectedFilters.workArea.includes(img.workAreaName)) &&
- (selectedFilters.uploadedBy.length === 0 ||
- selectedFilters.uploadedBy.includes(
- `${img.uploadedBy?.firstName || ""} ${
- img.uploadedBy?.lastName || ""
- }`.trim()
- )) &&
- (selectedFilters.workCategory.length === 0 ||
- selectedFilters.workCategory.includes(img.workCategoryName))
+ const getUniqueValuesWithIds = useCallback(
+ (idKey, nameKey) => {
+ const uniqueMap = new Map();
+ images.forEach(img => {
+ if (img[idKey] && img[nameKey]) {
+ uniqueMap.set(img[idKey], img[nameKey]);
+ }
+ });
+ return Array.from(uniqueMap.entries());
+ },
+ [images]
+ );
+
+ 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);
+ }
+ }
+ });
+ return Array.from(uniqueUsersMap.entries());
+ },
+ [images]
+ );
+
+ const buildings = getUniqueValuesWithIds("buildingId", "buildingName");
+ const floors = getUniqueValuesWithIds("floorIds", "floorName");
+ const activities = getUniqueValuesWithIds("activityId", "activityName");
+ const workAreas = getUniqueValuesWithIds("workAreaId", "workAreaName");
+ const uploadedByUsers = getUniqueUploadedByUsers();
+ const workCategories = getUniqueValuesWithIds("workCategoryId", "workCategoryName");
+
+ const toggleFilter = useCallback((type, itemId, itemName) => {
+ setSelectedFilters((prev) => {
+ const current = prev[type];
+ const isSelected = current.some(item => item[0] === itemId);
+
+ const newArray = isSelected
+ ? current.filter((item) => item[0] !== itemId)
+ : [...current, [itemId, itemName]];
+
+ return {
+ ...prev,
+ [type]: newArray,
+ };
+ });
+ }, []);
+
+ const handleDateChange = useCallback((type, date) => {
+ setSelectedFilters((prev) => ({
+ ...prev,
+ [type]: date,
+ }));
+ }, []);
+
+ const toggleCollapse = useCallback((type) => {
+ setCollapsedFilters((prev) => ({
+ ...prev,
+ [type]: !prev[type],
+ }));
+ }, []);
+
+ const handleApplyFilters = useCallback(() => {
+ const payload = {
+ buildingIds: selectedFilters.building.length > 0 ? selectedFilters.building.map(item => item[0]) : null,
+ floorIds: selectedFilters.floor.length > 0 ? selectedFilters.floor.map(item => item[0]) : null,
+ workAreaIds: selectedFilters.workArea.length > 0 ? selectedFilters.workArea.map(item => item[0]) : null,
+ workCategoryIds: selectedFilters.workCategory.length > 0 ? selectedFilters.workCategory.map(item => item[0]) : null,
+ activityIds: selectedFilters.activity.length > 0 ? selectedFilters.activity.map(item => item[0]) : null,
+ uploadedByIds: selectedFilters.uploadedBy.length > 0 ? selectedFilters.uploadedBy.map(item => item[0]) : null,
+ startDate: selectedFilters.startDate || null,
+ endDate: selectedFilters.endDate || null,
+ };
+ setAppliedFilters(payload);
+ setIsFilterPanelOpen(false);
+ }, [selectedFilters]);
+
+
+ const handleClearAllFilters = useCallback(() => {
+ const initialStateSelected = {
+ building: [],
+ floor: [],
+ activity: [],
+ uploadedBy: [],
+ workCategory: [],
+ workArea: [],
+ startDate: "",
+ endDate: "",
+ };
+ setSelectedFilters(initialStateSelected);
+
+ const initialStateApplied = {
+ buildingIds: null,
+ floorIds: null,
+ activityIds: null,
+ uploadedByIds: null,
+ workCategoryIds: null,
+ workAreaIds: null,
+ startDate: null,
+ endDate: null,
+ };
+ setAppliedFilters(initialStateApplied);
+
+ setIsFilterPanelOpen(false);
+ }, []);
+
+ 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;
+ }
);
- // Group images by Activity, Uploader, and Work Area
const imagesByActivityUser = {};
filteredImages.forEach((img) => {
- const userName = `${img.uploadedBy?.firstName || ""} ${
- img.uploadedBy?.lastName || ""
- }`.trim();
- const workArea = img.workAreaName || "Unknown"; // Handle cases where workAreaName might be null/undefined
+ 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);
});
- // Scroll functionality for horizontal image groups
- const scrollLeft = (key) => {
+ const scrollLeft = useCallback((key) => {
imageGroupRefs.current[key]?.scrollBy({ left: -200, behavior: "smooth" });
- };
+ }, []);
- const scrollRight = (key) => {
+ const scrollRight = useCallback((key) => {
imageGroupRefs.current[key]?.scrollBy({ left: 200, behavior: "smooth" });
- };
+ }, []);
- // Helper function to render filter categories dropdowns
const renderFilterCategory = (label, items, type) => (
-
-
-
-
{label}
- {selectedFilters[type].length > 0 && (
+
+
toggleCollapse(type)}>
+
{label}
+
+ {type === 'dateRange' && (selectedFilters.startDate || selectedFilters.endDate) && (
- setSelectedFilters((prev) => ({ ...prev, [type]: [] }))
- }
+ onClick={(e) => {
+ e.stopPropagation();
+ setSelectedFilters(prev => ({ ...prev, startDate: "", endDate: "" }));
+ }}
+ >
+ Clear
+
+ )}
+ {type !== 'dateRange' && selectedFilters[type] && selectedFilters[type].length > 0 && (
+ {
+ e.stopPropagation();
+ setSelectedFilters((prev) => ({ ...prev, [type]: [] }));
+ }}
>
Clear
)}
-
- {items.map((item) => (
-
- toggleFilter(type, item)}
- />
- {item}
-
- ))}
+ {!collapsedFilters[type] && (
+
+ )}
);
@@ -191,11 +326,8 @@ const ImageGallery = () => {
) : Object.entries(imagesByActivityUser).length > 0 ? (
- // Render each grouped section of images
Object.entries(imagesByActivityUser).map(([key, imgs]) => {
- // Destructure the key to get activity, user, and work area
const [activity, userName, workArea] = key.split("__");
- // Get details from the first image in the group (assuming common details for the group)
const { buildingName, floorName, uploadedAt, workCategoryName } =
imgs[0];
const date = moment(uploadedAt).format("YYYY-MM-DD");
@@ -224,7 +356,6 @@ const ImageGallery = () => {
- {/* Location and Work Category display */}
{buildingName} > {floorName} > {workArea} >{" "}
@@ -239,7 +370,6 @@ const ImageGallery = () => {
- {/* Left scroll arrow */}
scrollLeft(key)}
@@ -250,7 +380,6 @@ const ImageGallery = () => {
className="image-group-horizontal"
ref={(el) => (imageGroupRefs.current[key] = el)}
>
- {/* Render individual image cards within the group */}
{imgs.map((img, idx) => {
const hoverDate = moment(img.uploadedAt).format(
"YYYY-MM-DD"
@@ -261,9 +390,8 @@ const ImageGallery = () => {
return (
openModal(
)}
onMouseEnter={() => setHoveredImage(img)}
onMouseLeave={() => setHoveredImage(null)}
@@ -271,7 +399,6 @@ const ImageGallery = () => {
- {/* Hover description for image details */}
{hoveredImage === img && (
@@ -290,7 +417,6 @@ const ImageGallery = () => {
);
})}
- {/* Right scroll arrow */}
scrollRight(key)}
@@ -309,7 +435,6 @@ const ImageGallery = () => {
- {/* Filter drawer section */}
{
>
{isFilterPanelOpen ? (
<>
- Filter ✖ {/* X Mark when open */}
+ Filter ✖
>
) : (
- ▼
+ ▼
)}
+ {renderFilterCategory("Date Range", [], "dateRange")}
{renderFilterCategory("Building", buildings, "building")}
{renderFilterCategory("Floor", floors, "floor")}
{renderFilterCategory("Work Area", workAreas, "workArea")}
{renderFilterCategory("Activity", activities, "activity")}
{renderFilterCategory("Uploaded By (User)", uploadedByUsers, "uploadedBy")}
{renderFilterCategory("Work Category", workCategories, "workCategory")}
+
+
+
+ Clear All
+
+
+ Apply Filters
+
+
);
};
-export default ImageGallery;
-
+export default ImageGallery;
\ No newline at end of file
diff --git a/src/pages/Gallary/ImageGallery.css b/src/pages/Gallary/ImageGallery.css
index 1795d188..5fc79da0 100644
--- a/src/pages/Gallary/ImageGallery.css
+++ b/src/pages/Gallary/ImageGallery.css
@@ -1,25 +1,19 @@
.gallery-container {
- display: grid; /* Use CSS Grid for layout */
- /*
- * MODIFIED: When filter is closed, main content takes 1fr,
- * and the filter column is just big enough for the small floating button (e.g., 50px).
- */
+ display: grid;
grid-template-columns: 1fr 50px;
- gap: 0px; /* Gap between main content and filter */
+ gap: 4px;
padding: 25px;
font-family: sans-serif;
height: calc(100vh - 20px);
box-sizing: border-box;
background-color: #f7f9fc;
- transition: grid-template-columns 0.3s ease-in-out; /* Smooth transition for column resizing */
+ transition: grid-template-columns 0.3s ease-in-out;
}
.gallery-container.filter-panel-open {
- /* When open, main content shrinks, filter expands to 250px */
grid-template-columns: 1fr 250px;
}
-/* Main content area (images) - No changes needed here */
.main-content {
overflow-y: auto;
max-height: 100%;
@@ -46,19 +40,17 @@
background: #888;
}
-/* New: Wrapper for the filter drawer and its button */
.filter-drawer-wrapper {
flex-shrink: 0;
max-height: 100%;
box-sizing: border-box;
- position: relative; /* Essential for positioning the filter panel within */
- /* Hides the scrollbar from the wrapper itself, as the panel will handle its own scrolling. */
- scrollbar-width: none; /* For Firefox */
- -ms-overflow-style: none; /* For IE and Edge */
+ position: relative;
+ scrollbar-width: none;
+ -ms-overflow-style: none;
}
.filter-drawer-wrapper::-webkit-scrollbar {
- display: none; /* For Chrome, Safari, and Opera */
+ display: none;
}
.filter-button {
@@ -74,31 +66,26 @@
justify-content: center;
align-items: center;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
- /* Added padding to transition properties for smoothness */
transition: background-color 0.2s ease, box-shadow 0.2s ease, width 0.3s ease-in-out, height 0.3s ease-in-out, border-radius 0.3s ease-in-out, padding 0.3s ease-in-out;
-
- /* Floating / Positioning */
position: absolute;
top: 0;
right: 0;
- height: 40px; /* Fixed height when closed */
- width: 40px; /* Fixed width when closed (making it square) */
- z-index: 100; /* Ensure it stays on top */
+ height: 40px;
+ width: 40px;
+ z-index: 100;
}
-/* When the filter panel is open, the button should match its width and blend with the panel */
.gallery-container.filter-panel-open .filter-button {
- width: calc(100% - 16px); /* Match filter-panel width minus its padding (8px on each side) */
- height: auto; /* Allow height to adjust for text content */
- padding: 8px 12px; /* Restore padding when expanded */
- border-radius: 6px 6px 0 0; /* Adjust border-radius to blend with panel below */
- justify-content: space-between; /* Space between "Filter" text and "X" icon */
+ width: calc(100% - 16px);
+ height: auto;
+ padding: 8px 12px;
+ border-radius: 6px 6px 0 0;
+ justify-content: space-between;
}
-/* Add a class for the button when the panel is closed to show only icon */
.filter-button.closed-icon {
- padding: 0; /* Remove padding to make it compact */
- font-size: 20px; /* Make icon larger when it's just an icon */
+ padding: 0;
+ font-size: 20px;
}
.filter-button:hover {
@@ -106,9 +93,10 @@
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
-/* The actual panel that contains all filter categories */
.filter-panel {
- display: flex; /* Always display as flex to manage children */
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
background-color: #fff;
border: 1px solid #e5e7eb;
border-radius: 6px;
@@ -116,314 +104,442 @@
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
width: 100%;
box-sizing: border-box;
- flex-direction: column;
- gap: 12px;
- max-height: 0;
- /* Use overflow-y: hidden for the max-height transition to work smoothly */
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ max-height: calc(100% - 40px);
+ padding-top: 37px;
overflow-y: hidden;
opacity: 0;
transform: translateY(-10px);
- /* Added border-radius to transition properties for smoothness */
transition: max-height 0.3s ease-out, opacity 0.3s ease-out, transform 0.3s ease-out, border-radius 0.3s ease-in-out;
-
- /* Position it below the button when open */
- margin-top: 40px; /* Account for the button's fixed height */
}
.filter-panel.open {
- max-height: 1000px; /* A value larger than the expected height of content */
+ max-height: calc(100% - 40px);
opacity: 1;
transform: translateY(0);
- /* Remove top radius to blend with button when open */
- border-top-left-radius: 0;
- border-top-right-radius: 0;
+ overflow-y: auto;
+ /* padding-bottom: 8px; */
+ /* Adjust padding to accommodate the new buttons */
+ padding-bottom: -1px; /* Enough space for buttons + some padding */
}
-/* ... (rest of your CSS remains the same) ... */
-/* Individual dropdown sections within the filter panel */
+.dropdown {
+ transition: all 0.3s ease-in-out;
+}
+
.dropdown-content {
- display: block !important;
- position: static;
- background-color: #f9fafb;
- box-shadow: none;
- padding: 4px 0;
- margin-top: 0;
- border-radius: 0 0 4px 4px;
- max-height: unset;
- overflow-y: visible;
+ display: block;
+ position: static;
+ box-shadow: none;
+ padding: 4px 10px;
+ border-radius: 0 0 4px 4px;
+ max-height: 150px;
+ overflow-y: auto;
+ transition: max-height 0.3s ease-in-out, padding 0.3s ease-in-out;
+}
+
+.dropdown.collapsed .dropdown-content {
+ max-height: 0;
+ padding-top: 0;
+ padding-bottom: 0;
+ overflow: hidden;
+}
+
+.dropdown-content::-webkit-scrollbar {
+ width: 6px;
+}
+
+.dropdown-content::-webkit-scrollbar-track {
+ background: #f1f1f1;
+ border-radius: 10px;
+}
+
+.dropdown-content::-webkit-scrollbar-thumb {
+ background: #c0c0c0;
+ border-radius: 10px;
+}
+
+.dropdown-content::-webkit-scrollbar-thumb:hover {
+ background: #a7a7a7;
}
.dropdown-content label {
- display: flex;
- align-items: center;
- gap: 6px;
- padding: 5px 10px;
- font-size: 12px;
- font-weight: 500;
- color: #333;
- cursor: pointer;
- transition: background 0.2s;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ padding: 5px 0px;
+ font-size: 12px;
+ font-weight: 500;
+ color: #333;
+ cursor: pointer;
+ transition: background 0.2s;
}
.dropdown-content label:hover {
- background-color: #eef2ff;
+ background-color: #eef2ff;
}
.dropdown-content input[type="checkbox"] {
- -webkit-appearance: none;
- -moz-appearance: none;
- appearance: none;
- width: 16px;
- height: 16px;
- border-radius: 3px;
- border: 1px solid #c7d2fe;
- background-color: #fff;
- cursor: pointer;
- position: relative;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.2s ease-in-out;
+ -webkit-appearance: none;
+ -moz-appearance: none;
+ appearance: none;
+ width: 16px;
+ height: 16px;
+ border-radius: 3px;
+ border: 1px solid #c7d2fe;
+ background-color: #fff;
+ cursor: pointer;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease-in-out;
}
.dropdown-content input[type="checkbox"]:checked {
- background-color: #6366f1;
- border-color: #6366f1;
+ background-color: #6366f1;
+ border-color: #6366f1;
}
.dropdown-content input[type="checkbox"]:checked::after {
- content: '✔';
- font-size: 10px;
- color: white;
- position: absolute;
+ content: '✔';
+ font-size: 10px;
+ color: white;
+ position: absolute;
}
-
.dropdown-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 0 10px 5px;
- border-bottom: 1px solid #eee;
- margin-bottom: 6px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 8px 10px;
+ background-color: #eef2ff;
+ border-bottom: 1px solid #c7d2fe;
+ font-weight: bold;
+ font-size: 13px;
+ color: #333;
+ cursor: pointer;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
+ transition: background-color 0.3s ease, border-bottom 0.3s ease;
+}
+
+.dropdown.collapsed .dropdown-header {
+ border-bottom: none;
+ border-radius: 4px;
+ box-shadow: none;
}
.clear-button {
- font-size: 10px;
- background: none;
- border: none;
- color: #6366f1;
- cursor: pointer;
- padding: 3px 6px;
- border-radius: 4px;
- transition: background-color 0.2s ease;
+ font-size: 10px;
+ background: none;
+ border: none;
+ color: #6366f1;
+ cursor: pointer;
+ padding: 3px 6px;
+ border-radius: 4px;
+ transition: background-color 0.2s ease;
+ flex-shrink: 0;
}
.clear-button:hover {
- background-color: #eef2ff;
+ background-color: #eef2ff;
+}
+
+.header-controls {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.toggle-icon {
+ font-size: 10px;
+ color: #6366f1;
+ transition: transform 0.2s ease;
+}
+
+.dropdown .toggle-icon {
+ transform: rotate(0deg);
+}
+
+.dropdown.collapsed .toggle-icon {
+ transform: rotate(-90deg);
+}
+
+.date-range-inputs {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ padding: 5px 0;
+}
+
+.date-range-inputs label {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+ font-weight: 500;
+ color: #333;
+}
+
+.date-input {
+ flex-grow: 1;
+ padding: 6px 8px;
+ border: 1px solid #c7d2fe;
+ border-radius: 4px;
+ font-size: 12px;
+ color: #333;
+ background-color: #fff;
+ outline: none;
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
+}
+
+.date-input:focus {
+ border-color: #6366f1;
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
}
-/* --- Image Card Section --- */
.grouped-section {
- margin-bottom: 5px;
- border: 1px solid #e0e0e0;
- border-radius: 8px;
- padding: 10px;
- background-color: #fff;
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
+ margin-bottom: 5px;
+ border: 1px solid #e0e0e0;
+ border-radius: 8px;
+ padding: 10px;
+ background-color: #fff;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.group-heading {
- display: flex;
- justify-content: space-between;
- align-items: flex-start;
- margin-bottom: 3px;
- font-size: 12px;
- flex-wrap: wrap;
- padding-bottom: 8px;
- border-bottom: 1px dashed #eee;
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ margin-bottom: 3px;
+ font-size: 12px;
+ flex-wrap: wrap;
+ padding-bottom: 8px;
+ border-bottom: 1px dashed #eee;
}
.group-heading > div {
- margin-right: 15px;
+ margin-right: 15px;
}
.datetime-line {
- font-size: 12px;
- color: #777;
- margin-top: 0px;
-
+ font-size: 12px;
+ color: #777;
+ margin-top: 0px;
}
.location-line {
- font-weight: 600;
- font-size: 12px;
- color: #555;
- text-align: right; /* Keep this if you want the whole block aligned right */
- display: flex; /* Use flexbox to manage children's layout */
- flex-direction: column; /* Stack children vertically */
- align-items: flex-end; /* Align items to the end (right) if text-align: right is desired */
-}
-.work-category-display {
- /* Basic styling for the work category, if needed */
- margin-top: 4px; /* Add some space above it */
- padding: 2px 6px;
- /* A light background for better visibility */
- border-radius: 5px;
- font-size: 12px;
- /* Override the parent's bold if desired */
- color: #555;
+ font-weight: 600;
+ font-size: 12px;
+ color: #555;
+ text-align: right;
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+}
+
+.work-category-display {
+ margin-top: 4px;
+ padding: 2px 6px;
+ border-radius: 5px;
+ font-size: 12px;
+ color: #555;
}
-/* New: Wrapper for image group and arrows */
.image-group-wrapper {
- position: relative;
- display: flex;
- align-items: center;
- padding: 0 0px;
+ position: relative;
+ display: flex;
+ align-items: center;
+ padding: 0 0px;
}
.image-group-horizontal {
- display: flex;
- flex-direction: row;
- flex-wrap: nowrap;
- overflow-x: hidden;
- gap: 3px;
- padding-bottom: 8px;
- -webkit-overflow-scrolling: touch;
- scroll-behavior: smooth;
- width: 100%;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: nowrap;
+ overflow-x: hidden;
+ gap: 3px;
+ padding-bottom: 8px;
+ -webkit-overflow-scrolling: touch;
+ scroll-behavior: smooth;
+ width: 100%;
}
+
.scroll-arrow {
- background-color: rgba(0, 0, 0, 0.5);
- color: white;
- border: none;
- border-radius: 50%; /* This makes it a circle */
- width: 30px; /* Ensure width and height are equal */
- height: 44px; /* Ensure width and height are equal */
- display: flex;
- justify-content: center;
- align-items: center;
- cursor: pointer;
- font-size: 18px;
- z-index: 10;
- position: absolute;
- top: 50%;
- transform: translateY(-50%);
- opacity: 0;
- transition: opacity 0.3s ease, background-color 0.2s ease;
- pointer-events: none;
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ border: none;
+ border-radius: 50%;
+ width: 30px;
+ height: 44px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ font-size: 18px;
+ z-index: 10;
+ position: absolute;
+ top: 50%;
+ transform: translateY(-50%);
+ opacity: 0;
+ transition: opacity 0.3s ease, background-color 0.2s ease;
+ pointer-events: none;
}
.image-group-wrapper:hover .scroll-arrow {
- opacity: 1;
- pointer-events: auto;
+ opacity: 1;
+ pointer-events: auto;
}
.scroll-arrow:hover {
- background-color: rgba(0, 0, 0, 0.7);
+ background-color: rgba(0, 0, 0, 0.7);
}
.left-arrow {
- left: 5px;
+ left: 5px;
}
.right-arrow {
- right: 5px;
+ right: 5px;
}
.image-card {
- width: 150px;
- border: 1px solid #ddd;
- border-radius: 8px;
- background: #fff;
- cursor: pointer;
- overflow: hidden;
- flex-shrink: 0;
- box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
- transition: transform 0.2s ease, box-shadow 0.2s ease;
- position: relative;
+ width: 150px;
+ border: 1px solid #ddd;
+ border-radius: 8px;
+ background: #fff;
+ cursor: pointer;
+ overflow: hidden;
+ flex-shrink: 0;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+ position: relative;
}
hr {
- margin: 0rem 0;
- color: var(--bs-border-color);
- border: 0;
- border-top: var(--bs-border-width) solid;
- opacity: 1;
+ display: none;
}
.image-card:hover {
- transform: translateY(-2px) scale(1.03);
- box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
+ transform: translateY(-2px) scale(1.03);
+ box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
}
.image-wrapper img {
- width: 100%;
- height: 100px;
- object-fit: cover;
- display: block;
- border-radius: 8px 8px 0 0;
+ width: 100%;
+ height: 100px;
+ object-fit: cover;
+ display: block;
+ border-radius: 8px 8px 0 0;
}
-/* NEW: Styles for the hover description */
.image-hover-description {
- position: absolute;
- bottom: 0;
- left: 0;
- width: 100%;
- background-color: rgba(0, 0, 0, 0.75);
- color: white;
- padding: 5px 8px;
- box-sizing: border-box;
- font-size: 11px;
- line-height: 1.4;
- text-align: left;
- opacity: 0;
- transform: translateY(100%);
- transition: opacity 0.2s ease-out, transform 0.2s ease-out;
- border-bottom-left-radius: 8px;
- border-bottom-right-radius: 8px;
- pointer-events: none;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ background-color: rgba(0, 0, 0, 0.75);
+ color: white;
+ padding: 5px 8px;
+ box-sizing: border-box;
+ font-size: 11px;
+ line-height: 1.4;
+ text-align: left;
+ opacity: 0;
+ transform: translateY(100%);
+ transition: opacity 0.2s ease-out, transform 0.2s ease-out;
+ border-bottom-left-radius: 8px;
+ border-bottom-right-radius: 8px;
+ pointer-events: none;
}
.image-card:hover .image-hover-description {
- opacity: 1;
- transform: translateY(0);
+ opacity: 1;
+ transform: translateY(0);
}
.image-hover-description p {
- margin: 0;
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
+ margin: 0;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
}
.spinner-container {
- display: flex;
- justify-content: center;
- align-items: center;
- min-height: 200px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ min-height: 200px;
}
.spinner {
- border: 6px solid #f3f3f3;
- border-top: 6px solid #6658f6;
- border-radius: 50%;
- width: 40px;
- height: 40px;
- animation: spin 0.8s linear infinite;
+ border: 6px solid #f3f3f3;
+ border-top: 6px solid #6658f6;
+ border-radius: 50%;
+ width: 40px;
+ height: 40px;
+ animation: spin 0.8s linear infinite;
}
@keyframes spin {
- 0% {
- transform: rotate(0deg);
- }
- 100% {
- transform: rotate(360deg);
- }
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
}
+/* New styles for filter action buttons */
+.filter-actions {
+ display: flex;
+ justify-content: space-between;
+ margin-top: auto; /* Pushes buttons to the bottom */
+ padding-top: 10px;
+ border-top: 1px solid #e5e7eb;
+ background-color: #fff;
+ position: sticky;
+ bottom: 0;
+ z-index: 10;
+ padding-bottom: 0px;
+}
+.apply-filters-button,
+.clear-all-button {
+ padding: 8px 15px;
+ border-radius: 5px;
+ font-size: 13px;
+ cursor: pointer;
+ transition: background-color 0.2s ease, box-shadow 0.2s ease;
+ flex: 1; /* Make buttons take equal width */
+ margin: 0 4px; /* Add some spacing between them */
+}
+
+.apply-filters-button {
+ /* background-color: #6366f1;
+ color: white; */
+ border: none;
+}
+
+.apply-filters-button:hover {
+ background-color: #4f46e5;
+ /* box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1); */
+}
+
+.clear-all-button {
+ border: 1px solid #cbd5e1;
+}
+
+.clear-all-button:hover {
+ background-color: #e2e8f0;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.05);
+}
diff --git a/src/pages/Gallary/ImageGalleryAPI.jsx b/src/pages/Gallary/ImageGalleryAPI.jsx
index 0c844b35..df18611d 100644
--- a/src/pages/Gallary/ImageGalleryAPI.jsx
+++ b/src/pages/Gallary/ImageGalleryAPI.jsx
@@ -2,6 +2,9 @@ import { api } from "../../utils/axiosClient";
export const ImageGalleryAPI = {
- ImagesGet: (projectId) =>
- api.get(`/api/image/images/${projectId}`),
+ ImagesGet: (projectId, filter) => {
+ const payloadJsonString = JSON.stringify(filter);
+ console.log("Applying filters with payload JSON string:", payloadJsonString);
+ return api.get(`/api/image/images/${projectId}?filter=${payloadJsonString}`)
+ },
}
\ No newline at end of file