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 Avatar from "../../components/common/Avatar";
import ImagePopup from "./ImagePopup"; import ImagePopup from "./ImagePopup";
@ -14,8 +14,23 @@ const ImageGalleryListView = ({
moment, moment,
}) => { }) => {
const [hoveredImage, setHoveredImage] = useState(null); const [hoveredImage, setHoveredImage] = useState(null);
const [scrollThreshold, setScrollThreshold] = useState(5);
const imageGroupRefs = useRef({}); 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( const scrollLeft = useCallback(
(key) => (key) =>
imageGroupRefs.current[key]?.scrollBy({ left: -200, behavior: "smooth" }), imageGroupRefs.current[key]?.scrollBy({ left: -200, behavior: "smooth" }),
@ -37,11 +52,10 @@ const ImageGalleryListView = ({
) : images.length ? ( ) : images.length ? (
images.map((batch) => { images.map((batch) => {
const doc = batch.documents[0]; const doc = batch.documents[0];
const userName = `${doc.uploadedBy?.firstName || ""} ${ const userName = `${doc.uploadedBy?.firstName || ""} ${doc.uploadedBy?.lastName || ""
doc.uploadedBy?.lastName || "" }`.trim();
}`.trim();
const date = formatUTCToLocalTime(doc.uploadedAt); const date = formatUTCToLocalTime(doc.uploadedAt);
const hasArrows = batch.documents.length > SCROLL_THRESHOLD; const hasArrows = batch.documents.length > scrollThreshold;
return ( return (
<div key={batch.batchId} className="grouped-section"> <div key={batch.batchId} className="grouped-section">

View File

@ -1,86 +1,124 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import "./ImagePopup.css"
import { useModal } from "./ModalContext"; import { useModal } from "./ModalContext";
import moment from "moment";
import { formatUTCToLocalTime } from "../../utils/dateUtils"; import { formatUTCToLocalTime } from "../../utils/dateUtils";
const ImagePopup = ({ batch, initialIndex = 0 }) => { const ImagePopup = ({ batch, initialIndex = 0 }) => {
const { closeModal } = useModal(); const { closeModal } = useModal();
const [currentIndex, setCurrentIndex] = useState(initialIndex); const [currentIndex, setCurrentIndex] = useState(initialIndex);
// Effect to update currentIndex if the initialIndex prop changes
useEffect(() => { useEffect(() => {
setCurrentIndex(initialIndex); setCurrentIndex(initialIndex);
}, [initialIndex, batch]); }, [initialIndex, batch]);
// If no batch or documents are provided, don't render
if (!batch || !batch.documents || batch.documents.length === 0) return null; 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]; const image = batch.documents[currentIndex];
// Fallback if for some reason the image at the current index doesn't exist
if (!image) return null; if (!image) return null;
// Format details for display from the individual image document
const fullName = `${image.uploadedBy?.firstName || ""} ${image.uploadedBy?.lastName || "" const fullName = `${image.uploadedBy?.firstName || ""} ${image.uploadedBy?.lastName || ""
}`.trim(); }`.trim();
const date = formatUTCToLocalTime(image.uploadedAt); const date = formatUTCToLocalTime(image.uploadedAt);
// Location and category details from the 'batch' object (as previously corrected)
const buildingName = batch.buildingName; const buildingName = batch.buildingName;
const floorName = batch.floorName; const floorName = batch.floorName;
const workAreaName = batch.workAreaName; const workAreaName = batch.workAreaName;
const activityName = batch.activityName; const activityName = batch.activityName;
const batchComment = batch.comment; const batchComment = batch.comment;
// Handler for navigating to the previous image
const handlePrev = () => { const handlePrev = () => {
setCurrentIndex((prevIndex) => Math.max(0, prevIndex - 1)); setCurrentIndex((prevIndex) => Math.max(0, prevIndex - 1));
}; };
// Handler for navigating to the next image
const handleNext = () => { const handleNext = () => {
setCurrentIndex((prevIndex) => setCurrentIndex((prevIndex) =>
Math.min(batch.documents.length - 1, prevIndex + 1) Math.min(batch.documents.length - 1, prevIndex + 1)
); );
}; };
// Determine if previous/next buttons should be enabled/visible
const hasPrev = currentIndex > 0; const hasPrev = currentIndex > 0;
const hasNext = currentIndex < batch.documents.length - 1; const hasNext = currentIndex < batch.documents.length - 1;
return ( return (
<div className="image-modal-overlay"> <div
<div className="image-modal-content"> 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 && ( <img
<button className="nav-button prev-button" onClick={handlePrev}> src={image.url}
<i className='bx bx-chevron-left'></i> alt="Preview"
</button> className="img-fluid rounded"
)} style={{
maxHeight: "500px", // bigger image
width: "100%",
objectFit: "contain",
}}
/>
<div className="image-container"> {hasNext && (
<img src={image.url} alt="Preview" className="modal-image" /> <button
</div> 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 && ( {/* Details */}
<button className="nav-button next-button" onClick={handleNext}> <div className="mt-3 text-start small">
<i className='bx bx-chevron-right'></i> <p className="mb-1">
</button> <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"> <p className="mb-1">
<i className="bx bxs-calendar me-2"></i>
<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> <span className="text-muted">Date: </span>
<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> <span className="fw-semibold">{date}</span>
<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> </p>
{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 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> </div>
</div> </div>

View File

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