← back to Wine Finder Next
components/mobile/MobileWineCard.tsx
149 lines
'use client'
import { useState } from 'react';
import Image from 'next/image';
import { Wine } from '@/lib/types/wine';
import WineDetailModal from './WineDetailModal';
interface MobileWineCardProps {
wine: Wine;
onClick?: (wine: Wine) => void;
}
export default function MobileWineCard({ wine, onClick }: MobileWineCardProps) {
const [showModal, setShowModal] = useState(false);
const handleClick = () => {
if (onClick) {
onClick(wine);
} else {
setShowModal(true);
}
};
const isInStock = wine.availability?.toLowerCase().includes('stock');
const priceNum = typeof wine.price === 'string' ? parseFloat(wine.price) : wine.price;
return (
<>
<div
onClick={handleClick}
className="bg-white rounded-xl shadow-md overflow-hidden active:scale-[0.98] transition-transform duration-150 cursor-pointer touch-manipulation"
style={{ WebkitTapHighlightColor: 'transparent' }}
data-wine-id={wine.id || wine.name}
>
{/* Image Section */}
<div className="relative h-48 bg-gradient-to-br from-purple-100 to-pink-100">
{wine.image ? (
<Image
src={wine.image}
alt={wine.name}
fill
className="object-contain p-4"
sizes="(max-width: 428px) 100vw, 50vw"
loading="lazy"
/>
) : (
<div className="flex items-center justify-center h-full text-5xl">
🍷
</div>
)}
{/* Badge */}
{wine.badge && (
<div className="absolute top-2 right-2">
<span className="bg-gradient-to-r from-purple-600 to-pink-600 text-white text-xs px-3 py-1 rounded-full font-bold shadow-lg">
{wine.badge === 'new' && '🆕 New'}
{wine.badge === 'deal' && '🔥 Deal'}
{wine.badge === 'featured' && '⭐'}
</span>
</div>
)}
{/* Price Change */}
{wine.priceHistory?.priceChange && (
<div className="absolute bottom-2 left-2 bg-black/70 text-white px-2 py-1 rounded-full text-xs font-bold">
{wine.priceHistory.priceChange.startsWith('-') ? '📉' : '📈'} {wine.priceHistory.priceChange}
</div>
)}
</div>
{/* Content */}
<div className="p-4 space-y-2">
{/* Title */}
<h3 className="font-bold text-base text-gray-900 line-clamp-2 leading-tight min-h-[2.5rem]">
{wine.name}
</h3>
{/* Winery */}
{wine.winery && (
<p className="text-xs text-purple-600 font-medium truncate">
🏛️ {wine.winery}
</p>
)}
{/* Price & Rating Row */}
<div className="flex items-center justify-between pt-2">
<div className="flex-1">
<div className="text-2xl font-bold text-purple-700">
${priceNum.toFixed(2)}
</div>
{wine.priceHistory?.previousPrice && (
<div className="text-xs text-gray-500 line-through">
${wine.priceHistory.previousPrice}
</div>
)}
</div>
{wine.rating && (
<div className="flex items-center gap-1 bg-yellow-50 px-2 py-1 rounded-lg border border-yellow-200">
<span className="text-yellow-500 text-sm">⭐</span>
<span className="text-sm font-bold text-gray-800">
{wine.rating}
</span>
</div>
)}
</div>
{/* Quick Info Row */}
<div className="flex items-center gap-2 text-xs">
{wine.vintage && (
<span className="bg-purple-50 text-purple-700 px-2 py-1 rounded font-medium">
{wine.vintage}
</span>
)}
{wine.region && (
<span className="bg-gray-50 text-gray-700 px-2 py-1 rounded truncate flex-1">
📍 {wine.region}
</span>
)}
</div>
{/* Source & Availability */}
<div className="flex items-center justify-between text-xs pt-2 border-t border-gray-100">
<span className="text-gray-600 font-medium">
{wine.source}
</span>
{wine.availability && (
<span className={`px-2 py-1 rounded-full font-bold ${
isInStock
? 'bg-green-100 text-green-700'
: 'bg-gray-100 text-gray-600'
}`}>
{isInStock ? '✓' : '○'} {wine.availability}
</span>
)}
</div>
</div>
</div>
{/* Detail Modal */}
<WineDetailModal
wine={wine}
isOpen={showModal}
onClose={() => setShowModal(false)}
/>
</>
);
}