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

View File

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