diff --git a/frontend/src/app/terminal/radar/page.tsx b/frontend/src/app/terminal/radar/page.tsx index 330cca0..f3c29f7 100644 --- a/frontend/src/app/terminal/radar/page.tsx +++ b/frontend/src/app/terminal/radar/page.tsx @@ -18,7 +18,15 @@ import { Crosshair, Zap, Globe, - Target + Target, + Search, + Home, + BarChart3, + Settings, + Bell, + ChevronRight, + TrendingUp, + RefreshCw } from 'lucide-react' import clsx from 'clsx' import Link from 'next/link' @@ -47,12 +55,311 @@ interface SearchResult { } // ============================================================================ -// LIVE TICKER +// MOBILE BOTTOM NAV +// ============================================================================ + +function MobileBottomNav({ active }: { active: string }) { + const navItems = [ + { id: 'radar', label: 'Radar', icon: Crosshair, href: '/terminal/radar' }, + { id: 'market', label: 'Market', icon: Gavel, href: '/terminal/market' }, + { id: 'watchlist', label: 'Watch', icon: Eye, href: '/terminal/watchlist' }, + { id: 'intel', label: 'Intel', icon: BarChart3, href: '/terminal/intel' }, + ] + + return ( + + ) +} + +// ============================================================================ +// MOBILE HEADER +// ============================================================================ + +function MobileHeader({ + onSearchOpen, + isRefreshing, + onRefresh +}: { + onSearchOpen: () => void + isRefreshing: boolean + onRefresh: () => void +}) { + return ( +
+
+
+
+ +
+
+

Radar

+
+
+ Live +
+
+
+ +
+ + +
+
+
+ ) +} + +// ============================================================================ +// MOBILE SEARCH MODAL +// ============================================================================ + +function MobileSearchModal({ + isOpen, + onClose, + searchQuery, + setSearchQuery, + searchResult, + addingToWatchlist, + onAddToWatchlist +}: { + isOpen: boolean + onClose: () => void + searchQuery: string + setSearchQuery: (q: string) => void + searchResult: SearchResult | null + addingToWatchlist: boolean + onAddToWatchlist: () => void +}) { + const inputRef = useRef(null) + + useEffect(() => { + if (isOpen && inputRef.current) { + setTimeout(() => inputRef.current?.focus(), 100) + } + }, [isOpen]) + + if (!isOpen) return null + + return ( +
+ {/* Header */} +
+ +
+ + setSearchQuery(e.target.value)} + placeholder="Search domain..." + className="w-full h-10 bg-white/5 border border-white/10 pl-11 pr-4 text-white placeholder:text-white/30 outline-none focus:border-accent/50 rounded-lg" + autoComplete="off" + autoCorrect="off" + autoCapitalize="none" + /> +
+
+ + {/* Results */} +
+ {searchResult?.loading && ( +
+ + Checking... +
+ )} + + {searchResult && !searchResult.loading && ( +
+ {/* Status Banner */} +
+ {searchResult.is_available ? ( +
+ +
+ ) : ( +
+ +
+ )} +
+
{searchResult.domain}
+
+ {searchResult.is_available ? 'Available for registration' : 'Already registered'} +
+
+
+ + {/* Details */} + {!searchResult.is_available && searchResult.registrar && ( +
+
+ Registrar + {searchResult.registrar} +
+
+ )} + + {/* Actions */} +
+ + + {searchResult.is_available && ( + + Register Now + + + )} +
+
+ )} + + {!searchResult && searchQuery.length === 0 && ( +
+
+ +
+

Enter a domain to check availability

+
+ )} +
+
+ ) +} + +// ============================================================================ +// STAT CARD - Mobile Optimized +// ============================================================================ + +function StatCard({ label, value, highlight, icon: Icon }: { + label: string + value: string | number + highlight?: boolean + icon: any +}) { + return ( +
+
+ + {label} +
+
+ {value} +
+
+ ) +} + +// ============================================================================ +// AUCTION CARD - Mobile Optimized +// ============================================================================ + +function AuctionCard({ auction }: { auction: HotAuction }) { + return ( + +
+ {auction.platform.substring(0, 2)} +
+
+
{auction.domain}
+
{auction.time_remaining}
+
+
+
${auction.current_bid.toLocaleString()}
+
Current bid
+
+ +
+ ) +} + +// ============================================================================ +// DESKTOP LIVE TICKER // ============================================================================ function LiveTicker({ items }: { items: { label: string; value: string; highlight?: boolean }[] }) { return ( -
+
@@ -82,11 +389,13 @@ export default function RadarPage() { const [hotAuctions, setHotAuctions] = useState([]) const [marketStats, setMarketStats] = useState({ totalAuctions: 0, endingSoon: 0 }) const [loadingData, setLoadingData] = useState(true) + const [isRefreshing, setIsRefreshing] = useState(false) const [searchQuery, setSearchQuery] = useState('') const [searchResult, setSearchResult] = useState(null) const [addingToWatchlist, setAddingToWatchlist] = useState(false) const [searchFocused, setSearchFocused] = useState(false) + const [mobileSearchOpen, setMobileSearchOpen] = useState(false) const searchInputRef = useRef(null) // Load Data @@ -102,9 +411,15 @@ export default function RadarPage() { console.error('Failed to load data:', error) } finally { setLoadingData(false) + setIsRefreshing(false) } }, []) + const handleRefresh = useCallback(async () => { + setIsRefreshing(true) + await loadDashboardData() + }, [loadDashboardData]) + useEffect(() => { if (isAuthenticated) loadDashboardData() }, [isAuthenticated, loadDashboardData]) @@ -144,6 +459,7 @@ export default function RadarPage() { showToast(`Target acquired: ${searchQuery.trim()}`, 'success') setSearchQuery('') setSearchResult(null) + setMobileSearchOpen(false) } catch (err: any) { showToast(err.message || 'Mission failed', 'error') } finally { @@ -171,272 +487,397 @@ export default function RadarPage() { ] return ( - - {toast && } - - {/* ═══════════════════════════════════════════════════════════════════════ */} - {/* HERO - Compact for Laptops */} - {/* ═══════════════════════════════════════════════════════════════════════ */} -
-
- - {/* Left: Typography */} -
-
-
- - Intelligence Hub - -
- -

- Domain Radar - Find your next acquisition. -

- -

- Real-time monitoring across {marketStats.totalAuctions.toLocaleString()}+ auctions. - Your targets. Your intel. -

- - {/* Stats Row */} -
-
-
{totalDomains}
-
Tracking
+ <> + {/* Mobile Header */} + setMobileSearchOpen(true)} + isRefreshing={isRefreshing} + onRefresh={handleRefresh} + /> + + {/* Mobile Search Modal */} + setMobileSearchOpen(false)} + searchQuery={searchQuery} + setSearchQuery={setSearchQuery} + searchResult={searchResult} + addingToWatchlist={addingToWatchlist} + onAddToWatchlist={handleAddToWatchlist} + /> + + {/* Mobile Content */} +
+ {toast && } + + {/* Stats Grid */} +
+ + 0} icon={CheckCircle2} /> + + +
+ + {/* Available Alert */} + {availableDomains.length > 0 && ( +
+
+
+
-
-
{availableDomains.length}
-
Available
-
-
-
{marketStats.endingSoon}
-
Ending Soon
+
+
{availableDomains.length} Domain{availableDomains.length > 1 ? 's' : ''} Available!
+
Check your watchlist now
+ + +
+ )} + + {/* Section: Live Auctions */} +
+
+
+ + Live Auctions +
+ + See all + +
- {/* Right: Search Terminal */} -
-
- -
- {/* Header Bar */} -
- - - Domain Search - -
-
-
-
+ {loadingData ? ( +
+ +
+ ) : hotAuctions.length > 0 ? ( +
+ {hotAuctions.map((auction, i) => ( + + ))} +
+ ) : ( +
+ +

No active auctions

+
+ )} +
+ + {/* Section: Quick Links */} +
+
+ + Quick Actions +
+ +
+ {[ + { label: 'TLD Intel', desc: 'Price trends', href: '/terminal/intel', icon: TrendingUp }, + { label: 'Sniper', desc: 'Set alerts', href: '/terminal/sniper', icon: Target }, + ].map((item) => ( + + +
{item.label}
+
{item.desc}
+ + ))} +
+
+
+ + {/* Mobile Bottom Nav */} + + + {/* ═══════════════════════════════════════════════════════════════════════ */} + {/* DESKTOP LAYOUT */} + {/* ═══════════════════════════════════════════════════════════════════════ */} +
+ + {toast && } + + {/* HERO */} +
+
+ + {/* Left: Typography */} +
+
+
+ + Intelligence Hub + +
+ +

+ Domain Radar + Find your next acquisition. +

+ +

+ Real-time monitoring across {marketStats.totalAuctions.toLocaleString()}+ auctions. + Your targets. Your intel. +

+ + {/* Stats Row */} +
+
+
{totalDomains}
+
Tracking
+
+
+
{availableDomains.length}
+
Available
+
+
+
{marketStats.endingSoon}
+
Ending Soon
+
-
- {/* Input */} -
- setSearchQuery(e.target.value)} - onFocus={() => setSearchFocused(true)} - onBlur={() => setSearchFocused(false)} - placeholder="example.com" - className="w-full bg-transparent px-4 py-4 text-lg text-white placeholder:text-white/20 outline-none" - /> - {searchQuery && ( - - )} -
+ {/* Right: Search Terminal */} +
+
- {/* Results */} - {searchResult && ( -
- {searchResult.loading ? ( -
- - Checking availability... -
- ) : ( -
- {/* Status Header */} -
-
- {searchResult.is_available ? ( - - ) : ( - - )} - {searchResult.domain} +
+ {/* Header Bar */} +
+ + + Domain Search + +
+
+
+
+
+
+ +
+ {/* Input */} +
+ setSearchQuery(e.target.value)} + onFocus={() => setSearchFocused(true)} + onBlur={() => setSearchFocused(false)} + placeholder="example.com" + className="w-full bg-transparent px-4 py-4 text-lg text-white placeholder:text-white/20 outline-none" + /> + {searchQuery && ( + + )} +
+ + {/* Results */} + {searchResult && ( +
+ {searchResult.loading ? ( +
+ + Checking availability...
- - {searchResult.is_available ? 'Available' : 'Taken'} - -
- - {/* Registrar Info for taken domains */} - {!searchResult.is_available && searchResult.registrar && ( -

- Registered with {searchResult.registrar} -

- )} - - {/* Actions - Always show */} -
-
+ + {/* Registrar Info for taken domains */} + {!searchResult.is_available && searchResult.registrar && ( +

+ Registered with {searchResult.registrar} +

)} - > - {addingToWatchlist ? : } - {searchResult.is_available ? 'Add to Watchlist' : 'Track for Availability'} - - {searchResult.is_available && ( - - Register Now - - )} -
+ + {/* Actions */} +
+ + {searchResult.is_available && ( + + Register Now + + )} +
+
+ )}
)} + + {/* Hint */} + {!searchResult && ( +

+ Enter a domain name to check availability +

+ )}
- )} +
+
+
+
+ + {/* Ticker */} + + + {/* CONTENT GRID */} +
+
+ + {/* Hot Auctions - 2 cols */} +
+
+
+ + Live Auctions +
+ + View all → + +
- {/* Hint */} - {!searchResult && ( -

- Enter a domain name to check availability -

+ {loadingData ? ( +
+ +
+ ) : hotAuctions.length > 0 ? ( + + ) : ( +
No active auctions
)}
-
-
-
-
- - {/* Ticker */} - - - {/* ═══════════════════════════════════════════════════════════════════════ */} - {/* CONTENT GRID */} - {/* ═══════════════════════════════════════════════════════════════════════ */} -
-
- - {/* Hot Auctions - 2 cols */} -
-
-
- - Live Auctions + + {/* Quick Links */} +
+
+ + Quick Access +
+ +
+ {[ + { label: 'Watchlist', href: '/terminal/watchlist', icon: Eye }, + { label: 'Market', href: '/terminal/market', icon: Gavel }, + { label: 'Intel', href: '/terminal/intel', icon: Globe }, + ].map((item) => ( + + + {item.label} + + + ))} +
+ + {/* Status */} +
+
+
+ System online +
+
- - View all → - +
- - {loadingData ? ( -
- -
- ) : hotAuctions.length > 0 ? ( - - ) : ( -
No active auctions
- )} -
- - {/* Quick Links */} -
-
- - Quick Access -
- -
- {[ - { label: 'Watchlist', href: '/terminal/watchlist', icon: Eye }, - { label: 'Market', href: '/terminal/market', icon: Gavel }, - { label: 'Intel', href: '/terminal/intel', icon: Globe }, - ].map((item) => ( - - - {item.label} - - - ))} -
- - {/* Status */} -
-
-
- System online -
-
-
- -
-
+ +
+
- + ) -} \ No newline at end of file +}