feat(ecommerce): página de producto estilo Amazon (buy-box + más ancho)
- max-w de la página de producto pasa de 1280px a 1600px -- aprovecha pantallas grandes en vez de dejar margenes vacíos enormes. - Nueva 3ra columna desde xl: buy-box propio (tarjeta con borde, sticky) para precio/variantes/carrito, separado del texto descriptivo. Por debajo de xl cae al flujo normal de la 2da columna sin duplicar el componente (mismo <ProductPurchase>, un solo id="product-buy-button" en el DOM). - Dentro del buy-box: un producto sugerido real de la misma categoría/colección (mismo cálculo que ya alimenta el carrusel de abajo, no un dato inventado). - MobileAddToCart: la barra fija ahora también requiere haber scrolleado más de 80px además de que el botón real no esté en pantalla -- antes aparecía inmediatamente sobre la vista inicial del producto (imagen + variantes) apenas cargaba la página. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV
This commit is contained in:
1 parent
b23f19ac6b
commit
401f43a136
2 files changed
+77
-8
No files matched your search
@@ -6,16 +6,22 @@ import { Button } from "@/components/ui/button";
|
||||
import type { Product } from "@/lib/headless/types";
|
||||
import { useCartStore } from "@/store/cart-store";
|
||||
|
||||
const SCROLL_THRESHOLD_PX = 80;
|
||||
|
||||
// Antes esta barra quedaba fija desde el primer render en mobile,
|
||||
// tapando permanentemente parte de la imagen del producto solo para
|
||||
// duplicar un boton que ya está visible más abajo. Ahora se muestra
|
||||
// recien cuando el boton real (#product-buy-button en ProductPurchase,
|
||||
// visible tambien en mobile) sale del viewport -- mismo patron que el
|
||||
// "Buy Now" sticky de Amazon, que solo aparece al scrollear mas alla
|
||||
// del boton real.
|
||||
// duplicar un boton que ya está visible más abajo. Ahora combina dos
|
||||
// condiciones, igual que el "Buy Now" sticky de Amazon:
|
||||
// - scrolled: recien aparece despues de que el usuario empieza a
|
||||
// scrollear (nunca sobre la vista inicial de la imagen/hero).
|
||||
// - !buttonVisible: el boton real (#product-buy-button en
|
||||
// ProductPurchase, visible tambien en mobile) no esta en pantalla --
|
||||
// ya sea porque todavia no se llego a el, o porque se scrolleo mas
|
||||
// alla.
|
||||
export function MobileAddToCart({ product }: { product: Product }) {
|
||||
const addItem = useCartStore((state) => state.addItem);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [buttonVisible, setButtonVisible] = useState(true);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const outOfStock = product.variants[0]?.available === false;
|
||||
|
||||
useEffect(() => {
|
||||
@@ -23,12 +29,21 @@ export function MobileAddToCart({ product }: { product: Product }) {
|
||||
if (!target) return;
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
const entry = entries[0];
|
||||
if (entry) setVisible(!entry.isIntersecting);
|
||||
if (entry) setButtonVisible(entry.isIntersecting);
|
||||
}, { threshold: 0 });
|
||||
observer.observe(target);
|
||||
return () => observer.disconnect();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > SCROLL_THRESHOLD_PX);
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
const visible = scrolled && !buttonVisible;
|
||||
|
||||
return (
|
||||
<div
|
||||
aria-hidden={!visible}
|
||||
|
||||
Reference in new issue
Block a user