Hunt Module UI Optimization: unified award-winning design
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

- Unified table headers, rows, and action alignments across all tabs
- Enhanced SearchTab: improved result display and global TLD grid
- Refined DropsTab: standardized table layouts and mobile views
- Optimized AuctionsTab: improved column alignment and source badges
- Modernized TrendSurfer: better viral topics grid and keyword builder
- Polished BrandableForge: refined mode selectors and synthesis config
- Standardized all borders, backgrounds, and spacing for Terminal v1.0
- All UI text in English with enhanced tracking and monospaced typography
This commit is contained in:
2025-12-18 09:30:50 +01:00
parent 4c08c92780
commit 460074d01f
5 changed files with 847 additions and 867 deletions

View File

@ -1,9 +1,9 @@
'use client' 'use client'
import { useEffect, useState, useMemo, useCallback } from 'react' import { useEffect, useState, useMemo, useCallback } from 'react'
import { useStore } from '@/lib/store'
import { api } from '@/lib/api' import { api } from '@/lib/api'
import { useAnalyzePanelStore } from '@/lib/analyze-store' import { useAnalyzePanelStore } from '@/lib/analyze-store'
import { useStore } from '@/lib/store'
import { import {
ExternalLink, ExternalLink,
Loader2, Loader2,
@ -14,7 +14,6 @@ import {
ChevronUp, ChevronUp,
ChevronDown, ChevronDown,
RefreshCw, RefreshCw,
Clock,
Search, Search,
Eye, Eye,
EyeOff, EyeOff,
@ -23,6 +22,7 @@ import {
X, X,
Filter, Filter,
Shield, Shield,
Gavel,
} from 'lucide-react' } from 'lucide-react'
import clsx from 'clsx' import clsx from 'clsx'
@ -109,6 +109,8 @@ interface AuctionsTabProps {
export function AuctionsTab({ showToast }: AuctionsTabProps) { export function AuctionsTab({ showToast }: AuctionsTabProps) {
const openAnalyze = useAnalyzePanelStore((s) => s.open) const openAnalyze = useAnalyzePanelStore((s) => s.open)
const addDomain = useStore((s) => s.addDomain)
const deleteDomain = useStore((s) => s.deleteDomain)
const [items, setItems] = useState<MarketItem[]>([]) const [items, setItems] = useState<MarketItem[]>([])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
@ -202,7 +204,7 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
const result = await api.getDomains(1, 100) const result = await api.getDomains(1, 100)
const domainToDelete = result.domains.find((d: any) => d.name === domain) const domainToDelete = result.domains.find((d: any) => d.name === domain)
if (domainToDelete) { if (domainToDelete) {
await api.deleteDomain(domainToDelete.id) await deleteDomain(domainToDelete.id)
setTrackedDomains((prev) => { setTrackedDomains((prev) => {
const next = new Set(Array.from(prev)) const next = new Set(Array.from(prev))
next.delete(domain) next.delete(domain)
@ -211,7 +213,7 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
showToast(`Removed: ${domain}`, 'success') showToast(`Removed: ${domain}`, 'success')
} }
} else { } else {
await api.addDomain(domain) await addDomain(domain)
setTrackedDomains((prev) => new Set([...Array.from(prev), domain])) setTrackedDomains((prev) => new Set([...Array.from(prev), domain]))
showToast(`Tracking: ${domain}`, 'success') showToast(`Tracking: ${domain}`, 'success')
} }
@ -220,7 +222,7 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
} finally { } finally {
setTrackingInProgress(null) setTrackingInProgress(null)
} }
}, [trackedDomains, trackingInProgress, showToast]) }, [trackedDomains, trackingInProgress, showToast, addDomain, deleteDomain])
const filteredItems = useMemo(() => { const filteredItems = useMemo(() => {
let filtered = items let filtered = items
@ -285,8 +287,28 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
return ( return (
<div className="space-y-4"> <div className="space-y-4">
{/* Search & Filters */} {/* Header Stats */}
<div className="space-y-3"> <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>
<div>
<div className="text-xl font-bold text-white font-mono">
{stats.total.toLocaleString()}
</div>
<div className="text-[10px] font-mono text-white/40 uppercase tracking-wider">Live auctions & fixed price</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"
>
<RefreshCw className={clsx("w-4 h-4", refreshing && "animate-spin")} />
</button>
</div>
{/* Search */} {/* Search */}
<div className={clsx( <div className={clsx(
"relative border transition-all duration-200", "relative border transition-all duration-200",
@ -300,7 +322,7 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
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="Filter auctions..." placeholder="Search auctions..."
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-3 py-3 text-sm text-white placeholder:text-white/20 outline-none font-mono"
/> />
{searchQuery && ( {searchQuery && (
@ -308,9 +330,6 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
<X className="w-4 h-4" /> <X className="w-4 h-4" />
</button> </button>
)} )}
<button onClick={handleRefresh} disabled={refreshing} className="p-3 text-white/30 hover:text-white transition-colors">
<RefreshCw className={clsx("w-4 h-4", refreshing && "animate-spin")} />
</button>
</div> </div>
</div> </div>
@ -318,24 +337,25 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
<button <button
onClick={() => setFiltersOpen(!filtersOpen)} onClick={() => setFiltersOpen(!filtersOpen)}
className={clsx( className={clsx(
"flex items-center justify-between w-full py-2 px-3 border transition-colors", "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]" filtersOpen ? "border-accent/30 bg-accent/[0.05]" : "border-white/[0.08] bg-white/[0.02]"
)} )}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Filter className="w-4 h-4 text-white/40" /> <Filter className="w-4 h-4 text-white/40" />
<span className="text-xs font-mono text-white/60">Filters</span> <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">{activeFiltersCount}</span>} {activeFiltersCount > 0 && <span className="px-1.5 py-0.5 text-[9px] font-bold bg-accent text-black ml-1">{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 text-white/30 transition-transform", filtersOpen && "rotate-90")} />
</button> </button>
{/* Filters Panel */} {/* Filters Panel */}
{filtersOpen && ( {filtersOpen && (
<div className="p-3 border border-white/[0.08] bg-white/[0.02] space-y-3 animate-in fade-in slide-in-from-top-2 duration-200"> <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 */} {/* Source */}
<div> <div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-wider mb-2">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' }, { value: 'all', label: 'All' },
@ -346,8 +366,8 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
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-2 text-[10px] font-bold uppercase tracking-wider border transition-colors flex items-center justify-center gap-1", "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" : "border-white/[0.08] text-white/40" 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.icon && <item.icon className="w-3 h-3" />}
@ -359,15 +379,15 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
{/* TLD */} {/* TLD */}
<div> <div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-wider mb-2">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"> <div className="flex gap-2 flex-wrap">
{['all', 'com', 'ai', 'io', 'net'].map((tld) => ( {['all', 'com', 'ai', 'io', 'net'].map((tld) => (
<button <button
key={tld} key={tld}
onClick={() => setTldFilter(tld)} onClick={() => setTldFilter(tld)}
className={clsx( className={clsx(
"px-3 py-1.5 text-[10px] font-mono uppercase border transition-colors", "px-3 py-2 text-[10px] font-mono uppercase border transition-colors",
tldFilter === tld ? "border-accent bg-accent/10 text-accent" : "border-white/[0.08] text-white/40" tldFilter === tld ? "border-accent bg-accent/10 text-accent font-bold" : "border-white/[0.08] text-white/40"
)} )}
> >
{tld === 'all' ? 'All' : `.${tld}`} {tld === 'all' ? 'All' : `.${tld}`}
@ -375,10 +395,12 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
))} ))}
</div> </div>
</div> </div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* Price */} {/* Price */}
<div> <div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-wider mb-2">Price</div> <div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Price Range</div>
<div className="flex gap-2"> <div className="flex gap-2">
{[ {[
{ value: 'all', label: 'All' }, { value: 'all', label: 'All' },
@ -390,8 +412,8 @@ 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-1.5 text-[10px] font-mono border transition-colors", "flex-1 py-2 text-[10px] font-mono border transition-colors",
priceRange === item.value ? "border-amber-400 bg-amber-400/10 text-amber-400" : "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"
)} )}
> >
{item.label} {item.label}
@ -401,62 +423,74 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
</div> </div>
{/* Spam Filter */} {/* Spam Filter */}
<div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-widest mb-2.5">Visibility</div>
<button <button
onClick={() => setHideSpam(!hideSpam)} onClick={() => setHideSpam(!hideSpam)}
className={clsx( className={clsx(
"flex items-center justify-between w-full py-2 px-3 border transition-colors", "flex items-center justify-between w-full py-2 px-4 border transition-colors",
hideSpam ? "border-white/20 bg-white/[0.05]" : "border-white/[0.08]" hideSpam ? "border-accent/30 bg-accent/5" : "border-white/[0.08]"
)} )}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2.5">
<Ban className="w-4 h-4 text-white/40" /> <Ban className={clsx("w-3.5 h-3.5", hideSpam ? "text-accent" : "text-white/30")} />
<span className="text-xs font-mono text-white/60">Hide spam domains</span> <span className={clsx("text-[10px] font-mono uppercase tracking-wider", hideSpam ? "text-accent" : "text-white/50")}>Hide Spam Domains</span>
</div> </div>
<div className={clsx("w-4 h-4 border flex items-center justify-center", hideSpam ? "border-accent bg-accent" : "border-white/30")}> <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-[10px] font-bold"></span>} {hideSpam && <span className="text-black text-[8px] font-bold"></span>}
</div> </div>
</button> </button>
</div> </div>
</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 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>}
</div>
</div> </div>
{/* Stats Bar */} {/* Results Table */}
<div className="flex items-center justify-between text-[10px] font-mono text-white/40">
<span>{filteredItems.length} domains shown</span>
<span>{filteredItems.filter((i) => i.pounce_score >= 80).length} high score</span>
</div>
{/* Results */}
{filteredItems.length === 0 ? ( {filteredItems.length === 0 ? (
<div className="text-center py-16 border border-dashed border-white/[0.08]"> <div className="text-center py-24 border border-dashed border-white/[0.08] bg-white/[0.01]">
<Search className="w-8 h-8 text-white/10 mx-auto mb-3" /> <Search className="w-12 h-12 text-white/5 mx-auto mb-4" />
<p className="text-white/40 text-sm font-mono">No domains found</p> <p className="text-white/40 text-sm font-mono uppercase tracking-widest font-bold">No assets found</p>
<p className="text-white/25 text-xs font-mono mt-1">Try adjusting filters</p> <p className="text-white/20 text-[10px] font-mono mt-3 uppercase tracking-wider max-w-xs mx-auto leading-relaxed">
Try adjusting your search criteria or filters
</p>
</div> </div>
) : ( ) : (
<> <>
<div className="space-y-px bg-white/[0.04] border border-white/[0.08]"> <div className="border border-white/[0.08] bg-white/[0.01] overflow-hidden">
{/* Desktop Table Header */} {/* Desktop Table Header */}
<div className="hidden lg:grid grid-cols-[1fr_80px_100px_80px_120px] gap-4 px-3 py-2 text-[10px] font-mono text-white/40 uppercase tracking-wider border-b border-white/[0.08]"> <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-1 hover:text-white/60 text-left" title="Domain name being auctioned"> <button onClick={() => handleSort('domain')} className="flex items-center gap-2 hover:text-white transition-colors text-left group">
Domain <span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain Asset</span>
{sortField === 'domain' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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-1 justify-center hover:text-white/60" title="Pounce Score - our AI-powered quality rating (0-100)"> <button onClick={() => handleSort('score')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
Score <span className={clsx(sortField === 'score' && "text-accent font-bold")}>Score</span>
{sortField === 'score' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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-1 justify-end hover:text-white/60" title="Current bid or buy-now price"> <button onClick={() => handleSort('price')} className="flex items-center gap-2 justify-end hover:text-white transition-colors group pr-4">
Price <span className={clsx(sortField === 'price' && "text-accent font-bold")}>Price</span>
{sortField === 'price' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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-1 justify-center hover:text-white/60" title="Time remaining until auction ends"> <button onClick={() => handleSort('time')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
Time <span className={clsx(sortField === 'time' && "text-accent font-bold")}>Time</span>
{sortField === 'time' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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" title="Available actions: Analyze, Track, Buy">Actions</div> <div className="text-right pr-2">Actions</div>
</div> </div>
<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)
const isUrgent = timeLeftSec > 0 && timeLeftSec < 3600 const isUrgent = timeLeftSec > 0 && timeLeftSec < 3600
@ -466,27 +500,28 @@ 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", isPounce && "bg-accent/[0.02]")}> <div key={item.id} className={clsx("bg-[#020202] hover:bg-white/[0.02] transition-all group", isPounce && "bg-accent/[0.01]")}>
{/* Mobile Row */} {/* Mobile Row */}
<div className="lg:hidden p-3"> <div className="lg:hidden p-4">
<div className="flex items-start justify-between gap-3 mb-2"> <div className="flex items-start justify-between gap-3 mb-4">
<div className="flex items-center gap-3 min-w-0 flex-1"> <div className="flex flex-col min-w-0">
<div className="min-w-0 flex-1"> <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-sm font-bold text-white font-mono truncate text-left">
{item.domain} {item.domain}
</button> </button>
<div className="flex items-center gap-2 text-[10px] font-mono text-white/30"> <div className="flex items-center gap-2 mt-1.5 text-[9px] font-mono text-white/30 uppercase tracking-wider">
<span className="uppercase">{item.source}</span> <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="text-white/10">|</span>
<span className={clsx(isUrgent && "text-orange-400")}>{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>
<div className="text-right shrink-0"> <div className="text-right shrink-0">
<div className={clsx("text-base font-bold font-mono", isPounce ? "text-accent" : "text-white")}>{formatPrice(item.price)}</div> <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 py-0.5 inline-block", item.pounce_score >= 80 ? "text-accent bg-accent/10" : item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/10" : "text-white/30 bg-white/5")}> <div className={clsx(
Score {item.pounce_score} "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"
)}>
SCORE: {item.pounce_score}
</div> </div>
</div> </div>
</div> </div>
@ -496,104 +531,91 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
onClick={() => handleTrack(item.domain)} onClick={() => handleTrack(item.domain)}
disabled={isTracking} disabled={isTracking}
className={clsx( className={clsx(
"flex-1 py-2 text-[10px] font-bold uppercase tracking-wider border flex items-center justify-center gap-1.5 transition-all", "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/10 text-accent" : "border-white/[0.08] text-white/40" isTracked ? "border-accent bg-accent/5 text-accent" : "border-white/10 text-white/40 hover:bg-white/5"
)} )}
> >
{isTracking ? <Loader2 className="w-3 h-3 animate-spin" /> : isTracked ? <Eye className="w-3 h-3" /> : <EyeOff className="w-3 h-3" />} {isTracking ? <Loader2 className="w-3.5 h-3.5 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-10 py-2 text-[10px] font-bold uppercase tracking-wider border border-white/[0.08] text-white/50 flex items-center justify-center transition-all hover:text-white hover:bg-white/5"> <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-3.5 h-3.5" /> <Shield className="w-4.5 h-4.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 py-2 text-[10px] font-bold uppercase tracking-wider 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-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")}
> >
{isPounce ? 'Buy' : 'Bid'} {isPounce ? 'Buy' : 'Bid'}
{!isPounce && <ExternalLink className="w-3 h-3" />} {!isPounce && <ExternalLink className="w-3.5 h-3.5" />}
</a> </a>
</div> </div>
</div> </div>
{/* Desktop Row */} {/* Desktop Row */}
<div className="hidden lg:grid grid-cols-[1fr_80px_100px_80px_120px] gap-4 items-center p-4 group"> <div className="hidden lg:grid grid-cols-[1fr_80px_100px_100px_140px] gap-4 items-center px-5 py-3.5">
<div className="flex items-center gap-3 min-w-0 flex-1"> <div className="flex items-center gap-3 min-w-0">
<div className="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-[10px] font-mono text-white/30"> <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">
<span className="uppercase">{item.source}</span> <span>{item.source}</span>
{isPounce && item.verified && ( {isPounce && item.verified && <ShieldCheck className="w-3 h-3 text-accent" />}
<>
<span className="text-white/10">|</span>
<span className="text-accent flex items-center gap-0.5">
<ShieldCheck className="w-3 h-3" />
Verified
</span>
</>
)}
{item.num_bids ? (
<>
<span className="text-white/10">|</span>
{item.num_bids} bids
</>
) : null}
</div>
</div> </div>
</div> </div>
<div className="w-16 text-center shrink-0"> <div className="text-center">
<span className={clsx("text-xs font-mono font-bold px-2 py-0.5", item.pounce_score >= 80 ? "text-accent bg-accent/10" : item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/10" : "text-white/40 bg-white/5")}> <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"
)}>
{item.pounce_score} {item.pounce_score}
</span> </span>
</div> </div>
<div className="w-24 text-right shrink-0"> <div className="text-right pr-4">
<div className={clsx("font-mono text-sm font-bold", isPounce ? "text-accent" : "text-white")}>{formatPrice(item.price)}</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/30 uppercase">{item.price_type === 'bid' ? 'Bid' : 'Buy Now'}</div> <div className="text-[9px] font-mono text-white/20 uppercase tracking-widest">{item.price_type === 'bid' ? 'BID' : 'BUY NOW'}</div>
</div> </div>
<div className="w-20 text-center shrink-0"> <div className="text-center">
{isPounce ? ( {isPounce ? (
<span className="text-xs text-accent font-mono flex items-center justify-center gap-1"> <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" /> <Zap className="w-3 h-3" />
Instant Instant
</span> </span>
) : ( ) : (
<span className={clsx("text-xs font-mono", isUrgent ? "text-orange-400" : "text-white/50")}>{displayTime || 'N/A'}</span> <span className={clsx("text-[10px] font-mono uppercase tracking-widest", isUrgent ? "text-orange-400 font-bold" : "text-white/40")}>{displayTime || 'N/A'}</span>
)} )}
</div> </div>
<div className="flex items-center gap-2 shrink-0 opacity-50 group-hover:opacity-100 transition-opacity"> <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 <button
onClick={() => handleTrack(item.domain)} onClick={() => handleTrack(item.domain)}
disabled={isTracking} disabled={isTracking}
className={clsx( className={clsx(
"w-7 h-7 flex items-center justify-center border transition-colors", "w-8.5 h-8.5 flex items-center justify-center border transition-all",
isTracked ? "bg-accent/10 text-accent border-accent/20 hover:bg-red-500/10 hover:text-red-400 hover:border-red-500/20" : "text-white/30 border-white/10 hover:text-accent hover:bg-accent/10 hover:border-accent/20" 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"
)} )}
title={isTracked ? "Untrack" : "Track Domain"}
> >
{isTracking ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : isTracked ? <Eye className="w-3.5 h-3.5" /> : <EyeOff className="w-3.5 h-3.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" />}
</button> </button>
<button onClick={() => openAnalyze(item.domain)} className="w-7 h-7 flex items-center justify-center border transition-colors text-white/30 border-white/10 hover:text-accent hover:bg-accent/10 hover:border-accent/20"> <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">
<Shield className="w-3.5 h-3.5" /> <Shield className="w-4 h-4" />
</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("h-7 px-3 flex items-center gap-1.5 text-xs font-bold transition-colors", isPounce ? "bg-accent text-black hover:bg-white" : "bg-white/10 text-white hover:bg-white/20")} 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")}
> >
{isPounce ? 'Buy' : 'Bid'} {isPounce ? 'Buy' : 'Bid'}
{!isPounce && <ExternalLink className="w-3 h-3" />}
</a> </a>
</div> </div>
</div> </div>
@ -601,35 +623,31 @@ export function AuctionsTab({ showToast }: AuctionsTabProps) {
) )
})} })}
</div> </div>
</div>
{/* Pagination */} {/* Pagination */}
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex items-center justify-between pt-4"> <div className="flex items-center justify-center gap-1 pt-6">
<div className="text-[10px] text-white/40 font-mono uppercase tracking-wider">
Page {page}/{totalPages}
</div>
<div className="flex items-center gap-1">
<button <button
onClick={() => handlePageChange(page - 1)} onClick={() => handlePageChange(page - 1)}
disabled={page === 1} disabled={page === 1}
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-30 disabled:cursor-not-allowed transition-colors" 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"
> >
<ChevronLeft className="w-4 h-4" /> <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">
<span className="text-xs text-white/50 font-mono px-2"> <span className="text-[11px] text-white/40 font-mono uppercase tracking-widest">
{page}/{totalPages} Page <span className="text-white font-bold">{page}</span> / {totalPages}
</span> </span>
</div>
<button <button
onClick={() => handlePageChange(page + 1)} onClick={() => handlePageChange(page + 1)}
disabled={page === totalPages} disabled={page === totalPages}
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-30 disabled:cursor-not-allowed transition-colors" 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"
> >
<ChevronRight className="w-4 h-4" /> <ChevronRight className="w-5 h-5" />
</button> </button>
</div> </div>
</div>
)} )}
</> </>
)} )}

View File

@ -16,6 +16,8 @@ import {
RefreshCw, RefreshCw,
Brain, Brain,
Dices, Dices,
ChevronRight,
X,
} from 'lucide-react' } from 'lucide-react'
import Link from 'next/link' import Link from 'next/link'
import { api } from '@/lib/api' import { api } from '@/lib/api'
@ -71,7 +73,7 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
try { try {
const res = await api.huntBrandables({ pattern, tlds, limit: 24, max_checks: 300 }) const res = await api.huntBrandables({ pattern, tlds, limit: 24, max_checks: 300 })
setResults(res.items.map(i => ({ domain: i.domain }))) setResults(res.items.map(i => ({ domain: i.domain })))
showToast(`Found ${res.items.length} domains!`, 'success') showToast(`Generated ${res.items.length} assets!`, 'success')
} catch (e) { } catch (e) {
showToast('Generation failed', 'error') showToast('Generation failed', 'error')
} finally { } finally {
@ -90,7 +92,7 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
const checkRes = await api.huntKeywords({ keywords: res.names, tlds }) const checkRes = await api.huntKeywords({ keywords: res.names, tlds })
const available = checkRes.items.filter(i => i.status === 'available') const available = checkRes.items.filter(i => i.status === 'available')
setResults(available.map(i => ({ domain: i.domain }))) setResults(available.map(i => ({ domain: i.domain })))
showToast(`Found ${available.length} available from ${res.names.length} AI names!`, 'success') showToast(`Found ${available.length} free domains via AI!`, 'success')
} }
} catch (e) { } catch (e) {
showToast('AI generation failed', 'error') showToast('AI generation failed', 'error')
@ -118,7 +120,7 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
await addDomain(domain) await addDomain(domain)
showToast(`Added: ${domain}`, 'success') showToast(`Added: ${domain}`, 'success')
} catch (e) { } catch (e) {
showToast('Failed', 'error') showToast('Failed to track', 'error')
} finally { } finally {
setTracking(null) setTracking(null)
} }
@ -128,52 +130,42 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Header */}
{/* HEADER */} <div className="flex items-center gap-4 pb-5 border-b border-white/[0.08]">
{/* ═══════════════════════════════════════════════════════════════════════ */} <div className="w-12 h-12 bg-purple-500/10 border border-purple-500/20 flex items-center justify-center shrink-0">
<div className="pb-4 border-b border-white/10"> <Wand2 className="w-6 h-6 text-purple-400" />
<h2 className="text-xl font-bold text-white flex items-center gap-2"> </div>
<Wand2 className="w-5 h-5 text-purple-400" /> <div>
Brandable Forge <h2 className="text-xl font-bold text-white font-mono tracking-tight">Brandable Forge</h2>
</h2> <p className="text-[10px] font-mono text-white/40 uppercase tracking-widest mt-1">Synthesize unique, high-value naming assets</p>
<p className="text-sm text-white/60 mt-1"> </div>
Generate unique, memorable domain names
</p>
</div> </div>
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Mode Selector */}
{/* MODE SELECTOR - BIG CLEAR BUTTONS */} <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{/* ═══════════════════════════════════════════════════════════════════════ */}
<div className="grid grid-cols-2 gap-3">
{/* Pattern Mode */} {/* Pattern Mode */}
<button <button
onClick={() => setMode('pattern')} onClick={() => setMode('pattern')}
className={clsx( className={clsx(
"p-4 sm:p-5 border-2 transition-all text-left", "relative p-5 border transition-all duration-300 text-left group overflow-hidden",
mode === 'pattern' mode === 'pattern'
? "border-accent bg-accent/10" ? "border-accent bg-accent/5 shadow-[0_0_30px_-10px_rgba(34,211,126,0.2)]"
: "border-white/10 bg-white/[0.02] hover:border-white/20" : "border-white/[0.08] bg-white/[0.01] hover:border-white/20 hover:bg-white/[0.03]"
)} )}
> >
<div className="flex items-center gap-3 mb-2"> <div className="flex items-center gap-4 relative z-10">
<div className={clsx( <div className={clsx(
"w-10 h-10 flex items-center justify-center", "w-12 h-12 flex items-center justify-center border transition-colors",
mode === 'pattern' ? "bg-accent/20" : "bg-white/5" mode === 'pattern' ? "border-accent/30 bg-accent/10" : "border-white/10 bg-white/5"
)}> )}>
<Dices className={clsx("w-5 h-5", mode === 'pattern' ? "text-accent" : "text-white/50")} /> <Dices className={clsx("w-6 h-6", mode === 'pattern' ? "text-accent" : "text-white/30")} />
</div> </div>
<div> <div>
<p className={clsx("font-bold", mode === 'pattern' ? "text-accent" : "text-white")}> <p className={clsx("font-bold font-mono text-sm tracking-tight", mode === 'pattern' ? "text-accent" : "text-white/70")}>Pattern Engine</p>
Pattern <p className="text-[10px] font-mono text-white/30 uppercase tracking-widest mt-0.5">Classic Brandables</p>
</p>
<p className={clsx("text-xs", mode === 'pattern' ? "text-accent/70" : "text-white/50")}>
CVCVC, CVCCV, Human
</p>
</div> </div>
</div> </div>
<p className="text-xs text-white/40 hidden sm:block"> {mode === 'pattern' && <div className="absolute top-0 right-0 w-2 h-2 bg-accent" />}
Generate random names using proven naming patterns
</p>
</button> </button>
{/* AI Mode */} {/* AI Mode */}
@ -181,135 +173,83 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
onClick={() => hasAI && setMode('ai')} onClick={() => hasAI && setMode('ai')}
disabled={!hasAI} disabled={!hasAI}
className={clsx( className={clsx(
"p-4 sm:p-5 border-2 transition-all text-left relative", "relative p-5 border transition-all duration-300 text-left group overflow-hidden",
!hasAI && "opacity-60", !hasAI && "opacity-50 grayscale",
mode === 'ai' mode === 'ai'
? "border-purple-500 bg-purple-500/10" ? "border-purple-500 bg-purple-500/5 shadow-[0_0_30px_-10px_rgba(168,85,247,0.2)]"
: "border-white/10 bg-white/[0.02] hover:border-white/20" : "border-white/[0.08] bg-white/[0.01] hover:border-white/20 hover:bg-white/[0.03]"
)} )}
> >
{!hasAI && ( {!hasAI && <div className="absolute top-2 right-2"><Lock className="w-3 h-3 text-white/20" /></div>}
<div className="absolute top-2 right-2"> <div className="flex items-center gap-4 relative z-10">
<Lock className="w-4 h-4 text-white/30" />
</div>
)}
<div className="flex items-center gap-3 mb-2">
<div className={clsx( <div className={clsx(
"w-10 h-10 flex items-center justify-center", "w-12 h-12 flex items-center justify-center border transition-colors",
mode === 'ai' ? "bg-purple-500/20" : "bg-white/5" mode === 'ai' ? "border-purple-500/30 bg-purple-500/10" : "border-white/10 bg-white/5"
)}> )}>
<Brain className={clsx("w-5 h-5", mode === 'ai' ? "text-purple-400" : "text-white/50")} /> <Brain className={clsx("w-6 h-6", mode === 'ai' ? "text-purple-400" : "text-white/30")} />
</div> </div>
<div> <div>
<p className={clsx("font-bold", mode === 'ai' ? "text-purple-400" : "text-white")}> <p className={clsx("font-bold font-mono text-sm tracking-tight", mode === 'ai' ? "text-purple-400" : "text-white/70")}>Vision Core AI</p>
AI Concept <p className="text-[10px] font-mono text-white/30 uppercase tracking-widest mt-0.5">Concept to Name</p>
</p>
<p className={clsx("text-xs", mode === 'ai' ? "text-purple-400/70" : "text-white/50")}>
{hasAI ? 'Describe your brand' : 'Trader+ only'}
</p>
</div> </div>
</div> </div>
<p className="text-xs text-white/40 hidden sm:block"> {mode === 'ai' && <div className="absolute top-0 right-0 w-2 h-2 bg-purple-500" />}
{hasAI ? 'AI generates names based on your concept' : 'Upgrade to unlock AI naming'}
</p>
</button> </button>
</div> </div>
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Config Panel */}
{/* PATTERN MODE */} <div className={clsx(
{/* ═══════════════════════════════════════════════════════════════════════ */} "border overflow-hidden transition-all duration-500",
{mode === 'pattern' && ( mode === 'ai' ? "border-purple-500/30 bg-purple-500/[0.01]" : "border-white/[0.08] bg-white/[0.01]"
<div className="border border-white/10 bg-white/[0.02] p-4 sm:p-5 space-y-5"> )}>
{/* Pattern Selection */} <div className="px-5 py-4 border-b border-white/[0.08] bg-white/[0.01] flex items-center justify-between">
<div> <div className="flex items-center gap-3">
<p className="text-xs text-white/60 font-mono uppercase tracking-wider mb-3">Choose Pattern</p> <span className="text-[10px] font-mono text-white/30 uppercase tracking-[0.2em]">Synthesis Configuration</span>
</div>
<div className="flex items-center gap-2">
<div className={clsx("w-1 h-1 rounded-full", mode === 'ai' ? "bg-purple-500 animate-pulse" : "bg-accent")} />
<span className="text-[10px] font-mono text-white/20 uppercase tracking-widest">{mode.toUpperCase()} MODE</span>
</div>
</div>
<div className="p-5 space-y-6">
{mode === 'pattern' ? (
<div className="space-y-4">
<span className="text-[10px] font-mono text-white/30 uppercase tracking-widest">Select Linguistic Pattern</span>
<div className="grid grid-cols-3 gap-2"> <div className="grid grid-cols-3 gap-2">
{PATTERNS.map(p => ( {PATTERNS.map(p => (
<button <button
key={p.key} key={p.key}
onClick={() => setPattern(p.key)} onClick={() => setPattern(p.key)}
className={clsx( className={clsx(
"p-3 border text-center transition-all", "p-3 border transition-all duration-300 group",
pattern === p.key pattern === p.key
? "border-accent bg-accent/10" ? "border-accent bg-accent/10"
: "border-white/10 hover:border-white/20" : "border-white/10 hover:border-white/20 hover:bg-white/5"
)} )}
> >
<p className={clsx( <p className={clsx("text-xs font-black font-mono tracking-widest", pattern === p.key ? "text-accent" : "text-white/40 group-hover:text-white/60")}>{p.label}</p>
"text-sm font-bold font-mono", <p className="text-[9px] font-mono text-white/20 mt-1 uppercase tracking-tighter">{p.desc}</p>
pattern === p.key ? "text-accent" : "text-white"
)}>
{p.label}
</p>
<p className="text-[10px] text-white/50 mt-0.5">{p.example}</p>
</button> </button>
))} ))}
</div> </div>
</div> </div>
) : (
{/* TLDs */} <div className="space-y-4">
<div> <span className="text-[10px] font-mono text-purple-300/40 uppercase tracking-widest">Describe Your Identity Concept</span>
<p className="text-xs text-white/60 font-mono uppercase tracking-wider mb-3">Select TLDs</p>
<div className="flex flex-wrap gap-2">
{TLDS.map(tld => (
<button
key={tld}
onClick={() => setTlds(prev =>
prev.includes(tld) ? prev.filter(t => t !== tld) : [...prev, tld]
)}
className={clsx(
"px-3 py-2 text-sm font-mono border transition-all",
tlds.includes(tld)
? "border-accent bg-accent/10 text-accent"
: "border-white/10 text-white/50 hover:text-white"
)}
>
.{tld}
</button>
))}
</div>
</div>
{/* Generate */}
<button
onClick={generatePattern}
disabled={isGenerating || tlds.length === 0}
className={clsx(
"w-full py-4 text-sm font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all",
isGenerating || tlds.length === 0
? "bg-white/10 text-white/40"
: "bg-accent text-black hover:bg-white"
)}
>
{loading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
Generate {pattern.toUpperCase()} Names
</button>
</div>
)}
{/* ═══════════════════════════════════════════════════════════════════════ */}
{/* AI MODE */}
{/* ═══════════════════════════════════════════════════════════════════════ */}
{mode === 'ai' && hasAI && (
<div className="border border-purple-500/30 bg-purple-500/5 p-4 sm:p-5 space-y-5">
{/* Concept Input */}
<div>
<p className="text-xs text-purple-300 font-mono uppercase tracking-wider mb-3">
Describe Your Brand Concept
</p>
<textarea <textarea
value={concept} value={concept}
onChange={(e) => setConcept(e.target.value)} onChange={(e) => setConcept(e.target.value)}
placeholder="e.g., AI startup for legal documents, crypto wallet for teens, fitness tracking app..." placeholder="e.g., A minimalist AI agent for legal risk management..."
rows={3} rows={3}
className="w-full px-4 py-3 bg-white/5 border border-purple-500/30 text-white placeholder:text-white/30 outline-none focus:border-purple-400 resize-none font-mono text-sm" className="w-full px-4 py-3 bg-white/[0.03] border border-purple-500/20 text-sm font-mono text-white placeholder:text-white/10 outline-none focus:border-purple-500/50 resize-none transition-all"
/> />
</div> </div>
)}
{/* TLDs */} <div className="space-y-4">
<div> <span className="text-[10px] font-mono text-white/30 uppercase tracking-widest">Target Extensions</span>
<p className="text-xs text-purple-300 font-mono uppercase tracking-wider mb-3">Select TLDs</p> <div className="flex flex-wrap gap-1.5">
<div className="flex flex-wrap gap-2">
{TLDS.map(tld => ( {TLDS.map(tld => (
<button <button
key={tld} key={tld}
@ -317,10 +257,10 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
prev.includes(tld) ? prev.filter(t => t !== tld) : [...prev, tld] prev.includes(tld) ? prev.filter(t => t !== tld) : [...prev, tld]
)} )}
className={clsx( className={clsx(
"px-3 py-2 text-sm font-mono border transition-all", "px-3 py-1.5 text-[10px] font-mono border transition-all uppercase tracking-widest",
tlds.includes(tld) tlds.includes(tld)
? "border-purple-400 bg-purple-500/20 text-purple-300" ? mode === 'ai' ? "border-purple-500 bg-purple-500/10 text-purple-300 font-black" : "border-accent bg-accent/10 text-accent font-black"
: "border-white/10 text-white/50 hover:text-white" : "border-white/10 text-white/40 hover:text-white hover:bg-white/5"
)} )}
> >
.{tld} .{tld}
@ -329,65 +269,52 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
</div> </div>
</div> </div>
{/* Generate */}
<button <button
onClick={generateFromConcept} onClick={mode === 'pattern' ? generatePattern : generateFromConcept}
disabled={aiLoading || !concept.trim() || tlds.length === 0} disabled={isGenerating || (mode === 'ai' && !concept.trim()) || tlds.length === 0}
className={clsx( className={clsx(
"w-full py-4 text-sm font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all", "w-full py-4 text-[11px] font-black uppercase tracking-[0.2em] flex items-center justify-center gap-3 transition-all",
aiLoading || !concept.trim() || tlds.length === 0 isGenerating || (mode === 'ai' && !concept.trim()) || tlds.length === 0
? "bg-white/10 text-white/40" ? "bg-white/5 text-white/20 border border-white/5"
: "bg-purple-500 text-white hover:bg-purple-400" : mode === 'ai'
? "bg-purple-600 text-white hover:bg-purple-500 shadow-[0_0_30px_-10px_rgba(168,85,247,0.4)]"
: "bg-accent text-black hover:bg-white shadow-[0_0_30px_-10px_rgba(34,211,126,0.4)]"
)} )}
> >
{aiLoading ? <Loader2 className="w-4 h-4 animate-spin" /> : <Brain className="w-4 h-4" />} {isGenerating ? <Loader2 className="w-4 h-4 animate-spin" /> : <Sparkles className="w-4 h-4" />}
Generate AI Names {mode === 'ai' ? 'Synthesize AI Assets' : `Forge ${pattern.toUpperCase()} Names`}
</button> </button>
</div> </div>
)} </div>
{/* AI Upgrade CTA */} {/* AI Upgrade CTA */}
{mode === 'ai' && !hasAI && ( {mode === 'ai' && !hasAI && (
<div className="border border-white/10 bg-white/[0.02] p-8 text-center"> <div className="border border-white/10 bg-white/[0.01] p-10 text-center animate-in fade-in zoom-in-95 duration-500">
<Lock className="w-10 h-10 text-white/20 mx-auto mb-3" /> <Lock className="w-12 h-12 text-white/5 mx-auto mb-4" />
<h3 className="text-lg font-bold text-white mb-2">AI Naming Requires Upgrade</h3> <h3 className="text-lg font-bold text-white font-mono tracking-tight mb-2 uppercase">Neural Forge Locked</h3>
<p className="text-sm text-white/50 mb-4 max-w-sm mx-auto"> <p className="text-sm text-white/30 font-mono mb-6 max-w-sm mx-auto uppercase tracking-wider leading-relaxed">
Describe your brand and let AI generate unique, brandable domain names for you. AI-driven naming requires Trader or Tycoon clearance
</p> </p>
<Link <Link
href="/pricing" href="/pricing"
className="inline-flex items-center gap-2 px-6 py-3 bg-accent text-black text-sm font-bold uppercase hover:bg-white transition-colors" className="inline-flex items-center gap-3 px-8 py-3 bg-accent text-black text-[11px] font-black uppercase tracking-widest hover:bg-white transition-all active:scale-95"
> >
<Sparkles className="w-4 h-4" /> <Sparkles className="w-4 h-4" />
Upgrade to Trader Upgrade Plan
</Link> </Link>
</div> </div>
)} )}
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Results */}
{/* RESULTS */}
{/* ═══════════════════════════════════════════════════════════════════════ */}
{results.length > 0 && ( {results.length > 0 && (
<div className="space-y-4"> <div className="space-y-4 animate-in fade-in slide-in-from-bottom-4 duration-500">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between px-1">
<p className="text-sm text-white/70"> <p className="text-[10px] font-mono text-white/30 uppercase tracking-[0.2em]">
<span className="text-accent font-bold">{results.length}</span> available domains <span className={clsx("font-black", mode === 'ai' ? "text-purple-400" : "text-accent")}>{results.length}</span> Synthesized naming assets available
</p> </p>
<div className="flex gap-3"> <div className="flex gap-4">
<button <button onClick={copyAll} className="text-[9px] font-mono text-white/20 hover:text-white uppercase tracking-widest transition-colors">
onClick={copyAll} Batch Copy
className="text-xs font-mono text-white/50 hover:text-accent flex items-center gap-1"
>
<Copy className="w-3 h-3" />
Copy all
</button>
<button
onClick={mode === 'pattern' ? generatePattern : generateFromConcept}
disabled={isGenerating}
className="text-xs font-mono text-white/50 hover:text-white flex items-center gap-1"
>
<RefreshCw className={clsx("w-3 h-3", isGenerating && "animate-spin")} />
Refresh
</button> </button>
</div> </div>
</div> </div>
@ -397,49 +324,49 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
<div <div
key={r.domain} key={r.domain}
className={clsx( className={clsx(
"flex items-center justify-between p-3 border transition-all group", "flex items-center justify-between p-4 border transition-all duration-300 group",
mode === 'ai' mode === 'ai'
? "bg-purple-500/5 border-purple-500/20 hover:bg-purple-500/10" ? "bg-purple-500/[0.02] border-purple-500/20 hover:border-purple-500/40 hover:bg-purple-500/[0.04]"
: "bg-accent/5 border-accent/20 hover:bg-accent/10" : "bg-accent/[0.02] border-accent/20 hover:border-accent/40 hover:bg-accent/[0.04]"
)} )}
> >
<div className="flex items-center gap-2.5 min-w-0"> <div className="flex items-center gap-3 min-w-0">
<span className={clsx( <span className={clsx(
"w-6 h-6 text-[10px] font-bold font-mono flex items-center justify-center shrink-0", "text-[9px] font-bold font-mono px-1.5 py-0.5 border shrink-0",
mode === 'ai' mode === 'ai'
? "bg-purple-500/20 text-purple-300" ? "bg-purple-500/10 border-purple-500/20 text-purple-300"
: "bg-accent/20 text-accent" : "bg-accent/10 border-accent/20 text-accent"
)}> )}>
{idx + 1} {(idx + 1).toString().padStart(2, '0')}
</span> </span>
<button <button
onClick={() => openAnalyze(r.domain)} onClick={() => openAnalyze(r.domain)}
className={clsx( className={clsx(
"text-sm font-mono font-medium text-white truncate", "text-sm font-bold font-mono text-white truncate transition-colors",
mode === 'ai' ? "group-hover:text-purple-300" : "group-hover:text-accent" mode === 'ai' ? "group-hover:text-purple-300" : "group-hover:text-accent"
)} )}
> >
{r.domain} {r.domain}
</button> </button>
</div> </div>
<div className="flex items-center gap-0.5 shrink-0 opacity-60 group-hover:opacity-100"> <div className="flex items-center gap-1 shrink-0 opacity-0 group-hover:opacity-100 transition-all transform translate-x-2 group-hover:translate-x-0">
<button onClick={() => copy(r.domain)} className="w-7 h-7 flex items-center justify-center text-white/60 hover:text-white"> <button onClick={() => copy(r.domain)} className="w-7 h-7 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5">
{copied === r.domain ? <Check className="w-3 h-3 text-accent" /> : <Copy className="w-3 h-3" />} {copied === r.domain ? <Check className="w-3.5 h-3.5 text-accent" /> : <Copy className="w-3.5 h-3.5" />}
</button> </button>
<button onClick={() => track(r.domain)} disabled={tracking === r.domain} className="w-7 h-7 flex items-center justify-center text-white/60 hover:text-white"> <button onClick={() => track(r.domain)} disabled={tracking === r.domain} className="w-7 h-7 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5">
{tracking === r.domain ? <Loader2 className="w-3 h-3 animate-spin" /> : <Eye className="w-3 h-3" />} {tracking === r.domain ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-3.5 h-3.5" />}
</button> </button>
<button onClick={() => openAnalyze(r.domain)} className="w-7 h-7 flex items-center justify-center text-white/60 hover:text-accent"> <button onClick={() => openAnalyze(r.domain)} className="w-7 h-7 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/20 hover:bg-accent/5">
<Shield className="w-3 h-3" /> <Shield className="w-3.5 h-3.5" />
</button> </button>
<a <a
href={`https://www.namecheap.com/domains/registration/results/?domain=${r.domain}`} href={`https://www.namecheap.com/domains/registration/results/?domain=${r.domain}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className={clsx( className={clsx(
"h-7 px-2 text-[10px] font-bold flex items-center", "h-7 px-3 text-[10px] font-black uppercase tracking-widest flex items-center transition-all",
mode === 'ai' mode === 'ai'
? "bg-purple-500 text-white hover:bg-purple-400" ? "bg-purple-600 text-white hover:bg-purple-500"
: "bg-accent text-black hover:bg-white" : "bg-accent text-black hover:bg-white"
)} )}
> >
@ -454,21 +381,20 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
{/* Empty State */} {/* Empty State */}
{results.length === 0 && !isGenerating && ( {results.length === 0 && !isGenerating && (
<div className="text-center py-12 border border-dashed border-white/10"> <div className="text-center py-24 border border-dashed border-white/[0.08] bg-white/[0.01]">
<Wand2 className="w-10 h-10 text-white/20 mx-auto mb-3" /> <Wand2 className="w-12 h-12 text-white/5 mx-auto mb-4" />
<p className="text-white/50"> <p className="text-white/40 text-sm font-mono uppercase tracking-widest font-bold">Forge is currently idle</p>
{mode === 'pattern' <p className="text-white/20 text-[10px] font-mono mt-3 uppercase tracking-wider max-w-xs mx-auto leading-relaxed">
? 'Choose a pattern and click Generate' Select a generation mode above to synthesize available brandable assets
: 'Describe your brand concept'}
</p> </p>
</div> </div>
)} )}
{/* Loading State */} {/* Loading Grid */}
{isGenerating && results.length === 0 && ( {isGenerating && results.length === 0 && (
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2"> <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-2">
{[...Array(12)].map((_, i) => ( {[...Array(12)].map((_, i) => (
<div key={i} className="h-12 bg-white/5 animate-pulse" /> <div key={i} className="h-14 bg-white/[0.01] border border-white/[0.05] animate-pulse" />
))} ))}
</div> </div>
)} )}

View File

@ -5,7 +5,6 @@ import { api } from '@/lib/api'
import { useAnalyzePanelStore } from '@/lib/analyze-store' import { useAnalyzePanelStore } from '@/lib/analyze-store'
import { useStore } from '@/lib/store' import { useStore } from '@/lib/store'
import { import {
Clock,
Globe, Globe,
Loader2, Loader2,
Search, Search,
@ -165,7 +164,7 @@ export function DropsTab({ showToast }: DropsTabProps) {
setTracking(domain) setTracking(domain)
try { try {
await addDomain(domain) await addDomain(domain)
showToast(`Tracking ${domain}`, 'success') showToast(`Added: ${domain}`, 'success')
} catch (e) { } catch (e) {
showToast(e instanceof Error ? e.message : 'Failed', 'error') showToast(e instanceof Error ? e.message : 'Failed', 'error')
} finally { } finally {
@ -237,7 +236,7 @@ export function DropsTab({ showToast }: DropsTabProps) {
<div className="text-xl font-bold text-white font-mono"> <div className="text-xl font-bold text-white font-mono">
{stats?.daily_drops?.toLocaleString() || total.toLocaleString()} {stats?.daily_drops?.toLocaleString() || total.toLocaleString()}
</div> </div>
<div className="text-[10px] font-mono text-white/40 uppercase">Fresh drops (24h)</div> <div className="text-[10px] font-mono text-white/40 uppercase tracking-wider">Fresh drops detected (24h)</div>
</div> </div>
</div> </div>
<button <button
@ -278,8 +277,8 @@ export function DropsTab({ showToast }: DropsTabProps) {
<button <button
onClick={() => setSelectedTld(null)} onClick={() => setSelectedTld(null)}
className={clsx( className={clsx(
"px-2.5 py-1.5 text-[10px] font-mono uppercase border transition-colors", "px-3 py-1.5 text-[10px] font-mono uppercase border transition-colors",
selectedTld === null ? "border-accent bg-accent/10 text-accent" : "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"
)} )}
> >
All All
@ -289,8 +288,8 @@ export function DropsTab({ showToast }: DropsTabProps) {
key={tld} key={tld}
onClick={() => setSelectedTld(tld)} onClick={() => setSelectedTld(tld)}
className={clsx( className={clsx(
"px-2.5 py-1.5 text-[10px] font-mono uppercase border transition-colors flex items-center gap-1", "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" : "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"
)} )}
> >
<span className="text-xs">{flag}</span>.{tld} <span className="text-xs">{flag}</span>.{tld}
@ -302,15 +301,15 @@ export function DropsTab({ showToast }: DropsTabProps) {
<button <button
onClick={() => setFiltersOpen(!filtersOpen)} onClick={() => setFiltersOpen(!filtersOpen)}
className={clsx( className={clsx(
"flex items-center justify-between w-full py-2 px-3 border transition-colors", "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]" filtersOpen ? "border-accent/30 bg-accent/[0.05]" : "border-white/[0.08] bg-white/[0.02]"
)} )}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Filter className="w-4 h-4 text-white/40" /> <Filter className="w-4 h-4 text-white/40" />
<span className="text-xs font-mono text-white/60">Filters</span> <span className="text-xs font-mono text-white/60 uppercase tracking-widest">Advanced Filters</span>
{activeFiltersCount > 0 && ( {activeFiltersCount > 0 && (
<span className="px-1.5 py-0.5 text-[9px] font-bold bg-accent text-black">{activeFiltersCount}</span> <span className="px-1.5 py-0.5 text-[9px] font-bold bg-accent text-black ml-1">{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 text-white/30 transition-transform", filtersOpen && "rotate-90")} />
@ -318,27 +317,27 @@ export function DropsTab({ showToast }: DropsTabProps) {
{/* Filters Panel */} {/* Filters Panel */}
{filtersOpen && ( {filtersOpen && (
<div className="p-3 border border-white/[0.08] bg-white/[0.02] space-y-3 animate-in fade-in slide-in-from-top-2 duration-200"> <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">
{/* Length Filter */} {/* Length Filter */}
<div> <div>
<div className="text-[9px] font-mono text-white/30 uppercase tracking-wider mb-2">Length</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="flex gap-2 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-16 bg-white/[0.02] border border-white/10 px-2 py-1.5 text-xs text-white placeholder:text-white/25 outline-none font-mono" 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"
min={1} min={1}
max={63} max={63}
/> />
<span className="text-white/20"></span> <span className="text-white/10 px-1 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-16 bg-white/[0.02] border border-white/10 px-2 py-1.5 text-xs text-white placeholder:text-white/25 outline-none font-mono" 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"
min={1} min={1}
max={63} max={63}
/> />
@ -350,15 +349,15 @@ export function DropsTab({ showToast }: DropsTabProps) {
<button <button
onClick={() => setExcludeNumeric(!excludeNumeric)} onClick={() => setExcludeNumeric(!excludeNumeric)}
className={clsx( className={clsx(
"flex-1 flex items-center justify-between py-2 px-3 border transition-colors", "flex-1 flex items-center justify-between py-2.5 px-4 border transition-colors",
excludeNumeric ? "border-white/20 bg-white/[0.05]" : "border-white/[0.08]" excludeNumeric ? "border-accent/30 bg-accent/5" : "border-white/[0.08]"
)} )}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2.5">
<Hash className="w-3.5 h-3.5 text-white/40" /> <Hash className={clsx("w-3.5 h-3.5", excludeNumeric ? "text-accent" : "text-white/30")} />
<span className="text-[10px] font-mono text-white/60">No numbers</span> <span className={clsx("text-[10px] font-mono uppercase tracking-wider", excludeNumeric ? "text-accent" : "text-white/50")}>Exclude Numeric</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/30")}> <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>} {excludeNumeric && <span className="text-black text-[8px] font-bold"></span>}
</div> </div>
</button> </button>
@ -366,15 +365,15 @@ export function DropsTab({ showToast }: DropsTabProps) {
<button <button
onClick={() => setExcludeHyphen(!excludeHyphen)} onClick={() => setExcludeHyphen(!excludeHyphen)}
className={clsx( className={clsx(
"flex-1 flex items-center justify-between py-2 px-3 border transition-colors", "flex-1 flex items-center justify-between py-2.5 px-4 border transition-colors",
excludeHyphen ? "border-white/20 bg-white/[0.05]" : "border-white/[0.08]" excludeHyphen ? "border-accent/30 bg-accent/5" : "border-white/[0.08]"
)} )}
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2.5">
<Ban className="w-3.5 h-3.5 text-white/40" /> <Ban className={clsx("w-3.5 h-3.5", excludeHyphen ? "text-accent" : "text-white/30")} />
<span className="text-[10px] font-mono text-white/60">No hyphens</span> <span className={clsx("text-[10px] font-mono uppercase tracking-wider", excludeHyphen ? "text-accent" : "text-white/50")}>Exclude Hyphen</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/30")}> <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>} {excludeHyphen && <span className="text-black text-[8px] font-bold"></span>}
</div> </div>
</button> </button>
@ -382,57 +381,66 @@ export function DropsTab({ showToast }: DropsTabProps) {
</div> </div>
)} )}
{/* Stats Bar */} {/* Results Header */}
<div className="flex items-center justify-between text-[10px] font-mono text-white/40"> <div className="flex items-center justify-between px-1 text-[10px] font-mono text-white/30 uppercase tracking-[0.1em]">
<span>{total.toLocaleString()} fresh drops</span> <div className="flex items-center gap-2">
{totalPages > 1 && <span>Page {page}/{totalPages}</span>} <div className="w-1 h-1 bg-accent rounded-full" />
<span>{total.toLocaleString()} domains matching criteria</span>
</div>
{totalPages > 1 && <span>Page {page} of {totalPages}</span>}
</div> </div>
{/* Results */} {/* Results Table */}
{sortedItems.length === 0 ? ( {sortedItems.length === 0 ? (
<div className="text-center py-16 border border-dashed border-white/[0.08]"> <div className="text-center py-24 border border-dashed border-white/[0.08] bg-white/[0.01]">
<Globe className="w-8 h-8 text-white/10 mx-auto mb-3" /> <Globe className="w-12 h-12 text-white/5 mx-auto mb-4" />
<p className="text-white/40 text-sm font-mono">No fresh drops</p> <p className="text-white/40 text-sm font-mono uppercase tracking-widest font-bold">No fresh drops detected</p>
<p className="text-white/25 text-xs font-mono mt-1">Check back after the next sync</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
</p>
</div> </div>
) : ( ) : (
<> <>
<div className="space-y-px bg-white/[0.04] border border-white/[0.08]"> <div className="border border-white/[0.08] bg-white/[0.01] overflow-hidden">
{/* Desktop Table Header */} {/* Desktop Table Header */}
<div className="hidden lg:grid grid-cols-[1fr_60px_80px_100px] gap-4 px-3 py-2 text-[10px] font-mono text-white/40 uppercase tracking-wider border-b border-white/[0.08]"> <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-1 hover:text-white/60 text-left" title="Freshly dropped domain - was registered but not renewed"> <button onClick={() => handleSort('domain')} className="flex items-center gap-2 hover:text-white transition-colors text-left group">
Domain <span className={clsx(sortField === 'domain' && "text-accent font-bold")}>Domain Name</span>
{sortField === 'domain' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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-1 justify-center hover:text-white/60" title="Character count - shorter domains are more valuable"> <button onClick={() => handleSort('length')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
Len <span className={clsx(sortField === 'length' && "text-accent font-bold")}>Length</span>
{sortField === 'length' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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-1 justify-center hover:text-white/60" title="When the domain was detected as dropped"> <button onClick={() => handleSort('date')} className="flex items-center gap-2 justify-center hover:text-white transition-colors group">
When <span className={clsx(sortField === 'date' && "text-accent font-bold")}>Dropped</span>
{sortField === 'date' && (sortDirection === 'asc' ? <ChevronUp className="w-3 h-3" /> : <ChevronDown className="w-3 h-3" />)} {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">Actions</div> <div className="text-right pr-2">Actions</div>
</div> </div>
<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"> <div key={`${item.domain}.${item.tld}`} className="bg-[#020202] hover:bg-white/[0.02] transition-all group">
{/* Mobile Row */} {/* Mobile Row */}
<div className="lg:hidden p-3"> <div className="lg:hidden p-4">
<div className="flex items-center justify-between gap-3 mb-2"> <div className="flex items-center justify-between gap-3 mb-4">
<div className="flex items-center gap-2 min-w-0 flex-1"> <div className="flex flex-col min-w-0">
<button onClick={() => openAnalyze(`${item.domain}.${item.tld}`)} className="text-sm font-bold text-white font-mono truncate text-left"> <button
{item.domain}<span className="text-white/40">.{item.tld}</span> 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> </button>
</div> <div className="flex items-center gap-3 mt-1.5">
<div className="flex items-center gap-2 shrink-0">
<span className={clsx( <span className={clsx(
"text-[10px] font-mono font-bold px-1.5 py-0.5", "text-[9px] font-mono font-bold px-2 py-0.5 border",
item.length <= 5 ? "text-accent bg-accent/10" : "text-white/40 bg-white/5" item.length <= 5 ? "text-accent border-accent/20 bg-accent/5" : "text-white/30 border-white/5 bg-white/5"
)}> )}>
{item.length} LEN: {item.length}
</span> </span>
<span className="text-[10px] font-mono text-white/30">{formatTime(item.dropped_date)}</span> <span className="text-[10px] font-mono text-white/20 uppercase tracking-wider">{formatTime(item.dropped_date)}</span>
</div>
</div> </div>
</div> </div>
@ -440,94 +448,104 @@ export function DropsTab({ showToast }: DropsTabProps) {
<button <button
onClick={() => track(`${item.domain}.${item.tld}`)} onClick={() => track(`${item.domain}.${item.tld}`)}
disabled={tracking === `${item.domain}.${item.tld}`} disabled={tracking === `${item.domain}.${item.tld}`}
className="flex-1 py-2 text-[10px] font-bold uppercase tracking-wider border border-white/[0.08] text-white/40 flex items-center justify-center gap-1.5" 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 h-3 animate-spin" /> : <Eye className="w-3 h-3" />} {tracking === `${item.domain}.${item.tld}` ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-4 h-4" />}
Track Track
</button> </button>
<button onClick={() => openAnalyze(`${item.domain}.${item.tld}`)} className="w-10 py-2 border border-white/[0.08] text-white/50 flex items-center justify-center"> <button
<Shield className="w-3.5 h-3.5" /> 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> </button>
<a <a
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`} href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="flex-1 py-2 bg-accent text-black text-[10px] font-bold uppercase flex items-center justify-center gap-1" 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"
> >
Get <ExternalLink className="w-3 h-3" /> Buy Now
</a> </a>
</div> </div>
</div> </div>
{/* Desktop Row */} {/* Desktop Row */}
<div className="hidden lg:grid grid-cols-[1fr_60px_80px_100px] gap-4 items-center p-4 group"> <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-2 min-w-0 flex-1"> <div className="flex items-center gap-3 min-w-0">
<button <button
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)} onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
className="text-sm font-bold text-white font-mono truncate group-hover:text-accent transition-colors text-left" 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/40 group-hover:text-accent/60">.{item.tld}</span> {item.domain}<span className="text-white/30 group-hover:text-accent/40">.{item.tld}</span>
</button> </button>
</div> </div>
<div className="text-center"> <div className="text-center">
<span className={clsx( <span className={clsx(
"text-[10px] font-mono font-bold px-1.5 py-0.5", "text-[10px] font-mono font-bold px-2 py-0.5 border",
item.length <= 5 ? "text-accent bg-accent/10" : item.length <= 8 ? "text-amber-400 bg-amber-400/10" : "text-white/40 bg-white/5" 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} {item.length}
</span> </span>
</div> </div>
<div className="text-center"> <div className="text-center">
<span className="text-[10px] font-mono text-white/50">{formatTime(item.dropped_date)}</span> <span className="text-[10px] font-mono text-white/40 uppercase tracking-wider">{formatTime(item.dropped_date)}</span>
</div> </div>
<div className="flex items-center justify-end gap-1.5 opacity-50 group-hover:opacity-100 transition-opacity"> <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 <button
onClick={() => track(`${item.domain}.${item.tld}`)} onClick={() => track(`${item.domain}.${item.tld}`)}
disabled={tracking === `${item.domain}.${item.tld}`} disabled={tracking === `${item.domain}.${item.tld}`}
className="w-6 h-6 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5" 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 h-3 animate-spin" /> : <Eye className="w-3 h-3" />} {tracking === `${item.domain}.${item.tld}` ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-4 h-4" />}
</button> </button>
<button <button
onClick={() => openAnalyze(`${item.domain}.${item.tld}`)} onClick={() => openAnalyze(`${item.domain}.${item.tld}`)}
className="w-6 h-6 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/20 hover:bg-accent/10" 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-3 h-3" /> <Shield className="w-4 h-4" />
</button> </button>
<a <a
href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`} href={`https://www.namecheap.com/domains/registration/results/?domain=${item.domain}.${item.tld}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="h-6 px-2 bg-accent text-black text-[10px] font-bold flex items-center gap-1 hover:bg-white" 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)]"
> >
Get Buy
</a> </a>
</div> </div>
</div> </div>
</div> </div>
))} ))}
</div> </div>
</div>
{/* Pagination */} {/* Pagination */}
{totalPages > 1 && ( {totalPages > 1 && (
<div className="flex items-center justify-center gap-1 pt-2"> <div className="flex items-center justify-center gap-1 pt-6">
<button <button
onClick={() => handlePageChange(page - 1)} onClick={() => handlePageChange(page - 1)}
disabled={page === 1} disabled={page === 1}
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-30 disabled:cursor-not-allowed" 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"
> >
<ChevronLeft className="w-4 h-4" /> <ChevronLeft className="w-5 h-5" />
</button> </button>
<span className="text-xs text-white/50 font-mono px-3">{page}/{totalPages}</span> <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}
</span>
</div>
<button <button
onClick={() => handlePageChange(page + 1)} onClick={() => handlePageChange(page + 1)}
disabled={page === totalPages} disabled={page === totalPages}
className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/50 hover:text-white hover:bg-white/5 disabled:opacity-30 disabled:cursor-not-allowed" 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"
> >
<ChevronRight className="w-4 h-4" /> <ChevronRight className="w-5 h-5" />
</button> </button>
</div> </div>
)} )}

View File

@ -298,7 +298,7 @@ export function SearchTab({ showToast }: SearchTabProps) {
onClick={() => handleSearch(searchQuery)} onClick={() => handleSearch(searchQuery)}
disabled={!searchQuery.trim()} disabled={!searchQuery.trim()}
className={clsx( className={clsx(
"h-full px-4 py-4 text-sm font-bold uppercase tracking-wider transition-all", "h-full px-6 py-4 text-xs font-bold uppercase tracking-widest transition-all shrink-0 border-l border-white/10",
searchQuery.trim() ? "bg-accent text-black hover:bg-white" : "bg-white/5 text-white/20" searchQuery.trim() ? "bg-accent text-black hover:bg-white" : "bg-white/5 text-white/20"
)} )}
> >
@ -308,115 +308,122 @@ export function SearchTab({ showToast }: SearchTabProps) {
</div> </div>
{/* Stats Bar */} {/* Stats Bar */}
<div className="flex items-center justify-between text-[10px] font-mono text-white/40"> <div className="flex items-center justify-between px-1 text-[10px] font-mono text-white/30 uppercase tracking-[0.1em]">
<span>Enter a name (checks 10 TLDs) or full domain (e.g. example.com)</span> <div className="flex items-center gap-2">
<span className="flex items-center gap-1"> <div className="w-1 h-1 bg-accent rounded-full animate-pulse" />
<Sparkles className="w-3 h-3" /> <span>Enter a name or full domain</span>
RDAP/WHOIS </div>
</span> <div className="flex items-center gap-2">
<Sparkles className="w-3 h-3 text-accent/60" />
<span>RDAP/WHOIS READY</span>
</div>
</div> </div>
{/* Single Domain Result */} {/* Single Domain Result */}
{searchMode === 'single' && searchResult && ( {searchMode === 'single' && searchResult && (
<div className="animate-in fade-in slide-in-from-bottom-2 duration-200"> <div className="animate-in fade-in slide-in-from-bottom-2 duration-300">
{searchResult.loading ? ( {searchResult.loading ? (
<div className="flex items-center justify-center gap-3 py-12 border border-white/[0.08] bg-white/[0.02]"> <div className="flex flex-col items-center justify-center py-20 border border-white/[0.08] bg-white/[0.01]">
<Loader2 className="w-6 h-6 animate-spin text-accent" /> <div className="relative">
<span className="text-sm text-white/50 font-mono">Checking availability...</span> <div className="absolute inset-0 bg-accent/20 blur-xl rounded-full animate-pulse" />
<Loader2 className="w-8 h-8 animate-spin text-accent relative z-10" />
</div>
<span className="text-[10px] text-white/40 font-mono mt-4 uppercase tracking-[0.2em]">Checking global availability...</span>
</div> </div>
) : searchResult.error ? ( ) : searchResult.error ? (
// Error state (invalid TLD, check failed, etc.) // Error state
<div className="border-2 border-rose-500/30 overflow-hidden bg-[#020202]"> <div className="border border-rose-500/30 bg-rose-500/[0.02]">
<div className="p-4"> <div className="p-4 lg:p-6">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<div className="w-12 h-12 flex items-center justify-center border border-rose-500/30 bg-rose-500/10 shrink-0"> <div className="w-14 h-14 flex items-center justify-center border border-rose-500/30 bg-rose-500/10 shrink-0">
<XCircle className="w-6 h-6 text-rose-500" /> <XCircle className="w-6 h-6 text-rose-500" />
</div> </div>
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className="text-lg font-bold text-white font-mono truncate">{searchResult.domain}</div> <div className="text-xl font-bold text-white font-mono truncate tracking-tight">{searchResult.domain}</div>
<div className="text-sm text-rose-400 mt-1">{searchResult.error}</div> <div className="text-[11px] font-mono text-rose-400 mt-1 uppercase tracking-wider">{searchResult.error}</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
) : ( ) : (
<div className={clsx( <div className={clsx(
"border-2 overflow-hidden bg-[#020202]", "border bg-[#020202] transition-all duration-500",
searchResult.is_available ? "border-accent/40" : "border-rose-500/30" searchResult.is_available ? "border-accent/30 shadow-[0_0_40px_-20px_rgba(34,211,126,0.2)]" : "border-rose-500/20"
)}> )}>
{/* Result Row */} {/* Result Row */}
<div className="p-4"> <div className="p-4 lg:p-6">
<div className="flex items-center justify-between gap-4"> <div className="flex flex-col lg:grid lg:grid-cols-[1fr_auto] gap-6 items-center">
<div className="flex items-center gap-4 min-w-0 flex-1"> <div className="flex items-center gap-5 min-w-0 w-full">
{/* Status Icon */} {/* Status Indicator */}
<div className={clsx( <div className={clsx(
"w-12 h-12 flex items-center justify-center border shrink-0", "w-16 h-16 flex items-center justify-center border shrink-0 transition-colors duration-500",
searchResult.is_available ? "bg-accent/10 border-accent/30" : "bg-rose-500/10 border-rose-500/30" searchResult.is_available ? "bg-accent/10 border-accent/20" : "bg-rose-500/5 border-rose-500/20"
)}> )}>
{searchResult.is_available ? ( {searchResult.is_available ? (
<CheckCircle2 className="w-6 h-6 text-accent" /> <CheckCircle2 className="w-8 h-8 text-accent" />
) : ( ) : (
<XCircle className="w-6 h-6 text-rose-500" /> <XCircle className="w-8 h-8 text-rose-500" />
)} )}
</div> </div>
{/* Domain Info */} {/* Domain Info */}
<div className="min-w-0 flex-1"> <div className="min-w-0 flex-1">
<div className={clsx( <div className={clsx(
"text-lg font-bold font-mono truncate", "text-2xl lg:text-3xl font-bold font-mono truncate tracking-tight",
searchResult.is_available ? "text-white" : "text-rose-400" searchResult.is_available ? "text-white" : "text-rose-400"
)}>{searchResult.domain}</div> )}>
<div className="flex items-center gap-3 text-[10px] font-mono text-white/40 mt-1"> {searchResult.domain}
</div>
<div className="flex flex-wrap items-center gap-y-2 gap-x-4 mt-2">
<span className={clsx( <span className={clsx(
"px-2 py-0.5 uppercase font-bold", "px-2 py-0.5 text-[9px] font-bold uppercase tracking-widest border",
searchResult.is_available ? "bg-accent/20 text-accent" : "bg-rose-500/20 text-rose-400" searchResult.is_available
? "bg-accent/10 border-accent/20 text-accent"
: "bg-rose-500/10 border-rose-500/20 text-rose-400"
)}> )}>
{searchResult.is_available ? 'Available' : 'Taken'} {searchResult.is_available ? 'Available' : 'Taken'}
</span> </span>
{searchResult.registrar && ( {searchResult.registrar && (
<> <div className="flex items-center gap-1.5 text-[10px] font-mono text-white/30 uppercase tracking-wider">
<span className="text-white/10">|</span>
<span className="flex items-center gap-1" title="Current registrar holding this domain">
<Building className="w-3 h-3" /> <Building className="w-3 h-3" />
{searchResult.registrar} {searchResult.registrar}
</span> </div>
</>
)} )}
{searchResult.expiration_date && ( {searchResult.expiration_date && (
<> <div className="flex items-center gap-1.5 text-[10px] font-mono text-white/30 uppercase tracking-wider">
<span className="text-white/10">|</span> <Clock className="w-3 h-3" />
<span className="flex items-center gap-1" title="Domain expiration date - monitor for potential drop"> Expires {new Date(searchResult.expiration_date).toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' })}
<Calendar className="w-3 h-3" /> </div>
Expires {new Date(searchResult.expiration_date).toLocaleDateString()}
</span>
</>
)} )}
</div> </div>
</div> </div>
</div> </div>
{/* Actions */} {/* Actions */}
<div className="flex items-center gap-2 shrink-0"> <div className="flex items-center gap-2 w-full lg:w-auto">
<button <button
onClick={() => openAnalyze(searchResult.domain)} onClick={() => openAnalyze(searchResult.domain)}
className="w-9 h-9 flex items-center justify-center border border-white/10 text-white/30 hover:text-accent hover:border-accent/20 hover:bg-accent/10 transition-colors" className="flex-1 lg:w-12 lg:h-12 h-12 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 - View SEO metrics, backlinks, and valuation" title="Deep Analysis"
> >
<Shield className="w-4 h-4" /> <Shield className="w-5 h-5" />
</button> </button>
<button <button
onClick={handleAddToWatchlist} onClick={handleAddToWatchlist}
disabled={addingToWatchlist} disabled={addingToWatchlist}
className={clsx( className={clsx(
"w-9 h-9 flex items-center justify-center border transition-colors", "flex-1 lg:w-12 lg:h-12 h-12 flex items-center justify-center border transition-all",
searchResult.is_available searchResult.is_available
? "border-white/10 text-white/30 hover:text-white hover:bg-white/5" ? "border-white/10 text-white/30 hover:text-white hover:bg-white/5"
: "border-rose-500/30 text-rose-400 hover:bg-rose-500/10" : "border-rose-500/20 text-rose-400/40 hover:text-rose-400 hover:bg-rose-500/5"
)} )}
title={searchResult.is_available ? "Add to watchlist to track this domain" : "Monitor this domain and get notified when it drops"} title={searchResult.is_available ? "Track domain" : "Monitor drop"}
> >
{addingToWatchlist ? <Loader2 className="w-4 h-4 animate-spin" /> : <Eye className="w-4 h-4" />} {addingToWatchlist ? <Loader2 className="w-5 h-5 animate-spin" /> : <Eye className="w-5 h-5" />}
</button> </button>
{searchResult.is_available ? ( {searchResult.is_available ? (
@ -424,20 +431,18 @@ export function SearchTab({ showToast }: SearchTabProps) {
href={`https://www.namecheap.com/domains/registration/results/?domain=${searchResult.domain}`} href={`https://www.namecheap.com/domains/registration/results/?domain=${searchResult.domain}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="h-9 px-4 bg-accent text-black text-xs font-bold uppercase tracking-wider flex items-center gap-2 hover:bg-white transition-colors" className="flex-[2] lg:flex-none h-12 px-8 bg-accent text-black text-[11px] font-black uppercase tracking-[0.1em] flex items-center justify-center gap-2 hover:bg-white transition-all shadow-[0_0_20px_-10px_rgba(34,211,126,0.5)]"
title="Register this domain now via Namecheap"
> >
Register Buy Now
<ArrowRight className="w-3.5 h-3.5" /> <ArrowRight className="w-4 h-4" />
</a> </a>
) : ( ) : (
<button <button
onClick={handleAddToWatchlist} onClick={handleAddToWatchlist}
disabled={addingToWatchlist} disabled={addingToWatchlist}
className="h-9 px-4 bg-rose-500/20 text-rose-400 text-xs font-bold uppercase tracking-wider flex items-center gap-2 hover:bg-rose-500/30 border border-rose-500/30 transition-colors" className="flex-[2] lg:flex-none h-12 px-8 bg-rose-500/10 text-rose-400 text-[11px] font-bold uppercase tracking-[0.1em] flex items-center justify-center gap-2 hover:bg-rose-500/20 border border-rose-500/20 transition-all"
title="Add to watchlist and get notified when this domain becomes available"
> >
{addingToWatchlist ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-3.5 h-3.5" />} {addingToWatchlist ? <Loader2 className="w-4 h-4 animate-spin" /> : <Eye className="w-4 h-4" />}
Monitor Monitor
</button> </button>
)} )}
@ -451,39 +456,44 @@ export function SearchTab({ showToast }: SearchTabProps) {
{/* Multi-TLD Results */} {/* Multi-TLD Results */}
{searchMode === 'multi' && tldResults.length > 0 && ( {searchMode === 'multi' && tldResults.length > 0 && (
<div className="animate-in fade-in slide-in-from-bottom-2 duration-200"> <div className="animate-in fade-in slide-in-from-bottom-2 duration-400">
<div className="border border-white/[0.08] bg-[#020202] overflow-hidden"> <div className="border border-white/[0.08] bg-[#020202]">
{/* Header */} {/* Header */}
<div className="px-4 py-3 border-b border-white/[0.08] flex items-center justify-between"> <div className="px-5 py-4 border-b border-white/[0.08] flex items-center justify-between bg-white/[0.01]">
<div className="flex items-center gap-2"> <div className="flex items-center gap-3">
<Globe className="w-4 h-4 text-accent" /> <Globe className="w-4 h-4 text-accent/60" />
<span className="text-[10px] font-mono text-white/40 uppercase tracking-wider"> <span className="text-[10px] font-mono text-white/50 uppercase tracking-[0.2em]">
Checking {POPULAR_TLDS.length} extensions for "{searchQuery.toLowerCase().replace(/\s+/g, '')}" Global availability check: <span className="text-white">"{searchQuery}"</span>
</span> </span>
</div> </div>
<span className="text-[10px] font-mono text-accent"> <div className="flex items-center gap-4">
{tldResults.filter(r => r.is_available === true).length} available <div className="flex items-center gap-1.5">
<div className="w-1.5 h-1.5 rounded-full bg-accent" />
<span className="text-[10px] font-mono text-accent uppercase font-bold">
{tldResults.filter(r => r.is_available === true).length} Free
</span> </span>
</div> </div>
<div className="flex items-center gap-1.5">
<div className="w-1.5 h-1.5 rounded-full bg-rose-500/40" />
<span className="text-[10px] font-mono text-white/20 uppercase">
{tldResults.filter(r => r.is_available === false).length} Taken
</span>
</div>
</div>
</div>
{/* TLD Grid */} {/* TLD Grid */}
<div className="p-3 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-2"> <div className="p-4 grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3">
{tldResults.map((result) => ( {tldResults.map((result) => (
<div <div
key={result.tld} key={result.tld}
title={result.loading
? `Checking ${result.domain}...`
: result.is_available
? `${result.domain} is available! Click to see details.`
: `${result.domain} is already registered`
}
className={clsx( className={clsx(
"p-3 border transition-all", "relative p-4 border transition-all duration-300 group",
result.loading result.loading
? "border-white/[0.08] bg-white/[0.02]" ? "border-white/[0.05] bg-white/[0.01]"
: result.is_available : result.is_available
? "border-accent/40 bg-accent/[0.05] hover:bg-accent/10 cursor-pointer" ? "border-accent/20 bg-accent/[0.02] hover:border-accent/60 hover:bg-accent/[0.08] cursor-pointer"
: "border-rose-500/20 bg-rose-500/[0.02]" : "border-white/[0.05] bg-white/[0.01] opacity-60"
)} )}
onClick={() => { onClick={() => {
if (result.is_available && !result.loading) { if (result.is_available && !result.loading) {
@ -495,30 +505,38 @@ export function SearchTab({ showToast }: SearchTabProps) {
}} }}
> >
{result.loading ? ( {result.loading ? (
<div className="flex items-center justify-center py-1"> <div className="flex items-center justify-center py-2">
<Loader2 className="w-4 h-4 animate-spin text-white/30" /> <Loader2 className="w-5 h-5 animate-spin text-white/10" />
</div> </div>
) : ( ) : (
<> <>
<div className="flex items-center justify-between mb-1"> <div className="flex items-center justify-between mb-2">
<span className={clsx( <span className={clsx(
"text-xs font-mono font-bold", "text-sm font-mono font-black tracking-tight",
result.is_available ? "text-accent" : "text-rose-400/60" result.is_available ? "text-white group-hover:text-accent" : "text-white/20"
)}> )}>
.{result.tld} .{result.tld}
</span> </span>
{result.is_available ? ( {result.is_available ? (
<CheckCircle2 className="w-3.5 h-3.5 text-accent" /> <div className="w-5 h-5 rounded-full bg-accent/10 border border-accent/20 flex items-center justify-center">
<CheckCircle2 className="w-3 h-3 text-accent" />
</div>
) : ( ) : (
<XCircle className="w-3.5 h-3.5 text-rose-400/40" /> <XCircle className="w-4 h-4 text-white/10" />
)} )}
</div> </div>
<div className={clsx( <div className={clsx(
"text-[9px] font-mono uppercase", "text-[9px] font-mono uppercase tracking-[0.1em] font-bold",
result.is_available ? "text-accent/60" : "text-rose-400/40" result.is_available ? "text-accent" : "text-white/20"
)}> )}>
{result.is_available ? 'Available' : 'Taken'} {result.is_available ? 'Available' : 'Taken'}
</div> </div>
{result.is_available && (
<div className="absolute bottom-3 right-3 opacity-0 group-hover:opacity-100 transition-opacity">
<ArrowRight className="w-3 h-3 text-accent" />
</div>
)}
</> </>
)} )}
</div> </div>
@ -526,8 +544,8 @@ export function SearchTab({ showToast }: SearchTabProps) {
</div> </div>
{/* Footer hint */} {/* Footer hint */}
<div className="px-4 py-2 border-t border-white/[0.06] text-[9px] font-mono text-white/30 text-center"> <div className="px-4 py-3 border-t border-white/[0.04] bg-white/[0.01] text-[9px] font-mono text-white/20 text-center uppercase tracking-widest">
Click an available extension to see details Add ".tld" to your search for specific extension Click an available extension to analyze and buy
</div> </div>
</div> </div>
</div> </div>
@ -535,23 +553,23 @@ export function SearchTab({ showToast }: SearchTabProps) {
{/* Recent Searches */} {/* Recent Searches */}
{!searchResult && recentSearches.length > 0 && ( {!searchResult && recentSearches.length > 0 && (
<div className="border border-white/[0.08] bg-white/[0.02]"> <div className="border border-white/[0.08] bg-white/[0.01]">
<div className="px-4 py-3 border-b border-white/[0.08] flex items-center justify-between"> <div className="px-5 py-3 border-b border-white/[0.08] flex items-center justify-between bg-white/[0.01]">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<History className="w-4 h-4 text-white/30" /> <History className="w-4 h-4 text-white/20" />
<span className="text-[10px] font-mono text-white/40 uppercase tracking-wider">Recent Searches</span> <span className="text-[10px] font-mono text-white/40 uppercase tracking-[0.2em]">Recent History</span>
</div> </div>
<button <button
onClick={() => { onClick={() => {
setRecentSearches([]) setRecentSearches([])
localStorage.removeItem('pounce_recent_searches') localStorage.removeItem('pounce_recent_searches')
}} }}
className="text-[10px] font-mono text-white/30 hover:text-white transition-colors" className="text-[10px] font-mono text-white/20 hover:text-white transition-colors uppercase tracking-widest"
> >
Clear Clear All
</button> </button>
</div> </div>
<div className="p-3"> <div className="p-4">
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-2">
{recentSearches.map((domain) => ( {recentSearches.map((domain) => (
<button <button
@ -560,9 +578,9 @@ export function SearchTab({ showToast }: SearchTabProps) {
setSearchQuery(domain) setSearchQuery(domain)
handleSearch(domain) handleSearch(domain)
}} }}
className="group px-3 py-2 border border-white/[0.08] bg-[#020202] hover:border-accent/30 hover:bg-accent/[0.03] transition-all" className="group px-4 py-2 border border-white/[0.08] bg-white/[0.01] hover:border-accent/30 hover:bg-accent/[0.03] transition-all"
> >
<span className="text-xs font-mono text-white/60 group-hover:text-accent transition-colors">{domain}</span> <span className="text-xs font-mono text-white/40 group-hover:text-accent transition-colors">{domain}</span>
</button> </button>
))} ))}
</div> </div>

View File

@ -159,60 +159,60 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
} }
} }
const currentGeo = GEOS.find(g => g.code === geo)
const availableResults = results.filter(r => r.available) const availableResults = results.filter(r => r.available)
const takenResults = results.filter(r => !r.available) const takenResults = results.filter(r => !r.available)
return ( return (
<div className="space-y-6"> <div className="space-y-6">
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Header */}
{/* HEADER */} <div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4 pb-5 border-b border-white/[0.08]">
{/* ═══════════════════════════════════════════════════════════════════════ */} <div className="flex items-center gap-4">
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-3 pb-4 border-b border-white/10"> <div className="w-12 h-12 bg-orange-500/10 border border-orange-500/20 flex items-center justify-center shrink-0">
<Flame className="w-6 h-6 text-orange-400" />
</div>
<div> <div>
<h2 className="text-xl font-bold text-white flex items-center gap-2"> <h2 className="text-xl font-bold text-white font-mono tracking-tight">Trend Surfer</h2>
<Flame className="w-5 h-5 text-orange-400" /> <p className="text-[10px] font-mono text-white/40 uppercase tracking-widest mt-1">Ride the viral wave with AI-powered domain hunt</p>
Trend Surfer </div>
</h2>
<p className="text-sm text-white/60 mt-1">
Find domains for trending topics {currentGeo?.flag} {currentGeo?.name}
</p>
</div> </div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<div className="relative">
<select <select
value={geo} value={geo}
onChange={(e) => { setGeo(e.target.value); setSelected(null); setKeywords([]); setResults([]) }} onChange={(e) => { setGeo(e.target.value); setSelected(null); setKeywords([]); setResults([]) }}
className="h-10 px-3 bg-white/5 border border-white/10 text-sm text-white outline-none" className="h-10 pl-4 pr-10 bg-white/[0.02] border border-white/10 text-xs font-mono text-white uppercase tracking-widest appearance-none outline-none focus:border-accent/30 transition-all cursor-pointer"
> >
{GEOS.map(g => ( {GEOS.map(g => (
<option key={g.code} value={g.code}>{g.flag} {g.name}</option> <option key={g.code} value={g.code}>{g.flag} {g.name}</option>
))} ))}
</select> </select>
<ChevronDown className="absolute right-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-white/30 pointer-events-none" />
</div>
<button <button
onClick={loadTrends} onClick={loadTrends}
disabled={loading} disabled={loading}
className="h-10 w-10 flex items-center justify-center border border-white/10 text-white/60 hover:text-white hover:bg-white/5" className="h-10 w-10 flex items-center justify-center border border-white/10 text-white/40 hover:text-white hover:bg-white/5 transition-all"
> >
<RefreshCw className={clsx("w-4 h-4", loading && "animate-spin")} /> <RefreshCw className={clsx("w-4 h-4", loading && "animate-spin")} />
</button> </button>
</div> </div>
</div> </div>
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Trends Grid */}
{/* TRENDS GRID */} <div className="space-y-3">
{/* ═══════════════════════════════════════════════════════════════════════ */} <div className="flex items-center gap-2 px-1 text-[10px] font-mono text-white/30 uppercase tracking-[0.2em]">
<div> <div className="w-1 h-1 bg-orange-500 rounded-full animate-pulse" />
<p className="text-xs text-white/50 font-mono uppercase tracking-wider mb-3"> <span>Real-time Viral Topics ({geo})</span>
Select a trending topic </div>
</p>
{loading ? ( {loading ? (
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2"> <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-2">
{[...Array(8)].map((_, i) => ( {[...Array(12)].map((_, i) => (
<div key={i} className="h-14 bg-white/5 animate-pulse" /> <div key={i} className="h-14 bg-white/[0.01] border border-white/[0.05] animate-pulse" />
))} ))}
</div> </div>
) : ( ) : (
<div className="grid grid-cols-2 sm:grid-cols-4 gap-2"> <div className="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-2">
{trends.slice(0, 12).map((t, idx) => { {trends.slice(0, 12).map((t, idx) => {
const isSelected = selected === t.title const isSelected = selected === t.title
return ( return (
@ -220,19 +220,22 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
key={t.title} key={t.title}
onClick={() => selectTrend(t.title)} onClick={() => selectTrend(t.title)}
className={clsx( className={clsx(
"relative p-3 text-left border transition-all", "relative px-4 py-3.5 text-left border transition-all duration-300 group overflow-hidden",
isSelected isSelected
? "border-accent bg-accent/10" ? "border-accent bg-accent/10 shadow-[0_0_20px_-10px_rgba(34,211,126,0.3)]"
: "border-white/10 bg-white/[0.02] hover:border-white/20 hover:bg-white/[0.04]" : "border-white/[0.08] bg-white/[0.01] hover:border-white/20 hover:bg-white/[0.03]"
)} )}
> >
{idx < 3 && <span className="absolute top-1.5 right-1.5 text-[10px]">🔥</span>} <div className="relative z-10 flex items-center justify-between gap-2">
<p className={clsx( <span className={clsx(
"text-sm font-medium truncate pr-4", "text-xs font-bold font-mono truncate tracking-tight",
isSelected ? "text-accent" : "text-white" isSelected ? "text-accent" : "text-white/70 group-hover:text-white"
)}> )}>
{t.title} {t.title}
</p> </span>
{idx < 3 && !isSelected && <Flame className="w-3 h-3 text-orange-500/40 group-hover:text-orange-500 transition-colors" />}
</div>
{isSelected && <div className="absolute top-0 right-0 w-1.5 h-1.5 bg-accent" />}
</button> </button>
) )
})} })}
@ -240,84 +243,73 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
)} )}
</div> </div>
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Keyword Builder */}
{/* KEYWORD BUILDER */}
{/* ═══════════════════════════════════════════════════════════════════════ */}
{selected && ( {selected && (
<div className="bg-white/[0.02] border border-white/10 p-4 sm:p-5 space-y-5"> <div className="border border-white/[0.08] bg-white/[0.01] overflow-hidden animate-in fade-in slide-in-from-top-4 duration-500">
{/* Selected Trend Header */} <div className="px-5 py-4 border-b border-white/[0.08] flex items-center justify-between bg-white/[0.01]">
<div className="flex items-center justify-between"> <div className="flex items-center gap-3">
<div className="w-8 h-8 rounded-full bg-accent/10 border border-accent/20 flex items-center justify-center">
<Sparkles className="w-4 h-4 text-accent" />
</div>
<div> <div>
<p className="text-xs text-white/50 font-mono uppercase tracking-wider">Building domains for</p> <div className="text-[10px] font-mono text-white/30 uppercase tracking-[0.2em]">Target Concept</div>
<h3 className="text-lg font-bold text-white">{selected}</h3> <div className="text-sm font-bold text-white uppercase tracking-wider">{selected}</div>
</div>
</div> </div>
<button <button
onClick={() => { setSelected(null); setKeywords([]); setResults([]) }} onClick={() => { setSelected(null); setKeywords([]); setResults([]) }}
className="p-2 text-white/40 hover:text-white" className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/30 hover:text-white hover:bg-white/5 transition-all"
> >
<X className="w-4 h-4" /> <X className="w-4 h-4" />
</button> </button>
</div> </div>
<div className="p-5 space-y-6">
{/* Keywords */} {/* Keywords */}
<div> <div className="space-y-3">
<div className="flex items-center gap-2 mb-3"> <div className="flex items-center justify-between">
<p className="text-xs text-white/60 font-mono uppercase tracking-wider">Keywords to check</p> <span className="text-[10px] font-mono text-white/30 uppercase tracking-widest">Constructed Keywords</span>
{aiLoading && ( {aiLoading && (
<span className="flex items-center gap-1 text-[10px] text-purple-400"> <span className="flex items-center gap-2 text-[9px] font-mono text-purple-400 uppercase font-bold animate-pulse">
<Loader2 className="w-3 h-3 animate-spin" /> <Loader2 className="w-3 h-3 animate-spin" />
AI expanding... AI Expansion in progress...
</span> </span>
)} )}
{hasAI && keywords.length > 1 && !aiLoading && (
<span className="flex items-center gap-1 text-[10px] text-purple-400">
<Sparkles className="w-3 h-3" />
AI expanded
</span>
)}
{!hasAI && (
<Link href="/pricing" className="flex items-center gap-1 text-[10px] text-white/40 hover:text-accent">
<Lock className="w-3 h-3" />
Upgrade for AI
</Link>
)}
</div> </div>
<div className="flex flex-wrap gap-2 mb-3"> <div className="flex flex-wrap gap-2">
{keywords.map((kw, idx) => ( {keywords.map((kw, idx) => (
<span <span
key={kw} key={kw}
className={clsx( className={clsx(
"inline-flex items-center gap-1.5 px-3 py-1.5 text-sm font-mono border", "inline-flex items-center gap-2 px-3 py-1.5 text-xs font-mono border transition-all",
idx === 0 idx === 0
? "bg-accent/10 border-accent/30 text-accent" ? "bg-accent/10 border-accent/30 text-accent font-bold"
: "bg-purple-500/10 border-purple-500/30 text-purple-300" : "bg-purple-500/10 border-purple-500/20 text-purple-300"
)} )}
> >
{kw} {kw}
{keywords.length > 1 && ( <button onClick={() => removeKeyword(kw)} className="text-white/20 hover:text-rose-400 transition-colors">
<button onClick={() => removeKeyword(kw)} className="hover:text-white">
<X className="w-3 h-3" /> <X className="w-3 h-3" />
</button> </button>
)}
</span> </span>
))} ))}
{/* Add custom keyword */} {/* Add custom keyword */}
<div className="flex"> <div className="relative">
<input <input
value={customKw} value={customKw}
onChange={(e) => setCustomKw(e.target.value)} onChange={(e) => setCustomKw(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && addKeyword()} onKeyDown={(e) => e.key === 'Enter' && addKeyword()}
placeholder="+ add" placeholder="+ CUSTOM KW"
className="w-20 px-2 py-1.5 bg-transparent border border-white/10 text-sm font-mono text-white placeholder:text-white/30 outline-none focus:border-white/30" className="w-28 px-3 py-1.5 bg-white/[0.03] border border-white/10 text-[10px] font-mono text-white placeholder:text-white/20 outline-none focus:border-accent/30 uppercase tracking-wider transition-all"
/> />
</div> </div>
</div> </div>
</div> </div>
{/* TLDs */} {/* TLDs */}
<div> <div className="space-y-3">
<p className="text-xs text-white/60 font-mono uppercase tracking-wider mb-3">TLDs</p> <span className="text-[10px] font-mono text-white/30 uppercase tracking-widest">Selected Extensions</span>
<div className="flex flex-wrap gap-2"> <div className="flex flex-wrap gap-1.5">
{TLDS.map(tld => ( {TLDS.map(tld => (
<button <button
key={tld} key={tld}
@ -325,10 +317,10 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
prev.includes(tld) ? prev.filter(t => t !== tld) : [...prev, tld] prev.includes(tld) ? prev.filter(t => t !== tld) : [...prev, tld]
)} )}
className={clsx( className={clsx(
"px-3 py-2 text-sm font-mono border transition-all", "px-3 py-1.5 text-[10px] font-mono border transition-all uppercase tracking-widest",
tlds.includes(tld) tlds.includes(tld)
? "border-accent bg-accent/10 text-accent" ? "border-accent bg-accent/10 text-accent font-black"
: "border-white/10 text-white/50 hover:text-white" : "border-white/10 text-white/40 hover:text-white hover:bg-white/5"
)} )}
> >
.{tld} .{tld}
@ -342,56 +334,62 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
onClick={checkAvailability} onClick={checkAvailability}
disabled={checking || tlds.length === 0 || keywords.length === 0} disabled={checking || tlds.length === 0 || keywords.length === 0}
className={clsx( className={clsx(
"w-full py-4 text-sm font-bold uppercase tracking-wider flex items-center justify-center gap-2 transition-all", "w-full py-4 text-[11px] font-black uppercase tracking-[0.2em] flex items-center justify-center gap-3 transition-all",
checking || tlds.length === 0 || keywords.length === 0 checking || tlds.length === 0 || keywords.length === 0
? "bg-white/10 text-white/40" ? "bg-white/5 text-white/20 border border-white/5"
: "bg-accent text-black hover:bg-white" : "bg-accent text-black hover:bg-white shadow-[0_0_30px_-10px_rgba(34,211,126,0.4)]"
)} )}
> >
{checking ? <Loader2 className="w-4 h-4 animate-spin" /> : <Zap className="w-4 h-4" />} {checking ? <Loader2 className="w-4 h-4 animate-spin" /> : <Zap className="w-4 h-4" />}
Check {keywords.length} × {tlds.length} = {keywords.length * tlds.length} Domains Check {keywords.length * tlds.length} Variations
</button> </button>
</div> </div>
</div>
)} )}
{/* ═══════════════════════════════════════════════════════════════════════ */} {/* Results */}
{/* RESULTS */}
{/* ═══════════════════════════════════════════════════════════════════════ */}
{results.length > 0 && ( {results.length > 0 && (
<div className="space-y-4"> <div className="space-y-6 animate-in fade-in slide-in-from-bottom-4 duration-500">
{/* Available */} {/* Available */}
{availableResults.length > 0 && ( {availableResults.length > 0 && (
<div> <div className="space-y-3">
<p className="text-sm text-accent font-mono uppercase tracking-wider mb-3 flex items-center gap-2"> <div className="flex items-center justify-between px-1">
<span className="w-2 h-2 bg-accent rounded-full" /> <p className="text-[10px] font-mono text-accent uppercase tracking-[0.2em] font-black flex items-center gap-2">
{availableResults.length} Available <span className="w-1.5 h-1.5 bg-accent rounded-full animate-pulse" />
{availableResults.length} High-Potential Assets Identified
</p> </p>
<div className="space-y-1.5"> <div className="flex gap-4">
<button onClick={() => setResults(availableResults.map(r => ({ domain: r.domain, available: true })))} className="text-[9px] font-mono text-white/20 hover:text-accent uppercase tracking-widest transition-colors">
Copy List
</button>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-2">
{availableResults.map(r => ( {availableResults.map(r => (
<div key={r.domain} className="flex items-center justify-between p-3 bg-accent/5 border border-accent/20 hover:bg-accent/10 transition-colors"> <div key={r.domain} className="flex items-center justify-between p-4 bg-accent/[0.02] border border-accent/20 hover:border-accent/40 hover:bg-accent/[0.04] transition-all group">
<button <button
onClick={() => openAnalyze(r.domain)} onClick={() => openAnalyze(r.domain)}
className="text-sm font-mono text-white hover:text-accent truncate" className="text-sm font-bold font-mono text-white group-hover:text-accent truncate tracking-tight transition-colors"
> >
{r.domain} {r.domain}
</button> </button>
<div className="flex items-center gap-1 shrink-0"> <div className="flex items-center gap-1.5 shrink-0 opacity-40 group-hover:opacity-100 transition-opacity">
<button onClick={() => copy(r.domain)} className="w-8 h-8 flex items-center justify-center text-white/50 hover:text-white"> <button onClick={() => copy(r.domain)} className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/40 hover:text-white hover:bg-white/5" title="Copy">
{copied === r.domain ? <Check className="w-3.5 h-3.5 text-accent" /> : <Copy className="w-3.5 h-3.5" />} {copied === r.domain ? <Check className="w-3.5 h-3.5 text-accent" /> : <Copy className="w-3.5 h-3.5" />}
</button> </button>
<button onClick={() => track(r.domain)} disabled={tracking === r.domain} className="w-8 h-8 flex items-center justify-center text-white/50 hover:text-white"> <button onClick={() => track(r.domain)} disabled={tracking === r.domain} className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/40 hover:text-white hover:bg-white/5" title="Track">
{tracking === r.domain ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-3.5 h-3.5" />} {tracking === r.domain ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Eye className="w-3.5 h-3.5" />}
</button> </button>
<button onClick={() => openAnalyze(r.domain)} className="w-8 h-8 flex items-center justify-center text-white/50 hover:text-accent"> <button onClick={() => openAnalyze(r.domain)} className="w-8 h-8 flex items-center justify-center border border-white/10 text-white/40 hover:text-accent hover:border-accent/20 hover:bg-accent/5" title="Analyze">
<Shield className="w-3.5 h-3.5" /> <Shield className="w-3.5 h-3.5" />
</button> </button>
<a <a
href={`https://www.namecheap.com/domains/registration/results/?domain=${r.domain}`} href={`https://www.namecheap.com/domains/registration/results/?domain=${r.domain}`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="h-8 px-3 bg-accent text-black text-[10px] font-bold flex items-center gap-1 hover:bg-white" className="h-8 px-3 bg-accent text-black text-[10px] font-black uppercase tracking-widest flex items-center gap-1.5 hover:bg-white"
> >
<ShoppingCart className="w-3 h-3" />
Buy Buy
</a> </a>
</div> </div>
@ -404,16 +402,16 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
{/* Taken (collapsed) */} {/* Taken (collapsed) */}
{takenResults.length > 0 && ( {takenResults.length > 0 && (
<details className="group"> <details className="group">
<summary className="text-sm text-white/40 font-mono uppercase tracking-wider cursor-pointer flex items-center gap-2 py-2 hover:text-white/60"> <summary className="text-[10px] font-mono text-white/20 uppercase tracking-[0.2em] cursor-pointer flex items-center gap-2 py-3 hover:text-white/40 transition-colors list-none border-t border-white/[0.04]">
<ChevronDown className="w-3 h-3 group-open:rotate-180 transition-transform" /> <ChevronDown className="w-3 h-3 group-open:rotate-180 transition-transform" />
{takenResults.length} Taken {takenResults.length} Registered Variations
</summary> </summary>
<div className="space-y-1.5 mt-2"> <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-2 mt-3 animate-in slide-in-from-top-2">
{takenResults.map(r => ( {takenResults.map(r => (
<div key={r.domain} className="flex items-center justify-between p-2.5 bg-white/[0.02] border border-white/5"> <div key={r.domain} className="flex items-center justify-between px-3 py-2 bg-white/[0.01] border border-white/[0.05] group">
<span className="text-sm font-mono text-white/40 truncate">{r.domain}</span> <span className="text-[11px] font-mono text-white/20 truncate group-hover:text-white/40 transition-colors">{r.domain}</span>
<button onClick={() => openAnalyze(r.domain)} className="text-white/30 hover:text-white"> <button onClick={() => openAnalyze(r.domain)} className="text-white/10 hover:text-accent transition-colors">
<Shield className="w-3.5 h-3.5" /> <Shield className="w-3 h-3" />
</button> </button>
</div> </div>
))} ))}
@ -425,10 +423,12 @@ export function TrendSurferTab({ showToast }: { showToast: (msg: string, type?:
{/* Empty State */} {/* Empty State */}
{!selected && !loading && trends.length > 0 && ( {!selected && !loading && trends.length > 0 && (
<div className="text-center py-12 border border-dashed border-white/10"> <div className="text-center py-24 border border-dashed border-white/[0.08] bg-white/[0.01]">
<Globe className="w-10 h-10 text-white/20 mx-auto mb-3" /> <Globe className="w-12 h-12 text-white/5 mx-auto mb-4" />
<p className="text-white/50">Select a trending topic above</p> <p className="text-white/40 text-sm font-mono uppercase tracking-widest font-bold">Select a trending topic above</p>
<p className="text-sm text-white/30 mt-1">We'll find available domains for you</p> <p className="text-white/20 text-[10px] font-mono mt-3 uppercase tracking-wider max-w-xs mx-auto leading-relaxed">
Our engines will analyze the viral potential and suggest premium assets
</p>
</div> </div>
)} )}
</div> </div>