All files / src/components/features/home/Hero HeroCarousel.tsx

0% Statements 0/130
100% Branches 0/0
0% Functions 0/1
0% Lines 0/130

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131                                                                                                                                                                                                                                                                     
"use client";

import { Swiper, SwiperSlide } from "swiper/react";
import { Autoplay, Pagination } from "swiper/modules";
import Image from "next/image";
import Link from "next/link";

// Import Swiper styles
import "swiper/css/pagination";
import "swiper/css";

import type { HeroCarouselItem } from "./types";

interface HeroCarouselProps {
  items: HeroCarouselItem[];
  /** Mark first image as priority for LCP optimization */
  priorityFirstImage?: boolean;
}

const HeroCarousel = ({ items, priorityFirstImage = true }: HeroCarouselProps) => {
  // If no items, show placeholder
  if (!items || items.length === 0) {
    return (
      <div className="flex items-center justify-center h-[400px] text-gray-500 dark:text-gray-400">
        <p>No carousel items configured</p>
      </div>
    );
  }

  const formatPrice = (price: number) => {
    return new Intl.NumberFormat("en-US", {
      style: "currency",
      currency: "USD",
      minimumFractionDigits: 0,
      maximumFractionDigits: 0}).format(price);
  };

  return (
    <Swiper
      spaceBetween={30}
      centeredSlides={true}
      autoplay={{
        delay: 2500,
        disableOnInteraction: false}}
      pagination={{
        clickable: true}}
      modules={[Autoplay, Pagination]}
      className="hero-carousel"
    >
      {items.map((item, index) => (
        <SwiperSlide key={item.id}>
          <Link href={`/product/${item.product.slug}`} className="block">
            <div className="flex items-center pt-6 sm:pt-0 flex-col-reverse sm:flex-row">
              <div className="max-w-[394px] py-10 sm:py-15 lg:py-24.5 pl-4 sm:pl-7.5 lg:pl-12.5">
                {/* Badge */}
                {item.badgeText && (
                  <div className="flex items-center gap-4 mb-7.5 sm:mb-10">
                    <span className="block font-semibold text-heading-3 sm:text-heading-1 text-blue">
                      {item.badgeText}
                    </span>
                    {item.badgeSubtext && (
                      <span className="block text-dark dark:text-gray-200 text-sm sm:text-custom-1 sm:leading-[24px]">
                        {item.badgeSubtext.split(" ").map((word, i) => (
                          <span key={i}>
                            {word}
                            {i < item.badgeSubtext!.split(" ").length - 1 && <br />}
                          </span>
                        ))}
                      </span>
                    )}
                  </div>
                )}

                {/* Title */}
                <h2 className="font-semibold text-dark dark:text-gray-100 text-xl sm:text-3xl mb-3">
                  {item.headline}
                </h2>

                {/* Description */}
                {item.subheadline && (
                  <p className="dark:text-gray-300 line-clamp-2">
                    {item.subheadline}
                  </p>
                )}

                {/* Price (optional) */}
                {item.product.discountedPrice && item.product.discountedPrice < item.product.price && (
                  <div className="flex items-center gap-3 mt-4">
                    <span className="font-medium text-xl text-red dark:text-red-400">
                      {formatPrice(item.product.discountedPrice)}
                    </span>
                    <span className="font-medium text-lg text-dark-3 dark:text-gray-400 line-through">
                      {formatPrice(item.product.price)}
                    </span>
                  </div>
                )}

                {/* CTA Button */}
                <span className="inline-flex font-medium text-white text-custom-sm rounded-md bg-dark dark:bg-gray-700 py-3 px-9 ease-out duration-200 hover:bg-blue mt-10">
                  {item.ctaText}
                </span>
              </div>

              {/* Product Image */}
              <div className="flex-shrink-0 w-full sm:w-auto max-w-[280px] sm:max-w-[351px] mx-auto sm:mx-0 aspect-square sm:aspect-[351/358]">
                {item.product.image ? (
                  <Image
                    src={item.product.image.url}
                    alt={item.headline}
                    width={351}
                    height={358}
                    className="object-contain w-full h-full"
                    sizes="(max-width: 640px) 280px, 351px"
                    priority={priorityFirstImage && index === 0}
                  />
                ) : (
                  <div className="w-full h-full bg-gray-200 dark:bg-gray-700 rounded-lg flex items-center justify-center">
                    <span className="text-gray-400">No image</span>
                  </div>
                )}
              </div>
            </div>
          </Link>
        </SwiperSlide>
      ))}
    </Swiper>
  );
};

export default HeroCarousel;