diff --git a/frontend/src/app/auctions/page.tsx b/frontend/src/app/auctions/page.tsx index 3027de0..2cd496a 100644 --- a/frontend/src/app/auctions/page.tsx +++ b/frontend/src/app/auctions/page.tsx @@ -5,7 +5,7 @@ import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { Header } from '@/components/Header' import { Footer } from '@/components/Footer' -import { PremiumTable, Badge, PlatformBadge, StatCard } from '@/components/PremiumTable' +import { PremiumTable, PlatformBadge } from '@/components/PremiumTable' import { Clock, ExternalLink, @@ -14,12 +14,12 @@ import { Timer, Gavel, DollarSign, - RefreshCw, - Target, X, - ArrowRight, Lock, - Sparkles, + TrendingUp, + ChevronUp, + ChevronDown, + ChevronsUpDown, } from 'lucide-react' import Link from 'next/link' import clsx from 'clsx' @@ -42,7 +42,8 @@ interface Auction { } type TabType = 'all' | 'ending' | 'hot' -type SortField = 'ending' | 'bid_asc' | 'bid_desc' | 'bids' +type SortField = 'domain' | 'ending' | 'bid' | 'bids' +type SortDirection = 'asc' | 'desc' const PLATFORMS = [ { id: 'All', name: 'All Sources' }, @@ -52,17 +53,25 @@ const PLATFORMS = [ { id: 'DropCatch', name: 'DropCatch' }, ] +function SortIcon({ field, currentField, direction }: { field: SortField, currentField: SortField, direction: SortDirection }) { + if (field !== currentField) { + return + } + return direction === 'asc' + ? + : +} + export default function AuctionsPage() { - const { isAuthenticated, checkAuth } = useStore() + const { isAuthenticated, checkAuth, isLoading: authLoading } = useStore() const [allAuctions, setAllAuctions] = useState([]) const [endingSoon, setEndingSoon] = useState([]) const [hotAuctions, setHotAuctions] = 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') + const [sortField, setSortField] = useState('ending') + const [sortDirection, setSortDirection] = useState('asc') const [searchQuery, setSearchQuery] = useState('') const [selectedPlatform, setSelectedPlatform] = useState('All') @@ -91,12 +100,6 @@ export default function AuctionsPage() { } } - const handleRefresh = async () => { - setRefreshing(true) - await loadAuctions() - setRefreshing(false) - } - const getCurrentAuctions = (): Auction[] => { switch (activeTab) { case 'ending': return endingSoon @@ -118,28 +121,29 @@ export default function AuctionsPage() { return true }) + const handleSort = (field: SortField) => { + if (sortField === field) { + setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc') + } else { + setSortField(field) + setSortDirection('asc') + } + } + const sortedAuctions = [...filteredAuctions].sort((a, b) => { - switch (sortBy) { - case 'bid_asc': - return sortDirection === 'asc' ? a.current_bid - b.current_bid : b.current_bid - a.current_bid - case 'bid_desc': - return sortDirection === 'asc' ? b.current_bid - a.current_bid : a.current_bid - b.current_bid + const modifier = sortDirection === 'asc' ? 1 : -1 + switch (sortField) { + case 'domain': + return a.domain.localeCompare(b.domain) * modifier + case 'bid': + return (a.current_bid - b.current_bid) * modifier case 'bids': - return sortDirection === 'asc' ? a.num_bids - b.num_bids : b.num_bids - a.num_bids + return (a.num_bids - b.num_bids) * modifier default: return 0 } }) - const handleSort = (field: SortField) => { - if (sortBy === field) { - setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc') - } else { - setSortBy(field) - setSortDirection('asc') - } - } - const formatCurrency = (amount: number, currency = 'USD') => { return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount) } @@ -150,16 +154,20 @@ export default function AuctionsPage() { return 'text-foreground-muted' } - 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` + // Hot auctions preview for the hero section + const hotPreview = hotAuctions.slice(0, 4) + + if (authLoading) { + return ( +
+
+
+ ) } return (
- {/* Background Effects */} + {/* Background Effects - matching landing page */}
@@ -176,244 +184,309 @@ export default function AuctionsPage() {
- {/* Header */} -
-
- Live Market -

- Domain Auctions -

-

{getSubtitle()}

-
- + {/* Hero Header - centered like TLD pricing */} +
+ Live Market +

+ {allAuctions.length}+ Auctions. Real-Time. +

+

+ Track domain auctions across GoDaddy, Sedo, NameJet & DropCatch. +

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

Unlock Smart Opportunities

-

Get AI-powered auction analysis and personalized recommendations

-
+
+
+
+
- - Sign In - +
+

Unlock Smart Opportunities

+

+ Sign in for AI-powered analysis and personalized recommendations. +

+
+
+ + Hunt Free + +
+ )} + + {/* Hot Auctions Preview */} + {hotPreview.length > 0 && ( + )} + {/* 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-10 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" + /> +
+
+
+ {/* 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: 'all' as const, label: 'All Auctions', icon: Gavel, count: allAuctions.length }, + { id: 'ending' as const, label: 'Ending Soon', icon: Timer, count: endingSoon.length }, { id: 'hot' as const, label: 'Hot', icon: Flame, count: hotAuctions.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" - /> + {/* Auctions Table */} +
+
+ + + + + + + + + + + + + {loading ? ( + // Loading skeleton + Array.from({ length: 10 }).map((_, idx) => ( + + + + + + + + + )) + ) : sortedAuctions.length === 0 ? ( + + + + ) : ( + sortedAuctions.map((auction) => ( + + + + + + + + + )) + )} + +
+ + + Platform + + + + + + +
+ {searchQuery ? `No auctions found matching "${searchQuery}"` : 'No auctions found'} +
+ + +
+ + {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-amber-400" : "text-foreground-muted" + )}> + {auction.num_bids} + {auction.num_bids >= 20 && } + + + + {auction.time_remaining} + + + + Bid + + +
- {/* Auctions 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} - - ), - }, - { - key: 'action', - header: '', - align: 'right', - render: (a) => ( - - Bid - - ), - }, - ]} - /> -
+ {/* Stats */} + {!loading && ( +
+

+ {searchQuery + ? `Found ${sortedAuctions.length} auctions matching "${searchQuery}"` + : `${allAuctions.length} auctions available across ${PLATFORMS.length - 1} platforms` + } +

+
+ )}