← back to Watches

src/components/WatchCard.jsx

199 lines

import React, { useState } from 'react';
import { motion } from 'framer-motion';
import { FiTrendingUp, FiTrendingDown, FiEye, FiHeart } from 'react-icons/fi';

// Local SVG watch illustrations - authentic Omega dial designs
const watchImages = {
  // Speedmaster Collection - Black chronograph dial
  'speedmaster': '/watches/speedmaster.svg',
  'speedmaster-moonwatch': '/watches/speedmaster.svg',
  'speedmaster-alaska': '/watches/speedmaster.svg',
  'speedmaster-reduced': '/watches/speedmaster.svg',
  'speedmaster-mark': '/watches/speedmaster.svg',
  'speedmaster-racing': '/watches/speedmaster.svg',
  'speedmaster-57': '/watches/speedmaster.svg',
  'speedmaster-snoopy': '/watches/speedmaster.svg',
  'speedmaster-ultraman': '/watches/speedmaster.svg',
  'speedmaster-dark': '/watches/speedmaster.svg',

  // Seamaster Collection - Blue diver dial
  'seamaster': '/watches/seamaster.svg',
  'seamaster-300': '/watches/seamaster.svg',
  'seamaster-ploprof': '/watches/seamaster.svg',
  'seamaster-bond': '/watches/seamaster.svg',
  'seamaster-diver': '/watches/seamaster.svg',
  'seamaster-aqua': '/watches/seamaster.svg',
  'seamaster-planet': '/watches/seamaster.svg',
  'seamaster-ultra': '/watches/seamaster.svg',

  // Constellation Collection - Gold elegant dial
  'constellation': '/watches/constellation.svg',
  'constellation-pie': '/watches/constellation.svg',
  'constellation-manhattan': '/watches/constellation.svg',

  // De Ville Collection - Dark moonphase dial
  'deville': '/watches/deville.svg',
  'deville-tresor': '/watches/deville.svg',
  'deville-co-axial': '/watches/deville.svg',

  // Other Collections
  'railmaster': '/watches/railmaster.svg',
  'flightmaster': '/watches/speedmaster.svg',
  'la-magique': '/watches/deville.svg',
  'cosmic': '/watches/deville.svg',
  'geneve': '/watches/constellation.svg',
  'louis-brandt': '/watches/deville.svg',

  // Fallback
  'default': '/watches/speedmaster.svg'
};

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

  // Check for specific model matches first
  if (model.includes('moonwatch') || id.includes('moonwatch')) return watchImages['speedmaster-moonwatch'];
  if (model.includes('alaska')) return watchImages['speedmaster-alaska'];
  if (model.includes('snoopy')) return watchImages['speedmaster-snoopy'];
  if (model.includes('ultraman')) return watchImages['speedmaster-ultraman'];
  if (model.includes('dark side')) return watchImages['speedmaster-dark'];
  if (model.includes('mark ii') || model.includes('mark 2')) return watchImages['speedmaster-mark'];
  if (model.includes('reduced')) return watchImages['speedmaster-reduced'];
  if (model.includes('racing')) return watchImages['speedmaster-racing'];
  if (model.includes("'57") || model.includes('57')) return watchImages['speedmaster-57'];

  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 (model.includes('aqua terra')) return watchImages['seamaster-aqua'];
  if (model.includes('planet ocean')) return watchImages['seamaster-planet'];
  if (model.includes('ultra deep')) return watchImages['seamaster-ultra'];

  if (model.includes('pie pan') || model.includes('globemaster')) return watchImages['constellation-pie'];
  if (model.includes('manhattan')) return watchImages['constellation-manhattan'];

  if (model.includes('trésor') || model.includes('tresor')) return watchImages['deville-tresor'];
  if (model.includes('co-axial') && series.includes('de ville')) return watchImages['deville-co-axial'];

  if (model.includes('railmaster') || series.includes('railmaster')) return watchImages['railmaster'];
  if (model.includes('flightmaster') || series.includes('flightmaster')) return watchImages['flightmaster'];
  if (model.includes('magique')) return watchImages['la-magique'];
  if (model.includes('cosmic')) return watchImages['cosmic'];
  if (model.includes('genève') || model.includes('geneve') || model.includes('dynamic')) return watchImages['geneve'];
  if (model.includes('louis brandt')) return watchImages['louis-brandt'];

  // Collection-based fallbacks
  if (series.includes('speedmaster')) return watchImages['speedmaster-moonwatch'];
  if (series.includes('seamaster')) return watchImages['seamaster-diver'];
  if (series.includes('constellation')) return watchImages['constellation'];
  if (series.includes('de ville') || series.includes('deville')) return watchImages['deville'];

  return watchImages['default'];
}

function WatchCard({ watch, onSelect, index = 0 }) {
  const [isHovered, setIsHovered] = useState(false);
  const [isFavorite, setIsFavorite] = useState(false);
  const [imageError, setImageError] = useState(false);

  const firstPrice = watch.priceHistory[0]?.price || 1;
  const currentPrice = watch.priceHistory[watch.priceHistory.length - 1]?.price || firstPrice;
  const appreciation = ((currentPrice - firstPrice) / firstPrice) * 100;
  const isPositive = appreciation >= 0;

  return (
    <motion.div
      initial={{ opacity: 0, y: 30 }}
      animate={{ opacity: 1, y: 0 }}
      transition={{ delay: Math.min(index * 0.05, 0.5), duration: 0.5 }}
      whileHover={{ y: -8, scale: 1.02 }}
      onHoverStart={() => setIsHovered(true)}
      onHoverEnd={() => setIsHovered(false)}
      onClick={() => onSelect(watch)}
      className="group relative bg-gradient-to-br from-slate-800 to-slate-900 rounded-2xl overflow-hidden border border-slate-700 hover:border-amber-500/50 shadow-xl hover:shadow-amber-500/20 transition-all duration-300 cursor-pointer"
    >
      {/* Image Container */}
      <div className="relative h-56 overflow-hidden bg-gradient-to-br from-slate-700 to-slate-800 flex items-center justify-center p-4">
        <motion.img
          src={imageError ? watchImages['default'] : getWatchImage(watch)}
          alt={watch.model}
          className="w-full h-full object-contain drop-shadow-2xl"
          animate={{ scale: isHovered ? 1.1 : 1, rotate: isHovered ? 2 : 0 }}
          transition={{ duration: 0.4 }}
          onError={() => setImageError(true)}
        />

        {/* Overlay Gradient */}
        <div className="absolute inset-0 bg-gradient-to-t from-slate-900 via-transparent to-transparent opacity-80" />

        {/* Appreciation Badge */}
        <div className={`absolute top-3 left-3 px-3 py-1.5 rounded-full backdrop-blur-sm flex items-center gap-1.5 ${
          isPositive
            ? 'bg-green-500/20 border border-green-500/50 text-green-400'
            : 'bg-red-500/20 border border-red-500/50 text-red-400'
        }`}>
          {isPositive ? <FiTrendingUp /> : <FiTrendingDown />}
          <span className="text-sm font-bold">{isPositive ? '+' : ''}{appreciation.toFixed(0)}%</span>
        </div>

        {/* Favorite Button */}
        <button
          onClick={(e) => { e.stopPropagation(); setIsFavorite(!isFavorite); }}
          className={`absolute top-3 right-3 p-2 rounded-full backdrop-blur-sm transition-all ${
            isFavorite
              ? 'bg-red-500 text-white scale-110'
              : 'bg-slate-800/80 text-gray-400 hover:text-white hover:scale-110'
          }`}
        >
          <FiHeart className={isFavorite ? 'fill-current' : ''} size={16} />
        </button>

        {/* Year Badge */}
        <div className="absolute bottom-3 right-3 px-3 py-1 bg-amber-500/20 border border-amber-500/50 rounded-full backdrop-blur-sm">
          <span className="text-amber-400 text-sm font-semibold">{watch.yearIntroduced}</span>
        </div>
      </div>

      {/* Content */}
      <div className="p-5">
        <div className="mb-3">
          <p className="text-amber-400 text-xs uppercase tracking-wider font-semibold mb-1">
            {watch.series}
          </p>
          <h3 className="text-white text-lg font-bold leading-tight group-hover:text-amber-400 transition-colors line-clamp-2">
            {watch.model}
          </h3>
        </div>

        {watch.description && (
          <p className="text-gray-400 text-sm mb-4 line-clamp-2">
            {watch.description}
          </p>
        )}

        {/* Price Info */}
        <div className="flex items-end justify-between pt-4 border-t border-slate-700">
          <div>
            <p className="text-gray-500 text-xs uppercase tracking-wider">Current Value</p>
            <p className="text-white text-2xl font-bold">
              ${currentPrice.toLocaleString()}
            </p>
          </div>
          <motion.div
            whileHover={{ scale: 1.05 }}
            whileTap={{ scale: 0.95 }}
            className="flex items-center gap-2 px-4 py-2 bg-amber-500/10 hover:bg-amber-500 border border-amber-500/50 hover:border-amber-500 text-amber-400 hover:text-black font-medium text-sm rounded-lg transition-all"
          >
            <FiEye />
            <span>View</span>
          </motion.div>
        </div>
      </div>
    </motion.div>
  );
}

export default WatchCard;