← back to Wine Finder Next
components/mobile/PriceComparisonSheet.tsx
179 lines
'use client'
import { useState, useEffect } from 'react';
import { Wine } from '@/lib/types/wine';
import { getEnabledPartners } from '@/lib/affiliates/partners';
import { buildAffiliateUrl } from '@/lib/affiliates/urlBuilder';
import { trackAffiliateClick } from '@/lib/affiliates/tracker';
import AffiliateButton from './AffiliateButton';
interface PriceComparisonSheetProps {
wine: Wine;
isOpen: boolean;
onClose: () => void;
}
export default function PriceComparisonSheet({
wine,
isOpen,
onClose
}: PriceComparisonSheetProps) {
const [prices, setPrices] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const partners = getEnabledPartners().slice(0, 5); // Top 5 partners
useEffect(() => {
if (isOpen) {
loadPrices();
}
}, [isOpen]);
const loadPrices = async () => {
setLoading(true);
try {
// In a real app, fetch prices from multiple sources
// For now, show the wine's source and simulate others
const mockPrices = partners.map((partner, index) => ({
partnerId: partner.id,
partnerName: partner.name,
price: parseFloat(wine.price.toString()) + (index * 2.99),
inStock: index < 3,
shipping: index === 0 ? 'Free' : '$9.99'
}));
setPrices(mockPrices);
} catch (error) {
console.error('Failed to load prices:', error);
} finally {
setLoading(false);
}
};
if (!isOpen) return null;
const lowestPrice = Math.min(...prices.map(p => p.price));
return (
<>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50 z-40 animate-fadeIn"
onClick={onClose}
/>
{/* Bottom Sheet */}
<div
className="fixed inset-x-0 bottom-0 z-50 bg-white rounded-t-3xl shadow-2xl max-h-[80vh] overflow-hidden animate-slideUp"
style={{
animation: 'slideUp 0.3s ease-out'
}}
>
{/* Handle */}
<div className="flex justify-center pt-3 pb-2">
<div className="w-12 h-1 bg-gray-300 rounded-full" />
</div>
{/* Header */}
<div className="px-6 py-4 border-b border-gray-200">
<h3 className="text-xl font-bold text-gray-900 mb-1">
Compare Prices
</h3>
<p className="text-sm text-gray-600 line-clamp-2">
{wine.name}
</p>
</div>
{/* Price List */}
<div className="overflow-y-auto max-h-[60vh] pb-6">
{loading ? (
<div className="flex justify-center items-center py-12">
<div className="animate-spin rounded-full h-12 w-12 border-4 border-purple-600 border-t-transparent"></div>
</div>
) : (
<div className="space-y-3 px-6 pt-4">
{prices.map((price, index) => (
<div
key={price.partnerId}
className={`
p-4 rounded-xl border-2 transition-all
${price.price === lowestPrice
? 'border-green-500 bg-green-50'
: 'border-gray-200 bg-white'
}
`}
>
<div className="flex items-start justify-between mb-3">
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<h4 className="font-bold text-gray-900">
{price.partnerName}
</h4>
{price.price === lowestPrice && (
<span className="bg-green-500 text-white text-xs px-2 py-1 rounded-full font-bold">
Best Deal
</span>
)}
</div>
<div className="flex items-center gap-2 text-sm">
<span className={`
${price.inStock ? 'text-green-600' : 'text-red-600'}
font-medium
`}>
{price.inStock ? '✓ In Stock' : '○ Out of Stock'}
</span>
<span className="text-gray-500">•</span>
<span className="text-gray-600">
Shipping: {price.shipping}
</span>
</div>
</div>
<div className="text-right">
<div className="text-2xl font-bold text-purple-700">
${price.price.toFixed(2)}
</div>
</div>
</div>
<AffiliateButton
wine={wine}
partnerId={price.partnerId}
partnerName={price.partnerName}
variant={price.price === lowestPrice ? 'primary' : 'outline'}
fullWidth
/>
</div>
))}
</div>
)}
</div>
{/* Footer */}
<div className="px-6 py-4 border-t border-gray-200 bg-gray-50">
<p className="text-xs text-gray-500 text-center">
Affiliate Disclosure: We may earn a commission from purchases made through these links.
</p>
</div>
</div>
<style jsx>{`
@keyframes slideUp {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
`}</style>
</>
);
}