Yves Gugger b4c853f5a6
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled
fix: Use correct getAuctions parameters in market page
2025-12-10 20:01:09 +01:00

253 lines
9.2 KiB
TypeScript

'use client'
import { useEffect, useState } from 'react'
import { useRouter } from 'next/navigation'
import { useStore } from '@/lib/store'
import { api } from '@/lib/api'
import { CommandCenterLayout } from '@/components/CommandCenterLayout'
import {
Search,
Filter,
Clock,
TrendingUp,
Flame,
Sparkles,
ExternalLink,
ChevronDown,
Globe,
Gavel,
ArrowUpDown,
} from 'lucide-react'
import clsx from 'clsx'
type ViewType = 'all' | 'ending' | 'hot' | 'opportunities'
interface Auction {
domain: string
platform: string
current_bid: number
num_bids: number
end_time: string
time_remaining: string
affiliate_url: string
tld: string
}
export default function MarketPage() {
const router = useRouter()
const { subscription } = useStore()
const [auctions, setAuctions] = useState<Auction[]>([])
const [loading, setLoading] = useState(true)
const [activeView, setActiveView] = useState<ViewType>('all')
const [searchQuery, setSearchQuery] = useState('')
const [platformFilter, setPlatformFilter] = useState<string>('all')
const [sortBy, setSortBy] = useState<string>('end_time')
useEffect(() => {
loadAuctions()
}, [activeView])
const loadAuctions = async () => {
setLoading(true)
try {
let data
switch (activeView) {
case 'ending':
data = await api.getEndingSoonAuctions(50)
break
case 'hot':
data = await api.getHotAuctions(50)
break
case 'opportunities':
const oppData = await api.getAuctionOpportunities()
data = oppData.opportunities || []
break
default:
data = await api.getAuctions(undefined, undefined, undefined, undefined, undefined, false, sortBy, 50)
}
setAuctions(data)
} catch (error) {
console.error('Failed to load auctions:', error)
setAuctions([])
} finally {
setLoading(false)
}
}
// Filter auctions
const filteredAuctions = auctions.filter(auction => {
if (searchQuery && !auction.domain.toLowerCase().includes(searchQuery.toLowerCase())) {
return false
}
if (platformFilter !== 'all' && auction.platform !== platformFilter) {
return false
}
return true
})
const platforms = ['GoDaddy', 'Sedo', 'NameJet', 'DropCatch', 'ExpiredDomains']
const views = [
{ id: 'all', label: 'All Auctions', icon: Gavel },
{ id: 'ending', label: 'Ending Soon', icon: Clock },
{ id: 'hot', label: 'Hot', icon: Flame },
{ id: 'opportunities', label: 'Opportunities', icon: Sparkles },
]
return (
<CommandCenterLayout
title="Market Scanner"
subtitle="Live auctions from all major platforms"
>
<div className="max-w-7xl mx-auto space-y-6">
{/* View Tabs */}
<div className="flex flex-wrap gap-2 p-1 bg-background-secondary/50 rounded-xl border border-border">
{views.map((view) => (
<button
key={view.id}
onClick={() => setActiveView(view.id as ViewType)}
className={clsx(
"flex items-center gap-2 px-4 py-2.5 rounded-lg text-sm font-medium transition-all",
activeView === view.id
? "bg-foreground text-background"
: "text-foreground-muted hover:text-foreground hover:bg-foreground/5"
)}
>
<view.icon className="w-4 h-4" />
{view.label}
</button>
))}
</div>
{/* Filters */}
<div className="flex flex-col sm:flex-row gap-3">
{/* Search */}
<div className="relative flex-1">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-foreground-muted" />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
placeholder="Search domains..."
className="w-full h-10 pl-10 pr-4 bg-background-secondary border border-border rounded-lg
text-sm text-foreground placeholder:text-foreground-subtle
focus:outline-none focus:border-accent"
/>
</div>
{/* Platform Filter */}
<div className="relative">
<select
value={platformFilter}
onChange={(e) => setPlatformFilter(e.target.value)}
className="h-10 pl-4 pr-10 bg-background-secondary border border-border rounded-lg
text-sm text-foreground appearance-none cursor-pointer
focus:outline-none focus:border-accent"
>
<option value="all">All Platforms</option>
{platforms.map((p) => (
<option key={p} value={p}>{p}</option>
))}
</select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-foreground-muted pointer-events-none" />
</div>
{/* Sort */}
<div className="relative">
<select
value={sortBy}
onChange={(e) => {
setSortBy(e.target.value)
loadAuctions()
}}
className="h-10 pl-4 pr-10 bg-background-secondary border border-border rounded-lg
text-sm text-foreground appearance-none cursor-pointer
focus:outline-none focus:border-accent"
>
<option value="end_time">Ending Soon</option>
<option value="bid_asc">Price: Low to High</option>
<option value="bid_desc">Price: High to Low</option>
<option value="bids">Most Bids</option>
</select>
<ArrowUpDown className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-foreground-muted pointer-events-none" />
</div>
</div>
{/* Stats Bar */}
<div className="flex items-center gap-6 text-sm text-foreground-muted">
<span>{filteredAuctions.length} auctions</span>
<span></span>
<span className="flex items-center gap-1.5">
<Globe className="w-3.5 h-3.5" />
{platforms.length} platforms
</span>
</div>
{/* Auction List */}
{loading ? (
<div className="space-y-3">
{[...Array(5)].map((_, i) => (
<div key={i} className="h-20 bg-background-secondary/50 border border-border rounded-xl animate-pulse" />
))}
</div>
) : filteredAuctions.length === 0 ? (
<div className="text-center py-16 bg-background-secondary/30 border border-border rounded-xl">
<Gavel className="w-12 h-12 text-foreground-subtle mx-auto mb-4" />
<p className="text-foreground-muted">No auctions found</p>
<p className="text-sm text-foreground-subtle mt-1">Try adjusting your filters</p>
</div>
) : (
<div className="space-y-3">
{filteredAuctions.map((auction, idx) => (
<div
key={`${auction.domain}-${idx}`}
className="group p-4 sm:p-5 bg-background-secondary/50 border border-border rounded-xl
hover:border-foreground/20 transition-all"
>
<div className="flex flex-col sm:flex-row sm:items-center gap-4">
{/* Domain Info */}
<div className="flex-1 min-w-0">
<div className="flex items-center gap-3 mb-1">
<h3 className="text-lg font-semibold text-foreground truncate">{auction.domain}</h3>
<span className="shrink-0 px-2 py-0.5 bg-foreground/5 text-foreground-muted text-xs rounded">
{auction.platform}
</span>
</div>
<div className="flex items-center gap-4 text-sm text-foreground-muted">
<span className="flex items-center gap-1.5">
<Clock className="w-3.5 h-3.5" />
{auction.time_remaining}
</span>
<span>{auction.num_bids} bids</span>
</div>
</div>
{/* Price + Action */}
<div className="flex items-center gap-4 shrink-0">
<div className="text-right">
<p className="text-xl font-semibold text-foreground">${auction.current_bid.toLocaleString()}</p>
<p className="text-xs text-foreground-subtle">Current bid</p>
</div>
<a
href={auction.affiliate_url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-2 px-4 py-2.5 bg-foreground text-background rounded-lg
font-medium text-sm hover:bg-foreground/90 transition-colors"
>
Bid
<ExternalLink className="w-3.5 h-3.5" />
</a>
</div>
</div>
</div>
))}
</div>
)}
</div>
</CommandCenterLayout>
)
}