← back to Wine Finder Next
app/arbitrage/page.tsx
181 lines
'use client';
import { useState } from 'react';
import SearchBar from '@/src/components/SearchBar';
import ListingsTable from '@/src/components/ListingsTable';
import PriceHistoryChart from '@/src/components/PriceHistoryChart';
import { searchWineEverywhere, fetchPriceHistory } from '@/src/api/wineProviders';
import { WineListing, PriceHistoryPoint } from '@/src/types/wine';
import { TOP_10_AUCTION_WINES } from '@/src/data/topAuctionWines';
export default function ArbitrageFinder() {
const [listings, setListings] = useState<WineListing[]>([]);
const [priceHistory, setPriceHistory] = useState<PriceHistoryPoint[]>([]);
const [selectedWine, setSelectedWine] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [lastQuery, setLastQuery] = useState('');
const handleSearch = async (query: string) => {
setIsLoading(true);
setLastQuery(query);
try {
const wines = await searchWineEverywhere(query);
setListings(wines);
// Load price history for first wine (if any)
if (wines.length > 0) {
const firstWine = wines[0];
setSelectedWine(firstWine.name);
const history = await fetchPriceHistory(firstWine.id);
setPriceHistory(history);
}
} catch (error) {
console.error('Search failed:', error);
setListings([]);
} finally {
setIsLoading(false);
}
};
return (
<main className="min-h-screen bg-gradient-to-br from-purple-50 via-pink-50 to-red-50">
{/* Header */}
<div className="bg-gradient-to-r from-purple-900 via-pink-900 to-red-900 text-white py-12 shadow-2xl">
<div className="container mx-auto px-4">
<div className="text-center mb-8">
<div className="text-6xl mb-4">💎</div>
<h1 className="text-5xl font-black mb-4 bg-clip-text text-transparent bg-gradient-to-r from-yellow-200 via-pink-200 to-purple-200">
Wine Arbitrage Finder
</h1>
<p className="text-xl text-purple-200 max-w-3xl mx-auto">
Find profitable opportunities by comparing auction prices vs retail prices across all major wine sources
</p>
</div>
<SearchBar onSearch={handleSearch} isLoading={isLoading} />
{/* Stats Bar */}
{listings.length > 0 && (
<div className="mt-8 grid grid-cols-3 gap-4 max-w-3xl mx-auto">
<div className="bg-white/10 backdrop-blur rounded-lg p-4 text-center border border-white/20">
<div className="text-3xl font-black text-yellow-300">{listings.length}</div>
<div className="text-sm text-purple-200">Total Listings</div>
</div>
<div className="bg-white/10 backdrop-blur rounded-lg p-4 text-center border border-white/20">
<div className="text-3xl font-black text-green-300">
{listings.filter(l => l.provider === 'retail').length}
</div>
<div className="text-sm text-purple-200">Retail</div>
</div>
<div className="bg-white/10 backdrop-blur rounded-lg p-4 text-center border border-white/20">
<div className="text-3xl font-black text-blue-300">
{listings.filter(l => l.provider === 'auction').length}
</div>
<div className="text-sm text-purple-200">Auctions</div>
</div>
</div>
)}
</div>
</div>
{/* Main Content */}
<div className="container mx-auto px-4 py-12">
{/* Empty State */}
{!isLoading && listings.length === 0 && !lastQuery && (
<div className="text-center py-20">
<div className="text-8xl mb-6">🔍</div>
<h2 className="text-3xl font-bold text-gray-800 mb-4">Start Finding Arbitrage Opportunities</h2>
<p className="text-lg text-gray-600 max-w-2xl mx-auto mb-8">
Search for premium wines to compare auction vs retail prices. We search across:
</p>
<div className="flex flex-wrap justify-center gap-3 max-w-3xl mx-auto mb-8">
{['WineBid', 'Sotheby\'s', 'Zachys', 'Wine.com', 'Vivino', 'Wine-Searcher'].map(source => (
<span key={source} className="px-4 py-2 bg-purple-100 text-purple-700 rounded-full font-semibold text-sm">
{source}
</span>
))}
</div>
<div className="bg-gradient-to-r from-purple-100 via-pink-100 to-red-100 rounded-2xl p-8 mt-8 border-2 border-purple-300">
<h3 className="text-2xl font-bold text-center text-gray-800 mb-2">🔥 Top 10 Wines at Auction</h3>
<p className="text-center text-gray-600 mb-6">Most profitable arbitrage opportunities by brand</p>
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
{TOP_10_AUCTION_WINES.map((wine, idx) => (
<button
key={wine}
onClick={() => handleSearch(wine)}
className="group relative px-4 py-3 bg-white hover:bg-gradient-to-r hover:from-purple-600 hover:to-pink-600 text-gray-800 hover:text-white rounded-xl transition-all font-semibold text-sm shadow-md hover:shadow-xl border-2 border-purple-200 hover:border-purple-400 transform hover:scale-105"
>
<div className="text-xs text-purple-600 group-hover:text-yellow-200 mb-1">#{idx + 1}</div>
<div className="font-bold">{wine}</div>
</button>
))}
</div>
<div className="mt-6 text-center text-sm text-gray-500">
Click any wine to find arbitrage opportunities instantly
</div>
</div>
</div>
)}
{/* Loading State */}
{isLoading && (
<div className="text-center py-20">
<div className="inline-block animate-spin rounded-full h-20 w-20 border-t-4 border-b-4 border-purple-600 mb-6"></div>
<p className="text-xl text-gray-700 font-semibold">Searching all sources...</p>
<p className="text-sm text-gray-500 mt-2">This may take a moment as we check multiple providers</p>
</div>
)}
{/* No Results */}
{!isLoading && listings.length === 0 && lastQuery && (
<div className="text-center py-20">
<div className="text-8xl mb-6">😕</div>
<h2 className="text-3xl font-bold text-gray-800 mb-4">No wines found for "{lastQuery}"</h2>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
Try searching for:
</p>
<div className="flex flex-wrap justify-center gap-2 mt-4">
{['Caymus Cabernet', 'Opus One', 'Silver Oak', 'Screaming Eagle', 'Dom Perignon'].map(suggestion => (
<button
key={suggestion}
onClick={() => handleSearch(suggestion)}
className="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition-all font-semibold"
>
{suggestion}
</button>
))}
</div>
</div>
)}
{/* Results */}
{!isLoading && listings.length > 0 && (
<>
<ListingsTable listings={listings} />
{/* Price History Chart */}
{priceHistory.length > 0 && (
<div className="mt-12">
<PriceHistoryChart history={priceHistory} wineName={selectedWine || undefined} />
</div>
)}
</>
)}
</div>
{/* Footer */}
<footer className="bg-gradient-to-r from-purple-900 to-pink-900 text-white py-8 mt-20">
<div className="container mx-auto px-4 text-center">
<p className="text-purple-200 mb-2">
Wine Arbitrage Finder - Compare auction vs retail prices
</p>
<p className="text-sm text-purple-300">
Part of Red Thunder Wine Tracker © 2025
</p>
</div>
</footer>
</main>
);
}