// — shop product detail at /shop/:productSlug. // // Composition: // ShopHeader // ├ ProductGallery (left column) // └ ProductInfo + ProductBuyPanel (right column) // TemplateGalleryForProduct (full width, customizable kind only) // // Loading / error / 404 states are handled inline (kept simple — no // dedicated error boundary needed for a leaf route). // // The buy panel's "Customize this product" CTA scrolls the template // gallery into view via a ref + scrollIntoView. The gallery section // has id="templates" so the URL hash also works as a fallback. import { useParams, Link } from 'react-router-dom'; import { useRef } from 'react'; import { ShopHeader } from './ShopHeader.jsx'; import { ProductGallery } from './ProductGallery.jsx'; import { ProductInfo } from './ProductInfo.jsx'; import { ProductBuyPanel } from './ProductBuyPanel.jsx'; import { TemplateGalleryForProduct } from './TemplateGalleryForProduct.jsx'; import { useShopProduct } from '../../hooks/useShopProduct.js'; export function ProductPage() { const { productSlug } = useParams(); const { product, loading, error } = useShopProduct(productSlug); const templatesRef = useRef(null); const handleScrollToTemplates = () => { if (templatesRef.current) { templatesRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }; return (
{loading && (

Loading…

)} {error && (

Product not found

We couldn't find that product. Back to the shop →

)} {product && ( <>
{product.kind === 'customizable' && (
)} )}
); }