feat: Complete redesign of Hunt tabs with award-winning UI
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

- DropsTab: Fixed domain display, added alert banner explaining zone file data
- AuctionsTab: Improved table layout, cleaner action buttons
- Both: Consistent header stats, unified search, better filters
This commit is contained in:
2025-12-18 11:07:57 +01:00
parent a70439c51a
commit 6001676058
2 changed files with 490 additions and 334 deletions

View File

@ -23,6 +23,10 @@ import {
Filter,
Shield,
Gavel,
Clock,
DollarSign,
Timer,
CheckCircle2,
} from 'lucide-react'
import clsx from 'clsx'
@ -277,130 +281,117 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
const activeFiltersCount = [sourceFilter !== 'all', priceRange !== 'all', tldFilter !== 'all', hideSpam].filter(Boolean).length
// Loading State
if (loading && items.length === 0) {
return (
<div className="flex items-center justify-center py-20">
<Loader2 className="w-6 h-6 text-accent animate-spin" />
<div className="flex flex-col items-center justify-center py-24">
<Loader2 className="w-8 h-8 text-accent animate-spin mb-4" />
<span className="text-xs font-mono text-white/30 uppercase tracking-widest">Loading marketplace...</span>
</div>
)
}
return (
<div className="space-y-4">
<div className="space-y-6">
{/* Header Stats */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-accent/10 border border-accent/20 flex items-center justify-center">
<Gavel className="w-5 h-5 text-accent" />
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-gradient-to-br from-accent/20 to-accent/5 border border-accent/30 flex items-center justify-center">
<Gavel className="w-6 h-6 text-accent" />
</div>
<div>
<div className="text-xl font-bold text-white font-mono">
<div className="text-2xl font-black text-white font-mono tracking-tight">
{stats.total.toLocaleString()}
</div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-wider">Live auctions & fixed price</div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-widest">Live auctions & listings</div>
</div>
</div>
<button
onClick={handleRefresh}
disabled={refreshing}
className="p-2 border border-white/10 text-white/30 hover:text-white hover:bg-white/5 transition-colors"
className="p-3 border border-white/10 text-white/40 hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
title="Refresh auctions"
>
<RefreshCw className={clsx("w-4 h-4", refreshing && "animate-spin")} />
<RefreshCw className={clsx("w-5 h-5", refreshing && "animate-spin")} />
</button>
</div>
{/* Search */}
<div className={clsx(
"relative border transition-all duration-200",
"relative border-2 transition-all duration-200",
searchFocused ? "border-accent/50 bg-accent/[0.02]" : "border-white/[0.08] bg-white/[0.02]"
)}>
<div className="flex items-center">
<Search className={clsx("w-4 h-4 ml-3 transition-colors", searchFocused ? "text-accent" : "text-white/30")} />
<Search className={clsx("w-5 h-5 ml-4 transition-colors", searchFocused ? "text-accent" : "text-white/30")} />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onFocus={() => setSearchFocused(true)}
onBlur={() => setSearchFocused(false)}
placeholder="Search auctions..."
className="flex-1 bg-transparent px-3 py-3 text-sm text-white placeholder:text-white/20 outline-none font-mono"
placeholder="Search auctions and listings..."
className="flex-1 bg-transparent px-4 py-4 text-sm text-white placeholder:text-white/20 outline-none font-mono"
/>
{searchQuery && (
<button onClick={() => setSearchQuery('')} className="p-3 text-white/30 hover:text-white transition-colors">
<X className="w-4 h-4" />
<button onClick={() => setSearchQuery('')} className="p-4 text-white/30 hover:text-white transition-colors">
<X className="w-5 h-5" />
</button>
)}
</div>
</div>
{/* Filter Toggle */}
{/* Advanced Filters */}
<button
onClick={() => setFiltersOpen(!filtersOpen)}
className={clsx(
"flex items-center justify-between w-full py-2.5 px-4 border transition-colors",
filtersOpen ? "border-accent/30 bg-accent/[0.05]" : "border-white/[0.08] bg-white/[0.02]"
"flex items-center justify-between w-full py-3 px-5 border transition-all",
filtersOpen ? "border-accent/30 bg-accent/[0.03]" : "border-white/[0.08] bg-white/[0.02] hover:border-white/20"
)}
>
<div className="flex items-center gap-2">
<Filter className="w-4 h-4 text-white/40" />
<span className="text-xs font-mono text-white/60 uppercase tracking-widest">Market Filters</span>
{activeFiltersCount > 0 && <span className="px-1.5 py-0.5 text-[9px] font-bold bg-accent text-black ml-1">{activeFiltersCount}</span>}
<div className="flex items-center gap-3">
<Filter className={clsx("w-4 h-4", filtersOpen ? "text-accent" : "text-white/40")} />
<span className={clsx("text-xs font-mono uppercase tracking-widest", filtersOpen ? "text-accent" : "text-white/50")}>
Market Filters
</span>
{activeFiltersCount > 0 && (
<span className="px-2 py-0.5 text-[9px] font-black bg-accent text-black">{activeFiltersCount}</span>
)}
</div>
<ChevronRight className={clsx("w-4 h-4 text-white/30 transition-transform", filtersOpen && "rotate-90")} />
<ChevronRight className={clsx("w-4 h-4 transition-transform", filtersOpen ? "rotate-90 text-accent" : "text-white/30")} />
</button>
{/* Filters Panel */}
{filtersOpen && (
<div className="p-4 border border-white/[0.08] bg-white/[0.02] space-y-4 animate-in fade-in slide-in-from-top-2 duration-200">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Source */}
<div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Source</div>
<div className="flex gap-2">
{[
{ value: 'all', label: 'All' },
{ value: 'pounce', label: 'Pounce', icon: Diamond },
{ value: 'external', label: 'External' },
].map((item) => (
<button
key={item.value}
onClick={() => setSourceFilter(item.value as SourceFilter)}
className={clsx(
"flex-1 py-2 text-[10px] font-bold uppercase tracking-wider border transition-colors flex items-center justify-center gap-1.5",
sourceFilter === item.value ? "border-accent bg-accent/10 text-accent font-bold" : "border-white/[0.08] text-white/40"
)}
>
{item.icon && <item.icon className="w-3 h-3" />}
{item.label}
</button>
))}
</div>
</div>
{/* TLD */}
<div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Quick TLD</div>
<div className="flex gap-2 flex-wrap">
{['all', 'com', 'ai', 'io', 'net'].map((tld) => (
<button
key={tld}
onClick={() => setTldFilter(tld)}
className={clsx(
"px-3 py-2 text-[10px] font-mono uppercase border transition-colors",
tldFilter === tld ? "border-accent bg-accent/10 text-accent font-bold" : "border-white/[0.08] text-white/40"
)}
>
{tld === 'all' ? 'All' : `.${tld}`}
</button>
))}
</div>
<div className="p-5 border border-white/[0.08] bg-white/[0.01] space-y-5 animate-in fade-in slide-in-from-top-2 duration-200">
{/* Source Filter */}
<div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-widest mb-3">Source</div>
<div className="flex gap-2">
{[
{ value: 'all', label: 'All Sources' },
{ value: 'pounce', label: 'Pounce Direct', icon: Diamond },
{ value: 'external', label: 'External' },
].map((item) => (
<button
key={item.value}
onClick={() => setSourceFilter(item.value as SourceFilter)}
className={clsx(
"flex-1 py-3 text-xs font-bold uppercase tracking-wider border transition-all flex items-center justify-center gap-2",
sourceFilter === item.value
? "border-accent bg-accent/10 text-accent"
: "border-white/[0.08] text-white/40 hover:border-white/20"
)}
>
{item.icon && <item.icon className="w-4 h-4" />}
{item.label}
</button>
))}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Price */}
{/* Price & TLD */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-5">
<div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Price Range</div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-widest mb-3">Price Range</div>
<div className="flex gap-2">
{[
{ value: 'all', label: 'All' },
@ -412,8 +403,10 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
key={item.value}
onClick={() => setPriceRange(item.value as PriceRange)}
className={clsx(
"flex-1 py-2 text-[10px] font-mono border transition-colors",
priceRange === item.value ? "border-amber-400 bg-amber-400/10 text-amber-400 font-bold" : "border-white/[0.08] text-white/40"
"flex-1 py-3 text-xs font-mono border transition-all",
priceRange === item.value
? "border-amber-400 bg-amber-400/10 text-amber-400 font-bold"
: "border-white/[0.08] text-white/40 hover:border-white/20"
)}
>
{item.label}
@ -422,74 +415,116 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
</div>
</div>
{/* Spam Filter */}
<div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Visibility</div>
<button
onClick={() => setHideSpam(!hideSpam)}
className={clsx(
"flex items-center justify-between w-full py-2 px-4 border transition-colors",
hideSpam ? "border-accent/30 bg-accent/5" : "border-white/[0.08]"
)}
>
<div className="flex items-center gap-2.5">
<Ban className={clsx("w-3.5 h-3.5", hideSpam ? "text-accent" : "text-white/30")} />
<span className={clsx("text-[10px] font-mono uppercase tracking-wider", hideSpam ? "text-accent" : "text-white/50")}>Hide Spam Domains</span>
</div>
<div className={clsx("w-3.5 h-3.5 border flex items-center justify-center", hideSpam ? "border-accent bg-accent" : "border-white/20")}>
{hideSpam && <span className="text-black text-[8px] font-bold"></span>}
</div>
</button>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-widest mb-3">TLD Filter</div>
<div className="flex gap-2 flex-wrap">
{['all', 'com', 'ai', 'io', 'net', 'ch'].map((tld) => (
<button
key={tld}
onClick={() => setTldFilter(tld)}
className={clsx(
"px-4 py-3 text-xs font-mono uppercase border transition-all",
tldFilter === tld
? "border-accent bg-accent/10 text-accent font-bold"
: "border-white/[0.08] text-white/40 hover:border-white/20"
)}
>
{tld === 'all' ? 'All' : `.${tld}`}
</button>
))}
</div>
</div>
</div>
{/* Hide Spam */}
<button
onClick={() => setHideSpam(!hideSpam)}
className={clsx(
"flex items-center justify-between w-full py-3 px-4 border transition-all",
hideSpam ? "border-accent/30 bg-accent/5" : "border-white/[0.08] hover:border-white/20"
)}
>
<div className="flex items-center gap-3">
<Ban className={clsx("w-4 h-4", hideSpam ? "text-accent" : "text-white/30")} />
<span className={clsx("text-xs font-mono uppercase tracking-wider", hideSpam ? "text-accent" : "text-white/50")}>
Hide spam domains (numbers, hyphens)
</span>
</div>
<div className={clsx(
"w-5 h-5 border flex items-center justify-center transition-all",
hideSpam ? "border-accent bg-accent" : "border-white/20"
)}>
{hideSpam && <CheckCircle2 className="w-3 h-3 text-black" />}
</div>
</button>
</div>
)}
{/* Results Header */}
<div className="flex items-center justify-between px-1 text-[10px] font-mono text-white/30 uppercase tracking-[0.1em]">
<div className="flex items-center gap-2">
<div className="w-1 h-1 bg-accent rounded-full animate-pulse" />
<span>{filteredItems.length} active listings found</span>
</div>
<div className="flex items-center gap-3">
<span className="text-accent">{stats.highScore} premium assets</span>
{totalPages > 1 && <span>Page {page} / {totalPages}</span>}
{/* Results Info */}
<div className="flex items-center justify-between px-1">
<div className="flex items-center gap-3 text-[11px] font-mono text-white/40 uppercase tracking-widest">
<div className="w-1.5 h-1.5 bg-accent rounded-full animate-pulse" />
<span>{filteredItems.length} active listings</span>
{stats.highScore > 0 && (
<>
<span className="text-white/20"></span>
<span className="text-accent">{stats.highScore} premium</span>
</>
)}
</div>
{totalPages > 1 && (
<span className="text-[11px] font-mono text-white/30 uppercase tracking-widest">
Page {page} of {totalPages}
</span>
)}
</div>
{/* Results Table */}
{filteredItems.length === 0 ? (
<div className="text-center py-24 border border-dashed border-white/[0.08] bg-white/[0.01]">
<Search className="w-12 h-12 text-white/5 mx-auto mb-4" />
<p className="text-white/40 text-sm font-mono uppercase tracking-widest font-bold">No assets found</p>
<p className="text-white/20 text-[10px] font-mono mt-3 uppercase tracking-wider max-w-xs mx-auto leading-relaxed">
<Search className="w-16 h-16 text-white/5 mx-auto mb-6" />
<p className="text-white/50 text-sm font-mono uppercase tracking-widest font-bold">No auctions found</p>
<p className="text-white/20 text-xs font-mono mt-3 uppercase tracking-wider max-w-sm mx-auto leading-relaxed">
Try adjusting your search criteria or filters
</p>
</div>
) : (
<>
<div className="border border-white/[0.08] bg-white/[0.01] overflow-hidden">
{/* Desktop Table Header */}
<div className="hidden lg:grid grid-cols-[1fr_80px_100px_100px_140px] gap-4 px-5 py-3 text-[10px] font-mono text-white/30 uppercase tracking-[0.2em] border-b border-white/[0.08] bg-white/[0.02]">
<button onClick={() => handleSort('domain')} className="flex items-center gap-2 hover:text-white transition-colors text-left group">
<span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain Asset</span>
<div className="border border-white/[0.08] bg-[#020202] overflow-hidden">
{/* Table Header */}
<div className="hidden lg:grid grid-cols-[1fr_80px_120px_100px_180px] gap-6 px-6 py-4 text-[10px] font-mono text-white/40 uppercase tracking-[0.15em] border-b border-white/[0.08] bg-white/[0.02]">
<button
onClick={() => handleSort('domain')}
className="flex items-center gap-2 hover:text-white transition-colors text-left"
>
<span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain</span>
{sortField === 'domain' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<button onClick={() => handleSort('score')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
<button
onClick={() => handleSort('score')}
className="flex items-center gap-2 justify-center hover:text-white transition-colors"
>
<span className={clsx(sortField === 'score' && "text-accent font-bold")}>Score</span>
{sortField === 'score' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<button onClick={() => handleSort('price')} className="flex items-center gap-2 justify-end hover:text-white transition-colors group pr-4">
<button
onClick={() => handleSort('price')}
className="flex items-center gap-2 justify-end hover:text-white transition-colors"
>
<span className={clsx(sortField === 'price' && "text-accent font-bold")}>Price</span>
{sortField === 'price' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<button onClick={() => handleSort('time')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
<span className={clsx(sortField === 'time' && "text-accent font-bold")}>Time</span>
<button
onClick={() => handleSort('time')}
className="flex items-center gap-2 justify-center hover:text-white transition-colors"
>
<span className={clsx(sortField === 'time' && "text-accent font-bold")}>Ends</span>
{sortField === 'time' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<div className="text-right pr-2">Actions</div>
<div className="text-right">Actions</div>
</div>
{/* Table Body */}
<div className="divide-y divide-white/[0.04]">
{filteredItems.map((item) => {
const timeLeftSec = getSecondsUntilEnd(item.end_time)
@ -500,28 +535,40 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
const isTracking = trackingInProgress === item.domain
return (
<div key={item.id} className={clsx("bg-[#020202] hover:bg-white/[0.02] transition-all group", isPounce && "bg-accent/[0.01]")}>
<div key={item.id} className={clsx("group hover:bg-white/[0.02] transition-all", isPounce && "bg-accent/[0.02]")}>
{/* Mobile Row */}
<div className="lg:hidden p-4">
<div className="flex items-start justify-between gap-3 mb-4">
<div className="flex flex-col min-w-0">
<button onClick={() => openAnalyze(item.domain)} className="text-base font-bold text-white font-mono truncate text-left tracking-tight">
<div className="lg:hidden p-5">
<div className="flex items-start justify-between gap-4 mb-4">
<div className="min-w-0">
<button
onClick={() => openAnalyze(item.domain)}
className="text-lg font-bold text-white font-mono truncate block text-left hover:text-accent transition-colors"
>
{item.domain}
</button>
<div className="flex items-center gap-2 mt-1.5 text-[9px] font-mono text-white/30 uppercase tracking-wider">
<span className="bg-white/5 px-1.5 py-0.5 border border-white/5">{item.source}</span>
<span className="text-white/10">|</span>
<span className={clsx(isUrgent && "text-orange-400 font-bold")}>{isPounce ? 'INSTANT' : displayTime || 'N/A'}</span>
<div className="flex items-center gap-2 mt-2 text-[10px] font-mono text-white/30 uppercase tracking-wider">
<span className="bg-white/5 px-2 py-0.5 border border-white/5">{item.source}</span>
{isPounce && item.verified && <ShieldCheck className="w-3.5 h-3.5 text-accent" />}
<span className={clsx(isUrgent && "text-orange-400 font-bold")}>
{isPounce ? 'INSTANT' : displayTime || 'N/A'}
</span>
</div>
</div>
<div className="text-right shrink-0">
<div className={clsx("text-base font-bold font-mono tracking-tight", isPounce ? "text-accent" : "text-white")}>{formatPrice(item.price)}</div>
<div className={clsx(
"text-[9px] font-mono px-1.5 py-0.5 mt-1 inline-block border",
item.pounce_score >= 80 ? "text-accent bg-accent/5 border-accent/20" : item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/5 border-amber-400/20" : "text-white/30 bg-white/5 border-white/5"
"text-lg font-black font-mono tracking-tight",
isPounce ? "text-accent" : "text-white"
)}>
SCORE: {item.pounce_score}
{formatPrice(item.price)}
</div>
<div className={clsx(
"text-[10px] font-mono px-2 py-0.5 mt-1 inline-block border",
item.pounce_score >= 80 ? "text-accent bg-accent/5 border-accent/20" :
item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/5 border-amber-400/20" :
"text-white/30 bg-white/5 border-white/5"
)}>
SCORE {item.pounce_score}
</div>
</div>
</div>
@ -531,81 +578,118 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
onClick={() => handleTrack(item.domain)}
disabled={isTracking}
className={clsx(
"flex-1 h-10 text-[10px] font-bold uppercase tracking-widest border flex items-center justify-center gap-2 transition-all",
isTracked ? "border-accent bg-accent/5 text-accent" : "border-white/10 text-white/40 hover:bg-white/5"
"flex-1 h-12 text-xs font-bold uppercase tracking-widest border flex items-center justify-center gap-2 transition-all",
isTracked
? "border-accent bg-accent/5 text-accent"
: "border-white/10 text-white/50 hover:bg-white/5"
)}
>
{isTracking ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : isTracked ? <Eye className="w-4 h-4" /> : <EyeOff className="w-4 h-4" />}
{isTracking ? <Loader2 className="w-4 h-4 animate-spin" /> : isTracked ? <Eye className="w-4 h-4" /> : <EyeOff className="w-4 h-4" />}
{isTracked ? 'Tracked' : 'Track'}
</button>
<button onClick={() => openAnalyze(item.domain)} className="w-12 h-10 border border-white/10 text-white/40 flex items-center justify-center hover:text-accent hover:border-accent/20 hover:bg-accent/5 transition-all">
<Shield className="w-4.5 h-4.5" />
<button
onClick={() => openAnalyze(item.domain)}
className="w-14 h-12 border border-white/10 text-white/50 flex items-center justify-center hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
>
<Shield className="w-5 h-5" />
</button>
<a
href={item.url}
target={isPounce ? '_self' : '_blank'}
rel={isPounce ? undefined : 'noopener noreferrer'}
className={clsx("flex-1 h-10 text-[10px] font-black uppercase tracking-widest flex items-center justify-center gap-1.5 transition-all", isPounce ? "bg-accent text-black" : "bg-white/10 text-white")}
className={clsx(
"flex-1 h-12 text-xs font-black uppercase tracking-widest flex items-center justify-center gap-2 transition-all",
isPounce ? "bg-accent text-black hover:bg-white" : "bg-white/10 text-white hover:bg-white/20"
)}
>
{isPounce ? 'Buy' : 'Bid'}
{!isPounce && <ExternalLink className="w-3.5 h-3.5" />}
{!isPounce && <ExternalLink className="w-4 h-4" />}
</a>
</div>
</div>
{/* Desktop Row */}
<div className="hidden lg:grid grid-cols-[1fr_80px_100px_100px_140px] gap-4 items-center px-5 py-3.5">
<div className="hidden lg:grid grid-cols-[1fr_80px_120px_100px_180px] gap-6 items-center px-6 py-4">
{/* Domain */}
<div className="flex items-center gap-3 min-w-0">
<button onClick={() => openAnalyze(item.domain)} className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left tracking-tight">
<button
onClick={() => openAnalyze(item.domain)}
className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left"
>
{item.domain}
</button>
<div className="flex items-center gap-2 text-[9px] font-mono text-white/20 uppercase tracking-widest opacity-0 group-hover:opacity-100 transition-opacity">
<div className="flex items-center gap-2 text-[9px] font-mono text-white/20 uppercase tracking-wider opacity-0 group-hover:opacity-100 transition-opacity">
<span>{item.source}</span>
{isPounce && item.verified && <ShieldCheck className="w-3 h-3 text-accent" />}
</div>
</div>
{/* Score */}
<div className="text-center">
<span className={clsx(
"text-[10px] font-mono font-bold px-2 py-0.5 border",
item.pounce_score >= 80 ? "text-accent bg-accent/5 border-accent/20" : item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/5 border-amber-400/20" : "text-white/30 bg-white/5 border-white/5"
"text-xs font-mono font-bold px-3 py-1 border inline-block",
item.pounce_score >= 80 ? "text-accent bg-accent/5 border-accent/20" :
item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/5 border-amber-400/20" :
"text-white/30 bg-white/5 border-white/5"
)}>
{item.pounce_score}
</span>
</div>
<div className="text-right pr-4">
<div className={clsx("font-mono text-sm font-bold tracking-tight", isPounce ? "text-accent" : "text-white")}>{formatPrice(item.price)}</div>
<div className="text-[9px] font-mono text-white/20 uppercase tracking-widest">{item.price_type === 'bid' ? 'BID' : 'BUY NOW'}</div>
{/* Price */}
<div className="text-right">
<div className={clsx(
"font-mono text-sm font-bold tracking-tight",
isPounce ? "text-accent" : "text-white"
)}>
{formatPrice(item.price)}
</div>
<div className="text-[9px] font-mono text-white/20 uppercase tracking-wider">
{item.price_type === 'bid' ? 'Current Bid' : 'Buy Now'}
</div>
</div>
{/* Time */}
<div className="text-center">
{isPounce ? (
<span className="text-[10px] text-accent font-mono font-bold flex items-center justify-center gap-1 uppercase tracking-widest">
<Zap className="w-3 h-3" />
<span className="text-xs text-accent font-mono font-bold flex items-center justify-center gap-1.5 uppercase tracking-wider">
<Zap className="w-3.5 h-3.5" />
Instant
</span>
) : (
<span className={clsx("text-[10px] font-mono uppercase tracking-widest", isUrgent ? "text-orange-400 font-bold" : "text-white/40")}>{displayTime || 'N/A'}</span>
<span className={clsx(
"text-xs font-mono uppercase tracking-wider flex items-center justify-center gap-1.5",
isUrgent ? "text-orange-400 font-bold" : "text-white/40"
)}>
{isUrgent && <Timer className="w-3.5 h-3.5" />}
{displayTime || 'N/A'}
</span>
)}
</div>
<div className="flex items-center justify-end gap-1.5 opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-x-2 group-hover:translate-x-0">
{/* Actions */}
<div className="flex items-center justify-end gap-2 opacity-40 group-hover:opacity-100 transition-all">
<button
onClick={() => handleTrack(item.domain)}
disabled={isTracking}
className={clsx(
"w-8.5 h-8.5 flex items-center justify-center border transition-all",
isTracked ? "bg-accent/5 text-accent border-accent/20 hover:bg-red-500/5 hover:text-red-400 hover:border-red-500/20" : "text-white/30 border-white/10 hover:text-white hover:bg-white/5"
"w-10 h-10 flex items-center justify-center border transition-all",
isTracked
? "bg-accent/5 text-accent border-accent/20 hover:bg-red-500/5 hover:text-red-400 hover:border-red-500/20"
: "text-white/50 border-white/10 hover:text-white hover:bg-white/5"
)}
title={isTracked ? "Untrack" : "Track Domain"}
>
{isTracking ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : isTracked ? <Eye className="w-4 h-4" /> : <EyeOff className="w-4 h-4" />}
{isTracking ? <Loader2 className="w-4 h-4 animate-spin" /> : isTracked ? <Eye className="w-4 h-4" /> : <EyeOff className="w-4 h-4" />}
</button>
<button onClick={() => openAnalyze(item.domain)} className="w-8.5 h-8.5 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/20 hover:bg-accent/5 transition-all" title="Deep Analysis">
<button
onClick={() => openAnalyze(item.domain)}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
title="Analyze"
>
<Shield className="w-4 h-4" />
</button>
@ -613,9 +697,15 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
href={item.url}
target={isPounce ? '_self' : '_blank'}
rel={isPounce ? undefined : 'noopener noreferrer'}
className={clsx("h-8.5 px-4 flex items-center gap-1.5 text-[10px] font-black uppercase tracking-widest transition-all shadow-[0_0_15px_-5px_rgba(34,211,126,0.4)]", isPounce ? "bg-accent text-black hover:bg-white" : "bg-white/10 text-white hover:bg-white/20")}
className={clsx(
"h-10 px-5 flex items-center gap-2 text-[10px] font-black uppercase tracking-widest transition-all",
isPounce
? "bg-accent text-black hover:bg-white"
: "bg-white/10 text-white hover:bg-white/20"
)}
>
{isPounce ? 'Buy' : 'Bid'}
{!isPounce && <ExternalLink className="w-3.5 h-3.5" />}
</a>
</div>
</div>
@ -627,23 +717,23 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-1 pt-6">
<div className="flex items-center justify-center gap-2 pt-4">
<button
onClick={() => handlePageChange(page - 1)}
disabled={page === 1}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
className="w-12 h-12 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
>
<ChevronLeft className="w-5 h-5" />
</button>
<div className="flex items-center bg-white/[0.02] border border-white/[0.08] px-5 h-10">
<span className="text-[11px] text-white/40 font-mono uppercase tracking-widest">
Page <span className="text-white font-bold">{page}</span> / {totalPages}
<div className="flex items-center bg-white/[0.02] border border-white/[0.08] px-6 h-12">
<span className="text-xs text-white/50 font-mono uppercase tracking-widest">
Page <span className="text-white font-bold mx-1">{page}</span> / {totalPages}
</span>
</div>
<button
onClick={() => handlePageChange(page + 1)}
disabled={page === totalPages}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
className="w-12 h-12 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
>
<ChevronRight className="w-5 h-5" />
</button>

View File

@ -21,6 +21,9 @@ import {
Filter,
Ban,
Hash,
CheckCircle2,
AlertCircle,
Clock,
} from 'lucide-react'
import clsx from 'clsx'
@ -43,8 +46,7 @@ interface ZoneStats {
daily_drops: number
}
// All supported TLDs
type SupportedTld = 'ch' | 'li' | 'xyz' | 'org' | 'online' | 'info' | 'dev' | 'app'
type SupportedTld = 'ch' | 'li' | 'xyz' | 'org' | 'online' | 'info' | 'dev' | 'app' | 'club' | 'biz'
const ALL_TLDS: { tld: SupportedTld; flag: string }[] = [
{ tld: 'ch', flag: '🇨🇭' },
@ -55,6 +57,8 @@ const ALL_TLDS: { tld: SupportedTld; flag: string }[] = [
{ tld: 'info', flag: '' },
{ tld: 'dev', flag: '👨‍💻' },
{ tld: 'app', flag: '📱' },
{ tld: 'club', flag: '🎯' },
{ tld: 'biz', flag: '💼' },
]
// ============================================================================
@ -107,7 +111,7 @@ export function DropsTab({ showToast }: DropsTabProps) {
}
}, [])
// Load Drops (only last 24h)
// Load Drops
const loadDrops = useCallback(async (currentPage = 1, isRefresh = false) => {
if (isRefresh) setRefreshing(true)
else setLoading(true)
@ -115,7 +119,7 @@ export function DropsTab({ showToast }: DropsTabProps) {
try {
const result = await api.getDrops({
tld: selectedTld || undefined,
hours: 24, // Only last 24h - fresh drops only!
hours: 24,
min_length: minLength,
max_length: maxLength,
exclude_numeric: excludeNumeric,
@ -139,7 +143,6 @@ export function DropsTab({ showToast }: DropsTabProps) {
}
}, [selectedTld, minLength, maxLength, excludeNumeric, excludeHyphen, searchQuery, showToast])
// Initial Load
useEffect(() => {
loadStats()
}, [loadStats])
@ -216,128 +219,137 @@ export function DropsTab({ showToast }: DropsTabProps) {
return `${diffH}h ago`
}
// Loading State
if (loading && items.length === 0) {
return (
<div className="flex items-center justify-center py-20">
<Loader2 className="w-6 h-6 text-accent animate-spin" />
<div className="flex flex-col items-center justify-center py-24">
<Loader2 className="w-8 h-8 text-accent animate-spin mb-4" />
<span className="text-xs font-mono text-white/30 uppercase tracking-widest">Loading zone file drops...</span>
</div>
)
}
return (
<div className="space-y-4">
<div className="space-y-6">
{/* Header Stats */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 bg-accent/10 border border-accent/20 flex items-center justify-center">
<Zap className="w-5 h-5 text-accent" />
<div className="flex items-center gap-4">
<div className="w-12 h-12 bg-gradient-to-br from-accent/20 to-accent/5 border border-accent/30 flex items-center justify-center">
<Zap className="w-6 h-6 text-accent" />
</div>
<div>
<div className="text-xl font-bold text-white font-mono">
<div className="text-2xl font-black text-white font-mono tracking-tight">
{stats?.daily_drops?.toLocaleString() || total.toLocaleString()}
</div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-wider">Fresh drops detected (24h)</div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-widest">Fresh drops in last 24h</div>
</div>
</div>
<button
onClick={handleRefresh}
disabled={refreshing}
className="p-2 border border-white/10 text-white/30 hover:text-white hover:bg-white/5 transition-colors"
className="p-3 border border-white/10 text-white/40 hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
title="Refresh drops"
>
<RefreshCw className={clsx("w-4 h-4", refreshing && "animate-spin")} />
<RefreshCw className={clsx("w-5 h-5", refreshing && "animate-spin")} />
</button>
</div>
{/* Search */}
<div className={clsx(
"relative border transition-all duration-200",
"relative border-2 transition-all duration-200",
searchFocused ? "border-accent/50 bg-accent/[0.02]" : "border-white/[0.08] bg-white/[0.02]"
)}>
<div className="flex items-center">
<Search className={clsx("w-4 h-4 ml-3 transition-colors", searchFocused ? "text-accent" : "text-white/30")} />
<Search className={clsx("w-5 h-5 ml-4 transition-colors", searchFocused ? "text-accent" : "text-white/30")} />
<input
type="text"
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
onFocus={() => setSearchFocused(true)}
onBlur={() => setSearchFocused(false)}
placeholder="Search drops..."
className="flex-1 bg-transparent px-3 py-3 text-sm text-white placeholder:text-white/20 outline-none font-mono"
placeholder="Search dropped domains..."
className="flex-1 bg-transparent px-4 py-4 text-sm text-white placeholder:text-white/20 outline-none font-mono"
/>
{searchQuery && (
<button onClick={() => setSearchQuery('')} className="p-3 text-white/30 hover:text-white transition-colors">
<X className="w-4 h-4" />
<button onClick={() => setSearchQuery('')} className="p-4 text-white/30 hover:text-white transition-colors">
<X className="w-5 h-5" />
</button>
)}
</div>
</div>
{/* TLD Quick Filter */}
<div className="flex gap-1.5 flex-wrap">
<div className="flex gap-2 flex-wrap">
<button
onClick={() => setSelectedTld(null)}
className={clsx(
"px-3 py-1.5 text-[10px] font-mono uppercase border transition-colors",
selectedTld === null ? "border-accent bg-accent/10 text-accent font-bold" : "border-white/[0.08] text-white/40"
"px-4 py-2.5 text-xs font-mono uppercase tracking-wider border transition-all",
selectedTld === null
? "border-accent bg-accent/10 text-accent font-bold"
: "border-white/[0.08] text-white/40 hover:border-white/20 hover:text-white/60"
)}
>
All
All TLDs
</button>
{ALL_TLDS.map(({ tld, flag }) => (
<button
key={tld}
onClick={() => setSelectedTld(tld)}
className={clsx(
"px-3 py-1.5 text-[10px] font-mono uppercase border transition-colors flex items-center gap-1.5",
selectedTld === tld ? "border-accent bg-accent/10 text-accent font-bold" : "border-white/[0.08] text-white/40"
"px-4 py-2.5 text-xs font-mono uppercase tracking-wider border transition-all flex items-center gap-2",
selectedTld === tld
? "border-accent bg-accent/10 text-accent font-bold"
: "border-white/[0.08] text-white/40 hover:border-white/20 hover:text-white/60"
)}
>
<span className="text-xs">{flag}</span>.{tld}
<span className="text-sm">{flag}</span>
.{tld}
</button>
))}
</div>
{/* Filter Toggle */}
{/* Advanced Filters */}
<button
onClick={() => setFiltersOpen(!filtersOpen)}
className={clsx(
"flex items-center justify-between w-full py-2.5 px-4 border transition-colors",
filtersOpen ? "border-accent/30 bg-accent/[0.05]" : "border-white/[0.08] bg-white/[0.02]"
"flex items-center justify-between w-full py-3 px-5 border transition-all",
filtersOpen ? "border-accent/30 bg-accent/[0.03]" : "border-white/[0.08] bg-white/[0.02] hover:border-white/20"
)}
>
<div className="flex items-center gap-2">
<Filter className="w-4 h-4 text-white/40" />
<span className="text-xs font-mono text-white/60 uppercase tracking-widest">Advanced Filters</span>
<div className="flex items-center gap-3">
<Filter className={clsx("w-4 h-4", filtersOpen ? "text-accent" : "text-white/40")} />
<span className={clsx("text-xs font-mono uppercase tracking-widest", filtersOpen ? "text-accent" : "text-white/50")}>
Advanced Filters
</span>
{activeFiltersCount > 0 && (
<span className="px-1.5 py-0.5 text-[9px] font-bold bg-accent text-black ml-1">{activeFiltersCount}</span>
<span className="px-2 py-0.5 text-[9px] font-black bg-accent text-black">{activeFiltersCount}</span>
)}
</div>
<ChevronRight className={clsx("w-4 h-4 text-white/30 transition-transform", filtersOpen && "rotate-90")} />
<ChevronRight className={clsx("w-4 h-4 transition-transform", filtersOpen ? "rotate-90 text-accent" : "text-white/30")} />
</button>
{/* Filters Panel */}
{filtersOpen && (
<div className="p-4 border border-white/[0.08] bg-white/[0.02] space-y-4 animate-in fade-in slide-in-from-top-2 duration-200">
<div className="p-5 border border-white/[0.08] bg-white/[0.01] space-y-5 animate-in fade-in slide-in-from-top-2 duration-200">
{/* Length Filter */}
<div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Domain Length</div>
<div className="flex gap-2 items-center">
<div className="text-[10px] font-mono text-white/40 uppercase tracking-widest mb-3">Domain Length</div>
<div className="flex gap-3 items-center">
<input
type="number"
value={minLength || ''}
onChange={(e) => setMinLength(e.target.value ? Number(e.target.value) : undefined)}
placeholder="Min"
className="w-20 bg-white/[0.02] border border-white/10 px-3 py-2 text-xs text-white placeholder:text-white/20 outline-none font-mono focus:border-accent/30"
className="w-24 bg-white/[0.02] border border-white/10 px-4 py-3 text-sm text-white placeholder:text-white/20 outline-none font-mono focus:border-accent/30 transition-colors"
min={1}
max={63}
/>
<span className="text-white/10 px-1 font-mono text-xs">TO</span>
<span className="text-white/20 font-mono text-xs">to</span>
<input
type="number"
value={maxLength || ''}
onChange={(e) => setMaxLength(e.target.value ? Number(e.target.value) : undefined)}
placeholder="Max"
className="w-20 bg-white/[0.02] border border-white/10 px-3 py-2 text-xs text-white placeholder:text-white/20 outline-none font-mono focus:border-accent/30"
className="w-24 bg-white/[0.02] border border-white/10 px-4 py-3 text-sm text-white placeholder:text-white/20 outline-none font-mono focus:border-accent/30 transition-colors"
min={1}
max={63}
/>
@ -345,205 +357,259 @@ export function DropsTab({ showToast }: DropsTabProps) {
</div>
{/* Quality Filters */}
<div className="flex gap-2">
<div className="grid grid-cols-2 gap-3">
<button
onClick={() => setExcludeNumeric(!excludeNumeric)}
className={clsx(
"flex-1 flex items-center justify-between py-2.5 px-4 border transition-colors",
excludeNumeric ? "border-accent/30 bg-accent/5" : "border-white/[0.08]"
"flex items-center justify-between py-3 px-4 border transition-all",
excludeNumeric ? "border-accent/30 bg-accent/5" : "border-white/[0.08] hover:border-white/20"
)}
>
<div className="flex items-center gap-2.5">
<Hash className={clsx("w-3.5 h-3.5", excludeNumeric ? "text-accent" : "text-white/30")} />
<span className={clsx("text-[10px] font-mono uppercase tracking-wider", excludeNumeric ? "text-accent" : "text-white/50")}>Exclude Numeric</span>
<div className="flex items-center gap-3">
<Hash className={clsx("w-4 h-4", excludeNumeric ? "text-accent" : "text-white/30")} />
<span className={clsx("text-xs font-mono uppercase tracking-wider", excludeNumeric ? "text-accent" : "text-white/50")}>
No Numbers
</span>
</div>
<div className={clsx("w-3.5 h-3.5 border flex items-center justify-center", excludeNumeric ? "border-accent bg-accent" : "border-white/20")}>
{excludeNumeric && <span className="text-black text-[8px] font-bold"></span>}
<div className={clsx(
"w-5 h-5 border flex items-center justify-center transition-all",
excludeNumeric ? "border-accent bg-accent" : "border-white/20"
)}>
{excludeNumeric && <CheckCircle2 className="w-3 h-3 text-black" />}
</div>
</button>
<button
onClick={() => setExcludeHyphen(!excludeHyphen)}
className={clsx(
"flex-1 flex items-center justify-between py-2.5 px-4 border transition-colors",
excludeHyphen ? "border-accent/30 bg-accent/5" : "border-white/[0.08]"
"flex items-center justify-between py-3 px-4 border transition-all",
excludeHyphen ? "border-accent/30 bg-accent/5" : "border-white/[0.08] hover:border-white/20"
)}
>
<div className="flex items-center gap-2.5">
<Ban className={clsx("w-3.5 h-3.5", excludeHyphen ? "text-accent" : "text-white/30")} />
<span className={clsx("text-[10px] font-mono uppercase tracking-wider", excludeHyphen ? "text-accent" : "text-white/50")}>Exclude Hyphen</span>
<div className="flex items-center gap-3">
<Ban className={clsx("w-4 h-4", excludeHyphen ? "text-accent" : "text-white/30")} />
<span className={clsx("text-xs font-mono uppercase tracking-wider", excludeHyphen ? "text-accent" : "text-white/50")}>
No Hyphens
</span>
</div>
<div className={clsx("w-3.5 h-3.5 border flex items-center justify-center", excludeHyphen ? "border-accent bg-accent" : "border-white/20")}>
{excludeHyphen && <span className="text-black text-[8px] font-bold"></span>}
<div className={clsx(
"w-5 h-5 border flex items-center justify-center transition-all",
excludeHyphen ? "border-accent bg-accent" : "border-white/20"
)}>
{excludeHyphen && <CheckCircle2 className="w-3 h-3 text-black" />}
</div>
</button>
</div>
</div>
)}
{/* Results Header */}
<div className="flex items-center justify-between px-1 text-[10px] font-mono text-white/30 uppercase tracking-[0.1em]">
<div className="flex items-center gap-2">
<div className="w-1 h-1 bg-accent rounded-full" />
<span>{total.toLocaleString()} domains matching criteria</span>
{/* Results Info */}
<div className="flex items-center justify-between px-1">
<div className="flex items-center gap-3 text-[11px] font-mono text-white/40 uppercase tracking-widest">
<div className="w-1.5 h-1.5 bg-accent rounded-full animate-pulse" />
<span>{total.toLocaleString()} domains detected</span>
</div>
{totalPages > 1 && (
<span className="text-[11px] font-mono text-white/30 uppercase tracking-widest">
Page {page} of {totalPages}
</span>
)}
</div>
{/* Alert Banner */}
<div className="flex items-start gap-3 p-4 border border-amber-500/20 bg-amber-500/5">
<AlertCircle className="w-5 h-5 text-amber-400 shrink-0 mt-0.5" />
<div>
<div className="text-xs font-bold text-amber-400 uppercase tracking-wider mb-1">Zone File Analysis</div>
<div className="text-[11px] text-white/50 leading-relaxed">
Domains detected as dropped via zone file comparison. Some may have been re-registered. Click "Check" to verify live availability.
</div>
</div>
{totalPages > 1 && <span>Page {page} of {totalPages}</span>}
</div>
{/* Results Table */}
{sortedItems.length === 0 ? (
<div className="text-center py-24 border border-dashed border-white/[0.08] bg-white/[0.01]">
<Globe className="w-12 h-12 text-white/5 mx-auto mb-4" />
<p className="text-white/40 text-sm font-mono uppercase tracking-widest font-bold">No fresh drops detected</p>
<p className="text-white/20 text-[10px] font-mono mt-3 uppercase tracking-wider max-w-xs mx-auto leading-relaxed">
The zone file comparison engine will update in the next 24h cycle
<Globe className="w-16 h-16 text-white/5 mx-auto mb-6" />
<p className="text-white/50 text-sm font-mono uppercase tracking-widest font-bold">No drops found</p>
<p className="text-white/20 text-xs font-mono mt-3 uppercase tracking-wider max-w-sm mx-auto leading-relaxed">
Zone file comparison runs daily. Try adjusting your filters.
</p>
</div>
) : (
<>
<div className="border border-white/[0.08] bg-white/[0.01] overflow-hidden">
{/* Desktop Table Header */}
<div className="hidden lg:grid grid-cols-[1fr_80px_100px_140px] gap-4 px-5 py-3 text-[10px] font-mono text-white/30 uppercase tracking-[0.2em] border-b border-white/[0.08] bg-white/[0.02]">
<button onClick={() => handleSort('domain')} className="flex items-center gap-2 hover:text-white transition-colors text-left group">
<span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain Name</span>
<div className="border border-white/[0.08] bg-[#020202] overflow-hidden">
{/* Table Header */}
<div className="hidden lg:grid grid-cols-[1fr_100px_120px_180px] gap-6 px-6 py-4 text-[10px] font-mono text-white/40 uppercase tracking-[0.15em] border-b border-white/[0.08] bg-white/[0.02]">
<button
onClick={() => handleSort('domain')}
className="flex items-center gap-2 hover:text-white transition-colors text-left"
>
<span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain</span>
{sortField === 'domain' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<button onClick={() => handleSort('length')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
<button
onClick={() => handleSort('length')}
className="flex items-center gap-2 justify-center hover:text-white transition-colors"
>
<span className={clsx(sortField === 'length' && "text-accent font-bold")}>Length</span>
{sortField === 'length' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<button onClick={() => handleSort('date')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
<span className={clsx(sortField === 'date' && "text-accent font-bold")}>Dropped</span>
<button
onClick={() => handleSort('date')}
className="flex items-center gap-2 justify-center hover:text-white transition-colors"
>
<span className={clsx(sortField === 'date' && "text-accent font-bold")}>Detected</span>
{sortField === 'date' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3 text-accent" /> : <ChevronDown className="w-3 h-3 text-accent" />)}
</button>
<div className="text-right pr-2">Actions</div>
<div className="text-right">Actions</div>
</div>
{/* Table Body */}
<div className="divide-y divide-white/[0.04]">
{sortedItems.map((item) => (
<div key={`${item.domain}.${item.tld}`} className="bg-[#020202] hover:bg-white/[0.02] transition-all group">
{/* Mobile Row */}
<div className="lg:hidden p-4">
<div className="flex items-center justify-between gap-3 mb-4">
<div className="flex flex-col min-w-0">
<button
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
className="text-base font-bold text-white font-mono truncate text-left tracking-tight"
>
{item.domain}<span className="text-white/30">.{item.tld}</span>
</button>
<div className="flex items-center gap-3 mt-1.5">
<span className={clsx(
"text-[9px] font-mono font-bold px-2 py-0.5 border",
item.length <= 5 ? "text-accent border-accent/20 bg-accent/5" : "text-white/30 border-white/5 bg-white/5"
)}>
LEN: {item.length}
</span>
<span className="text-[10px] font-mono text-white/20 uppercase tracking-wider">{formatTime(item.dropped_date)}</span>
{sortedItems.map((item) => {
const fullDomain = `${item.domain}.${item.tld}`
const isTracking = tracking === fullDomain
return (
<div key={fullDomain} className="group hover:bg-white/[0.02] transition-all">
{/* Mobile Row */}
<div className="lg:hidden p-5">
<div className="flex items-start justify-between gap-4 mb-4">
<div className="min-w-0">
<button
onClick={() => openAnalyze(fullDomain)}
className="text-lg font-bold text-white font-mono truncate block text-left hover:text-accent transition-colors"
>
{item.domain}<span className="text-white/30">.{item.tld}</span>
</button>
<div className="flex items-center gap-3 mt-2">
<span className={clsx(
"text-[10px] font-mono font-bold px-2.5 py-1 border",
item.length <= 4 ? "text-accent border-accent/20 bg-accent/5" :
item.length <= 6 ? "text-amber-400 border-amber-400/20 bg-amber-400/5" :
"text-white/40 border-white/10 bg-white/5"
)}>
{item.length} chars
</span>
<span className="text-[10px] font-mono text-white/30 uppercase flex items-center gap-1.5">
<Clock className="w-3 h-3" />
{formatTime(item.dropped_date)}
</span>
</div>
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => track(fullDomain)}
disabled={isTracking}
className="flex-1 h-12 text-xs font-bold uppercase tracking-widest border border-white/10 text-white/50 flex items-center justify-center gap-2 hover:bg-white/5 active:scale-[0.98] transition-all"
>
{isTracking ? <Loader2 className="w-4 h-4 animate-spin" /> : <Eye className="w-4 h-4" />}
Track
</button>
<button
onClick={() => openAnalyze(fullDomain)}
className="w-14 h-12 border border-white/10 text-white/50 flex items-center justify-center hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
>
<Shield className="w-5 h-5" />
</button>
<a
href={`https://www.namecheap.com/domains/registration/results/?domain=${fullDomain}`}
target="_blank"
rel="noopener noreferrer"
className="flex-1 h-12 bg-accent text-black text-xs font-black uppercase tracking-widest flex items-center justify-center gap-2 hover:bg-white active:scale-[0.98] transition-all"
>
Check & Buy
</a>
</div>
</div>
<div className="flex gap-2">
<button
onClick={() => track(`${item.domain}.${item.tld}`)}
disabled={tracking === `${item.domain}.${item.tld}`}
className="flex-1 h-10 text-[10px] font-bold uppercase tracking-widest border border-white/10 text-white/40 flex items-center justify-center gap-2 hover:bg-white/5 active:scale-95 transition-all"
>
{tracking === `${item.domain}.${item.tld}` ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-4 h-4" />}
Track
</button>
<button
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
className="w-12 h-10 border border-white/10 text-white/40 flex items-center justify-center hover:text-accent hover:border-accent/20 hover:bg-accent/5 transition-all"
>
<Shield className="w-4.5 h-4.5" />
</button>
<a
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`}
target="_blank"
rel="noopener noreferrer"
className="flex-1 h-10 bg-accent text-black text-[10px] font-black uppercase tracking-widest flex items-center justify-center gap-1.5 hover:bg-white active:scale-95 transition-all"
>
Buy Now
</a>
{/* Desktop Row */}
<div className="hidden lg:grid grid-cols-[1fr_100px_120px_180px] gap-6 items-center px-6 py-4">
{/* Domain */}
<div className="min-w-0">
<button
onClick={() => openAnalyze(fullDomain)}
className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left block"
>
{item.domain}<span className="text-white/30 group-hover:text-accent/40">.{item.tld}</span>
</button>
</div>
{/* Length */}
<div className="text-center">
<span className={clsx(
"text-xs font-mono font-bold px-3 py-1 border inline-block",
item.length <= 4 ? "text-accent border-accent/20 bg-accent/5" :
item.length <= 6 ? "text-amber-400 border-amber-400/20 bg-amber-400/5" :
"text-white/40 border-white/10 bg-white/5"
)}>
{item.length}
</span>
</div>
{/* Time */}
<div className="text-center">
<span className="text-xs font-mono text-white/40 uppercase tracking-wider">
{formatTime(item.dropped_date)}
</span>
</div>
{/* Actions */}
<div className="flex items-center justify-end gap-2 opacity-40 group-hover:opacity-100 transition-all">
<button
onClick={() => track(fullDomain)}
disabled={isTracking}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 transition-all"
title="Add to Watchlist"
>
{isTracking ? <Loader2 className="w-4 h-4 animate-spin" /> : <Eye className="w-4 h-4" />}
</button>
<button
onClick={() => openAnalyze(fullDomain)}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
title="Analyze Domain"
>
<Shield className="w-4 h-4" />
</button>
<a
href={`https://www.namecheap.com/domains/registration/results/?domain=${fullDomain}`}
target="_blank"
rel="noopener noreferrer"
className="h-10 px-5 bg-accent text-black text-[10px] font-black uppercase tracking-widest flex items-center gap-2 hover:bg-white transition-all"
>
Check & Buy
<ExternalLink className="w-3.5 h-3.5" />
</a>
</div>
</div>
</div>
{/* Desktop Row */}
<div className="hidden lg:grid grid-cols-[1fr_80px_100px_140px] gap-4 items-center px-5 py-3.5">
<div className="flex items-center gap-3 min-w-0">
<button
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left tracking-tight"
>
{item.domain}<span className="text-white/30 group-hover:text-accent/40">.{item.tld}</span>
</button>
</div>
<div className="text-center">
<span className={clsx(
"text-[10px] font-mono font-bold px-2 py-0.5 border",
item.length <= 5 ? "text-accent border-accent/20 bg-accent/5" : item.length <= 8 ? "text-amber-400 border-amber-400/20 bg-amber-400/5" : "text-white/30 border-white/5 bg-white/5"
)}>
{item.length}
</span>
</div>
<div className="text-center">
<span className="text-[10px] font-mono text-white/40 uppercase tracking-wider">{formatTime(item.dropped_date)}</span>
</div>
<div className="flex items-center justify-end gap-1.5 opacity-0 group-hover:opacity-100 transition-all duration-300 transform translate-x-2 group-hover:translate-x-0">
<button
onClick={() => track(`${item.domain}.${item.tld}`)}
disabled={tracking === `${item.domain}.${item.tld}`}
className="w-8.5 h-8.5 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5 transition-all"
title="Add to Watchlist"
>
{tracking === `${item.domain}.${item.tld}` ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-4 h-4" />}
</button>
<button
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
className="w-8.5 h-8.5 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/30 hover:bg-accent/5 transition-all"
title="Deep Analysis"
>
<Shield className="w-4 h-4" />
</button>
<a
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`}
target="_blank"
rel="noopener noreferrer"
className="h-8.5 px-4 bg-accent text-black text-[10px] font-black uppercase tracking-widest flex items-center gap-1.5 hover:bg-white transition-all shadow-[0_0_15px_-5px_rgba(34,211,126,0.4)]"
>
Buy
</a>
</div>
</div>
</div>
))}
)
})}
</div>
</div>
{/* Pagination */}
{totalPages > 1 && (
<div className="flex items-center justify-center gap-1 pt-6">
<div className="flex items-center justify-center gap-2 pt-4">
<button
onClick={() => handlePageChange(page - 1)}
disabled={page === 1}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
className="w-12 h-12 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
>
<ChevronLeft className="w-5 h-5" />
</button>
<div className="flex items-center bg-white/[0.02] border border-white/[0.08] px-5 h-10">
<span className="text-[11px] text-white/40 font-mono uppercase tracking-widest">
Page <span className="text-white font-bold">{page}</span> / {totalPages}
<div className="flex items-center bg-white/[0.02] border border-white/[0.08] px-6 h-12">
<span className="text-xs text-white/50 font-mono uppercase tracking-widest">
Page <span className="text-white font-bold mx-1">{page}</span> / {totalPages}
</span>
</div>
<button
onClick={() => handlePageChange(page + 1)}
disabled={page === totalPages}
className="w-10 h-10 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
className="w-12 h-12 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-20 disabled:cursor-not-allowed transition-all"
>
<ChevronRight className="w-5 h-5" />
</button>