Reemplaza hero-bento.tsx (grid estatico de 3 tarjetas en fucsia/rosa/ rojo/violeta del esquema arcoiris original, nunca migrado -- sub-fase 4 del ADR 0008 pendiente) por hero-carousel.tsx: un slide a la vez sobre fondo azul de marca (--color-ari-marino) para los tres mensajes promocionales existentes, con dots de paginacion clicables abajo (patron pedido explicitamente por el usuario) y autoplay cada 6s (respeta prefers-reduced-motion). Actualiza app/page.tsx para usar el componente nuevo y elimina hero-bento.tsx.
114 lines
4.5 KiB
TypeScript
114 lines
4.5 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { ArrowRight, BadgePercent, Sparkles } from "lucide-react";
|
|
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
|
|
import { useEffect, useState } from "react";
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
// ADR 0008 sub-fase 4: hero azul con dots de paginacion (carrusel), en vez
|
|
// del bento estatico de 3 tarjetas en fucsia/rojo/violeta del esquema
|
|
// arcoiris original. Un slide a la vez, fondo azul de marca en los tres,
|
|
// para leerse como una sola pieza de marca en vez de 3 bloques de colores
|
|
// distintos.
|
|
const slides = [
|
|
{
|
|
id: "celebracion",
|
|
badge: "Celebración ARI",
|
|
icon: Sparkles,
|
|
title: "Hasta 50% OFF",
|
|
subtitle: "en tesoros seleccionados",
|
|
description: "Personajes, mochilas y regalos que convierten cualquier día en una celebración.",
|
|
href: "/catalog",
|
|
image: "/images/promo/hero-vibrant.webp",
|
|
imageAlt: "Promoción principal de Ari Shopping",
|
|
},
|
|
{
|
|
id: "marvel",
|
|
badge: "Héroes favoritos",
|
|
icon: BadgePercent,
|
|
title: "Acción y aventura",
|
|
subtitle: "colección Marvel",
|
|
description: "Figuras, mochilas y sets listos para la próxima misión.",
|
|
href: "/catalog?collection=Marvel",
|
|
image: "/images/promo/promo-marvel.webp",
|
|
imageAlt: "Colección Marvel",
|
|
},
|
|
{
|
|
id: "regalos",
|
|
badge: "Novedad",
|
|
icon: Sparkles,
|
|
title: "Tesoros para regalar",
|
|
subtitle: "",
|
|
description: "Personajes y peluches para sorprender en cualquier ocasión.",
|
|
href: "/catalog",
|
|
image: "/images/promo/promo-characters.webp",
|
|
imageAlt: "Personajes y peluches",
|
|
},
|
|
];
|
|
|
|
const AUTOPLAY_MS = 6000;
|
|
|
|
export function HeroCarousel() {
|
|
const reduceMotion = useReducedMotion();
|
|
const [index, setIndex] = useState(0);
|
|
|
|
useEffect(() => {
|
|
if (reduceMotion) return;
|
|
const timer = setInterval(() => setIndex((current) => (current + 1) % slides.length), AUTOPLAY_MS);
|
|
return () => clearInterval(timer);
|
|
}, [reduceMotion]);
|
|
|
|
const slide = slides[index];
|
|
const Icon = slide.icon;
|
|
|
|
return (
|
|
<section className="mx-auto max-w-7xl px-4 py-7 lg:py-10">
|
|
<div className="relative overflow-hidden rounded-[2rem] bg-ari-marino text-white shadow-2xl">
|
|
<AnimatePresence mode="wait">
|
|
<motion.article
|
|
key={slide.id}
|
|
initial={reduceMotion ? false : { opacity: 0 }}
|
|
animate={{ opacity: 1 }}
|
|
exit={reduceMotion ? undefined : { opacity: 0 }}
|
|
transition={{ duration: 0.4 }}
|
|
className="grid min-h-[420px] lg:min-h-[500px] lg:grid-cols-2"
|
|
>
|
|
<div className="relative z-10 flex flex-col justify-center p-7 lg:p-14">
|
|
<div className="mb-5 inline-flex w-fit items-center gap-2 rounded-full bg-ari-coral-claro px-4 py-2 text-xs font-black uppercase tracking-wider text-ari-marino">
|
|
<Icon className="size-4" />
|
|
{slide.badge}
|
|
</div>
|
|
<h1 className="text-5xl font-black leading-[.95] tracking-[-.04em] sm:text-6xl">{slide.title}</h1>
|
|
{slide.subtitle && <p className="mt-2 text-2xl font-bold text-ari-coral-claro">{slide.subtitle}</p>}
|
|
<p className="mt-6 max-w-md text-base font-semibold text-white/90 sm:text-lg">{slide.description}</p>
|
|
<Button asChild variant="yellow" size="lg" className="mt-8 w-fit">
|
|
<Link href={slide.href}>
|
|
Comprar ahora <ArrowRight className="size-5" />
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
<div className="relative min-h-[280px] bg-white lg:min-h-full">
|
|
<Image src={slide.image} alt={slide.imageAlt} fill priority={index === 0} sizes="(max-width: 1024px) 100vw, 50vw" className="object-cover object-center" />
|
|
</div>
|
|
</motion.article>
|
|
</AnimatePresence>
|
|
|
|
<div className="absolute inset-x-0 bottom-5 z-20 flex justify-center gap-2">
|
|
{slides.map((item, itemIndex) => (
|
|
<button
|
|
key={item.id}
|
|
type="button"
|
|
aria-label={`Ir a la diapositiva ${itemIndex + 1}: ${item.badge}`}
|
|
aria-current={itemIndex === index ? "true" : undefined}
|
|
onClick={() => setIndex(itemIndex)}
|
|
className={`h-2.5 rounded-full transition-all ${itemIndex === index ? "w-7 bg-ari-coral-claro" : "w-2.5 bg-white/50 hover:bg-white/80"}`}
|
|
/>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|