From 5c79cc33459c09157cfefb1666a31a6a275a3ec7 Mon Sep 17 00:00:00 2001 From: Cristian Felipe Cruz Buitron Date: Mon, 14 Sep 2026 03:30:54 +0000 Subject: [PATCH] =?UTF-8?q?fix(ecommerce):=20saca=20la=20barra=20flotante?= =?UTF-8?q?=20de=20"A=C3=B1adir=20al=20carrito"=20en=20mobile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pedido del usuario con capturas reales: la barra fija seguía superponiéndose sobre contenido de abajo al scrollear (ej. sobre la tarjeta de "También te puede interesar"), incluso después de varias vueltas ajustando cuándo debía aparecer/ocultarse. En vez de seguir parchando esa lógica, se saca la barra flotante por completo -- queda un solo botón "Añadir al carrito", visible en su lugar normal dentro de la página también en mobile (sin duplicar), igual que la mayoría de tiendas reales. Se borra components/product/mobile-add-to-cart.tsx (ya sin uso) y el id="product-buy-button"/id="recommendations-section" que solo existían para el IntersectionObserver de ese componente. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV --- .../ecommerce/app/product/[slug]/page.tsx | 7 +- .../components/product/mobile-add-to-cart.tsx | 93 ------------------- .../components/product/product-purchase.tsx | 11 +-- 3 files changed, 6 insertions(+), 105 deletions(-) delete mode 100644 workloads/ecommerce/components/product/mobile-add-to-cart.tsx diff --git a/workloads/ecommerce/app/product/[slug]/page.tsx b/workloads/ecommerce/app/product/[slug]/page.tsx index 1cd8827..ac48358 100644 --- a/workloads/ecommerce/app/product/[slug]/page.tsx +++ b/workloads/ecommerce/app/product/[slug]/page.tsx @@ -1,7 +1,6 @@ import type { Metadata } from "next"; import { notFound } from "next/navigation"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger } from "@/components/ui/accordion"; -import { MobileAddToCart } from "@/components/product/mobile-add-to-cart"; import { ProductCard } from "@/components/product/product-card"; import { ProductCarousel } from "@/components/product/product-carousel"; import { ProductGallery } from "@/components/product/product-gallery"; @@ -49,8 +48,7 @@ export default async function ProductPage({ params }: ProductPageProps) { apretado contra el texto o cortado por el panel de zoom de al lado (ver ProductGallery). Por debajo de xl, el buy box cae al flujo normal en la 2da columna, sin duplicar el componente en - el DOM (evita el id="product-buy-button" repetido que rompería - el IntersectionObserver de MobileAddToCart). + el DOM. El buy box ocupa las 2 filas (xl:row-span-2) para no quedar atado a la altura de galería+descripción -- "También te puede gustar" pasa a ser la 2da fila debajo de esas dos columnas @@ -107,13 +105,12 @@ 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 deleted file mode 100644 index c4781aa..0000000 --- a/workloads/ecommerce/components/product/mobile-add-to-cart.tsx +++ /dev/null @@ -1,93 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; -import { ShoppingBag } from "lucide-react"; -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 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). -// - !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. -// - !pastRecommendations: una vez que "También te puede gustar" -// (#recommendations-section en app/product/[slug]/page.tsx) empieza a -// entrar en pantalla, la barra se oculta y sigue oculta el resto del -// scroll (footer incluido) -- pedido explicito del usuario. OJO: -// condicionar esto a que el borde superior de la seccion cruce el -// TOPE del viewport (top <= 0) rompe en paginas cortas -- si no hay -// suficiente contenido/footer debajo, nunca se puede scrollear tan -// lejos y el bug original (barra pegada hasta el fondo) segunia -// reproduciendose. En cambio, se compara contra la ALTURA del -// viewport (top < innerHeight): se oculta apenas la seccion empieza a -// asomar desde abajo, se mantiene oculta al seguir scrolleando mas -// alla (top se vuelve cada vez mas negativo, sigue siendo < -// innerHeight), y solo reaparece si se vuelve a scrollear hacia -// arriba lo suficiente como para que la seccion salga de pantalla per -// completo otra vez. -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; - - useEffect(() => { - const target = document.getElementById("product-buy-button"); - if (!target) return; - const observer = new IntersectionObserver((entries) => { - const entry = entries[0]; - if (entry) setButtonVisible(entry.isIntersecting); - }, { threshold: 0 }); - observer.observe(target); - 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 < window.innerHeight); - }, { 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 && !pastRecommendations; - - return ( -
- -
- ); -} diff --git a/workloads/ecommerce/components/product/product-purchase.tsx b/workloads/ecommerce/components/product/product-purchase.tsx index 9d18309..f991f15 100644 --- a/workloads/ecommerce/components/product/product-purchase.tsx +++ b/workloads/ecommerce/components/product/product-purchase.tsx @@ -41,14 +41,11 @@ export function ProductPurchase({ product }: { product: Product }) { {formatMoney(product.price, product.currency)} {product.compareAtPrice && {formatMoney(product.compareAtPrice, product.currency)}}
- {/* Visible tambien en mobile (antes quedaba oculto