'use client' import { useEffect, useState } from 'react' import { Header } from '@/components/Header' import { Footer } from '@/components/Footer' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { Clock, TrendingUp, ExternalLink, Search, Flame, Timer, Users, ArrowUpRight, Lock, Gavel, ChevronUp, ChevronDown, ChevronsUpDown, DollarSign, RefreshCw, Target, Info, X, } from 'lucide-react' import Link from 'next/link' import clsx from 'clsx' interface Auction { domain: string platform: string platform_url: string current_bid: number currency: string num_bids: number end_time: string time_remaining: string buy_now_price: number | null reserve_met: boolean | null traffic: number | null age_years: number | null tld: string affiliate_url: string } interface Opportunity { auction: Auction analysis: { opportunity_score: number urgency?: string competition?: string price_range?: string recommendation: string reasoning?: string // Legacy fields estimated_value?: number current_bid?: number value_ratio?: number potential_profit?: number } } type TabType = 'all' | 'ending' | 'hot' | 'opportunities' type SortField = 'ending' | 'bid_asc' | 'bid_desc' | 'bids' const PLATFORMS = [ { id: 'All', name: 'All Sources' }, { id: 'GoDaddy', name: 'GoDaddy' }, { id: 'Sedo', name: 'Sedo' }, { id: 'NameJet', name: 'NameJet' }, { id: 'DropCatch', name: 'DropCatch' }, { id: 'ExpiredDomains', name: 'Expired Domains' }, ] const TAB_DESCRIPTIONS: Record = { all: { title: 'All Auctions', description: 'All active auctions from all platforms, sorted by ending time by default.', }, ending: { title: 'Ending Soon', description: 'Auctions ending within the next 24 hours. Best for last-minute sniping opportunities.', }, hot: { title: 'Hot Auctions', description: 'Auctions with the most bidding activity (20+ bids). High competition but proven demand.', }, opportunities: { title: 'Smart Opportunities', description: 'Our algorithm scores auctions based on: Time urgency (ending soon = higher score), Competition (fewer bids = higher score), and Price point (lower entry = higher score). Only auctions with a combined score ≥ 3 are shown.', }, } function SortIcon({ field, currentField, direction }: { field: SortField, currentField: SortField, direction: 'asc' | 'desc' }) { if (field !== currentField) { return } return direction === 'asc' ? : } function getPlatformBadgeClass(platform: string) { switch (platform) { case 'GoDaddy': return 'text-blue-400 bg-blue-400/10' case 'Sedo': return 'text-orange-400 bg-orange-400/10' case 'NameJet': return 'text-purple-400 bg-purple-400/10' case 'DropCatch': return 'text-teal-400 bg-teal-400/10' default: return 'text-foreground-muted bg-foreground/5' } } export default function AuctionsPage() { const { isAuthenticated, checkAuth, isLoading: authLoading } = useStore() const [allAuctions, setAllAuctions] = useState([]) const [endingSoon, setEndingSoon] = useState([]) const [hotAuctions, setHotAuctions] = useState([]) const [opportunities, setOpportunities] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [activeTab, setActiveTab] = useState('all') const [sortBy, setSortBy] = useState('ending') const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc') // Filters const [searchQuery, setSearchQuery] = useState('') const [selectedPlatform, setSelectedPlatform] = useState('All') const [maxBid, setMaxBid] = useState('') useEffect(() => { checkAuth() loadData() }, [checkAuth]) useEffect(() => { if (isAuthenticated && opportunities.length === 0) { loadOpportunities() } }, [isAuthenticated]) const loadOpportunities = async () => { try { const oppData = await api.getAuctionOpportunities() setOpportunities(oppData.opportunities || []) } catch (e) { console.error('Failed to load opportunities:', e) } } const loadData = async () => { setLoading(true) try { const [auctionsData, hotData, endingData] = await Promise.all([ api.getAuctions(), api.getHotAuctions(50), api.getEndingSoonAuctions(24, 50), ]) setAllAuctions(auctionsData.auctions || []) setHotAuctions(hotData || []) setEndingSoon(endingData || []) if (isAuthenticated) { await loadOpportunities() } } catch (error) { console.error('Failed to load auction data:', error) } finally { setLoading(false) } } const handleRefresh = async () => { setRefreshing(true) await loadData() setRefreshing(false) } const formatCurrency = (value: number) => { return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(value) } const getCurrentAuctions = (): Auction[] => { switch (activeTab) { case 'ending': return endingSoon case 'hot': return hotAuctions case 'opportunities': return opportunities.map(o => o.auction) default: return allAuctions } } const getOpportunityData = (domain: string) => { if (activeTab !== 'opportunities') return null return opportunities.find(o => o.auction.domain === domain)?.analysis } const filteredAuctions = getCurrentAuctions().filter(auction => { if (searchQuery && !auction.domain.toLowerCase().includes(searchQuery.toLowerCase())) { return false } if (selectedPlatform !== 'All' && auction.platform !== selectedPlatform) { return false } if (maxBid && auction.current_bid > parseFloat(maxBid)) { return false } return true }) const sortedAuctions = activeTab === 'opportunities' ? filteredAuctions : [...filteredAuctions].sort((a, b) => { const mult = sortDirection === 'asc' ? 1 : -1 switch (sortBy) { case 'ending': return mult * (new Date(a.end_time).getTime() - new Date(b.end_time).getTime()) case 'bid_asc': case 'bid_desc': return mult * (a.current_bid - b.current_bid) case 'bids': return mult * (b.num_bids - a.num_bids) default: return 0 } }) const getTimeColor = (timeRemaining: string) => { if (timeRemaining.includes('m') && !timeRemaining.includes('h') && !timeRemaining.includes('d')) { return 'text-danger' } if (timeRemaining.includes('h') && parseInt(timeRemaining) < 2) { return 'text-warning' } return 'text-foreground-muted' } const handleSort = (field: SortField) => { if (sortBy === field) { setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc') } else { setSortBy(field) setSortDirection('asc') } } if (authLoading) { return (
) } return (
{/* Background Effects - matching landing page */}
{/* Header */}
Auction Aggregator

Curated Opportunities

Real-time from GoDaddy, Sedo, NameJet & DropCatch. Find opportunities.

{/* Login Banner for non-authenticated users */} {!isAuthenticated && (

Unlock Smart Opportunities

Sign in for algorithmic deal-finding and alerts.

Hunt Free
)} {/* Tabs - flex-wrap to avoid horizontal scroll */}
{/* Search & Filters */}
setSearchQuery(e.target.value)} className="w-full pl-12 pr-10 py-3 bg-background-secondary/50 border border-border rounded-xl text-body text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent transition-all duration-300" /> {searchQuery && ( )}
setMaxBid(e.target.value)} className="w-32 pl-11 pr-4 py-3 bg-background-secondary/50 border border-border rounded-xl text-body text-foreground placeholder:text-foreground-subtle focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent transition-all" />
{/* Content */} {loading ? (
) : activeTab === 'opportunities' && !isAuthenticated ? (

Unlock Smart Opportunities

Our algorithm analyzes ending times, bid activity, and price points to find the best opportunities.

Get Started Free
) : ( /* Table - using proper like TLD Prices */
{activeTab === 'opportunities' && ( )} {sortedAuctions.length === 0 ? ( ) : ( sortedAuctions.map((auction, idx) => { const oppData = getOpportunityData(auction.domain) return ( {/* Domain */} {/* Platform */} {/* Current Bid */} {/* Bids */} {/* Time Left */} {/* Score (opportunities only) */} {activeTab === 'opportunities' && oppData && ( )} {/* Action */} ) }) )}
Platform Time Left Score
{activeTab === 'opportunities' ? 'No opportunities right now — check back later!' : searchQuery ? `No auctions found matching "${searchQuery}"` : 'No auctions found'}
{auction.domain}
{auction.platform} {auction.age_years && ( {auction.age_years}y )}
{auction.platform} {auction.age_years && ( {auction.age_years}y )}
{formatCurrency(auction.current_bid)} {auction.buy_now_price && (

Buy: {formatCurrency(auction.buy_now_price)}

)}
= 20 ? "text-accent" : auction.num_bids >= 10 ? "text-warning" : "text-foreground-muted" )} title={`${auction.num_bids} bids - ${auction.num_bids >= 20 ? 'High competition!' : auction.num_bids >= 10 ? 'Moderate interest' : 'Low competition'}`} > {auction.num_bids} {auction.num_bids >= 20 && } {auction.time_remaining} {oppData.opportunity_score} Bid
)} {/* Info Footer */}

{TAB_DESCRIPTIONS[activeTab].title}

{TAB_DESCRIPTIONS[activeTab].description}

Sources: GoDaddy, Sedo, NameJet, DropCatch, ExpiredDomains. Click "Bid" to go to the auction — we don't handle transactions.

) }