← 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;