PORTFOLIO VALUATION (100% Transparent): - Completely rewritten valuation algorithm with clear formula - Shows exact calculation: Base × Length × TLD × Keyword × Brand - Each factor explained with reason (e.g., '4-letter domain ×5.0') - Real TLD registration costs integrated from database - Confidence levels: high/medium/low based on score consistency - Detailed breakdown: scores, factors, calculation steps - Value-to-cost ratio for investment decisions - Disclaimer about algorithmic limitations SMART POUNCE - Auction Aggregator: - New /auctions page aggregating domain auctions - Platforms: GoDaddy, Sedo, NameJet, SnapNames, DropCatch - Features: - All Auctions: Search, filter by platform/price/TLD - Opportunities: AI-powered undervalued domain detection - Ending Soon: Snipe auctions ending in < 1 hour - Hot Auctions: Most-bid domains - Smart opportunity scoring: value_ratio × time_factor × bid_factor - Affiliate links to platforms (no payment handling = no GwG issues) - Full legal compliance for Switzerland (no escrow) API ENDPOINTS: - GET /auctions - Search all auctions - GET /auctions/ending-soon - Auctions ending soon - GET /auctions/hot - Most active auctions - GET /auctions/opportunities - Smart recommendations (auth required) - GET /auctions/stats - Platform statistics UI UPDATES: - New 'Auctions' link in navigation (desktop + mobile) - Auction cards with bid info, time remaining, platform badges - Opportunity analysis with profit potential - Color-coded time urgency (red < 1h, yellow < 2h)
614 lines
26 KiB
TypeScript
614 lines
26 KiB
TypeScript
'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 {
|
||
Zap,
|
||
Clock,
|
||
TrendingUp,
|
||
ExternalLink,
|
||
Filter,
|
||
Search,
|
||
Flame,
|
||
Timer,
|
||
DollarSign,
|
||
Users,
|
||
ArrowUpRight,
|
||
ChevronRight,
|
||
Lock,
|
||
BarChart3,
|
||
Target,
|
||
Sparkles,
|
||
} 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: {
|
||
estimated_value: number
|
||
current_bid: number
|
||
value_ratio: number
|
||
potential_profit: number
|
||
opportunity_score: number
|
||
recommendation: string
|
||
}
|
||
}
|
||
|
||
const PLATFORMS = ['All', 'GoDaddy', 'Sedo', 'NameJet', 'SnapNames', 'DropCatch']
|
||
|
||
export default function AuctionsPage() {
|
||
const { isAuthenticated, checkAuth, isLoading: authLoading } = useStore()
|
||
|
||
const [auctions, setAuctions] = useState<Auction[]>([])
|
||
const [opportunities, setOpportunities] = useState<Opportunity[]>([])
|
||
const [hotAuctions, setHotAuctions] = useState<Auction[]>([])
|
||
const [endingSoon, setEndingSoon] = useState<Auction[]>([])
|
||
const [loading, setLoading] = useState(true)
|
||
const [activeTab, setActiveTab] = useState<'all' | 'opportunities' | 'ending'>('all')
|
||
|
||
// Filters
|
||
const [searchQuery, setSearchQuery] = useState('')
|
||
const [selectedPlatform, setSelectedPlatform] = useState('All')
|
||
const [maxBid, setMaxBid] = useState<string>('')
|
||
|
||
useEffect(() => {
|
||
checkAuth()
|
||
loadData()
|
||
}, [checkAuth])
|
||
|
||
const loadData = async () => {
|
||
setLoading(true)
|
||
try {
|
||
const [auctionsData, hotData, endingData] = await Promise.all([
|
||
api.getAuctions(),
|
||
api.getHotAuctions(),
|
||
api.getEndingSoonAuctions(),
|
||
])
|
||
|
||
setAuctions(auctionsData.auctions || [])
|
||
setHotAuctions(hotData || [])
|
||
setEndingSoon(endingData || [])
|
||
|
||
// Load opportunities only for authenticated users
|
||
if (isAuthenticated) {
|
||
try {
|
||
const oppData = await api.getAuctionOpportunities()
|
||
setOpportunities(oppData.opportunities || [])
|
||
} catch (e) {
|
||
console.error('Failed to load opportunities:', e)
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('Failed to load auction data:', error)
|
||
} finally {
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const formatCurrency = (value: number) => {
|
||
return new Intl.NumberFormat('en-US', {
|
||
style: 'currency',
|
||
currency: 'USD',
|
||
minimumFractionDigits: 0,
|
||
maximumFractionDigits: 0,
|
||
}).format(value)
|
||
}
|
||
|
||
const filteredAuctions = auctions.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 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 getPlatformColor = (platform: string) => {
|
||
switch (platform) {
|
||
case 'GoDaddy':
|
||
return 'bg-blue-500/10 text-blue-400'
|
||
case 'Sedo':
|
||
return 'bg-green-500/10 text-green-400'
|
||
case 'NameJet':
|
||
return 'bg-purple-500/10 text-purple-400'
|
||
case 'SnapNames':
|
||
return 'bg-orange-500/10 text-orange-400'
|
||
case 'DropCatch':
|
||
return 'bg-pink-500/10 text-pink-400'
|
||
default:
|
||
return 'bg-background-tertiary text-foreground-muted'
|
||
}
|
||
}
|
||
|
||
if (authLoading) {
|
||
return (
|
||
<div className="min-h-screen flex items-center justify-center">
|
||
<div className="w-5 h-5 border-2 border-accent border-t-transparent rounded-full animate-spin" />
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-background relative flex flex-col">
|
||
{/* Ambient glow */}
|
||
<div className="fixed inset-0 pointer-events-none">
|
||
<div className="absolute top-0 left-1/4 w-[500px] h-[400px] bg-accent/[0.02] rounded-full blur-3xl" />
|
||
</div>
|
||
|
||
<Header />
|
||
|
||
<main className="relative pt-28 sm:pt-32 pb-16 sm:pb-20 px-4 sm:px-6 flex-1">
|
||
<div className="max-w-7xl mx-auto">
|
||
{/* Header */}
|
||
<div className="text-center mb-12 sm:mb-16 animate-fade-in">
|
||
<div className="inline-flex items-center gap-2 px-3 py-1.5 bg-accent-muted border border-accent/20 rounded-full mb-6">
|
||
<Zap className="w-4 h-4 text-accent" />
|
||
<span className="text-ui-sm text-accent font-medium">Smart Pounce</span>
|
||
</div>
|
||
<h1 className="font-display text-[2rem] sm:text-[2.75rem] md:text-[3.5rem] lg:text-[4rem] leading-[1.1] tracking-[-0.03em] text-foreground mb-4">
|
||
Domain Auctions
|
||
</h1>
|
||
<p className="text-body sm:text-body-lg text-foreground-muted max-w-2xl mx-auto">
|
||
Aggregated auctions from GoDaddy, Sedo, NameJet & more.
|
||
Find undervalued domains before anyone else.
|
||
</p>
|
||
</div>
|
||
|
||
{/* Strategy Banner */}
|
||
<div className="mb-8 p-5 bg-gradient-to-r from-accent/10 to-accent/5 border border-accent/20 rounded-xl">
|
||
<div className="flex items-start gap-4">
|
||
<div className="w-10 h-10 bg-accent/20 rounded-xl flex items-center justify-center shrink-0">
|
||
<Target className="w-5 h-5 text-accent" />
|
||
</div>
|
||
<div>
|
||
<h3 className="text-body font-medium text-foreground mb-1">Smart Pounce Strategy</h3>
|
||
<p className="text-body-sm text-foreground-muted">
|
||
We aggregate auctions from multiple platforms so you can find the best deals.
|
||
We don't handle payments — click through to the platform to bid.
|
||
Pro tip: Focus on auctions ending soon with low bid counts.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Quick Stats */}
|
||
<div className="grid grid-cols-2 lg:grid-cols-4 gap-3 mb-8">
|
||
<div className="p-4 bg-background-secondary/50 border border-border rounded-xl">
|
||
<div className="flex items-center gap-2 text-foreground-subtle mb-2">
|
||
<BarChart3 className="w-4 h-4" />
|
||
<span className="text-ui-sm">Active Auctions</span>
|
||
</div>
|
||
<p className="text-heading-sm font-medium text-foreground">{auctions.length}</p>
|
||
</div>
|
||
<div className="p-4 bg-background-secondary/50 border border-border rounded-xl">
|
||
<div className="flex items-center gap-2 text-foreground-subtle mb-2">
|
||
<Timer className="w-4 h-4" />
|
||
<span className="text-ui-sm">Ending Soon</span>
|
||
</div>
|
||
<p className="text-heading-sm font-medium text-warning">{endingSoon.length}</p>
|
||
</div>
|
||
<div className="p-4 bg-background-secondary/50 border border-border rounded-xl">
|
||
<div className="flex items-center gap-2 text-foreground-subtle mb-2">
|
||
<Flame className="w-4 h-4" />
|
||
<span className="text-ui-sm">Hot Auctions</span>
|
||
</div>
|
||
<p className="text-heading-sm font-medium text-accent">{hotAuctions.length}</p>
|
||
</div>
|
||
<div className="p-4 bg-background-secondary/50 border border-border rounded-xl">
|
||
<div className="flex items-center gap-2 text-foreground-subtle mb-2">
|
||
<Sparkles className="w-4 h-4" />
|
||
<span className="text-ui-sm">Opportunities</span>
|
||
</div>
|
||
<p className="text-heading-sm font-medium text-accent">
|
||
{isAuthenticated ? opportunities.length : '—'}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Tabs */}
|
||
<div className="flex items-center gap-1 p-1 bg-background-secondary border border-border rounded-xl w-fit mb-6">
|
||
<button
|
||
onClick={() => setActiveTab('all')}
|
||
className={clsx(
|
||
"flex items-center gap-2 px-4 py-2 text-ui-sm font-medium rounded-lg transition-all",
|
||
activeTab === 'all'
|
||
? "bg-foreground text-background"
|
||
: "text-foreground-muted hover:text-foreground"
|
||
)}
|
||
>
|
||
<BarChart3 className="w-4 h-4" />
|
||
All Auctions
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('opportunities')}
|
||
className={clsx(
|
||
"flex items-center gap-2 px-4 py-2 text-ui-sm font-medium rounded-lg transition-all",
|
||
activeTab === 'opportunities'
|
||
? "bg-foreground text-background"
|
||
: "text-foreground-muted hover:text-foreground"
|
||
)}
|
||
>
|
||
<Sparkles className="w-4 h-4" />
|
||
Opportunities
|
||
{!isAuthenticated && <Lock className="w-3 h-3" />}
|
||
</button>
|
||
<button
|
||
onClick={() => setActiveTab('ending')}
|
||
className={clsx(
|
||
"flex items-center gap-2 px-4 py-2 text-ui-sm font-medium rounded-lg transition-all",
|
||
activeTab === 'ending'
|
||
? "bg-foreground text-background"
|
||
: "text-foreground-muted hover:text-foreground"
|
||
)}
|
||
>
|
||
<Timer className="w-4 h-4" />
|
||
Ending Soon
|
||
</button>
|
||
</div>
|
||
|
||
{/* Filters (for All tab) */}
|
||
{activeTab === 'all' && (
|
||
<div className="flex flex-wrap gap-3 mb-6">
|
||
<div className="relative flex-1 min-w-[200px] max-w-md">
|
||
<Search className="absolute left-4 top-1/2 -translate-y-1/2 w-4 h-4 text-foreground-subtle" />
|
||
<input
|
||
type="text"
|
||
placeholder="Search domains..."
|
||
value={searchQuery}
|
||
onChange={(e) => setSearchQuery(e.target.value)}
|
||
className="w-full pl-11 pr-4 py-2.5 bg-background-secondary border border-border rounded-xl
|
||
text-body-sm text-foreground placeholder:text-foreground-subtle
|
||
focus:outline-none focus:border-border-hover transition-all"
|
||
/>
|
||
</div>
|
||
<select
|
||
value={selectedPlatform}
|
||
onChange={(e) => setSelectedPlatform(e.target.value)}
|
||
className="px-4 py-2.5 bg-background-secondary border border-border rounded-xl
|
||
text-body-sm text-foreground focus:outline-none focus:border-border-hover"
|
||
>
|
||
{PLATFORMS.map(p => (
|
||
<option key={p} value={p}>{p}</option>
|
||
))}
|
||
</select>
|
||
<input
|
||
type="number"
|
||
placeholder="Max bid..."
|
||
value={maxBid}
|
||
onChange={(e) => setMaxBid(e.target.value)}
|
||
className="w-32 px-4 py-2.5 bg-background-secondary border border-border rounded-xl
|
||
text-body-sm text-foreground placeholder:text-foreground-subtle
|
||
focus:outline-none focus:border-border-hover"
|
||
/>
|
||
</div>
|
||
)}
|
||
|
||
{/* Content */}
|
||
{loading ? (
|
||
<div className="flex items-center justify-center py-16">
|
||
<div className="w-6 h-6 border-2 border-accent border-t-transparent rounded-full animate-spin" />
|
||
</div>
|
||
) : activeTab === 'all' ? (
|
||
/* All Auctions Grid */
|
||
<div className="grid gap-4">
|
||
{filteredAuctions.length === 0 ? (
|
||
<div className="text-center py-12 text-foreground-muted">
|
||
No auctions match your filters
|
||
</div>
|
||
) : (
|
||
filteredAuctions.map((auction, idx) => (
|
||
<div
|
||
key={`${auction.domain}-${idx}`}
|
||
className="p-5 bg-background-secondary/50 border border-border rounded-xl hover:border-border-hover transition-all group"
|
||
>
|
||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
||
<div className="flex-1">
|
||
<div className="flex items-center gap-3 mb-2">
|
||
<span className="font-mono text-body-lg font-medium text-foreground">
|
||
{auction.domain}
|
||
</span>
|
||
<span className={clsx(
|
||
"text-ui-xs px-2 py-0.5 rounded-full",
|
||
getPlatformColor(auction.platform)
|
||
)}>
|
||
{auction.platform}
|
||
</span>
|
||
</div>
|
||
<div className="flex flex-wrap items-center gap-x-4 gap-y-2 text-body-sm text-foreground-muted">
|
||
<span className="flex items-center gap-1.5">
|
||
<Users className="w-3.5 h-3.5" />
|
||
{auction.num_bids} bids
|
||
</span>
|
||
{auction.age_years && (
|
||
<span className="flex items-center gap-1.5">
|
||
<Clock className="w-3.5 h-3.5" />
|
||
{auction.age_years} years old
|
||
</span>
|
||
)}
|
||
{auction.traffic && (
|
||
<span className="flex items-center gap-1.5">
|
||
<TrendingUp className="w-3.5 h-3.5" />
|
||
{auction.traffic.toLocaleString()} visits/mo
|
||
</span>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-6">
|
||
<div className="text-right">
|
||
<p className="text-ui-xs text-foreground-muted mb-1">Current Bid</p>
|
||
<p className="text-body-lg font-medium text-foreground">
|
||
{formatCurrency(auction.current_bid)}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="text-right">
|
||
<p className="text-ui-xs text-foreground-muted mb-1">Time Left</p>
|
||
<p className={clsx(
|
||
"text-body-lg font-medium flex items-center gap-1.5",
|
||
getTimeColor(auction.time_remaining)
|
||
)}>
|
||
<Timer className="w-4 h-4" />
|
||
{auction.time_remaining}
|
||
</p>
|
||
</div>
|
||
|
||
<a
|
||
href={auction.affiliate_url}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="flex items-center gap-2 px-4 py-2.5 bg-accent text-background text-ui-sm font-medium rounded-xl
|
||
hover:bg-accent-hover transition-all"
|
||
>
|
||
Bid Now
|
||
<ExternalLink className="w-3.5 h-3.5" />
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
{auction.buy_now_price && (
|
||
<div className="mt-3 pt-3 border-t border-border flex items-center justify-between">
|
||
<span className="text-body-sm text-foreground-subtle">
|
||
Buy Now: {formatCurrency(auction.buy_now_price)}
|
||
</span>
|
||
{auction.reserve_met !== null && (
|
||
<span className={clsx(
|
||
"text-ui-xs px-2 py-0.5 rounded-full",
|
||
auction.reserve_met
|
||
? "bg-accent-muted text-accent"
|
||
: "bg-warning-muted text-warning"
|
||
)}>
|
||
{auction.reserve_met ? 'Reserve Met' : 'Reserve Not Met'}
|
||
</span>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
))
|
||
)}
|
||
</div>
|
||
) : activeTab === 'opportunities' ? (
|
||
/* Smart Opportunities */
|
||
!isAuthenticated ? (
|
||
<div className="text-center py-16 border border-dashed border-border rounded-xl bg-background-secondary/30">
|
||
<div className="w-12 h-12 bg-background-tertiary rounded-xl flex items-center justify-center mx-auto mb-4">
|
||
<Lock className="w-6 h-6 text-foreground-subtle" />
|
||
</div>
|
||
<p className="text-body text-foreground-muted mb-2">Sign in to see opportunities</p>
|
||
<p className="text-body-sm text-foreground-subtle mb-6">
|
||
Our algorithm finds undervalued domains based on your watchlist and market data.
|
||
</p>
|
||
<Link
|
||
href="/register"
|
||
className="inline-flex items-center gap-2 px-5 py-2.5 bg-accent text-background text-ui font-medium rounded-xl
|
||
hover:bg-accent-hover transition-all"
|
||
>
|
||
Get Started Free
|
||
<ArrowUpRight className="w-4 h-4" />
|
||
</Link>
|
||
</div>
|
||
) : opportunities.length === 0 ? (
|
||
<div className="text-center py-12 text-foreground-muted">
|
||
No opportunities found right now. Check back later!
|
||
</div>
|
||
) : (
|
||
<div className="grid gap-4">
|
||
{opportunities.map((opp, idx) => (
|
||
<div
|
||
key={`${opp.auction.domain}-${idx}`}
|
||
className="p-5 bg-background-secondary/50 border border-accent/20 rounded-xl"
|
||
>
|
||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
||
<div className="flex-1">
|
||
<div className="flex items-center gap-3 mb-2">
|
||
<span className="font-mono text-body-lg font-medium text-foreground">
|
||
{opp.auction.domain}
|
||
</span>
|
||
<span className={clsx(
|
||
"text-ui-xs px-2 py-0.5 rounded-full",
|
||
opp.analysis.recommendation === 'Strong buy'
|
||
? "bg-accent-muted text-accent"
|
||
: opp.analysis.recommendation === 'Consider'
|
||
? "bg-warning-muted text-warning"
|
||
: "bg-background-tertiary text-foreground-muted"
|
||
)}>
|
||
{opp.analysis.recommendation}
|
||
</span>
|
||
</div>
|
||
|
||
{/* Analysis Breakdown */}
|
||
<div className="grid grid-cols-2 sm:grid-cols-4 gap-3 mt-3">
|
||
<div className="p-2 bg-background-tertiary rounded-lg">
|
||
<p className="text-ui-xs text-foreground-subtle">Current Bid</p>
|
||
<p className="text-body-sm font-medium text-foreground">
|
||
{formatCurrency(opp.analysis.current_bid)}
|
||
</p>
|
||
</div>
|
||
<div className="p-2 bg-background-tertiary rounded-lg">
|
||
<p className="text-ui-xs text-foreground-subtle">Est. Value</p>
|
||
<p className="text-body-sm font-medium text-accent">
|
||
{formatCurrency(opp.analysis.estimated_value)}
|
||
</p>
|
||
</div>
|
||
<div className="p-2 bg-background-tertiary rounded-lg">
|
||
<p className="text-ui-xs text-foreground-subtle">Value Ratio</p>
|
||
<p className="text-body-sm font-medium text-foreground">
|
||
{opp.analysis.value_ratio}×
|
||
</p>
|
||
</div>
|
||
<div className="p-2 bg-background-tertiary rounded-lg">
|
||
<p className="text-ui-xs text-foreground-subtle">Potential Profit</p>
|
||
<p className={clsx(
|
||
"text-body-sm font-medium",
|
||
opp.analysis.potential_profit > 0 ? "text-accent" : "text-danger"
|
||
)}>
|
||
{opp.analysis.potential_profit > 0 ? '+' : ''}
|
||
{formatCurrency(opp.analysis.potential_profit)}
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-4">
|
||
<div className="text-right">
|
||
<p className="text-ui-xs text-foreground-muted mb-1">Time Left</p>
|
||
<p className={clsx(
|
||
"text-body font-medium",
|
||
getTimeColor(opp.auction.time_remaining)
|
||
)}>
|
||
{opp.auction.time_remaining}
|
||
</p>
|
||
</div>
|
||
|
||
<a
|
||
href={opp.auction.affiliate_url}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="flex items-center gap-2 px-4 py-2.5 bg-accent text-background text-ui-sm font-medium rounded-xl
|
||
hover:bg-accent-hover transition-all"
|
||
>
|
||
Bid Now
|
||
<ExternalLink className="w-3.5 h-3.5" />
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)
|
||
) : (
|
||
/* Ending Soon */
|
||
<div className="grid gap-4">
|
||
{endingSoon.length === 0 ? (
|
||
<div className="text-center py-12 text-foreground-muted">
|
||
No auctions ending soon
|
||
</div>
|
||
) : (
|
||
endingSoon.map((auction, idx) => (
|
||
<div
|
||
key={`${auction.domain}-${idx}`}
|
||
className="p-5 bg-background-secondary/50 border border-warning/20 rounded-xl"
|
||
>
|
||
<div className="flex flex-col lg:flex-row lg:items-center justify-between gap-4">
|
||
<div className="flex items-center gap-4">
|
||
<div className="w-10 h-10 bg-warning-muted rounded-xl flex items-center justify-center">
|
||
<Timer className="w-5 h-5 text-warning" />
|
||
</div>
|
||
<div>
|
||
<span className="font-mono text-body-lg font-medium text-foreground block">
|
||
{auction.domain}
|
||
</span>
|
||
<span className="text-body-sm text-foreground-muted">
|
||
{auction.num_bids} bids on {auction.platform}
|
||
</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-6">
|
||
<div className="text-right">
|
||
<p className="text-heading-sm font-medium text-foreground">
|
||
{formatCurrency(auction.current_bid)}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="text-right">
|
||
<p className={clsx(
|
||
"text-body-lg font-bold",
|
||
getTimeColor(auction.time_remaining)
|
||
)}>
|
||
{auction.time_remaining}
|
||
</p>
|
||
</div>
|
||
|
||
<a
|
||
href={auction.affiliate_url}
|
||
target="_blank"
|
||
rel="noopener noreferrer"
|
||
className="flex items-center gap-2 px-4 py-2.5 bg-warning text-background text-ui-sm font-medium rounded-xl
|
||
hover:bg-warning/90 transition-all"
|
||
>
|
||
Snipe Now
|
||
<ExternalLink className="w-3.5 h-3.5" />
|
||
</a>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
))
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{/* Disclaimer */}
|
||
<div className="mt-12 p-5 bg-background-secondary/30 border border-border rounded-xl">
|
||
<h4 className="text-body-sm font-medium text-foreground mb-2">How Smart Pounce Works</h4>
|
||
<p className="text-body-sm text-foreground-subtle">
|
||
We aggregate domain auctions from multiple platforms (GoDaddy, Sedo, NameJet, etc.)
|
||
and display them in one place. When you click "Bid Now", you're taken directly to
|
||
the auction platform — we don't handle any payments or domain transfers.
|
||
This keeps things simple and compliant with Swiss regulations.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<Footer />
|
||
</div>
|
||
)
|
||
}
|
||
|