From cd72962fa46d92db494aa6bc624b081431f80b04 Mon Sep 17 00:00:00 2001 From: Cristian Felipe Cruz Buitron Date: Mon, 14 Sep 2026 01:09:51 +0000 Subject: [PATCH] fix(ecommerce): CTA fijo de mobile ya no sigue hasta el footer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit La barra de "Añadir al carrito" quedaba pegada abajo en mobile hasta el final de la página (incluso sobre el footer), mucho después de que el usuario ya pasó "También te puede gustar". Se agrega un tercer sentinel (#recommendations-section) observado por IntersectionObserver: una vez que su borde superior cruza el tope del viewport, la barra se oculta y no vuelve a aparecer bajando más. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV --- .../ecommerce/app/product/[slug]/page.tsx | 2 +- .../components/product/mobile-add-to-cart.tsx | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/workloads/ecommerce/app/product/[slug]/page.tsx b/workloads/ecommerce/app/product/[slug]/page.tsx index 2c15726..2b9b944 100644 --- a/workloads/ecommerce/app/product/[slug]/page.tsx +++ b/workloads/ecommerce/app/product/[slug]/page.tsx @@ -101,7 +101,7 @@ export default async function ProductPage({ params }: ProductPageProps) { carrusel. Confirmado en vivo: sin min-w-0 el grid medía 783px en un viewport real de 380px; con min-w-0 vuelve a 380px. */} {recommended.length > 0 && ( -
+

También te puede gustar

diff --git a/workloads/ecommerce/components/product/mobile-add-to-cart.tsx b/workloads/ecommerce/components/product/mobile-add-to-cart.tsx index 20501b7..e91ffaa 100644 --- a/workloads/ecommerce/components/product/mobile-add-to-cart.tsx +++ b/workloads/ecommerce/components/product/mobile-add-to-cart.tsx @@ -10,7 +10,7 @@ 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 combina dos +// duplicar un boton que ya está visible más abajo. Ahora combina tres // 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). @@ -18,9 +18,16 @@ const SCROLL_THRESHOLD_PX = 80; // ProductPurchase, visible tambien en mobile) no esta en pantalla -- // ya sea porque todavia no se llego a el, o porque se scrolleo mas // alla. +// - !pastRecommendations: una vez que se llega a "También te puede +// gustar" (#recommendations-section en app/product/[slug]/page.tsx) +// la barra se oculta y ya no vuelve a aparecer bajando mas -- pedido +// explicito del usuario (antes quedaba pegada hasta el footer, sin +// sentido una vez que el contenido principal del producto ya quedó +// atrás). export function MobileAddToCart({ product }: { product: Product }) { const addItem = useCartStore((state) => state.addItem); const [buttonVisible, setButtonVisible] = useState(true); + const [pastRecommendations, setPastRecommendations] = useState(false); const [scrolled, setScrolled] = useState(false); const outOfStock = product.variants[0]?.available === false; @@ -35,6 +42,17 @@ export function MobileAddToCart({ product }: { product: Product }) { return () => observer.disconnect(); }, []); + useEffect(() => { + const target = document.getElementById("recommendations-section"); + if (!target) return; + const observer = new IntersectionObserver((entries) => { + const entry = entries[0]; + if (entry) setPastRecommendations(entry.boundingClientRect.top <= 0); + }, { threshold: 0 }); + observer.observe(target); + return () => observer.disconnect(); + }, []); + useEffect(() => { const onScroll = () => setScrolled(window.scrollY > SCROLL_THRESHOLD_PX); onScroll(); @@ -42,7 +60,7 @@ export function MobileAddToCart({ product }: { product: Product }) { return () => window.removeEventListener("scroll", onScroll); }, []); - const visible = scrolled && !buttonVisible; + const visible = scrolled && !buttonVisible && !pastRecommendations; return (