← back to Wine Finder Next
app/wine/[id]/page.tsx
332 lines
'use client'
import { useState, useEffect } from 'react'
import { useRouter, useParams } from 'next/navigation'
import Image from 'next/image'
import PriceChart from '@/components/PriceChart'
import RelatedWines from '@/components/RelatedWines'
import WhereToBuy from '@/components/WhereToBuy'
interface WineDetails {
name: string
vintage?: string
price: string
rating?: number
image?: string
source: string
url?: string
winery?: string
region?: string
country?: string
type?: string
varietal?: string
alcohol?: string
description?: string
reviews?: number
tastingNotes?: string[]
foodPairings?: string[]
priceHistory?: any
availableAt?: Array<{
retailer: string
price: number
url: string
availability: string
logo?: string
}>
}
export default function WineDetailPage() {
const router = useRouter()
const params = useParams()
const [wine, setWine] = useState<WineDetails | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [showFullDescription, setShowFullDescription] = useState(false)
useEffect(() => {
loadWineDetails()
}, [params.id])
const loadWineDetails = async () => {
try {
setLoading(true)
// In a real implementation, fetch wine details from API
// For now, using mock data
await new Promise(resolve => setTimeout(resolve, 500)) // Simulate API call
// Mock wine data
setWine({
name: 'Caymus Cabernet Sauvignon',
vintage: '2021',
price: '89.99',
rating: 92,
image: 'https://images.vivino.com/thumbs/default-wine-bottle.png',
source: 'Vivino',
url: 'https://www.vivino.com/wines/12345',
winery: 'Caymus Vineyards',
region: 'Napa Valley',
country: 'USA',
type: 'Red Wine',
varietal: 'Cabernet Sauvignon',
alcohol: '14.5%',
description: 'Rich and concentrated, this Napa Valley Cabernet Sauvignon showcases the signature style of Caymus. Dark fruit flavors, velvety tannins, and a long, luxurious finish make this a wine to remember.',
reviews: 45231,
tastingNotes: ['Blackberry', 'Dark Cherry', 'Vanilla', 'Oak', 'Black Pepper'],
foodPairings: ['Grilled Steak', 'Lamb Chops', 'Aged Cheese', 'BBQ Ribs'],
availableAt: [
{
retailer: 'Vivino',
price: 89.99,
url: 'https://www.vivino.com/wines/12345',
availability: 'In Stock',
logo: '🍇'
},
{
retailer: 'Total Wine & More',
price: 94.99,
url: 'https://www.totalwine.com/product/12345',
availability: 'In Stock',
logo: '🏪'
},
{
retailer: 'K&L Wine Merchants',
price: 92.99,
url: 'https://www.klwines.com/p/12345',
availability: 'Limited Stock',
logo: '🍷'
}
]
})
} catch (err) {
console.error('Error loading wine details:', err)
setError('Failed to load wine details')
} finally {
setLoading(false)
}
}
if (loading) {
return (
<div className="min-h-screen bg-gradient-to-br from-wine-50 via-purple-50 to-pink-50 flex items-center justify-center">
<div className="text-center">
<div className="inline-block animate-spin rounded-full h-16 w-16 border-t-4 border-b-4 border-wine-600 mb-4"></div>
<p className="text-gray-600 text-lg">Loading wine details...</p>
</div>
</div>
)
}
if (error || !wine) {
return (
<div className="min-h-screen bg-gradient-to-br from-wine-50 via-purple-50 to-pink-50 flex items-center justify-center">
<div className="text-center max-w-md mx-auto p-6">
<div className="text-6xl mb-4">😔</div>
<h2 className="text-2xl font-bold text-gray-900 mb-3">Wine Not Found</h2>
<p className="text-gray-600 mb-6">{error || 'The wine you are looking for does not exist.'}</p>
<button
onClick={() => router.push('/')}
className="px-6 py-3 bg-wine-600 text-white rounded-full font-semibold hover:bg-wine-700 transition-colors"
>
Back to Home
</button>
</div>
</div>
)
}
return (
<div className="min-h-screen bg-gradient-to-br from-wine-50 via-purple-50 to-pink-50">
{/* Header */}
<header className="bg-gradient-to-r from-wine-900 via-burgundy-800 to-purple-900 text-white shadow-wine-lg">
<div className="container mx-auto px-4 py-4">
<button
onClick={() => router.back()}
className="flex items-center gap-2 text-white hover:text-gold-300 transition-colors"
aria-label="Go back"
>
<svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
<span className="font-semibold">Back</span>
</button>
</div>
</header>
<main className="container mx-auto px-4 py-8 sm:py-12">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 mb-12">
{/* Left Column - Image */}
<div className="animate-fade-in">
<div className="sticky top-8">
<div className="bg-white rounded-3xl shadow-wine-lg p-8 sm:p-12">
<div className="relative h-96 sm:h-128">
{wine.image ? (
<Image
src={wine.image}
alt={wine.name}
fill
className="object-contain"
sizes="(max-width: 768px) 100vw, 50vw"
priority
/>
) : (
<div className="flex items-center justify-center h-full text-9xl">🍷</div>
)}
</div>
{/* Rating Badge */}
{wine.rating && (
<div className="mt-6 text-center">
<div className="inline-flex items-center gap-2 bg-gold-100 border-2 border-gold-300 px-6 py-3 rounded-full">
<span className="text-3xl">⭐</span>
<div className="text-left">
<div className="text-3xl font-bold text-gray-900">{wine.rating}</div>
{wine.reviews && (
<div className="text-xs text-gray-600">
{wine.reviews.toLocaleString()} reviews
</div>
)}
</div>
</div>
</div>
)}
</div>
</div>
</div>
{/* Right Column - Details */}
<div className="animate-slide-up space-y-6">
{/* Title & Price */}
<div className="bg-white rounded-3xl shadow-wine-lg p-6 sm:p-8">
<div className="flex items-start justify-between gap-4 mb-4">
<div>
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900 mb-2">
{wine.name}
</h1>
{wine.vintage && (
<span className="inline-block bg-wine-100 text-wine-800 px-4 py-2 rounded-full font-semibold text-lg">
{wine.vintage}
</span>
)}
</div>
<div className="text-right">
<div className="text-sm text-gray-500 mb-1">Best Price</div>
<div className="text-4xl font-bold text-wine-700">${wine.price}</div>
</div>
</div>
{/* Winery & Region */}
{wine.winery && (
<p className="text-lg font-medium text-purple-700 mb-2 flex items-center gap-2">
<span>🏛️</span> {wine.winery}
</p>
)}
{(wine.region || wine.country) && (
<p className="text-gray-600 flex items-center gap-2 mb-4">
<span>📍</span> {wine.region}{wine.region && wine.country && ', '}{wine.country}
</p>
)}
{/* Quick Info */}
<div className="grid grid-cols-2 gap-3 mt-4">
{wine.type && (
<div className="bg-pink-50 px-4 py-3 rounded-xl">
<div className="text-xs text-gray-500 mb-1">Type</div>
<div className="font-semibold text-gray-900">{wine.type}</div>
</div>
)}
{wine.varietal && (
<div className="bg-purple-50 px-4 py-3 rounded-xl">
<div className="text-xs text-gray-500 mb-1">Varietal</div>
<div className="font-semibold text-gray-900">{wine.varietal}</div>
</div>
)}
{wine.alcohol && (
<div className="bg-wine-50 px-4 py-3 rounded-xl">
<div className="text-xs text-gray-500 mb-1">Alcohol</div>
<div className="font-semibold text-gray-900">{wine.alcohol}</div>
</div>
)}
</div>
</div>
{/* Description */}
{wine.description && (
<div className="bg-white rounded-3xl shadow-wine-lg p-6 sm:p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-4">About This Wine</h2>
<p className={`text-gray-700 leading-relaxed ${!showFullDescription && 'line-clamp-3'}`}>
{wine.description}
</p>
{wine.description.length > 150 && (
<button
onClick={() => setShowFullDescription(!showFullDescription)}
className="mt-3 text-wine-600 font-semibold hover:text-wine-700 transition-colors"
>
{showFullDescription ? 'Show less' : 'Read more'}
</button>
)}
</div>
)}
{/* Tasting Notes */}
{wine.tastingNotes && wine.tastingNotes.length > 0 && (
<div className="bg-white rounded-3xl shadow-wine-lg p-6 sm:p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-4">Tasting Notes</h2>
<div className="flex flex-wrap gap-2">
{wine.tastingNotes.map((note, i) => (
<span
key={i}
className="px-4 py-2 bg-gradient-to-r from-wine-100 to-purple-100 text-gray-800 rounded-full text-sm font-medium"
>
{note}
</span>
))}
</div>
</div>
)}
{/* Food Pairings */}
{wine.foodPairings && wine.foodPairings.length > 0 && (
<div className="bg-white rounded-3xl shadow-wine-lg p-6 sm:p-8">
<h2 className="text-2xl font-bold text-gray-900 mb-4 flex items-center gap-2">
<span>🍽️</span> Perfect Pairings
</h2>
<div className="grid grid-cols-2 gap-3">
{wine.foodPairings.map((pairing, i) => (
<div key={i} className="bg-gold-50 px-4 py-3 rounded-xl text-center font-medium text-gray-800">
{pairing}
</div>
))}
</div>
</div>
)}
</div>
</div>
{/* Where to Buy Section */}
{wine.availableAt && wine.availableAt.length > 0 && (
<WhereToBuy retailers={wine.availableAt} wineName={wine.name} />
)}
{/* Price History Chart */}
<div className="mt-12 bg-white rounded-3xl shadow-wine-lg p-6 sm:p-8">
<h2 className="text-2xl sm:text-3xl font-bold text-gray-900 mb-6">Price History</h2>
<PriceChart
data={[
{ date: '2024-01', price: 95 },
{ date: '2024-02', price: 92 },
{ date: '2024-03', price: 94 },
{ date: '2024-04', price: 89.99 }
]}
currentPrice={parseFloat(wine.price)}
trend="falling"
dealScore={75}
/>
</div>
{/* Related Wines */}
<RelatedWines currentWine={wine} />
</main>
</div>
)
}