import { useState, useEffect, useRef } from 'react' import { Link, useLocation } from 'react-router-dom' import Navbar from '../components/Navbar' import Footer from '../components/Footer' import ProductCard from '../components/ProductCard' import EmptyState from '../components/EmptyState' import { API_BASE, type Category, type Product } 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) // Handle coupon from URL useEffect(() => { const params = new URLSearchParams(location.search) const coupon = params.get('coupon') if (coupon) { localStorage.setItem('homaroma_active_coupon', coupon) // Optionally clean URL window.history.replaceState({}, document.title, window.location.pathname) } }, [location]) // Fetch categories useEffect(() => { fetch(`${API_BASE}/categories`) .then((r) => r.json()) .then((data: Category[]) => { setCategories(data) setLoading(false) }) .catch(console.error) }, []) // 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()) 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]) // 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]) return (
{/* Page Header */}

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" />
{/* 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 && (
)}
)}
) }