'use client' import { useEffect, useState, useMemo, useCallback, useRef } from 'react' import { useSearchParams } from 'next/navigation' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { TerminalLayout } from '@/components/TerminalLayout' import { Ticker, useTickerItems } from '@/components/Ticker' import { Toast, useToast } from '@/components/Toast' import { Eye, Gavel, Tag, Clock, ExternalLink, Sparkles, Plus, Zap, Crown, Activity, Bell, Search, TrendingUp, ArrowRight, Globe, CheckCircle2, XCircle, Loader2, Wifi, ShieldAlert, BarChart3, Command } from 'lucide-react' import clsx from 'clsx' import Link from 'next/link' // ============================================================================ // SHARED COMPONENTS // ============================================================================ function Tooltip({ children, content }: { children: React.ReactNode; content: string }) { return (
{children}
{content}
) } function StatCard({ label, value, subValue, icon: Icon, trend }: { label: string value: string | number subValue?: string icon: any trend?: 'up' | 'down' | 'neutral' | 'active' }) { return (

{label}

{value} {subValue && {subValue}}
) } // ============================================================================ // TYPES // ============================================================================ interface HotAuction { domain: string current_bid: number time_remaining: string platform: string affiliate_url?: string } interface TrendingTld { tld: string current_price: number price_change: number reason: string } interface SearchResult { available: boolean | null inAuction: boolean inMarketplace: boolean auctionData?: HotAuction loading: boolean } // ============================================================================ // MAIN PAGE // ============================================================================ export default function RadarPage() { const searchParams = useSearchParams() const { isAuthenticated, isLoading, user, domains, subscription, addDomain, } = useStore() const { toast, showToast, hideToast } = useToast() const [hotAuctions, setHotAuctions] = useState([]) const [trendingTlds, setTrendingTlds] = useState([]) const [loadingData, setLoadingData] = useState(true) // Universal Search State const [searchQuery, setSearchQuery] = useState('') const [searchResult, setSearchResult] = useState(null) const [addingToWatchlist, setAddingToWatchlist] = useState(false) const [searchFocused, setSearchFocused] = useState(false) const searchInputRef = useRef(null) // Load Data const loadDashboardData = useCallback(async () => { try { const [auctions, trending] = await Promise.all([ api.getEndingSoonAuctions(5).catch(() => []), api.getTrendingTlds().catch(() => ({ trending: [] })) ]) setHotAuctions(auctions.slice(0, 5)) setTrendingTlds(trending.trending?.slice(0, 6) || []) } catch (error) { console.error('Failed to load dashboard data:', error) } finally { setLoadingData(false) } }, []) useEffect(() => { if (isAuthenticated) loadDashboardData() }, [isAuthenticated, loadDashboardData]) // Search Logic const handleSearch = useCallback(async (domain: string) => { if (!domain.trim()) { setSearchResult(null) return } const cleanDomain = domain.trim().toLowerCase() setSearchResult({ available: null, inAuction: false, inMarketplace: false, loading: true }) try { const [whoisResult, auctionsResult] = await Promise.all([ api.checkDomain(cleanDomain, true).catch(() => null), api.getAuctions(cleanDomain).catch(() => ({ auctions: [] })), ]) const auctionMatch = (auctionsResult as any).auctions?.find( (a: any) => a.domain.toLowerCase() === cleanDomain ) const isAvailable = whoisResult && 'is_available' in whoisResult ? whoisResult.is_available : null setSearchResult({ available: isAvailable, inAuction: !!auctionMatch, inMarketplace: false, auctionData: auctionMatch, loading: false, }) } catch (error) { setSearchResult({ available: null, inAuction: false, inMarketplace: false, loading: false }) } }, []) const handleAddToWatchlist = useCallback(async () => { if (!searchQuery.trim()) return setAddingToWatchlist(true) try { await addDomain(searchQuery.trim()) showToast(`Added ${searchQuery.trim()} to watchlist`, 'success') setSearchQuery('') setSearchResult(null) } catch (err: any) { showToast(err.message || 'Failed to add domain', 'error') } finally { setAddingToWatchlist(false) } }, [searchQuery, addDomain, showToast]) // Debounce Search useEffect(() => { const timer = setTimeout(() => { if (searchQuery.length > 3) { handleSearch(searchQuery) } else { setSearchResult(null) } }, 500) return () => clearTimeout(timer) }, [searchQuery, handleSearch]) // Focus shortcut useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.metaKey && e.key === 'k') { e.preventDefault() searchInputRef.current?.focus() } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, []) // Computed const { availableDomains, totalDomains, greeting, subtitle } = useMemo(() => { const available = domains?.filter(d => d.is_available) || [] const total = domains?.length || 0 const hour = new Date().getHours() const greeting = hour < 12 ? 'Good morning' : hour < 18 ? 'Good afternoon' : 'Good evening' let subtitle = '' if (available.length > 0) subtitle = `${available.length} domain${available.length !== 1 ? 's' : ''} ready to pounce!` else if (total > 0) subtitle = `Monitoring ${total} domain${total !== 1 ? 's' : ''} for you` else subtitle = 'Start tracking domains to find opportunities' return { availableDomains: available, totalDomains: total, greeting, subtitle } }, [domains]) const tickerItems = useTickerItems(trendingTlds, availableDomains, hotAuctions) return ( {toast && } {/* GLOW BACKGROUND */}
{/* 1. TICKER */} {tickerItems.length > 0 && (
)} {/* 2. STAT GRID */}
0 ? 'up' : 'neutral'} />
{/* 3. AWARD-WINNING SEARCH (HERO STYLE) */}
setSearchQuery(e.target.value)} onFocus={() => setSearchFocused(true)} onBlur={() => setSearchFocused(false)} placeholder="Analyze any domain..." className="w-full bg-transparent text-xl sm:text-2xl text-white placeholder:text-zinc-600 font-light outline-none" /> {!searchQuery && (
K
)} {searchQuery && ( )}
{/* SEARCH RESULTS DROPDOWN */} {searchResult && (
{searchResult.loading ? (
Scanning global availability...
) : (
{/* Availability Card */}
{searchResult.available ? (
) : (
)}

{searchResult.available ? 'Available' : 'Registered'}

{searchResult.available ? 'Ready for immediate registration' : 'Currently owned by someone else'}

{searchResult.available && ( Register Now )}
{/* Auction Card */} {searchResult.inAuction && searchResult.auctionData && (

In Auction Live

Current Bid: ${searchResult.auctionData.current_bid} • Ends in {searchResult.auctionData.time_remaining}

Place Bid
)} {/* Add to Watchlist */}
)}
)}
{/* Helper Text */} {!searchQuery && !searchFocused && (

Search across Global Registrars, Auctions, and Marketplaces simultaneously.

)}
{/* 4. SPLIT VIEW: PULSE & ALERTS */}
{/* MARKET PULSE */}

Market Pulse

View All
{loadingData ? (
Loading market data...
) : hotAuctions.length > 0 ? ( hotAuctions.map((auction, i) => (
{/* WATCHLIST ACTIVITY */}

Recent Alerts

Manage
{availableDomains.length > 0 ? ( availableDomains.slice(0, 5).map((domain) => (

{domain.name}

Available for Registration

Register
)) ) : totalDomains > 0 ? (

All watched domains are taken

) : (

Your watchlist is empty

Use search to add domains

)}
) }