← back to Watches

src/components/HeroCarousel.jsx

227 lines

import React, { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { FiChevronLeft, FiChevronRight, FiTrendingUp } from 'react-icons/fi';

// Local SVG watch illustrations - authentic Omega dial designs
const watchImages = {
  'speedmaster-moonwatch': '/watches/speedmaster.svg',
  'speedmaster-alaska': '/watches/speedmaster.svg',
  'speedmaster': '/watches/speedmaster.svg',
  'seamaster-300': '/watches/seamaster.svg',
  'seamaster-ploprof': '/watches/seamaster.svg',
  'seamaster-bond': '/watches/seamaster.svg',
  'seamaster': '/watches/seamaster.svg',
  'constellation': '/watches/constellation.svg',
  'deville': '/watches/deville.svg',
  'railmaster': '/watches/railmaster.svg',
  'default': '/watches/speedmaster.svg'
};

function getWatchImage(watch) {
  const model = watch.model?.toLowerCase() || '';
  const series = watch.series?.toLowerCase() || '';

  if (model.includes('moonwatch')) return watchImages['speedmaster-moonwatch'];
  if (model.includes('alaska')) return watchImages['speedmaster-alaska'];
  if (model.includes('ploprof')) return watchImages['seamaster-ploprof'];
  if (model.includes('bond') || model.includes('diver 300')) return watchImages['seamaster-bond'];
  if (model.includes('seamaster 300') || (series.includes('seamaster') && model.includes('300'))) return watchImages['seamaster-300'];

  if (series.includes('speedmaster')) return watchImages['speedmaster'];
  if (series.includes('seamaster')) return watchImages['seamaster'];
  if (series.includes('constellation')) return watchImages['constellation'];
  if (series.includes('de ville') || series.includes('deville')) return watchImages['deville'];
  if (series.includes('railmaster')) return watchImages['railmaster'];

  return watchImages['default'];
}

function HeroCarousel({ watches, onWatchSelect }) {
  const [currentIndex, setCurrentIndex] = useState(0);
  const [isAutoPlaying, setIsAutoPlaying] = useState(true);

  // Get top 5 watches by appreciation
  const featuredWatches = React.useMemo(() => {
    if (!watches || watches.length === 0) return [];
    return [...watches]
      .map(watch => {
        const first = watch.priceHistory[0]?.price || 1;
        const last = watch.priceHistory[watch.priceHistory.length - 1]?.price || first;
        const appreciation = ((last - first) / first) * 100;
        return { ...watch, appreciation, currentPrice: last };
      })
      .sort((a, b) => b.appreciation - a.appreciation)
      .slice(0, 5);
  }, [watches]);

  useEffect(() => {
    if (!isAutoPlaying || featuredWatches.length === 0) return;
    const interval = setInterval(() => {
      setCurrentIndex((prev) => (prev + 1) % featuredWatches.length);
    }, 5000);
    return () => clearInterval(interval);
  }, [isAutoPlaying, featuredWatches.length]);

  const goToPrevious = () => {
    setIsAutoPlaying(false);
    setCurrentIndex((prev) => (prev - 1 + featuredWatches.length) % featuredWatches.length);
  };

  const goToNext = () => {
    setIsAutoPlaying(false);
    setCurrentIndex((prev) => (prev + 1) % featuredWatches.length);
  };

  if (featuredWatches.length === 0) return null;

  const currentWatch = featuredWatches[currentIndex];

  return (
    <div className="relative h-[500px] md:h-[600px] overflow-hidden bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900">
      {/* Background Pattern */}
      <div className="absolute inset-0 opacity-10">
        <div className="absolute inset-0" style={{
          backgroundImage: `radial-gradient(circle at 25% 25%, rgba(251, 191, 36, 0.1) 0%, transparent 50%),
                           radial-gradient(circle at 75% 75%, rgba(251, 191, 36, 0.05) 0%, transparent 50%)`
        }} />
      </div>

      {/* Gold Accent Lines */}
      <div className="absolute inset-0 pointer-events-none">
        <div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-amber-500 to-transparent opacity-60" />
        <div className="absolute bottom-0 left-0 w-full h-1 bg-gradient-to-r from-transparent via-amber-500 to-transparent opacity-60" />
      </div>

      <div className="relative z-10 container mx-auto px-6 h-full flex items-center">
        <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 items-center w-full">
          {/* Text Content */}
          <AnimatePresence mode="wait">
            <motion.div
              key={`text-${currentIndex}`}
              initial={{ opacity: 0, x: -50 }}
              animate={{ opacity: 1, x: 0 }}
              exit={{ opacity: 0, x: 50 }}
              transition={{ duration: 0.5 }}
              className="text-white"
            >
              <motion.div
                className="inline-flex items-center gap-2 px-4 py-2 bg-amber-500/20 border border-amber-500/50 rounded-full mb-6"
                initial={{ opacity: 0, y: 20 }}
                animate={{ opacity: 1, y: 0 }}
                transition={{ delay: 0.2 }}
              >
                <FiTrendingUp className="text-amber-400" />
                <span className="text-amber-400 text-sm font-medium">
                  +{currentWatch.appreciation.toFixed(0)}% Appreciation
                </span>
              </motion.div>

              <h2 className="text-4xl md:text-5xl lg:text-6xl font-bold mb-4 leading-tight">
                <span className="text-transparent bg-clip-text bg-gradient-to-r from-white to-gray-300">
                  {currentWatch.model}
                </span>
              </h2>

              <p className="text-xl md:text-2xl text-amber-400 font-semibold mb-4">
                {currentWatch.series} Collection
              </p>

              <p className="text-gray-300 text-lg mb-6 max-w-xl leading-relaxed">
                {currentWatch.description?.slice(0, 150)}...
              </p>

              <div className="flex items-center gap-8 mb-8">
                <div>
                  <p className="text-gray-400 text-sm uppercase tracking-wider">Current Value</p>
                  <p className="text-3xl font-bold text-white">
                    ${currentWatch.currentPrice.toLocaleString()}
                  </p>
                </div>
                <div>
                  <p className="text-gray-400 text-sm uppercase tracking-wider">Year Introduced</p>
                  <p className="text-3xl font-bold text-white">
                    {currentWatch.yearIntroduced}
                  </p>
                </div>
              </div>

              <motion.button
                whileHover={{ scale: 1.05 }}
                whileTap={{ scale: 0.95 }}
                onClick={() => onWatchSelect(currentWatch)}
                className="px-8 py-4 bg-gradient-to-r from-amber-500 to-amber-600 hover:from-amber-600 hover:to-amber-700 text-black font-bold rounded-lg shadow-lg shadow-amber-500/25 transition-all"
              >
                View Price History
              </motion.button>
            </motion.div>
          </AnimatePresence>

          {/* Watch Image */}
          <AnimatePresence mode="wait">
            <motion.div
              key={`image-${currentIndex}`}
              initial={{ opacity: 0, scale: 0.8, rotateY: -20 }}
              animate={{ opacity: 1, scale: 1, rotateY: 0 }}
              exit={{ opacity: 0, scale: 0.8 }}
              transition={{ duration: 0.6 }}
              className="hidden lg:flex justify-center items-center"
            >
              <div className="relative">
                {/* Glow Effect */}
                <div className="absolute inset-0 bg-amber-500/20 blur-3xl rounded-full transform scale-75" />

                {/* Watch Image */}
                <div className="relative z-10 w-80 h-80 flex items-center justify-center">
                  <img
                    src={getWatchImage(currentWatch)}
                    alt={currentWatch.model}
                    className="w-full h-full object-contain drop-shadow-2xl"
                    onError={(e) => { e.target.src = watchImages.default; }}
                  />
                </div>

                {/* Frame Decoration */}
                <div className="absolute -inset-4 border-2 border-amber-500/30 rounded-3xl" />
              </div>
            </motion.div>
          </AnimatePresence>
        </div>
      </div>

      {/* Navigation Arrows */}
      <button
        onClick={goToPrevious}
        className="absolute left-4 top-1/2 -translate-y-1/2 p-3 bg-black/50 hover:bg-amber-500 text-white rounded-full transition-all z-20"
      >
        <FiChevronLeft size={24} />
      </button>
      <button
        onClick={goToNext}
        className="absolute right-4 top-1/2 -translate-y-1/2 p-3 bg-black/50 hover:bg-amber-500 text-white rounded-full transition-all z-20"
      >
        <FiChevronRight size={24} />
      </button>

      {/* Dots Navigation */}
      <div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex gap-3 z-20">
        {featuredWatches.map((_, index) => (
          <button
            key={index}
            onClick={() => {
              setIsAutoPlaying(false);
              setCurrentIndex(index);
            }}
            className={`transition-all ${
              index === currentIndex
                ? 'w-8 h-3 bg-amber-500 rounded-full'
                : 'w-3 h-3 bg-white/50 hover:bg-white/80 rounded-full'
            }`}
          />
        ))}
      </div>
    </div>
  );
}

export default HeroCarousel;