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

29.92% Statements 41/137
100% Branches 0/0
0% Functions 0/4
29.92% Lines 41/137

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 132 133 134 135 136 137 1381x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                     1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x                                                                                                         1x 1x                                                     1x 1x  
'use client';
 
import dynamic from "next/dynamic";
import Image from "next/image";
import HeroFeatureCard from "./HeroFeatureCard";
import type { HeroData, HeroCarouselItem } from "./types";
 
// Dynamically import HeroCarousel since it uses Swiper (heavy library)
const HeroCarousel = dynamic(() => import("./HeroCarousel"), {
  loading: () => (
    <div className="flex items-center pt-6 sm:pt-0 flex-col-reverse sm:flex-row min-h-[358px]">
      {/* Text content skeleton */}
      <div className="max-w-[394px] py-10 sm:py-15 lg:py-24.5 pl-4 sm:pl-7.5 lg:pl-12.5 flex-1">
        <div className="animate-pulse space-y-4">
          <div className="flex items-center gap-4 mb-7.5">
            <div className="h-10 w-16 bg-gray-200 dark:bg-gray-700 rounded" />
            <div className="h-6 w-20 bg-gray-200 dark:bg-gray-700 rounded" />
          </div>
          <div className="h-8 w-48 bg-gray-200 dark:bg-gray-700 rounded" />
          <div className="h-4 w-64 bg-gray-200 dark:bg-gray-700 rounded" />
          <div className="h-12 w-32 bg-gray-200 dark:bg-gray-700 rounded mt-10" />
        </div>
      </div>
      {/* Image skeleton */}
      <div className="flex-shrink-0 w-full sm:w-auto max-w-[351px] mx-auto sm:mx-0">
        <div className="w-[280px] h-[280px] sm:w-[351px] sm:h-[358px] bg-gray-200 dark:bg-gray-700 rounded-lg animate-pulse mx-auto" />
      </div>
    </div>
  ),
});
 
// Fallback data when no items are configured
const fallbackCarouselItems: HeroCarouselItem[] = [
  {
    id: 0,
    product: {
      id: 0,
      title: "Featured Product",
      slug: "shop",
      price: 999,
      discountedPrice: 699,
      image: null},
    headline: "Discover Our Products",
    subheadline: "Browse our collection of quality products at great prices.",
    badgeText: "New",
    badgeSubtext: "Arrivals",
    ctaText: "Shop Now",
    order: 0},
];
 
interface HeroProps {
  heroData?: HeroData | null;
}
 
const Hero = ({ heroData }: HeroProps) => {
  const carouselItems = heroData?.carousel?.length
    ? heroData.carousel
    : fallbackCarouselItems;

  const topCard = heroData?.featureCards?.find((c) => c.position === "top");
  const bottomCard = heroData?.featureCards?.find((c) => c.position === "bottom");

  return (
    <section className="overflow-hidden pb-10 lg:pb-12.5 xl:pb-15 pt-57.5 sm:pt-45 lg:pt-30 xl:pt-51.5 bg-[#E5EAF4] dark:bg-gray-900">
      <div className="max-w-[1170px] w-full mx-auto px-4 sm:px-8 xl:px-0">
        <div className="flex flex-wrap gap-5">
          {/* Main Carousel */}
          <div className="xl:max-w-[757px] w-full">
            <div className="relative z-1 rounded-[10px] bg-white dark:bg-gray-800 overflow-hidden">
              {/* Background shapes - decorative but is LCP element, needs priority */}
              <Image
                src="/images/hero/hero-bg.png"
                alt=""
                className="absolute right-0 bottom-0 -z-1"
                width={534}
                height={520}
                priority
                fetchPriority="high"
              />

              <HeroCarousel items={carouselItems} />
            </div>
          </div>

          {/* Side Feature Cards */}
          <div className="xl:max-w-[393px] w-full">
            <div className="flex flex-col sm:flex-row xl:flex-col gap-5">
              {/* Top Card - priority for above-the-fold LCP */}
              {topCard ? (
                <HeroFeatureCard card={topCard} priority />
              ) : (
                <FeatureCardPlaceholder position="top" />
              )}

              {/* Bottom Card */}
              {bottomCard ? (
                <HeroFeatureCard card={bottomCard} />
              ) : (
                <FeatureCardPlaceholder position="bottom" />
              )}
            </div>
          </div>
        </div>
      </div>
    </section>
  );
};
 
// Placeholder for empty feature card slots
function FeatureCardPlaceholder({ position }: { position: "top" | "bottom" }) {
  return (
    <div className="w-full relative rounded-[10px] bg-white dark:bg-gray-800 p-4 sm:p-7.5">
      <div className="flex items-center gap-4 sm:gap-14">
        <div className="flex-1 min-w-0">
          <h3 className="max-w-[153px] font-semibold text-dark dark:text-gray-100 text-xl mb-20">
            <a href="/shop">Browse Products</a>
          </h3>
          <div>
            <p className="font-medium text-dark-4 dark:text-gray-400 text-custom-sm mb-1.5">
              {position === "top" ? "New arrivals" : "Best sellers"}
            </p>
            <span className="flex items-center gap-3">
              <span className="font-medium text-heading-5 text-blue dark:text-blue-400">
                Shop Now
              </span>
            </span>
          </div>
        </div>
        <div className="w-[80px] h-[100px] sm:w-[123px] sm:h-[161px] flex-shrink-0 bg-gray-100 dark:bg-gray-700 rounded flex items-center justify-center">
          <span className="text-gray-400 text-xs sm:text-sm">No product</span>
        </div>
      </div>
    </div>
  );
}
 
export default Hero;