180 lines
8.2 KiB
TypeScript
180 lines
8.2 KiB
TypeScript
'use client';
|
|
|
|
import React, { useState } from 'react';
|
|
import { Star, Zap, Truck, ShoppingBag, Layers, Palette, Sparkles } from 'lucide-react';
|
|
import { Product } from '@/types';
|
|
import { formatCOP, calculateSeparationBreakdown, getProductPriceRange } from '@/lib/pricing-engine';
|
|
import { useBrand } from '@/context/BrandContext';
|
|
import { getProductSocialProof } from '@/lib/social-proof';
|
|
|
|
interface ProductCardProps {
|
|
product: Product;
|
|
onOpenDetail: (product: Product) => void;
|
|
onQuickAdd: (product: Product, size: string) => void;
|
|
}
|
|
|
|
export const ProductCard: React.FC<ProductCardProps> = ({
|
|
product,
|
|
onOpenDetail,
|
|
}) => {
|
|
const { currentBrand } = useBrand();
|
|
const [activeImage, setActiveImage] = useState<string>(product.images[0]);
|
|
const [activeColorName, setActiveColorName] = useState<string>(product.colorName || '');
|
|
const socialProof = getProductSocialProof(product);
|
|
const priceRange = getProductPriceRange(product);
|
|
|
|
const discountPercent = product.originalPrice
|
|
? Math.round(((product.originalPrice - product.retailPrice) / product.originalPrice) * 100)
|
|
: 28;
|
|
|
|
const separation = calculateSeparationBreakdown(product.retailPrice);
|
|
const colorCount = product.colors?.length || 1;
|
|
|
|
return (
|
|
<div
|
|
onClick={() => onOpenDetail(product)}
|
|
className={`group rounded-3xl border transition-all duration-300 flex flex-col cursor-pointer overflow-hidden ${currentBrand.colors.cardBg} hover:shadow-2xl hover:border-purple-500/40`}
|
|
>
|
|
{/* Image Container */}
|
|
<div className="relative aspect-square w-full bg-slate-950 overflow-hidden">
|
|
<img
|
|
src={activeImage || product.images[0]}
|
|
alt={product.name}
|
|
className="w-full h-full object-cover group-hover:scale-108 transition-transform duration-500"
|
|
/>
|
|
|
|
{/* Badges */}
|
|
<div className="absolute top-2.5 left-2.5 flex flex-col gap-1 z-10">
|
|
<span className={`bg-gradient-to-r ${currentBrand.colors.primaryGradient} text-white text-[10px] sm:text-xs font-black px-2.5 py-0.5 rounded-lg shadow-md uppercase tracking-wider`}>
|
|
-{discountPercent}% OFF
|
|
</span>
|
|
{product.isFlashDeal && (
|
|
<span className="bg-white/95 text-slate-950 text-[10px] font-black px-2 py-0.5 rounded-lg shadow-md flex items-center gap-0.5">
|
|
<Zap className="w-3 h-3 fill-slate-950" /> FLASH
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* Colors count indicator badge */}
|
|
{colorCount > 1 && (
|
|
<div className="absolute top-2.5 right-2.5 z-10">
|
|
<span className="bg-slate-950/90 backdrop-blur-md text-purple-300 border border-purple-500/40 text-[10px] font-black px-2 py-0.5 rounded-full flex items-center gap-1 shadow-lg">
|
|
<Palette className="w-3 h-3 text-purple-400" />
|
|
<span>{colorCount} Colores</span>
|
|
</span>
|
|
</div>
|
|
)}
|
|
|
|
<div className="absolute bottom-2 left-2 right-2 flex items-center justify-between pointer-events-none">
|
|
<span className="bg-slate-950/80 backdrop-blur-xs text-white border border-white/10 text-[10px] font-semibold px-2 py-0.5 rounded-full flex items-center gap-1">
|
|
<Truck className="w-3 h-3 text-white" /> Contraentrega
|
|
</span>
|
|
<span className={`bg-gradient-to-r ${currentBrand.colors.primaryGradient} text-white text-[10px] font-bold px-2 py-0.5 rounded-full shadow-xs`}>
|
|
{product.brand}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Product Info */}
|
|
<div className="p-3.5 sm:p-4 flex-1 flex flex-col justify-between space-y-2.5">
|
|
<div>
|
|
{/* Variable Realistic Social Proof & Reviews */}
|
|
<div className="flex items-center justify-between text-xs text-slate-400 mb-1">
|
|
<div className="flex items-center text-amber-400 font-bold">
|
|
<Star className="w-3.5 h-3.5 fill-amber-400 mr-1" />
|
|
<span>{socialProof.rating}</span>
|
|
<span className="text-slate-500 font-normal ml-1">({socialProof.reviewsCount})</span>
|
|
</div>
|
|
<span className="text-[11px] font-medium text-slate-400">
|
|
+{socialProof.salesCount} pedidos
|
|
</span>
|
|
</div>
|
|
|
|
{/* Product Title */}
|
|
<h3 className="font-bold text-white text-sm sm:text-base line-clamp-2 transition-colors leading-snug">
|
|
{product.modelName || product.name}
|
|
</h3>
|
|
|
|
{/* Color Thumbnails Preview Swatches if multi-color */}
|
|
{product.colors && product.colors.length > 1 && (
|
|
<div className="flex items-center gap-1.5 mt-2 overflow-x-auto pb-1" onClick={(e) => e.stopPropagation()}>
|
|
<span className="text-[10px] text-slate-500 font-semibold uppercase shrink-0">Colores:</span>
|
|
<div className="flex items-center gap-1">
|
|
{product.colors.slice(0, 4).map((c) => (
|
|
<button
|
|
key={c.id}
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
setActiveImage(c.image);
|
|
setActiveColorName(c.colorName);
|
|
}}
|
|
title={c.colorName}
|
|
className={`w-6 h-6 rounded-full overflow-hidden border transition-all cursor-pointer ${
|
|
activeImage === c.image ? 'border-purple-400 scale-110 shadow-md ring-1 ring-purple-400' : 'border-slate-700 opacity-70 hover:opacity-100'
|
|
}`}
|
|
>
|
|
<img src={c.image} alt={c.colorName} className="w-full h-full object-cover" />
|
|
</button>
|
|
))}
|
|
{product.colors.length > 4 && (
|
|
<span className="text-[10px] font-bold text-purple-400 bg-purple-500/10 border border-purple-500/30 rounded-full px-1.5 py-0.5">
|
|
+{product.colors.length - 4}
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Available Sizes Pills */}
|
|
<div className="flex items-center gap-1 flex-wrap mt-2">
|
|
<span className="text-[10px] text-slate-500 font-semibold uppercase">Tallas:</span>
|
|
{product.sizes.slice(0, 5).map((size) => (
|
|
<span
|
|
key={size}
|
|
className="text-[10px] font-bold bg-slate-950 text-slate-300 px-1.5 py-0.5 rounded border border-slate-800"
|
|
>
|
|
{size}
|
|
</span>
|
|
))}
|
|
{product.sizes.length > 5 && (
|
|
<span className="text-[10px] font-semibold text-slate-500">+{product.sizes.length - 5}</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Pricing & Separation terms */}
|
|
<div className="pt-2 border-t border-slate-800">
|
|
<div className="flex items-baseline space-x-2">
|
|
<span className={`text-lg sm:text-xl font-black text-transparent bg-clip-text bg-gradient-to-r ${currentBrand.colors.primaryGradient}`}>
|
|
{priceRange.hasVariation ? `Desde ${formatCOP(priceRange.minPrice)}` : formatCOP(product.retailPrice)}
|
|
</span>
|
|
{product.originalPrice && (
|
|
<span className="text-xs text-slate-500 line-through font-semibold">
|
|
{formatCOP(product.originalPrice)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
|
|
<p className="text-[11px] font-semibold bg-white/5 px-2 py-1 rounded-md mt-1.5 border border-white/10 text-slate-300">
|
|
⚡ Separa con <strong className="text-white">{formatCOP(separation.depositAmount)}</strong> y cancelas al recibir.
|
|
</p>
|
|
|
|
{/* Quick Action Button */}
|
|
<div className="mt-3">
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onOpenDetail(product);
|
|
}}
|
|
className={`w-full bg-gradient-to-r ${currentBrand.colors.primaryGradient} text-white font-bold text-xs sm:text-sm py-2.5 px-3 rounded-2xl flex items-center justify-center space-x-1.5 shadow-md active:scale-95 transition-all cursor-pointer`}
|
|
>
|
|
<ShoppingBag className="w-3.5 h-3.5" />
|
|
<span>{colorCount > 1 ? `Ver ${colorCount} Colores & Tallas` : 'Ver Tallas & Pedir'}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|