'use client' import { useState, useEffect, useCallback } from 'react' import { useSearchParams } from 'next/navigation' import { TrendingUp, DollarSign, Zap, Plus, CheckCircle2, Clock, AlertCircle, MousePointer, Target, Wallet, RefreshCw, ChevronRight, Copy, Check, XCircle, Sparkles, Loader2, Eye, Gavel, Menu, Settings, Shield, LogOut, Crown, Coins, Tag, X, Briefcase, Trash2 } from 'lucide-react' import { api, YieldDomain, YieldTransaction } from '@/lib/api' import { useStore } from '@/lib/store' import { Sidebar } from '@/components/Sidebar' import clsx from 'clsx' import Link from 'next/link' import Image from 'next/image' // ============================================================================ // STATUS BADGE // ============================================================================ function StatusBadge({ status }: { status: string }) { const config: Record = { active: { color: 'bg-accent/10 text-accent border-accent/20', icon: CheckCircle2 }, pending: { color: 'bg-amber-400/10 text-amber-400 border-amber-400/20', icon: Clock }, verifying: { color: 'bg-blue-400/10 text-blue-400 border-blue-400/20', icon: RefreshCw }, paused: { color: 'bg-white/5 text-white/40 border-white/10', icon: AlertCircle }, error: { color: 'bg-red-400/10 text-red-400 border-red-400/20', icon: XCircle }, } const { color, icon: Icon } = config[status] || config.pending return ( {status} ) } // ============================================================================ // ACTIVATE MODAL - Only verified portfolio domains // ============================================================================ function ActivateModal({ isOpen, onClose, onSuccess, prefillDomain, }: { isOpen: boolean onClose: () => void onSuccess: () => void prefillDomain?: string | null }) { const [selectedDomain, setSelectedDomain] = useState('') const [verifiedDomains, setVerifiedDomains] = useState<{ id: number; domain: string }[]>([]) const [loadingDomains, setLoadingDomains] = useState(true) const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const [step, setStep] = useState<1 | 2>(1) const [activation, setActivation] = useState(null) const [dnsChecking, setDnsChecking] = useState(false) const [dnsResult, setDnsResult] = useState(null) const [copied, setCopied] = useState(null) useEffect(() => { if (!isOpen) return if (prefillDomain) { setSelectedDomain(prefillDomain) setStep(1) setActivation(null) setDnsResult(null) } const fetchVerifiedDomains = async () => { setLoadingDomains(true) try { const domains = await api.getVerifiedPortfolioDomains() setVerifiedDomains(domains.map(d => ({ id: d.id, domain: d.domain }))) } catch (err) { console.error('Failed to load verified domains:', err) } finally { setLoadingDomains(false) } } fetchVerifiedDomains() }, [isOpen]) useEffect(() => { if (!isOpen) return setStep(1) setActivation(null) setDnsResult(null) setDnsChecking(false) setError(null) setSelectedDomain('') }, [isOpen]) const copyToClipboard = async (value: string, key: string) => { try { await navigator.clipboard.writeText(value) setCopied(key) setTimeout(() => setCopied(null), 1200) } catch { // ignore } } const handleActivate = async () => { if (!selectedDomain) return setLoading(true) setError(null) try { const res = await api.activateYieldDomain(selectedDomain, true) setActivation({ domain_id: res.domain_id, domain: res.domain, status: res.status, dns_instructions: res.dns_instructions, }) setStep(2) } catch (err: any) { setError(err.message || 'Failed') } finally { setLoading(false) } } const checkDNS = useCallback(async (domainId: number) => { setDnsChecking(true) setError(null) try { const res = await api.verifyYieldDomainDNS(domainId) setDnsResult({ verified: res.verified, expected_ns: res.expected_ns, actual_ns: res.actual_ns, cname_ok: res.cname_ok, error: res.error, }) if (res.verified) { onSuccess() } } catch (err: any) { setError(err.message || 'DNS check failed') } finally { setDnsChecking(false) } }, [onSuccess]) if (!isOpen) return null return (
e.stopPropagation()}>
Activate Yield
{step === 1 && loadingDomains ? (
) : step === 1 && verifiedDomains.length === 0 ? (

No Verified Domains

You need to add domains to your portfolio and verify DNS ownership before activating Yield.

Go to Portfolio
) : step === 1 ? ( <>

Only DNS-verified domains from your portfolio can be activated for Yield.

{error &&
{error}
} ) : ( <>
Domain
{activation?.domain}
Option A (Recommended): Nameservers
{(activation?.dns_instructions.nameservers || []).map((ns, idx) => (
0 && "border-t border-white/[0.06]")}> {ns}
))}
Option B: CNAME / ALIAS
Host: {activation?.dns_instructions.cname_host} → Target: {activation?.dns_instructions.cname_target}

Some DNS providers use ALIAS/ANAME for apex. We accept both CNAME and ALIAS-style flattening.

{dnsResult && (
{dnsResult.verified ? 'Connected. Domain is active.' : 'Not connected yet. Waiting for DNS propagation.'} {dnsResult.verified ? : }
{dnsResult.error &&
Error: {dnsResult.error}
} {!dnsResult.verified && (
Expected NS: {dnsResult.expected_ns?.join(', ') || '—'}
Actual NS: {dnsResult.actual_ns?.join(', ') || '—'}
)}
)} {error &&
{error}
}
{dnsResult?.verified && ( )} )}
) } // ============================================================================ // MAIN PAGE // ============================================================================ export default function YieldPage() { const { subscription, user, logout, checkAuth } = useStore() const searchParams = useSearchParams() const [loading, setLoading] = useState(true) const [dashboard, setDashboard] = useState(null) const [showActivateModal, setShowActivateModal] = useState(false) const [prefillDomain, setPrefillDomain] = useState(null) const [refreshing, setRefreshing] = useState(false) const [menuOpen, setMenuOpen] = useState(false) const [deletingId, setDeletingId] = useState(null) useEffect(() => { checkAuth() }, [checkAuth]) const fetchDashboard = useCallback(async () => { try { const data = await api.getYieldDashboard() setDashboard(data) } catch (err) { console.error(err) } finally { setLoading(false); setRefreshing(false) } }, []) const handleDeleteYield = useCallback(async (domainId: number, domainName: string) => { if (!confirm(`Remove ${domainName} from Yield? This will stop all revenue tracking.`)) return setDeletingId(domainId) try { await api.deleteYieldDomain(domainId) fetchDashboard() } catch (err) { console.error('Failed to remove yield domain:', err) } finally { setDeletingId(null) } }, [fetchDashboard]) useEffect(() => { fetchDashboard() }, [fetchDashboard]) useEffect(() => { const activate = searchParams.get('activate') if (activate) { setPrefillDomain(activate) setShowActivateModal(true) } }, [searchParams]) const stats = dashboard?.stats const mobileNavItems = [ { href: '/terminal/hunt', label: 'Radar', icon: Target, active: false }, { href: '/terminal/market', label: 'Market', icon: Gavel, active: false }, { href: '/terminal/watchlist', label: 'Watch', icon: Eye, active: false }, { href: '/terminal/intel', label: 'Intel', icon: TrendingUp, active: false }, ] const tierName = subscription?.tier_name || subscription?.tier || 'Scout' const TierIcon = tierName === 'Tycoon' ? Crown : tierName === 'Trader' ? TrendingUp : Zap const drawerNavSections = [ { title: 'Discover', items: [ { href: '/terminal/hunt', label: 'Radar', icon: Target }, { href: '/terminal/market', label: 'Market', icon: Gavel }, { href: '/terminal/intel', label: 'Intel', icon: TrendingUp }, ]}, { title: 'Manage', items: [ { href: '/terminal/watchlist', label: 'Watchlist', icon: Eye }, { href: '/terminal/portfolio', label: 'Portfolio', icon: Briefcase }, { href: '/terminal/sniper', label: 'Sniper', icon: Target }, ]}, { title: 'Monetize', items: [ { href: '/terminal/yield', label: 'Yield', icon: Coins, active: true }, { href: '/terminal/listing', label: 'For Sale', icon: Tag }, ]} ] return (
{/* MOBILE HEADER */}
Yield
{stats?.active_domains || 0} active
${stats?.monthly_revenue || 0}
Monthly
{stats?.monthly_clicks || 0}
Clicks
${stats?.pending_payout || 0}
Pending
{/* DESKTOP HEADER */}
Yield

Yield

Monetize your parked domains. Route visitor intent to earn passive income.

{stats && (
${stats.monthly_revenue}
Monthly
{stats.active_domains}
Active
)}
{/* ADD BUTTON MOBILE */}
{/* FEATURE IN DEVELOPMENT BANNER */}

Feature in Development Coming Soon

We're building an automated yield system for your parked domains. Soon you'll be able to monetize idle domains with intelligent traffic routing. Stay tuned for updates!

{/* CONTENT */}
{loading ? (
) : !dashboard?.domains?.length ? (

No yield domains yet

Activate domains to earn passive income

) : (
{/* Header */}
Domain
Status
Intent
Clicks
Conv.
Revenue
Action
{dashboard.domains.map((domain: YieldDomain) => (
{/* Mobile */}
{domain.domain.charAt(0).toUpperCase()}
{domain.domain}
{domain.total_clicks} clicks ${domain.total_revenue}
{/* Desktop */}
{domain.domain.charAt(0).toUpperCase()}
{domain.domain}
{domain.detected_intent?.replace('_', ' ') || '—'}
{domain.total_clicks}
{domain.total_conversions}
${domain.total_revenue}
))}
)}
{/* MOBILE BOTTOM NAV */} {/* DRAWER */} {menuOpen && (
setMenuOpen(false)} />
Pounce

POUNCE

Terminal v1.0

{drawerNavSections.map((section) => (
{section.title}
{section.items.map((item: any) => ( setMenuOpen(false)} className={clsx("flex items-center gap-3 px-4 py-2.5 border-l-2 border-transparent", item.active ? "text-accent border-accent bg-white/[0.02]" : "text-white/60")}> {item.label} ))}
))}
setMenuOpen(false)} className="flex items-center gap-3 py-2.5 text-white/50">Settings {user?.is_admin && setMenuOpen(false)} className="flex items-center gap-3 py-2.5 text-amber-500/70">Admin}

{user?.name || user?.email?.split('@')[0] || 'User'}

{tierName}

{tierName === 'Scout' && setMenuOpen(false)} className="flex items-center justify-center gap-2 w-full py-2.5 bg-accent text-black text-xs font-bold uppercase mb-2">Upgrade}
)}
{ setShowActivateModal(false); setPrefillDomain(null) }} onSuccess={fetchDashboard} />
) }