'use client' import { useEffect, useState } from 'react' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { CommandCenterLayout } from '@/components/CommandCenterLayout' import { PremiumTable, Badge, PlatformBadge, StatCard, PageContainer } from '@/components/PremiumTable' import { Clock, ExternalLink, Search, Flame, Timer, Gavel, ChevronUp, ChevronDown, ChevronsUpDown, DollarSign, RefreshCw, Target, X, TrendingUp, Loader2, } 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 } } 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' }, ] export default function AuctionsPage() { const { isAuthenticated, subscription } = 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(() => { loadData() }, []) 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-red-400' if (timeRemaining.includes('h') && parseInt(timeRemaining) < 2) return 'text-amber-400' return 'text-foreground-muted' } const handleSort = (field: SortField) => { if (sortBy === field) { setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc') } else { setSortBy(field) setSortDirection('asc') } } // Dynamic subtitle const getSubtitle = () => { if (loading) return 'Loading live auctions...' const total = allAuctions.length if (total === 0) return 'No active auctions found' return `${total.toLocaleString()} live auctions across 4 platforms` } return ( Refresh } > {/* Stats */}
{/* Tabs */}
{[ { id: 'all' as const, label: 'All', icon: Gavel, count: allAuctions.length }, { id: 'ending' as const, label: 'Ending Soon', icon: Timer, count: endingSoon.length, color: 'warning' }, { id: 'hot' as const, label: 'Hot', icon: Flame, count: hotAuctions.length }, { id: 'opportunities' as const, label: 'Opportunities', icon: Target, count: opportunities.length }, ].map((tab) => ( ))}
{/* Filters */}
setSearchQuery(e.target.value)} className="w-full pl-11 pr-10 py-3 bg-background-secondary/50 border border-border/50 rounded-xl text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent/50 transition-all" /> {searchQuery && ( )}
setMaxBid(e.target.value)} className="w-32 pl-10 pr-4 py-3 bg-background-secondary/50 border border-border/50 rounded-xl text-sm text-foreground placeholder:text-foreground-subtle focus:outline-none focus:border-accent/50" />
{/* Table */} `${a.domain}-${a.platform}`} loading={loading} sortBy={sortBy} sortDirection={sortDirection} onSort={(key) => handleSort(key as SortField)} emptyIcon={} emptyTitle={searchQuery ? `No auctions matching "${searchQuery}"` : "No auctions found"} emptyDescription="Try adjusting your filters or check back later" columns={[ { key: 'domain', header: 'Domain', sortable: true, render: (a) => (
{a.domain}
{a.age_years && {a.age_years}y}
), }, { key: 'platform', header: 'Platform', hideOnMobile: true, render: (a) => (
{a.age_years && ( {a.age_years}y )}
), }, { key: 'bid_asc', header: 'Bid', sortable: true, align: 'right', render: (a) => (
{formatCurrency(a.current_bid)} {a.buy_now_price && (

Buy: {formatCurrency(a.buy_now_price)}

)}
), }, { key: 'bids', header: 'Bids', sortable: true, align: 'right', hideOnMobile: true, render: (a) => ( = 20 ? "text-accent" : a.num_bids >= 10 ? "text-amber-400" : "text-foreground-muted" )}> {a.num_bids} {a.num_bids >= 20 && } ), }, { key: 'ending', header: 'Time Left', sortable: true, align: 'right', hideOnMobile: true, render: (a) => ( {a.time_remaining} ), }, ...(activeTab === 'opportunities' ? [{ key: 'score', header: 'Score', align: 'center' as const, render: (a: Auction) => { const oppData = getOpportunityData(a.domain) if (!oppData) return null return ( {oppData.opportunity_score} ) }, }] : []), { key: 'action', header: '', align: 'right', render: (a) => ( Bid ), }, ]} />
) }