From f963b33b328bf1dc50eb84de0a02df64214be470 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Sun, 14 Dec 2025 22:09:51 +0100 Subject: [PATCH] feat: Pounce listings in acquire table, yield remove button, portfolio shows yield status --- frontend/src/app/about/page.tsx | 4 +- frontend/src/app/acquire/page.tsx | 241 +++++++++++-------- frontend/src/app/api/og/tld/route.tsx | 2 +- frontend/src/app/discover/[tld]/metadata.ts | 2 +- frontend/src/app/page.tsx | 2 +- frontend/src/app/register/page.tsx | 2 +- frontend/src/app/terminal/portfolio/page.tsx | 158 ++++++------ frontend/src/app/terminal/yield/page.tsx | 54 ++++- frontend/src/app/yield/page.tsx | 2 +- 9 files changed, 278 insertions(+), 189 deletions(-) diff --git a/frontend/src/app/about/page.tsx b/frontend/src/app/about/page.tsx index a0f6d76..daa31a3 100644 --- a/frontend/src/app/about/page.tsx +++ b/frontend/src/app/about/page.tsx @@ -96,7 +96,7 @@ export default function AboutPage() {
  • Refresh Rate - Real-time + Live
  • Data Source @@ -114,7 +114,7 @@ export default function AboutPage() { {[ { icon: Target, title: 'Precision', desc: 'Accurate data. No guesswork. Every check counts.' }, { icon: Shield, title: 'Privacy', desc: 'Your strategy stays yours. We never share or sell data.' }, - { icon: Zap, title: 'Speed', desc: 'Real-time intel. You see it first.' }, + { icon: Zap, title: 'Speed', desc: 'Fast intel. You move first.' }, { icon: Users, title: 'Transparency', desc: 'Clear pricing. No surprises. Ever.' }, ].map((value, i) => (
    (nowMs - 2000) // 2s grace }) - if (isAuthenticated) return activeAuctions - return activeAuctions.filter(isVanityDomain) + // Convert Pounce Direct items to Auction format and mark them + const pounceAuctions: Auction[] = pounceItems.map(item => ({ + domain: item.domain, + platform: 'Pounce', + platform_url: item.url, + current_bid: item.price, + currency: item.currency, + num_bids: 0, + end_time: '', + time_remaining: '', + buy_now_price: item.price, + reserve_met: null, + traffic: null, + age_years: null, + tld: item.tld, + affiliate_url: item.url, + is_pounce: true, // Special flag + })) + + // Apply auth filter + const filteredAuctions = isAuthenticated ? activeAuctions : activeAuctions.filter(isVanityDomain) + + // Pounce Direct always on top + return [...pounceAuctions, ...filteredAuctions] // eslint-disable-next-line react-hooks/exhaustive-deps - }, [activeTab, allAuctions, endingSoon, hotAuctions, isAuthenticated, tick]) + }, [activeTab, allAuctions, endingSoon, hotAuctions, pounceItems, isAuthenticated, tick]) const filteredAuctions = displayAuctions.filter(auction => { if (searchQuery && !auction.domain.toLowerCase().includes(searchQuery.toLowerCase())) return false @@ -407,27 +430,49 @@ export default function AcquirePage() { href={auction.affiliate_url} target="_blank" rel="noopener noreferrer" - className="block p-3 bg-[#0A0A0A] border border-white/[0.08] active:bg-white/[0.03] transition-all" + className={clsx( + "block p-3 border active:bg-white/[0.03] transition-all", + auction.is_pounce + ? "bg-accent/[0.03] border-accent/20" + : "bg-[#0A0A0A] border-white/[0.08]" + )} >
    -
    - {auction.domain} +
    + {auction.is_pounce && ( + + Direct + + )} + + {auction.domain} +
    - {auction.platform} - - - - {calcTimeRemaining(auction.end_time)} - + {auction.is_pounce ? ( + + Verified • Instant + + ) : ( + <> + {auction.platform} + + + + {calcTimeRemaining(auction.end_time)} + + + )}
    {formatCurrency(auction.current_bid)}
    - {auction.num_bids > 0 && ( + {auction.is_pounce ? ( +
    Buy Now
    + ) : auction.num_bids > 0 && (
    {auction.num_bids} bids
    @@ -501,94 +546,53 @@ export default function AcquirePage() {
    )} - {/* Featured Direct Listings */} - {pounceItems.length > 0 && ( -
    -
    -
    -
    - - Direct Listings -
    - // 0% COMMISSION -
    -
    - -
    - {pounceItems.slice(0, 3).map((item) => ( - -
    - - Available -
    -

    - {item.domain} -

    -
    - {formatCurrency(item.price)} - {item.verified && ( - - Verified - - )} -
    - - ))} -
    -
    - )} - {/* Search & Filters Bar */}
    - {/* Search */} + {/* Search */}
    - setSearchQuery(e.target.value)} + value={searchQuery} + onChange={(e) => setSearchQuery(e.target.value)} className="w-full pl-12 pr-4 py-3 bg-[#0A0A0A] border border-white/10 text-white placeholder:text-white/20 font-mono text-sm focus:outline-none focus:border-accent transition-all" - /> -
    - - {/* Filters */} -
    - + /> +
    -
    - {[ + {/* Filters */} +
    + + +
    + {[ { id: 'all' as const, label: 'All', icon: Gavel }, { id: 'ending' as const, label: 'Ending', icon: Timer }, { id: 'hot' as const, label: 'Hot', icon: Flame }, - ].map((tab) => ( - - ))} + {tab.label} + + ))} +
    -
    @@ -604,43 +608,68 @@ export default function AcquirePage() {
    {/* Table Body */} - {loading ? ( + {loading ? (
    ) : filteredAuctions.length === 0 ? (
    No assets found
    ) : ( -
    + + +
    )}
    diff --git a/frontend/src/app/api/og/tld/route.tsx b/frontend/src/app/api/og/tld/route.tsx index 3a8c883..21dbba8 100644 --- a/frontend/src/app/api/og/tld/route.tsx +++ b/frontend/src/app/api/og/tld/route.tsx @@ -149,7 +149,7 @@ export async function GET(request: NextRequest) { letterSpacing: '0.02em', }} > - Domain Intelligence • Real-time Market Data + Domain Intelligence • Pricing Intel diff --git a/frontend/src/app/discover/[tld]/metadata.ts b/frontend/src/app/discover/[tld]/metadata.ts index 32a1b7c..adbd3f1 100755 --- a/frontend/src/app/discover/[tld]/metadata.ts +++ b/frontend/src/app/discover/[tld]/metadata.ts @@ -7,7 +7,7 @@ export async function generateTLDMetadata(tld: string, price?: number, trend?: n const trendText = trend ? (trend > 0 ? `+${trend.toFixed(1)}%` : `${trend.toFixed(1)}%`) : '' const title = `.${tldUpper} Domain Pricing & Market Analysis ${new Date().getFullYear()}` - const description = `Complete .${tldUpper} domain pricing intelligence${price ? ` starting at $${price.toFixed(2)}` : ''}${trendText ? ` (${trendText} trend)` : ''}. Compare registration, renewal, and transfer costs across major registrars. Real-time market data and price alerts.` + const description = `Complete .${tldUpper} domain pricing intelligence${price ? ` starting at $${price.toFixed(2)}` : ''}${trendText ? ` (${trendText} trend)` : ''}. Compare registration, renewal, and transfer costs across major registrars. Updated daily with market data and price alerts.` return { title, diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index f7abceb..cfa0c02 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -321,7 +321,7 @@ export default function HomePage() { { module: '01', title: 'Intelligence', - desc: '"Identify Targets." We scan 886+ TLDs in real-time to uncover hidden opportunities.', + desc: '"Identify Targets." We scan 886+ TLDs to uncover pricing traps, trends, and opportunities.', features: [ { icon: Scan, title: 'Global Scan', desc: 'Zone file analysis' }, { icon: Target, title: 'Valuation AI', desc: 'Instant fair-market value' }, diff --git a/frontend/src/app/register/page.tsx b/frontend/src/app/register/page.tsx index fabf5de..7a90313 100644 --- a/frontend/src/app/register/page.tsx +++ b/frontend/src/app/register/page.tsx @@ -43,7 +43,7 @@ function GitHubIcon({ className }: { className?: string }) { } const benefits = [ - { text: 'Real-time Market Feed', icon: Zap }, + { text: 'Live Market Feed', icon: Zap }, { text: 'Daily Scan Reports', icon: Shield }, { text: 'Yield Intel Access', icon: TrendingUp }, ] diff --git a/frontend/src/app/terminal/portfolio/page.tsx b/frontend/src/app/terminal/portfolio/page.tsx index 41a1788..c4b501d 100755 --- a/frontend/src/app/terminal/portfolio/page.tsx +++ b/frontend/src/app/terminal/portfolio/page.tsx @@ -93,6 +93,9 @@ export default function PortfolioPage() { // Mobile Menu & Navigation Drawer const [menuOpen, setMenuOpen] = useState(false) const [navDrawerOpen, setNavDrawerOpen] = useState(false) + + // Yield domains - to show which are in Yield + const [yieldDomains, setYieldDomains] = useState>(new Set()) const tier = subscription?.tier || 'scout' const isScout = tier === 'scout' @@ -102,12 +105,15 @@ export default function PortfolioPage() { const loadData = useCallback(async () => { setLoading(true) try { - const [domainsData, summaryData] = await Promise.all([ + const [domainsData, summaryData, yieldData] = await Promise.all([ api.getPortfolio(), - api.getPortfolioSummary() + api.getPortfolioSummary(), + api.getYieldDomains().catch(() => ({ domains: [] })) ]) setDomains(domainsData) setSummary(summaryData) + // Create a set of domain names that are in Yield + setYieldDomains(new Set((yieldData.domains || []).map((d: any) => d.domain.toLowerCase()))) } catch (err) { console.error('Failed to load portfolio:', err) } finally { @@ -334,7 +340,7 @@ export default function PortfolioPage() { Add Domain - + {/* ═══════════════════════════════════════════════════════════════════════ */} @@ -356,7 +362,7 @@ export default function PortfolioPage() { > Add Domain - + ) : (
    {/* Desktop Table Header */} @@ -401,15 +407,20 @@ export default function PortfolioPage() {
    {domain.domain}
    - {domain.registrar && ( + {domain.registrar && ( {domain.registrar} )} {domain.is_dns_verified && ( Verified - - )} -
    + + )} + {yieldDomains.has(domain.domain.toLowerCase()) && ( + + Yield + + )} +
    @@ -434,27 +445,27 @@ export default function PortfolioPage() {
    {!domain.is_dns_verified && ( - + )} - - +
    )} @@ -506,32 +517,39 @@ export default function PortfolioPage() { {/* Status */} -
    +
    {domain.is_sold ? ( Sold ) : domain.is_dns_verified ? ( - - Verified - + <> + + + + {yieldDomains.has(domain.domain.toLowerCase()) && ( + + + + )} + ) : ( - + )}
    {/* Actions */}
    {!domain.is_sold && domain.is_dns_verified && ( - + > Sell - + )} -
    +
    {/* Navigation Drawer */} @@ -597,11 +615,11 @@ export default function PortfolioPage() {
    Pounce Terminal -
    + - +
    @@ -626,8 +644,8 @@ export default function PortfolioPage() { )} ))} -
    - + + ))}
    @@ -639,14 +657,14 @@ export default function PortfolioPage() { Settings - -
    + + @@ -677,7 +695,7 @@ export default function PortfolioPage() { )} {toast && } - + ) } @@ -729,52 +747,52 @@ function AddDomainModal({ onClose, onSuccess, showToast }: {
    -
    +
    - setDomain(e.target.value)} placeholder="example.com" className="w-full px-4 py-3 bg-white/[0.02] border border-white/[0.08] text-white text-sm font-mono placeholder:text-white/20 focus:border-accent focus:outline-none" autoFocus - /> -
    + /> +
    -
    +
    - setPurchasePrice(e.target.value)} placeholder="100" className="w-full px-4 py-3 bg-white/[0.02] border border-white/[0.08] text-white text-sm font-mono placeholder:text-white/20 focus:border-accent focus:outline-none" - /> -
    + /> +
    -
    +
    - setRegistrar(e.target.value)} placeholder="Namecheap, GoDaddy, etc." className="w-full px-4 py-3 bg-white/[0.02] border border-white/[0.08] text-white text-sm font-mono placeholder:text-white/20 focus:border-accent focus:outline-none" - /> -
    + /> +
    - -
    - - + + + + ) } @@ -838,7 +856,7 @@ function DNSVerificationModal({ domain, onClose, onVerified, showToast }: { setTimeout(() => setCopied(null), 2000) } catch {} } - + return (
    @@ -847,16 +865,16 @@ function DNSVerificationModal({ domain, onClose, onVerified, showToast }: {

    Verify Ownership

    -
    + + +

    Add a DNS TXT record to prove you own {domain.domain}

    - + {loading ? (
    -
    + ) : verificationData ? (
    @@ -871,10 +889,10 @@ function DNSVerificationModal({ domain, onClose, onVerified, showToast }: { > {copied === 'name' ? : } +
    - -
    +
    @@ -886,12 +904,12 @@ function DNSVerificationModal({ domain, onClose, onVerified, showToast }: { > {copied === 'value' ? : } -
    -
    - +
    +
    +
    💡 DNS changes can take 1-5 minutes to propagate. If verification fails, wait a moment and try again. -
    + - + + ) : (
    Failed to load verification data -
    + )} diff --git a/frontend/src/app/terminal/yield/page.tsx b/frontend/src/app/terminal/yield/page.tsx index c9e835e..4b30a83 100644 --- a/frontend/src/app/terminal/yield/page.tsx +++ b/frontend/src/app/terminal/yield/page.tsx @@ -5,7 +5,7 @@ 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 + Crown, Coins, Tag, X, Briefcase, Trash2 } from 'lucide-react' import { api, YieldDomain, YieldTransaction } from '@/lib/api' import { useStore } from '@/lib/store' @@ -148,6 +148,7 @@ export default function YieldPage() { const [showActivateModal, setShowActivateModal] = useState(false) const [refreshing, setRefreshing] = useState(false) const [menuOpen, setMenuOpen] = useState(false) + const [deletingId, setDeletingId] = useState(null) useEffect(() => { checkAuth() }, [checkAuth]) @@ -159,6 +160,19 @@ export default function YieldPage() { 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]) const stats = dashboard?.stats @@ -285,13 +299,14 @@ export default function YieldPage() { ) : (
    {/* Header */} -
    +
    Domain
    Status
    Intent
    Clicks
    Conv.
    Revenue
    +
    Action
    {dashboard.domains.map((domain: YieldDomain) => ( @@ -307,14 +322,27 @@ export default function YieldPage() {
    -
    - {domain.total_clicks} clicks - ${domain.total_revenue} +
    +
    + {domain.total_clicks} clicks + ${domain.total_revenue} +
    +
    {/* Desktop */} -
    +
    {domain.domain.charAt(0).toUpperCase()} @@ -326,6 +354,20 @@ export default function YieldPage() {
    {domain.total_clicks}
    {domain.total_conversions}
    ${domain.total_revenue}
    +
    + +
    ))} diff --git a/frontend/src/app/yield/page.tsx b/frontend/src/app/yield/page.tsx index edaf9d3..72d5b90 100644 --- a/frontend/src/app/yield/page.tsx +++ b/frontend/src/app/yield/page.tsx @@ -421,7 +421,7 @@ export default function YieldPage() { { icon: PieChart, title: 'Revenue Share', desc: 'Industry-leading 70/30 split. We only make money when you do.' }, { icon: Target, title: '20+ Verticals', desc: 'Finance, Insurance, Travel, Health, B2B Services, and more.' }, { icon: Shield, title: 'Swiss Partners', desc: 'Direct API integrations with premium Swiss & EU brands.' }, - { icon: BarChart3, title: 'Live Analytics', desc: 'Real-time dashboard showing every click, lead, and conversion.' }, + { icon: BarChart3, title: 'Live Analytics', desc: 'Live dashboard showing every click, lead, and conversion.' }, { icon: RefreshCw, title: 'Auto-Optimization', desc: 'AI automatically routes to the highest-paying partner for each visitor.' }, { icon: Zap, title: 'Instant DNS', desc: 'Zero downtime. Verify ownership and start earning in < 5 minutes.' } ].map((feat, i) => (