Creating Skeleton in Image_Gallery. #415

Closed
kartik.sharma wants to merge 17 commits from Image_Gallery_filter into Organization_Management
3 changed files with 102 additions and 129 deletions
Showing only changes of commit c2bac4e640 - Show all commits

View File

@ -9,7 +9,6 @@ const ImageGalleryListView = ({
hasNextPage, hasNextPage,
loaderRef, loaderRef,
openModal, openModal,
SCROLL_THRESHOLD,
formatUTCToLocalTime, formatUTCToLocalTime,
moment, moment,
}) => { }) => {
@ -19,18 +18,16 @@ const ImageGalleryListView = ({
useEffect(() => { useEffect(() => {
const updateThreshold = () => { const updateThreshold = () => {
if (window.innerWidth >= 1400) setScrollThreshold(6); // xl screens if (window.innerWidth >= 1400) setScrollThreshold(6);
else if (window.innerWidth >= 992) setScrollThreshold(5); // lg else if (window.innerWidth >= 992) setScrollThreshold(5);
else if (window.innerWidth >= 768) setScrollThreshold(4); // md else if (window.innerWidth >= 768) setScrollThreshold(4);
else setScrollThreshold(3); // sm & xs else setScrollThreshold(3);
}; };
updateThreshold(); updateThreshold();
window.addEventListener("resize", updateThreshold); window.addEventListener("resize", updateThreshold);
return () => window.removeEventListener("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" }),
@ -42,132 +39,104 @@ const ImageGalleryListView = ({
[] []
); );
if (!images.length && !isLoading) {
return <p className="text-center text-muted mt-5">No images match the selected filters.</p>;
}
return ( return (
<div className="main-content"> <div className="main-content">
<div className="activity-section"> <div className="activity-section">
{isLoading ? ( {images.map((batch) => {
<div className="text-center"> if (!batch.documents?.length) return null; // skip empty batches
<p>Loading...</p>
</div>
) : images.length ? (
images.map((batch) => {
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 ( const doc = batch.documents[0];
<div key={batch.batchId} className="grouped-section"> const userName = `${doc.uploadedBy?.firstName || ""} ${doc.uploadedBy?.lastName || ""}`.trim();
<div className="group-heading"> const date = formatUTCToLocalTime(doc.uploadedAt);
{/* Uploader Info */} const hasArrows = batch.documents.length > scrollThreshold;
<div className="d-flex align-items-center mb-1">
<Avatar
size="xs"
firstName={doc.uploadedBy?.firstName}
lastName={doc.uploadedBy?.lastName}
className="me-2"
/>
<div className="d-flex flex-column align-items-start">
<strong className="user-name-text">{userName}</strong>
<span className="text-muted small">{date}</span>
</div>
</div>
{/* Location Info */} return (
<div className="location-line text-secondary"> <div key={batch.batchId} className="grouped-section">
<div className="d-flex align-items-center flex-wrap gap-1 text-secondary"> <div className="group-heading">
<span className="d-flex align-items-center"> <div className="d-flex align-items-center mb-1">
<span>{batch.buildingName}</span> <Avatar
<i className="bx bx-chevron-right " /> size="xs"
</span> firstName={doc.uploadedBy?.firstName}
<span className="d-flex align-items-center"> lastName={doc.uploadedBy?.lastName}
<span>{batch.floorName}</span> className="me-2"
<i className="bx bx-chevron-right m" /> />
</span> <div className="d-flex flex-column align-items-start">
<span className="d-flex align-items-center "> <strong className="user-name-text">{userName}</strong>
<span>{batch.workAreaName || "Unknown"}</span> <span className="text-muted small">{date}</span>
<i className="bx bx-chevron-right " />
<span>{batch.activityName}</span>
</span>
</div>
{batch.workCategoryName && (
<span className="badge bg-label-primary ms-2">
{batch.workCategoryName}
</span>
)}
</div> </div>
</div> </div>
{/* Images */} <div className="location-line text-secondary">
<div className="image-group-wrapper"> <div className="d-flex align-items-center flex-wrap gap-1 text-secondary">
{hasArrows && ( <span className="d-flex align-items-center">
<button <span>{batch.buildingName}</span>
className="scroll-arrow left-arrow" <i className="bx bx-chevron-right " />
onClick={() => scrollLeft(batch.batchId)} </span>
> <span className="d-flex align-items-center">
<span>{batch.floorName}</span>
</button> <i className="bx bx-chevron-right m" />
)} </span>
<div <span className="d-flex align-items-center ">
className="image-group-horizontal" <span>{batch.workAreaName || "Unknown"}</span>
ref={(el) => <i className="bx bx-chevron-right " />
(imageGroupRefs.current[batch.batchId] = el) <span>{batch.activityName}</span>
} </span>
>
{batch.documents.map((d, i) => {
const hoverDate = moment(d.uploadedAt).format(
"DD MMMM, YYYY"
);
const hoverTime = moment(d.uploadedAt).format("hh:mm A");
return (
<div
key={d.id}
className="image-card"
onClick={() =>
openModal(<ImagePopup batch={batch} initialIndex={i} />)
}
onMouseEnter={() => setHoveredImage(d)}
onMouseLeave={() => setHoveredImage(null)}
>
<div className="image-wrapper">
<img src={d.url} alt={`Image ${i + 1}`} />
</div>
{hoveredImage === d && (
<div className="image-hover-description">
<p>
<strong>Date:</strong> {hoverDate}
</p>
<p>
<strong>Time:</strong> {hoverTime}
</p>
<p>
<strong>Activity:</strong> {batch.activityName}
</p>
</div>
)}
</div>
);
})}
</div> </div>
{hasArrows && ( {batch.workCategoryName && (
<button <span className="badge bg-label-primary ms-2">{batch.workCategoryName}</span>
className="scroll-arrow right-arrow"
onClick={() => scrollRight(batch.batchId)}
>
<i className="bx bx-chevron-right"></i>
</button>
)} )}
</div> </div>
</div> </div>
);
}) <div className="image-group-wrapper">
) : ( {hasArrows && (
<p className="text-center text-muted mt-5"> <button className="scroll-arrow left-arrow" onClick={() => scrollLeft(batch.batchId)}>
No images match the selected filters.
</p> </button>
)} )}
<div
className="image-group-horizontal"
ref={(el) => (imageGroupRefs.current[batch.batchId] = el)}
>
{batch.documents.map((d, i) => {
const hoverDate = moment(d.uploadedAt).format("DD MMMM, YYYY");
const hoverTime = moment(d.uploadedAt).format("hh:mm A");
return (
<div
key={d.id}
className="image-card"
onClick={() => openModal(<ImagePopup batch={batch} initialIndex={i} />)}
onMouseEnter={() => setHoveredImage(d)}
onMouseLeave={() => setHoveredImage(null)}
>
<div className="image-wrapper">
<img src={d.url} alt={`Image ${i + 1}`} />
</div>
{hoveredImage === d && (
<div className="image-hover-description">
<p><strong>Date:</strong> {hoverDate}</p>
<p><strong>Time:</strong> {hoverTime}</p>
<p><strong>Activity:</strong> {batch.activityName}</p>
</div>
)}
</div>
);
})}
</div>
{hasArrows && (
<button className="scroll-arrow right-arrow" onClick={() => scrollRight(batch.batchId)}>
</button>
)}
</div>
</div>
);
})}
<div ref={loaderRef}> <div ref={loaderRef}>
{isFetchingNextPage && hasNextPage && <p>Loading...</p>} {isFetchingNextPage && hasNextPage && <p>Loading...</p>}

View File

@ -89,16 +89,19 @@ import { useInfiniteQuery } from "@tanstack/react-query";
const PAGE_SIZE = 10; const PAGE_SIZE = 10;
const useImageGallery = (selectedProjectId, filters) => { const useImageGallery = (selectedProjectId, filters) => {
const hasFilters = filters && Object.values(filters).some( const hasFilters =
value => Array.isArray(value) ? value.length > 0 : value !== null && value !== "" filters &&
); Object.values(filters).some((value) =>
Array.isArray(value) ? value.length > 0 : value !== null && value !== ""
);
return useInfiniteQuery({ return useInfiniteQuery({
queryKey: ["imageGallery", selectedProjectId, hasFilters ? filters : null], queryKey: ["imageGallery", selectedProjectId, hasFilters ? filters : null],
enabled: !!selectedProjectId, enabled: !!selectedProjectId,
getNextPageParam: (lastPage, allPages) => { getNextPageParam: (lastPage) => {
if (!lastPage?.data?.length) return undefined; const currentPage = lastPage?.data?.currentPage || 1;
return allPages.length + 1; const totalPages = lastPage?.data?.totalPages || 1;
return currentPage < totalPages ? currentPage + 1 : undefined;
}, },
queryFn: async ({ pageParam = 1 }) => { queryFn: async ({ pageParam = 1 }) => {
const res = await ImageGalleryAPI.ImagesGet( const res = await ImageGalleryAPI.ImagesGet(
@ -107,7 +110,7 @@ const useImageGallery = (selectedProjectId, filters) => {
pageParam, pageParam,
PAGE_SIZE PAGE_SIZE
); );
return res; return res.data; // Important: use res.data to match API response
}, },
}); });
}; };

View File

@ -185,7 +185,8 @@ const ImageGalleryPage = () => {
}, [hasNextPage, isFetchingNextPage, isLoading, fetchNextPage]); }, [hasNextPage, isFetchingNextPage, isLoading, fetchNextPage]);
return ( return (
<div className="container my-3">
<div className="container-fluid">
<Breadcrumb data={[{ label: "Home", link: "/" }, { label: "Gallery" }]} /> <Breadcrumb data={[{ label: "Home", link: "/" }, { label: "Gallery" }]} />
{/* Card wrapper */} {/* Card wrapper */}