Files
lcepl-frontend/src/pages/Gallery.jsx

241 lines
8.5 KiB
JavaScript

// src/pages/Gallery.jsx
import React, { useState, useEffect, useRef } from "react";
import axios from "axios";
import heroBg from "../assets/awards-hero.jpg";
import { FiChevronLeft, FiChevronRight, FiX } from "react-icons/fi";
import "../styles/Gallery.css";
const Gallery = () => {
const [activeCategory, setActiveCategory] = useState("Awards & Achievements");
const [galleryItems, setGalleryItems] = useState([]);
const [loading, setLoading] = useState(true);
const [selectedItem, setSelectedItem] = useState(null);
const [currentIndex, setCurrentIndex] = useState(0);
const categoryRef = useRef(null);
const categories = [
{ label: "Awards & Achievements", value: "Awards & Achievements" },
{ label: "Client Testimonials", value: "Client Testimonials" },
];
// Disable right-click
useEffect(() => {
const handleContextMenu = (e) => e.preventDefault();
document.addEventListener("contextmenu", handleContextMenu);
return () => document.removeEventListener("contextmenu", handleContextMenu);
}, []);
// Fetch gallery items
useEffect(() => {
const fetchGalleryItems = async () => {
try {
const response = await axios.get(`${process.env.REACT_APP_API_BASE_URL}/api/gallery`);
setGalleryItems(Array.isArray(response.data) ? response.data : []);
} catch (error) {
console.error("Error fetching gallery items:", error);
} finally {
setLoading(false);
}
};
fetchGalleryItems();
}, []);
// Scroll to categories if hash exists
useEffect(() => {
if (window.location.hash === "#categories" && categoryRef.current) {
setTimeout(() => {
categoryRef.current.scrollIntoView({ behavior: "smooth", block: "start" });
}, 200);
}
}, []);
const filteredItems = galleryItems.filter(
(item) => item.category === activeCategory
);
const openLightbox = (item, index) => {
setSelectedItem(item);
setCurrentIndex(index);
};
const navigateLightbox = (direction) => {
const items = filteredItems;
let newIndex;
if (direction === "prev") {
newIndex = (currentIndex - 1 + items.length) % items.length;
} else {
newIndex = (currentIndex + 1) % items.length;
}
setSelectedItem(items[newIndex]);
setCurrentIndex(newIndex);
};
return (
<div className="min-h-screen bg-gray-50">
{/* Hero Section */}
<div
className="relative bg-cover bg-center h-[60vh] flex items-center justify-center"
style={{ backgroundImage: `url(${heroBg})` }}
>
<div className="absolute inset-0 bg-black bg-opacity-50"></div>
<div className="relative z-10 text-center px-4 animate-fadeIn">
<h1 className="text-3xl sm:text-4xl md:text-5xl font-extrabold text-white drop-shadow-lg">
Our Awards
</h1>
<p className="text-lg sm:text-xl md:text-2xl text-white mt-2 italic drop-shadow-md">
Celebrating Excellence & Achievements
</p>
</div>
</div>
{/* Category Buttons */}
<div
ref={categoryRef}
className="flex flex-wrap justify-center gap-4 px-4 mt-8 mb-10"
>
{categories.map((category) => {
const isSelected = activeCategory === category.value;
return (
<button
key={category.value}
onClick={() => setActiveCategory(category.value)}
className={`text-base lg:text-lg text-white font-semibold px-6 py-3 rounded-2xl shadow-lg transition-all duration-300 transform hover:scale-105
${isSelected ? "bg-gradient-to-r from-blue-600 to-blue-800 ring-4 ring-offset-2 ring-white" : "bg-gradient-to-r from-gray-600 to-gray-800"}`}
>
{category.label}
</button>
);
})}
</div>
{/* Gallery Grid */}
<div className="px-6">
{loading ? (
<p className="text-gray-500 text-xl text-center">Loading gallery items...</p>
) : filteredItems.length === 0 ? (
<p className="text-gray-600 text-xl text-center">No items available in this category.</p>
) : (
<div className="grid md:grid-cols-3 sm:grid-cols-2 gap-6">
{filteredItems.map((item, index) => (
<div
key={item.id}
className="rounded-xl shadow-lg overflow-hidden border bg-white hover:shadow-2xl transition-all duration-300"
>
{item.type === "image" ? (
<img
src={`${process.env.REACT_APP_API_BASE_URL}${item.url}`}
alt={item.caption || "Gallery Image"}
className="h-64 w-full object-cover cursor-pointer hover:scale-105 transition-transform duration-300"
onClick={() => openLightbox(item, index)}
draggable="false"
onDragStart={(e) => e.preventDefault()}
/>
) : (
<video
className="h-64 w-full object-cover cursor-pointer"
onClick={() => openLightbox(item, index)}
controls={false}
>
<source src={`${process.env.REACT_APP_API_BASE_URL}${item.url}`} type="video/mp4" />
</video>
)}
<div className="p-5 text-left">
<p className="text-sm text-gray-700 leading-relaxed">
{item.caption || "No caption available"}
</p>
{item.date && (
<p className="text-xs text-gray-500 mt-2">
{new Date(item.date).toLocaleDateString()}
</p>
)}
</div>
</div>
))}
</div>
)}
</div>
{/* Lightbox Overlay */}
{selectedItem && (
<div
className="fixed inset-0 bg-black bg-opacity-90 flex items-center justify-center z-50 p-4"
onClick={() => setSelectedItem(null)}
>
{/* Close */}
<button
className="absolute top-4 right-4 text-white text-3xl"
onClick={(e) => {
e.stopPropagation();
setSelectedItem(null);
}}
>
<FiX />
</button>
{/* Previous */}
<button
className="absolute left-4 text-white text-3xl p-2"
onClick={(e) => {
e.stopPropagation();
navigateLightbox("prev");
}}
>
<FiChevronLeft size={32} />
</button>
{/* Content */}
<div className="max-w-4xl w-full" onClick={(e) => e.stopPropagation()}>
{selectedItem.type === "image" ? (
<div className="relative">
<img
src={`${process.env.REACT_APP_API_BASE_URL}${selectedItem.url}`}
alt={selectedItem.caption || "Gallery Image"}
className="max-w-full max-h-[80vh] mx-auto rounded-lg shadow-lg"
draggable="false"
onDragStart={(e) => e.preventDefault()}
/>
{/* Watermark */}
<div className="absolute inset-0 flex items-center justify-center pointer-events-none">
<div className="text-2xl font-bold text-white opacity-30" style={{
transform: 'rotate(-45deg)',
textShadow: '2px 2px 4px rgba(0,0,0,0.5)'
}}>
SAMPLE WATERMARK
</div>
</div>
</div>
) : (
<video
controls
autoPlay
className="max-w-full max-h-[80vh] mx-auto rounded-lg shadow-lg"
>
<source src={`${process.env.REACT_APP_API_BASE_URL}${selectedItem.url}`} type="video/mp4" />
</video>
)}
<div className="text-center mt-4 text-white">
<p className="text-lg">{selectedItem.caption}</p>
{selectedItem.date && (
<p className="text-sm text-gray-300">{new Date(selectedItem.date).toLocaleDateString()}</p>
)}
</div>
</div>
{/* Next */}
<button
className="absolute right-4 text-white text-3xl p-2"
onClick={(e) => {
e.stopPropagation();
navigateLightbox("next");
}}
>
<FiChevronRight size={32} />
</button>
</div>
)}
</div>
);
};
export default Gallery;