241 lines
8.5 KiB
JavaScript
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;
|