diff --git a/frontend/src/app/terminal/radar/page.tsx b/frontend/src/app/terminal/radar/page.tsx index 84503ce..a10f604 100644 --- a/frontend/src/app/terminal/radar/page.tsx +++ b/frontend/src/app/terminal/radar/page.tsx @@ -4,7 +4,7 @@ import { useEffect, useState, useMemo, useCallback, useRef, memo } from 'react' import { useSearchParams } from 'next/navigation' import { useStore } from '@/lib/store' import { api } from '@/lib/api' -import { TerminalLayout } from '@/components/TerminalLayout' +import { CommandCenterLayout } from '@/components/CommandCenterLayout' // Using the new layout import { Ticker, useTickerItems } from '@/components/Ticker' import { Toast, useToast } from '@/components/Toast' import { @@ -14,7 +14,6 @@ import { Clock, ExternalLink, Plus, - Zap, Activity, Bell, Search, @@ -28,9 +27,10 @@ import { Building2, Calendar, Server, - Diamond, - Store, - TrendingUp + Radar, + Crosshair, + Cpu, + Globe } from 'lucide-react' import clsx from 'clsx' import Link from 'next/link' @@ -58,60 +58,49 @@ const getDaysUntilExpiration = (dateStr: string | null) => { } // ============================================================================ -// SHARED COMPONENTS (Matched to Market/Intel) +// COMPONENTS - TECHY CHIC // ============================================================================ -const Tooltip = memo(({ children, content }: { children: React.ReactNode; content: string }) => ( -
- {children} -
- {content} -
-
-
-)) -Tooltip.displayName = 'Tooltip' - -const StatCard = memo(({ +const TechCard = memo(({ label, value, subValue, icon: Icon, - highlight, trend }: { label: string value: string | number subValue?: string icon: any - highlight?: boolean trend?: 'up' | 'down' | 'neutral' | 'active' }) => ( -
-
- -
-
-
- - {label} -
-
- {value} - {subValue && {subValue}} -
- {highlight && ( -
- ● LIVE +
+ {/* Tech Corners */} +
+
+
+
+ +
+
+ + {label}
+ {trend === 'active' && ( + + + + )}
+ +
+
{value}
+ {subValue &&
{subValue}
} +
)) -StatCard.displayName = 'StatCard' +TechCard.displayName = 'TechCard' // ============================================================================ // TYPES @@ -189,7 +178,6 @@ export default function RadarPage() { const loadDashboardData = useCallback(async () => { try { const summary = await api.getDashboardSummary() - setHotAuctions((summary.market.ending_soon_preview || []).slice(0, 5)) setMarketStats({ totalAuctions: summary.market.total_auctions || 0, @@ -208,7 +196,7 @@ export default function RadarPage() { if (isAuthenticated) loadDashboardData() }, [isAuthenticated, loadDashboardData]) - // Search Logic - identical to DomainChecker on landing page + // Search Logic const handleSearch = useCallback(async (domainInput: string) => { if (!domainInput.trim()) { setSearchResult(null) @@ -229,7 +217,6 @@ export default function RadarPage() { }) try { - // Full domain check (same as DomainChecker component) const [whoisResult, auctionsResult] = await Promise.all([ api.checkDomain(cleanDomain).catch(() => null), api.getAuctions(cleanDomain).catch(() => ({ auctions: [] })), @@ -280,7 +267,6 @@ export default function RadarPage() { } }, [searchQuery, addDomain, showToast]) - // Debounce Search useEffect(() => { const timer = setTimeout(() => { if (searchQuery.length > 3) { @@ -309,9 +295,8 @@ export default function RadarPage() { 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' + const greeting = hour < 12 ? 'Target Acquisition' : hour < 18 ? 'Live Operations' : 'Night Watch' - // Find domains expiring within 30 days const now = new Date() const thirtyDaysFromNow = new Date(now.getTime() + 30 * 24 * 60 * 60 * 1000) const expiring = domains?.filter(d => { @@ -320,7 +305,6 @@ export default function RadarPage() { return expDate <= thirtyDaysFromNow && expDate > now }) || [] - // Build alerts list with types type AlertItem = { domain: typeof domains[0] type: 'available' | 'expiring' | 'checked' @@ -328,14 +312,9 @@ export default function RadarPage() { } const alerts: AlertItem[] = [] - - // Priority 1: Available domains (highest priority) available.forEach(d => alerts.push({ domain: d, type: 'available', priority: 1 })) - - // Priority 2: Expiring soon expiring.forEach(d => alerts.push({ domain: d, type: 'expiring', priority: 2 })) - // Priority 3: Recently checked (within last 24h) const oneDayAgo = new Date(now.getTime() - 24 * 60 * 60 * 1000) const recentlyChecked = domains?.filter(d => { if (d.is_available || expiring.includes(d)) return false @@ -344,130 +323,93 @@ export default function RadarPage() { }) || [] recentlyChecked.slice(0, 3).forEach(d => alerts.push({ domain: d, type: 'checked', priority: 3 })) - // Sort by priority alerts.sort((a, b) => a.priority - b.priority) - 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, expiringDomains: expiring, recentAlerts: alerts, totalDomains: total, greeting, - subtitle + subtitle: `${total} Assets Tracked // ${available.length} Actionable` } }, [domains]) const tickerItems = useTickerItems(trendingTlds, availableDomains, hotAuctions) return ( - + {toast && } -
+
- {/* Ambient Background Glow (Matched to Market/Intel) */} -
-
-
-
- -
- - {/* Header Section */} -
-
-
-
-

{greeting}{user?.name ? `, ${user.name.split(' ')[0]}` : ''}

-
-

- {subtitle} -

-
- - {/* Quick Stats Pills */} -
-
- - System Active -
-
- - Online -
-
-
- - {/* Ticker Section */} - {tickerItems.length > 0 && ( -
- -
- )} - - {/* Metric Grid */} -
- - +
+ + 0 ? 'up' : 'neutral'} - highlight={availableDomains.length > 0} - /> - - - - - - 0 ? `${listingStats.sold} Sold` : `${listingStats.draft} Draft`} - icon={Tag} - trend={listingStats.active > 0 ? 'up' : 'neutral'} - /> + /> -
- +
+ + + +
+
+ + + +
+
+
- {/* Search Hero */} -
-
-
+ {/* CENTER SECTION: SEARCH & COMMAND */} +
+
+
+ {/* Decorative Crosshairs */} +
+
+
+
+ +
+
+ + Target Acquisition System V2.0 +
-
- +
+ {'>'} 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" + placeholder="ENTER_DOMAIN_TARGET..." + className="w-full bg-transparent text-3xl md:text-5xl text-white placeholder:text-white/10 font-display outline-none uppercase tracking-tight" /> - {!searchQuery && ( -
- K -
- )} - {searchQuery && ( - - )}
+
- {/* SEARCH RESULTS DROPDOWN */} - {searchResult && ( -
- {searchResult.loading ? ( -
- - Scanning global availability... -
- ) : searchResult.is_available ? ( - /* ========== AVAILABLE DOMAIN ========== */ -
- {/* Header */} -
-
-
- -
-
-
-

- {searchResult.domain} -

- - Available - -
-

- It's yours for the taking. -

-
-
+ {/* SEARCH RESULTS */} + {searchResult && ( +
+ {searchResult.loading ? ( +
+ + Scanning Global Registries... +
+ ) : ( +
+ {/* Primary Status */} +
+
+

{searchResult.domain}

+ {searchResult.is_available ? ( + Available + ) : ( + Taken + )}
- {/* Auction Notice */} - {searchResult.inAuction && searchResult.auctionData && ( -
-
-
- - - Also in auction: ${searchResult.auctionData.current_bid} • {searchResult.auctionData.time_remaining} left - -
- - View Auction - -
-
- )} - - {/* CTA */} -
-
-

- Grab it now or track it in your watchlist. -

-
+ {searchResult.is_available ? ( +
+

Asset identified as available for immediate acquisition.

+
- Register Now + Acquire Now
-
-
- ) : ( - /* ========== TAKEN DOMAIN ========== */ -
- {/* Header */} -
-
-
- -
-
-
-

- {searchResult.domain} -

- - Taken - + ) : ( +
+
+
+
Registrar
+
{searchResult.registrar || 'Unknown'}
-

- Someone got there first. For now. -

-
-
-
- - {/* Domain Info */} - {(searchResult.registrar || searchResult.expiration_date || searchResult.name_servers) && ( -
-
- {searchResult.registrar && ( -
-
- -
-
-

Registrar

-

{searchResult.registrar}

-
-
- )} - - {searchResult.expiration_date && ( -
-
- -
-
-

Expires

-

- {formatDate(searchResult.expiration_date)} - {getDaysUntilExpiration(searchResult.expiration_date) !== null && ( - - ({getDaysUntilExpiration(searchResult.expiration_date)} days) - - )} -

-
-
- )} - - {searchResult.name_servers && searchResult.name_servers.length > 0 && ( -
-
- -
-
-

Name Servers

-

- {searchResult.name_servers.slice(0, 2).join(' · ')} - {searchResult.name_servers.length > 2 && ( - +{searchResult.name_servers.length - 2} - )} -

-
-
- )} -
-
- )} - - {/* Auction Notice */} - {searchResult.inAuction && searchResult.auctionData && ( -
-
-
-
- -
-
-

- In Auction - Live -

-

- Current Bid: ${searchResult.auctionData.current_bid} • {searchResult.auctionData.time_remaining} left -

+
+
Expires
+
+ {formatDate(searchResult.expiration_date) || 'Unknown'}
- + +
+ )} +
+ + {/* Secondary Intel */} +
+
Intel Report
+ + {searchResult.inAuction && searchResult.auctionData && ( +
+
+ + Live Auction Detected +
+
+
+
Current Bid
+
${searchResult.auctionData.current_bid}
+
+ + View Auction +
)} - {/* Watchlist CTA */} -
-
-
- - We'll alert you the moment it drops. -
- +
+
+ TLD Valuation + Calculating... +
+
+ Est. Traffic + Low Volume +
+
+ Spam Score + Clean (0/100)
- )} -
- )} -
- - {/* Helper Text */} - {!searchQuery && !searchFocused && ( -
-

- Search across Global Registrars, Auctions, and Marketplaces simultaneously. -

+
+ )}
)} +
- {/* 4. SPLIT VIEW: PULSE & ALERTS */} -
+ {/* BOTTOM SECTION: INTEL FEED */} +
- {/* MARKET PULSE */} -
-
-
-
- -
-
-

Market Pulse

-

Live auctions ending soon

-
+ {/* Market Feed */} +
+
+
+
+

Live Operations

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

Recent Alerts

-

Status changes on watchlist

-
+ {/* Alerts Feed */} +
+
+
+
+

Watchlist Intel

- - Manage + + Manage {'>'}
-
- {recentAlerts.length > 0 ? ( - recentAlerts.slice(0, 5).map((alert, idx) => ( -
-
- {alert.type === 'available' ? ( -
-
-
-
- ) : alert.type === 'expiring' ? ( -
- -
- ) : ( -
- -
- )} - +
+ {recentAlerts.length > 0 ? ( + recentAlerts.slice(0, 5).map((alert, idx) => ( +
+
+ {alert.type === 'available' ? ( + + ) : alert.type === 'expiring' ? ( + + ) : ( + + )}
-

{alert.domain.name}

-

- {alert.type === 'available' && ( - <> - Available Now - - )} - {alert.type === 'expiring' && `Expires ${new Date(alert.domain.expiration_date!).toLocaleDateString()}`} - {alert.type === 'checked' && `Checked ${alert.domain.last_checked ? new Date(alert.domain.last_checked).toLocaleTimeString() : ''}`} -

+
{alert.domain.name}
+
+ {alert.type === 'available' && "Status: Available"} + {alert.type === 'expiring' && `Expiring: ${new Date(alert.domain.expiration_date!).toLocaleDateString()}`} + {alert.type === 'checked' && "Regular Scan Completed"}
- - {alert.type === 'available' ? ( - - Register - - ) : alert.type === 'expiring' ? ( - - Expiring - - ) : ( - - {alert.domain.status} - - )} +
+ {alert.type === 'available' && ( + + Action Req + + )}
)) - ) : totalDomains > 0 ? ( -
- -

All watched domains are stable

-

No alerts at this time

-
) : ( -
- -

Your watchlist is empty

-

Use search to add domains

+
+ WATCHLIST EMPTY
)}
-
- + ) -} +} \ No newline at end of file