import { useState, useEffect, useRef } from 'react' import { Link, useLocation } from 'react-router-dom' import { useTranslation } from 'react-i18next' import Navbar from '../components/Navbar' import Footer from '../components/Footer' import ProductCard from '../components/ProductCard' import EmptyState from '../components/EmptyState' import DynamicPageRenderer from '../components/DynamicPageRenderer' import { API_BASE, type Category, type Product, type PageSection } from '../types' export default function ShopPage() { const [categories, setCategories] = useState([]) const [products, setProducts] = useState([]) const [activeCategory, setActiveCategory] = useState(null) const [searchInput, setSearchInput] = useState('') const [searchQuery, setSearchQuery] = useState('') const location = useLocation() // Local progressive rendering const [displayCount, setDisplayCount] = useState(24) const loaderRef = useRef(null) const [totalProducts, setTotalProducts] = useState(0) const [loading, setLoading] = useState(true) const [productsLoading, setProductsLoading] = useState(false) const [cmsSections, setCmsSections] = useState([]) const { i18n } = useTranslation() // Handle coupon from URL useEffect(() => { const params = new URLSearchParams(location.search) const coupon = params.get('coupon') if (coupon) { localStorage.setItem('app_active_coupon', coupon) // Optionally clean URL window.history.replaceState({}, document.title, window.location.pathname) } }, [location]) // Fetch categories useEffect(() => { fetch(`${API_BASE}/categories?lang=${i18n.language}`) .then((r) => r.json()) .then((data: Category[]) => { setCategories(data) setLoading(false) }) .catch(console.error) }, [i18n.language]) // Fetch CMS sections for shop-layout useEffect(() => { fetch(`${API_BASE}/pages/shop-layout?lang=${i18n.language}`) .then((r) => { if (!r.ok) throw new Error('Page not found'); return r.json(); }) .then((data) => { if (data && data.sections) { setCmsSections(data.sections); } }) .catch((err) => { console.log('Shop dynamic layout sections not loaded:', err.message); }); }, [i18n.language]) // Debounce search input useEffect(() => { const timer = setTimeout(() => { setSearchQuery(searchInput) }, 400) return () => clearTimeout(timer) }, [searchInput]) // Reset scroll and displayCount when category or search changes useEffect(() => { window.scrollTo({ top: 0, behavior: 'smooth' }) setDisplayCount(24) }, [activeCategory, searchQuery]) // Fetch products useEffect(() => { setProductsLoading(true) const params = new URLSearchParams() params.append('limit', '1000') // fetch all matching items locally if (activeCategory) params.append('categoryId', activeCategory.toString()) if (searchQuery.trim()) params.append('search', searchQuery.trim()) params.append('lang', i18n.language) fetch(`${API_BASE}/products?${params.toString()}`) .then((r) => r.json()) .then((data: { data: Product[], meta?: any }) => { setProducts(data.data || []) if (data.meta) { setTotalProducts(data.meta.total || 0) } }) .catch(console.error) .finally(() => setProductsLoading(false)) }, [activeCategory, searchQuery, i18n.language]) // Intersection Observer for infinite scroll useEffect(() => { const observer = new IntersectionObserver( (entries) => { if (entries[0].isIntersecting && products.length > displayCount) { setDisplayCount((prev) => Math.min(prev + 12, products.length)) } }, { threshold: 0.1, rootMargin: '200px' } ) if (loaderRef.current) observer.observe(loaderRef.current) return () => observer.disconnect() }, [products.length, displayCount, productsLoading]) const announcementSections = cmsSections.filter(s => s.isActive && s.type.toUpperCase() === 'ANNOUNCEMENT_BAR'); const heroSections = cmsSections.filter(s => s.isActive && (s.type.toUpperCase() === 'HERO' || s.type.toUpperCase() === 'BANNER')); const bottomSections = cmsSections.filter(s => s.isActive && s.type.toUpperCase() !== 'ANNOUNCEMENT_BAR' && s.type.toUpperCase() !== 'HERO' && s.type.toUpperCase() !== 'BANNER'); return (
{announcementSections.length > 0 && ( )} {/* Page Header */} {heroSections.length > 0 ? (
) : (

All Products

The Collection

Browse our full range of premium aroma products — crafted to elevate every space.

{/* Search */}
setSearchInput(e.target.value)} className="bg-transparent border-none outline-none text-sm p-2 w-full text-pop-black placeholder-spa-stone/60" />
)} {/* Render Search bar below Hero block if Hero is configured */} {heroSections.length > 0 && (
setSearchInput(e.target.value)} className="bg-transparent border-none outline-none text-sm p-2 w-full text-pop-black placeholder-spa-stone/60" />
)}
{/* Category Filter Tabs */}
{categories.map((cat) => ( ))}
{/* Results count */} {(!loading) && (

{productsLoading ? 'Loading...' : `${totalProducts} products`}

{activeCategory && ( View Category Page → )}
)} {/* Products Grid */} {loading || productsLoading ? (
{Array.from({ length: 9 }).map((_, i) => (
))}
) : products.length === 0 ? ( { setActiveCategory(null); setSearchInput(''); setSearchQuery(''); }} /> ) : (
{products.slice(0, displayCount).map((p) => ( ))}
{/* Invisible target for IntersectionObserver */} {displayCount < products.length && (
)}
)}
{bottomSections.length > 0 && ( )}
) }