Changes in Images gallery popup and page.
This commit is contained in:
parent
3df68dff89
commit
59b62d2f10
@ -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">
|
||||||
|
@ -1,90 +1,128 @@
|
|||||||
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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ImagePopup;
|
export default ImagePopup;
|
||||||
|
@ -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} />
|
||||||
|
Loading…
x
Reference in New Issue
Block a user