From 6001676058a3be4b65bfba5d8b4ba144120a7862 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Thu, 18 Dec 2025 11:07:57 +0100 Subject: [PATCH] feat: Complete redesign of Hunt tabs with award-winning UI - 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 --- frontend/src/components/hunt/AuctionsTab.tsx | 400 ++++++++++------- frontend/src/components/hunt/DropsTab.tsx | 424 +++++++++++-------- 2 files changed, 490 insertions(+), 334 deletions(-) diff --git a/frontend/src/components/hunt/AuctionsTab.tsx b/frontend/src/components/hunt/AuctionsTab.tsx index cb39653..5f39a70 100644 --- a/frontend/src/components/hunt/AuctionsTab.tsx +++ b/frontend/src/components/hunt/AuctionsTab.tsx @@ -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 ( -
- +
+ + Loading marketplace...
) } return ( -
+
{/* Header Stats */}
-
-
- +
+
+
-
+
{stats.total.toLocaleString()}
-
Live auctions & fixed price
+
Live auctions & listings
{/* Search */}
- + 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 && ( - )}
- {/* Filter Toggle */} + {/* Advanced Filters */} - {/* Filters Panel */} {filtersOpen && ( -
-
- {/* Source */} -
-
Source
-
- {[ - { value: 'all', label: 'All' }, - { value: 'pounce', label: 'Pounce', icon: Diamond }, - { value: 'external', label: 'External' }, - ].map((item) => ( - - ))} -
-
- - {/* TLD */} -
-
Quick TLD
-
- {['all', 'com', 'ai', 'io', 'net'].map((tld) => ( - - ))} -
+
+ {/* Source Filter */} +
+
Source
+
+ {[ + { value: 'all', label: 'All Sources' }, + { value: 'pounce', label: 'Pounce Direct', icon: Diamond }, + { value: 'external', label: 'External' }, + ].map((item) => ( + + ))}
-
- {/* Price */} + {/* Price & TLD */} +
-
Price Range
+
Price Range
{[ { 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) {
- {/* Spam Filter */}
-
Visibility
- +
TLD Filter
+
+ {['all', 'com', 'ai', 'io', 'net', 'ch'].map((tld) => ( + + ))} +
+ + {/* Hide Spam */} +
)} - {/* Results Header */} -
-
-
- {filteredItems.length} active listings found -
-
- {stats.highScore} premium assets - {totalPages > 1 && Page {page} / {totalPages}} + {/* Results Info */} +
+
+
+ {filteredItems.length} active listings + {stats.highScore > 0 && ( + <> + â€ĸ + {stats.highScore} premium + + )}
+ {totalPages > 1 && ( + + Page {page} of {totalPages} + + )}
{/* Results Table */} {filteredItems.length === 0 ? (
- -

No assets found

-

+ +

No auctions found

+

Try adjusting your search criteria or filters

) : ( <> -
- {/* Desktop Table Header */} -
- - - - -
Actions
+
Actions
+ {/* Table Body */}
{filteredItems.map((item) => { const timeLeftSec = getSecondsUntilEnd(item.end_time) @@ -500,28 +535,40 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) { const isTracking = trackingInProgress === item.domain return ( -
+
{/* Mobile Row */} -
-
-
- -
- {item.source} - | - {isPounce ? 'INSTANT' : displayTime || 'N/A'} +
+ {item.source} + {isPounce && item.verified && } + + {isPounce ? 'INSTANT' : displayTime || 'N/A'} +
-
{formatPrice(item.price)}
= 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)} +
+
= 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}
@@ -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 ? : isTracked ? : } + {isTracking ? : isTracked ? : } {isTracked ? 'Tracked' : 'Track'} - {isPounce ? 'Buy' : 'Bid'} - {!isPounce && } + {!isPounce && }
{/* Desktop Row */} -
+
+ {/* Domain */}
- -
+
{item.source} {isPounce && item.verified && }
+ {/* 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}
-
-
{formatPrice(item.price)}
-
{item.price_type === 'bid' ? 'BID' : 'BUY NOW'}
+ {/* Price */} +
+
+ {formatPrice(item.price)} +
+
+ {item.price_type === 'bid' ? 'Current Bid' : 'Buy Now'} +
+ {/* Time */}
{isPounce ? ( - - + + Instant ) : ( - {displayTime || 'N/A'} + + {isUrgent && } + {displayTime || 'N/A'} + )}
-
+ {/* Actions */} +
- @@ -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 && }
@@ -627,23 +717,23 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) { {/* Pagination */} {totalPages > 1 && ( -
+
-
- - Page {page} / {totalPages} +
+ + Page {page} / {totalPages}
diff --git a/frontend/src/components/hunt/DropsTab.tsx b/frontend/src/components/hunt/DropsTab.tsx index a052633..9b4e2a5 100644 --- a/frontend/src/components/hunt/DropsTab.tsx +++ b/frontend/src/components/hunt/DropsTab.tsx @@ -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 ( -
- +
+ + Loading zone file drops...
) } return ( -
+
{/* Header Stats */}
-
-
- +
+
+
-
+
{stats?.daily_drops?.toLocaleString() || total.toLocaleString()}
-
Fresh drops detected (24h)
+
Fresh drops in last 24h
{/* Search */}
- + 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 && ( - )}
{/* TLD Quick Filter */} -
+
{ALL_TLDS.map(({ tld, flag }) => ( ))}
- {/* Filter Toggle */} + {/* Advanced Filters */} - {/* Filters Panel */} {filtersOpen && ( -
+
{/* Length Filter */}
-
Domain Length
-
+
Domain Length
+
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} /> - TO + to 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) {
{/* Quality Filters */} -
+
)} - {/* Results Header */} -
-
-
- {total.toLocaleString()} domains matching criteria + {/* Results Info */} +
+
+
+ {total.toLocaleString()} domains detected +
+ {totalPages > 1 && ( + + Page {page} of {totalPages} + + )} +
+ + {/* Alert Banner */} +
+ +
+
Zone File Analysis
+
+ Domains detected as dropped via zone file comparison. Some may have been re-registered. Click "Check" to verify live availability. +
- {totalPages > 1 && Page {page} of {totalPages}}
{/* Results Table */} {sortedItems.length === 0 ? (
- -

No fresh drops detected

-

- The zone file comparison engine will update in the next 24h cycle + +

No drops found

+

+ Zone file comparison runs daily. Try adjusting your filters.

) : ( <> -
- {/* Desktop Table Header */} -
- - - -
Actions
+
Actions
+ {/* Table Body */}
- {sortedItems.map((item) => ( -
- {/* Mobile Row */} -
-
-
- -
- - LEN: {item.length} - - {formatTime(item.dropped_date)} + {sortedItems.map((item) => { + const fullDomain = `${item.domain}.${item.tld}` + const isTracking = tracking === fullDomain + + return ( +
+ {/* Mobile Row */} +
+
+
+ +
+ + {item.length} chars + + + + {formatTime(item.dropped_date)} + +
+ +
+ + + + Check & Buy + +
-
- - - - Buy Now - + {/* Desktop Row */} +
+ {/* Domain */} +
+ +
+ + {/* Length */} +
+ + {item.length} + +
+ + {/* Time */} +
+ + {formatTime(item.dropped_date)} + +
+ + {/* Actions */} +
+ + + + Check & Buy + + +
- - {/* Desktop Row */} -
-
- -
- -
- - {item.length} - -
- -
- {formatTime(item.dropped_date)} -
- -
- - - - Buy - -
-
-
- ))} + ) + })}
{/* Pagination */} {totalPages > 1 && ( -
+
-
- - Page {page} / {totalPages} +
+ + Page {page} / {totalPages}