Changes in Images gallery popup and page.

This commit is contained in:
Kartik Sharma 2025-09-23 10:27:14 +05:30
parent 3df68dff89
commit 59b62d2f10
3 changed files with 133 additions and 55 deletions

View File

@ -1,4 +1,4 @@
import React, { useRef, useState, useCallback } from "react";
import React, { useRef, useState, useCallback, useEffect } from "react";
import Avatar from "../../components/common/Avatar";
import ImagePopup from "./ImagePopup";
@ -14,8 +14,23 @@ const ImageGalleryListView = ({
moment,
}) => {
const [hoveredImage, setHoveredImage] = useState(null);
const [scrollThreshold, setScrollThreshold] = useState(5);
const imageGroupRefs = useRef({});
useEffect(() => {
const updateThreshold = () => {
if (window.innerWidth >= 1400) setScrollThreshold(6); // xl screens
else if (window.innerWidth >= 992) setScrollThreshold(5); // lg
else if (window.innerWidth >= 768) setScrollThreshold(4); // md
else setScrollThreshold(3); // sm & xs
};
updateThreshold();
window.addEventListener("resize", updateThreshold);
return () => window.removeEventListener("resize", updateThreshold);
}, []);
const scrollLeft = useCallback(
(key) =>
imageGroupRefs.current[key]?.scrollBy({ left: -200, behavior: "smooth" }),
@ -37,11 +52,10 @@ const ImageGalleryListView = ({
) : images.length ? (
images.map((batch) => {
const doc = batch.documents[0];
const userName = `${doc.uploadedBy?.firstName || ""} ${
doc.uploadedBy?.lastName || ""
}`.trim();
const userName = `${doc.uploadedBy?.firstName || ""} ${doc.uploadedBy?.lastName || ""
}`.trim();
const date = formatUTCToLocalTime(doc.uploadedAt);
const hasArrows = batch.documents.length > SCROLL_THRESHOLD;
const hasArrows = batch.documents.length > scrollThreshold;
return (
<div key={batch.batchId} className="grouped-section">

View File

@ -1,90 +1,128 @@
import React, { useState, useEffect } from "react";
import "./ImagePopup.css"
import { useModal } from "./ModalContext";
import moment from "moment";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
const ImagePopup = ({ batch, initialIndex = 0 }) => {
const { closeModal } = useModal();
const [currentIndex, setCurrentIndex] = useState(initialIndex);
// Effect to update currentIndex if the initialIndex prop changes
useEffect(() => {
setCurrentIndex(initialIndex);
}, [initialIndex, batch]);
// If no batch or documents are provided, don't render
if (!batch || !batch.documents || batch.documents.length === 0) return null;
// Get the current image document from the batch's documents array
const image = batch.documents[currentIndex];
// Fallback if for some reason the image at the current index doesn't exist
if (!image) return null;
// Format details for display from the individual image document
const fullName = `${image.uploadedBy?.firstName || ""} ${image.uploadedBy?.lastName || ""
}`.trim();
const date = formatUTCToLocalTime(image.uploadedAt);
// Location and category details from the 'batch' object (as previously corrected)
const buildingName = batch.buildingName;
const floorName = batch.floorName;
const workAreaName = batch.workAreaName;
const activityName = batch.activityName;
const batchComment = batch.comment;
// Handler for navigating to the previous image
const handlePrev = () => {
setCurrentIndex((prevIndex) => Math.max(0, prevIndex - 1));
};
// Handler for navigating to the next image
const handleNext = () => {
setCurrentIndex((prevIndex) =>
Math.min(batch.documents.length - 1, prevIndex + 1)
);
};
// Determine if previous/next buttons should be enabled/visible
const hasPrev = currentIndex > 0;
const hasNext = currentIndex < batch.documents.length - 1;
return (
<div className="image-modal-overlay">
<div className="image-modal-content">
<div
className="modal fade show"
style={{ display: "block", backgroundColor: "rgba(0,0,0,0.6)", overflow: "hidden", }}
tabIndex="-1"
role="dialog"
>
<div className="modal-dialog modal-md modal-dialog-centered" role="document">
<div className="modal-content border-0 shadow-lg rounded-3">
{/* Header */}
<div className="modal-header justify-content-center py-2">
<h5 className="modal-title">Image Preview</h5>
<button type="button" className="btn-close" onClick={closeModal}></button>
</div>
<i className='bx bx-x close-button' onClick={closeModal}></i>
{/* Body */}
<div className="modal-body p-2 text-center">
<div className="position-relative d-flex justify-content-center align-items-center">
{hasPrev && (
<button
className="btn btn-sm btn-outline-primary rounded-circle d-flex align-items-center justify-content-center position-absolute start-0 top-50 translate-middle-y shadow"
style={{ marginLeft: "-50px", width: "40px", height: "40px" }}
onClick={handlePrev}
>
<i className="bx bx-chevron-left fs-4"></i>
</button>
)}
{hasPrev && (
<button className="nav-button prev-button" onClick={handlePrev}>
<i className='bx bx-chevron-left'></i>
</button>
)}
<img
src={image.url}
alt="Preview"
className="img-fluid rounded"
style={{
maxHeight: "500px", // bigger image
width: "100%",
objectFit: "contain",
}}
/>
<div className="image-container">
<img src={image.url} alt="Preview" className="modal-image" />
</div>
{hasNext && (
<button
className="btn btn-sm btn-outline-primary rounded-circle d-flex align-items-center justify-content-center position-absolute end-0 top-50 translate-middle-y shadow"
style={{ marginRight: "-50px", width: "40px", height: "40px" }}
onClick={handleNext}
>
<i className="bx bx-chevron-right fs-4"></i>
</button>
)}
</div>
{hasNext && (
<button className="nav-button next-button" onClick={handleNext}>
<i className='bx bx-chevron-right'></i>
</button>
)}
{/* Details */}
<div className="mt-3 text-start small">
<p className="mb-1">
<i className="bx bxs-user me-2"></i>
<span className="text-muted">Uploaded By: </span>
<span className="fw-semibold">{fullName}</span>
</p>
<div className="image-details">
<div className="flex alig-items-center"> <i className='bx bxs-user'></i> <span className="text-muted">Uploaded By : </span> <span className="text-secondary">{fullName}</span></div>
<div className="flex alig-items-center"> <i class='bx bxs-calendar' ></i> <span className="text-muted">Date : </span> <span className="text-secondary"> {date}</span></div>
<div className="flex alig-items-center"> <i class='bx bx-map' ></i> <span className="text-muted">Uploaded By : </span> <span className="text-secondary">{buildingName} <i className='bx bx-chevron-right'></i> {floorName} <i className='bx bx-chevron-right'></i>
{workAreaName || "Unknown"} <i className='bx bx-chevron-right'></i> {activityName}</span></div>
<div className="flex alig-items-center"> <i className='bx bx-comment-dots'></i> <span className="text-muted">comment : </span> <span className="text-secondary">{batchComment}</span></div>
<p className="mb-1">
<i className="bx bxs-calendar me-2"></i>
<span className="text-muted">Date: </span>
<span className="fw-semibold">{date}</span>
</p>
<p className="mb-1">
<i className="bx bx-map me-2"></i>
<span className="text-muted">Location: </span>
<span className="fw-semibold">
{buildingName} <i className="bx bx-chevron-right"></i> {floorName}{" "}
<i className="bx bx-chevron-right"></i> {workAreaName || "Unknown"}{" "}
<i className="bx bx-chevron-right"></i> {activityName}
</span>
</p>
<p className="mb-0">
<i className="bx bx-comment-dots me-2"></i>
<span className="text-muted">Comment: </span>
<span className="fw-semibold">{batchComment}</span>
</p>
</div>
</div>
</div>
</div>
</div>
);
};
export default ImagePopup;
export default ImagePopup;

View File

@ -218,24 +218,50 @@ const ImageGalleryPage = () => {
)}
</div>
{/* Filter Chips */}
{/* Filter Chips */}
{appliedFiltersChips.length > 0 && (
<div className="mb-3 d-flex flex-wrap align-items-center gap-2 mb-3">
<strong className="me-2 fs-6">Filters:</strong>
{appliedFiltersChips.map((chip, idx) => (
<span key={idx} className="d-flex align-items-center bg-label-secondary px-2 py-1 rounded me-1">
{chip.label} : {chip.value}
<button
type="button"
className="btn-close btn-close-white btn-sm ms-1"
aria-label="Remove"
onClick={() => handleRemoveFilter(chip.key, chip.id)}
/>
</span>
))}
<div className="mb-3 d-flex flex-wrap gap-2 align-items-center">
<strong className="me-2">Filters:</strong>
{["Building", "Floor", "Work Area", "Activity", "Uploaded By", "Work Category"].map((label) => {
const chips = appliedFiltersChips.filter(c => c.label === label);
if (!chips.length) return null;
return (
<div key={label} className="d-flex align-items-center gap-1">
<strong>{label}:</strong>
{chips.map(chip => (
<span key={chip.id} className="d-flex align-items-center bg-label-secondary px-2 py-1 rounded">
{chip.value}
<button
type="button"
className="btn-close btn-close-white btn-sm ms-1"
onClick={() => handleRemoveFilter(chip.key, chip.id)}
/>
</span>
))}
</div>
);
})}
{/* Date Range */}
{appliedFiltersChips.some(c => c.label === "Date Range") && (
<div className="d-flex align-items-center bg-label-secondary px-2 py-1 rounded">
<strong>Date Range:</strong>
{appliedFiltersChips.filter(c => c.label === "Date Range").map((chip, idx) => (
<span key={idx} className="d-flex align-items-center ms-1">
{chip.value}
<button
type="button"
className="btn-close btn-close-white btn-sm ms-1"
onClick={() => handleRemoveFilter(chip.key, chip.id)}
/>
</span>
))}
</div>
)}
</div>
)}
{/* Gallery */}
{isLoading ? (
<ImageGallerySkeleton count={4} />