Files
apps-registry/workloads/ecommerce/components/product/mobile-add-to-cart.tsx
T
devopsandClaude Sonnet 5 9d4553f3a4
Build and Push Frontend / Escaneo de Secretos (Gitleaks) (push) Successful in 24s
Build and Push Frontend / Construir y Subir Imagen (push) Successful in 7m33s
fix(ecommerce): panel de zoom dinámico y CTA móvil con revelado progresivo
- El panel de zoom lateral ahora calcula tamaño/posición contra el
  viewport real (window.innerWidth/innerHeight) en cada mousemove, en
  vez de un breakpoint fijo (lg:block) con ancho fijo -- a resoluciones
  altas con poco espacio a la derecha de la imagen terminaba
  saliéndose de la pantalla. Si no entra un panel de al menos
  PANEL_MIN_SIZE, se cae automáticamente al overlay in-place (antes
  atado a "por debajo de lg", ahora a "no hay espacio real").
- La barra fija de "Añadir al carrito" en mobile quedaba fija desde el
  primer render, tapando permanentemente parte de la imagen del
  producto. Ahora el botón real (ProductPurchase) también se muestra
  en mobile, y la barra sticky solo aparece cuando ese botón sale del
  viewport (IntersectionObserver) -- mismo patrón que el "Buy Now"
  sticky de Amazon.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012Y4df3Z7odr9Cg8F2nw6uV
2026-09-13 20:34:43 +00:00

52 lines
1.9 KiB
TypeScript

"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";
// 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.
export function MobileAddToCart({ product }: { product: Product }) {
const addItem = useCartStore((state) => state.addItem);
const [visible, setVisible] = 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) setVisible(!entry.isIntersecting);
}, { threshold: 0 });
observer.observe(target);
return () => observer.disconnect();
}, []);
return (
<div
aria-hidden={!visible}
className={`fixed inset-x-0 bottom-0 z-40 border-t border-slate-200 bg-white/95 p-3 shadow-2xl backdrop-blur transition-transform duration-200 lg:hidden ${
visible ? "translate-y-0" : "pointer-events-none translate-y-full"
}`}
>
<Button
variant="primary"
size="lg"
className="w-full"
disabled={outOfStock}
onClick={() => addItem(product, product.variants[0]?.id)}
>
<ShoppingBag />
{outOfStock ? "Agotado" : "Añadir al carrito"}
</Button>
</div>
);
}