|
"use client"; |
|
|
|
import { useState, useEffect, useCallback } from "react"; |
|
import Image from "next/image"; |
|
import Link from "next/link"; |
|
import { ChevronLeft, ChevronRight } from "lucide-react"; |
|
import { Banner } from "@/types/banners"; |
|
import { cn } from "@/lib/utils"; |
|
|
|
interface HeroCarouselProps { |
|
initialBanners: Banner[]; |
|
} |
|
|
|
export default function HeroCarousel({ |
|
initialBanners = [], |
|
}: HeroCarouselProps) { |
|
// Usar initialBanners como estado inicial y garantizar que sea un array |
|
console.log("BANNERS EN HERO CAROUSEL:", initialBanners); // <-- Agrega este log |
|
const [banners] = useState<Banner[]>( |
|
Array.isArray(initialBanners) ? initialBanners : [] |
|
); |
|
const [currentIndex, setCurrentIndex] = useState(0); |
|
const [isPaused, setIsPaused] = useState(false); |
|
|
|
// Autoplay |
|
useEffect(() => { |
|
if (banners.length <= 1 || isPaused) return; |
|
|
|
const interval = setInterval(() => { |
|
setCurrentIndex((prevIndex) => (prevIndex + 1) % banners.length); |
|
}, 5000); |
|
|
|
return () => clearInterval(interval); |
|
}, [banners.length, isPaused]); |
|
|
|
// Navegación |
|
const goToPrevious = useCallback(() => { |
|
setCurrentIndex((prevIndex) => |
|
prevIndex === 0 ? banners.length - 1 : prevIndex - 1 |
|
); |
|
}, [banners.length]); |
|
|
|
const goToNext = useCallback(() => { |
|
setCurrentIndex((prevIndex) => (prevIndex + 1) % banners.length); |
|
}, [banners.length]); |
|
|
|
const goToSlide = useCallback((index: number) => { |
|
setCurrentIndex(index); |
|
}, []); |
|
|
|
// Renderizado cuando no hay banners |
|
if (banners.length === 0) { |
|
return null; // El componente contenedor ya maneja este caso |
|
} |
|
|
|
// Renderizado del carrusel |
|
return ( |
|
<div |
|
className="relative w-full h-[400px] md:h-[500px] overflow-hidden" |
|
onMouseEnter={() => setIsPaused(true)} |
|
onMouseLeave={() => setIsPaused(false)} |
|
> |
|
{/* Banners */} |
|
{banners.map((banner, index) => ( |
|
<div |
|
key={banner.id} |
|
className={cn( |
|
"absolute top-0 left-0 w-full h-full transition-opacity duration-500", |
|
index === currentIndex ? "opacity-100 z-10" : "opacity-0 z-0" |
|
)} |
|
> |
|
{/* Banner como enlace completo */} |
|
<Link |
|
href={banner.link_url || "#"} |
|
className="block w-full h-full cursor-pointer" |
|
aria-label={banner.title || "Banner promocional"} |
|
> |
|
{/* Imagen de fondo */} |
|
<div className="absolute inset-0 z-0 w-full h-full"> |
|
<div className="relative w-full h-full"> |
|
<Image |
|
src={banner.image_url || "/placeholder.svg"} |
|
alt={banner.title || "Banner promocional"} |
|
fill |
|
priority={index === 0} |
|
sizes="100vw" |
|
quality={100} |
|
style={{ |
|
objectFit: "cover", |
|
width: "100%", |
|
height: "100%", |
|
}} |
|
className="w-full h-full" |
|
/> |
|
</div> |
|
</div> |
|
</Link> |
|
</div> |
|
))} |
|
|
|
{/* Controles de navegación */} |
|
{banners.length > 1 && ( |
|
<> |
|
{/* Botones anterior/siguiente */} |
|
<button |
|
onClick={goToPrevious} |
|
className="absolute left-4 top-1/2 -translate-y-1/2 z-20 bg-black/30 hover:bg-black/50 text-white p-2 rounded-full" |
|
aria-label="Anterior" |
|
> |
|
<ChevronLeft className="h-6 w-6" /> |
|
</button> |
|
|
|
<button |
|
onClick={goToNext} |
|
className="absolute right-4 top-1/2 -translate-y-1/2 z-20 bg-black/30 hover:bg-black/50 text-white p-2 rounded-full" |
|
aria-label="Siguiente" |
|
> |
|
<ChevronRight className="h-6 w-6" /> |
|
</button> |
|
|
|
{/* Indicadores */} |
|
<div className="absolute bottom-4 left-1/2 -translate-x-1/2 z-20 flex space-x-2"> |
|
{banners.map((_, index) => ( |
|
<button |
|
key={index} |
|
onClick={() => goToSlide(index)} |
|
className={cn( |
|
"w-3 h-3 rounded-full transition-all", |
|
index === currentIndex |
|
? "bg-white scale-125" |
|
: "bg-white/50 hover:bg-white/80" |
|
)} |
|
aria-label={`Ir al banner ${index + 1}`} |
|
aria-current={index === currentIndex ? "true" : "false"} |
|
/> |
|
))} |
|
</div> |
|
</> |
|
)} |
|
</div> |
|
); |
|
} |