'use client' import { useEffect, useState, useMemo, useCallback, memo } from 'react' import { useStore } from '@/lib/store' import { api, PortfolioDomain, PortfolioSummary, DomainValuation, DomainHealthReport, HealthStatus } from '@/lib/api' import { TerminalLayout } from '@/components/TerminalLayout' import { PremiumTable, StatCard, PageContainer, ActionButton } from '@/components/PremiumTable' import { Toast, useToast } from '@/components/Toast' import { Plus, Trash2, Edit2, DollarSign, Calendar, Building, Loader2, ArrowUpRight, X, Briefcase, ShoppingCart, Activity, Shield, AlertTriangle, Tag, MoreVertical, ExternalLink, } from 'lucide-react' import clsx from 'clsx' import Link from 'next/link' // Health status configuration const healthStatusConfig: Record = { healthy: { label: 'Healthy', color: 'text-accent', bgColor: 'bg-accent/10', icon: Activity }, weakening: { label: 'Weak', color: 'text-amber-400', bgColor: 'bg-amber-400/10', icon: AlertTriangle }, parked: { label: 'Parked', color: 'text-orange-400', bgColor: 'bg-orange-400/10', icon: ShoppingCart }, critical: { label: 'Critical', color: 'text-red-400', bgColor: 'bg-red-400/10', icon: AlertTriangle }, unknown: { label: 'Unknown', color: 'text-foreground-muted', bgColor: 'bg-foreground/5', icon: Activity }, } export default function PortfolioPage() { const { subscription } = useStore() const { toast, showToast, hideToast } = useToast() const [portfolio, setPortfolio] = useState([]) const [summary, setSummary] = useState(null) const [loading, setLoading] = useState(true) const [showAddModal, setShowAddModal] = useState(false) const [showEditModal, setShowEditModal] = useState(false) const [showSellModal, setShowSellModal] = useState(false) const [showValuationModal, setShowValuationModal] = useState(false) const [selectedDomain, setSelectedDomain] = useState(null) const [valuation, setValuation] = useState(null) const [valuatingDomain, setValuatingDomain] = useState('') const [addingDomain, setAddingDomain] = useState(false) const [savingEdit, setSavingEdit] = useState(false) const [processingSale, setProcessingSale] = useState(false) const [refreshingId, setRefreshingId] = useState(null) // Health monitoring state const [healthReports, setHealthReports] = useState>({}) const [loadingHealth, setLoadingHealth] = useState>({}) const [selectedHealthDomain, setSelectedHealthDomain] = useState(null) // Dropdown menu state const [openMenuId, setOpenMenuId] = useState(null) const [addForm, setAddForm] = useState({ domain: '', purchase_price: '', purchase_date: '', registrar: '', renewal_date: '', renewal_cost: '', notes: '', }) const [editForm, setEditForm] = useState({ purchase_price: '', purchase_date: '', registrar: '', renewal_date: '', renewal_cost: '', notes: '', }) const [sellForm, setSellForm] = useState({ sale_date: new Date().toISOString().split('T')[0], sale_price: '', }) const loadPortfolio = useCallback(async () => { setLoading(true) try { const [portfolioData, summaryData] = await Promise.all([ api.getPortfolio(), api.getPortfolioSummary(), ]) setPortfolio(portfolioData) setSummary(summaryData) } catch (error) { console.error('Failed to load portfolio:', error) } finally { setLoading(false) } }, []) useEffect(() => { loadPortfolio() }, [loadPortfolio]) const handleAddDomain = useCallback(async (e: React.FormEvent) => { e.preventDefault() if (!addForm.domain.trim()) return setAddingDomain(true) try { await api.addPortfolioDomain({ domain: addForm.domain.trim(), purchase_price: addForm.purchase_price ? parseFloat(addForm.purchase_price) : undefined, purchase_date: addForm.purchase_date || undefined, registrar: addForm.registrar || undefined, renewal_date: addForm.renewal_date || undefined, renewal_cost: addForm.renewal_cost ? parseFloat(addForm.renewal_cost) : undefined, notes: addForm.notes || undefined, }) showToast(`Added ${addForm.domain} to portfolio`, 'success') setAddForm({ domain: '', purchase_price: '', purchase_date: '', registrar: '', renewal_date: '', renewal_cost: '', notes: '' }) setShowAddModal(false) loadPortfolio() } catch (err: any) { showToast(err.message || 'Failed to add domain', 'error') } finally { setAddingDomain(false) } }, [addForm, loadPortfolio, showToast]) const handleEditDomain = useCallback(async (e: React.FormEvent) => { e.preventDefault() if (!selectedDomain) return setSavingEdit(true) try { await api.updatePortfolioDomain(selectedDomain.id, { purchase_price: editForm.purchase_price ? parseFloat(editForm.purchase_price) : undefined, purchase_date: editForm.purchase_date || undefined, registrar: editForm.registrar || undefined, renewal_date: editForm.renewal_date || undefined, renewal_cost: editForm.renewal_cost ? parseFloat(editForm.renewal_cost) : undefined, notes: editForm.notes || undefined, }) showToast('Domain updated', 'success') setShowEditModal(false) loadPortfolio() } catch (err: any) { showToast(err.message || 'Failed to update', 'error') } finally { setSavingEdit(false) } }, [selectedDomain, editForm, loadPortfolio, showToast]) const handleSellDomain = useCallback(async (e: React.FormEvent) => { e.preventDefault() if (!selectedDomain || !sellForm.sale_price) return setProcessingSale(true) try { await api.markDomainSold(selectedDomain.id, sellForm.sale_date, parseFloat(sellForm.sale_price)) showToast(`Marked ${selectedDomain.domain} as sold`, 'success') setShowSellModal(false) loadPortfolio() } catch (err: any) { showToast(err.message || 'Failed to process sale', 'error') } finally { setProcessingSale(false) } }, [selectedDomain, sellForm, loadPortfolio, showToast]) const handleValuate = useCallback(async (domain: PortfolioDomain) => { setValuatingDomain(domain.domain) setShowValuationModal(true) try { const result = await api.getDomainValuation(domain.domain) setValuation(result) } catch (err: any) { showToast(err.message || 'Failed to get valuation', 'error') setShowValuationModal(false) } finally { setValuatingDomain('') } }, [showToast]) const handleRefresh = useCallback(async (domain: PortfolioDomain) => { setRefreshingId(domain.id) try { await api.refreshDomainValue(domain.id) showToast('Valuation refreshed', 'success') loadPortfolio() } catch (err: any) { showToast(err.message || 'Failed to refresh', 'error') } finally { setRefreshingId(null) } }, [loadPortfolio, showToast]) const handleHealthCheck = useCallback(async (domainName: string) => { if (loadingHealth[domainName]) return setLoadingHealth(prev => ({ ...prev, [domainName]: true })) try { const report = await api.quickHealthCheck(domainName) setHealthReports(prev => ({ ...prev, [domainName]: report })) setSelectedHealthDomain(domainName) } catch (err: any) { showToast(err.message || 'Health check failed', 'error') } finally { setLoadingHealth(prev => ({ ...prev, [domainName]: false })) } }, [loadingHealth, showToast]) const handleDelete = useCallback(async (domain: PortfolioDomain) => { if (!confirm(`Remove ${domain.domain} from your portfolio?`)) return try { await api.deletePortfolioDomain(domain.id) showToast(`Removed ${domain.domain}`, 'success') loadPortfolio() } catch (err: any) { showToast(err.message || 'Failed to remove', 'error') } }, [loadPortfolio, showToast]) const openEditModal = useCallback((domain: PortfolioDomain) => { setSelectedDomain(domain) setEditForm({ purchase_price: domain.purchase_price?.toString() || '', purchase_date: domain.purchase_date || '', registrar: domain.registrar || '', renewal_date: domain.renewal_date || '', renewal_cost: domain.renewal_cost?.toString() || '', notes: domain.notes || '', }) setShowEditModal(true) }, []) const openSellModal = useCallback((domain: PortfolioDomain) => { setSelectedDomain(domain) setSellForm({ sale_date: new Date().toISOString().split('T')[0], sale_price: '', }) setShowSellModal(true) }, []) const portfolioLimit = subscription?.portfolio_limit || 0 const canAddMore = portfolioLimit === -1 || portfolio.length < portfolioLimit // Memoized stats and subtitle const { expiringSoonCount, subtitle } = useMemo(() => { const expiring = portfolio.filter(d => { if (!d.renewal_date) return false const days = Math.ceil((new Date(d.renewal_date).getTime() - Date.now()) / (1000 * 60 * 60 * 24)) return days <= 30 && days > 0 }).length let sub = '' if (loading) sub = 'Loading your portfolio...' else if (portfolio.length === 0) sub = 'Start tracking your domains' else if (expiring > 0) sub = `${portfolio.length} domain${portfolio.length !== 1 ? 's' : ''} • ${expiring} expiring soon` else sub = `Managing ${portfolio.length} domain${portfolio.length !== 1 ? 's' : ''}` return { expiringSoonCount: expiring, subtitle: sub } }, [portfolio, loading]) return ( setShowAddModal(true)} disabled={!canAddMore} icon={Plus}> Add Domain } > {toast && } {/* Summary Stats - Only reliable data */}
r.status !== 'healthy').length} icon={AlertTriangle} />
{!canAddMore && (

You've reached your portfolio limit. Upgrade to add more.

Upgrade
)} {/* Portfolio Table */} d.id} loading={loading} emptyIcon={} emptyTitle="Your portfolio is empty" emptyDescription="Add your first domain to start tracking investments" columns={[ { key: 'domain', header: 'Domain', render: (domain) => (
{domain.domain} {domain.registrar && (

{domain.registrar}

)}
), }, { key: 'added', header: 'Added', hideOnMobile: true, hideOnTablet: true, render: (domain) => ( {domain.purchase_date ? new Date(domain.purchase_date).toLocaleDateString() : new Date(domain.created_at).toLocaleDateString() } ), }, { key: 'renewal', header: 'Expires', hideOnMobile: true, render: (domain) => { if (!domain.renewal_date) { return } const days = Math.ceil((new Date(domain.renewal_date).getTime() - Date.now()) / (1000 * 60 * 60 * 24)) const isExpiringSoon = days <= 30 && days > 0 const isExpired = days <= 0 return (
{new Date(domain.renewal_date).toLocaleDateString()} {isExpiringSoon && ( {days}d )} {isExpired && ( EXPIRED )}
) }, }, { key: 'health', header: 'Health', hideOnMobile: true, render: (domain) => { const report = healthReports[domain.domain] if (loadingHealth[domain.domain]) { return } if (report) { const config = healthStatusConfig[report.status] const Icon = config.icon return ( ) } return ( ) }, }, { key: 'actions', header: '', align: 'right', render: (domain) => (
{openMenuId === domain.id && ( <> {/* Backdrop */}
setOpenMenuId(null)} /> {/* Menu - opens downward */}
setOpenMenuId(null)} className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-accent hover:bg-accent/5 transition-colors" > List for Sale setOpenMenuId(null)} className="w-full flex items-center gap-3 px-4 py-2.5 text-sm text-foreground-muted hover:text-foreground hover:bg-foreground/5 transition-colors" > Visit Website
)}
), }, ]} /> {/* Add Modal */} {showAddModal && ( setShowAddModal(false)}>
setAddForm({ ...addForm, domain: e.target.value })} placeholder="example.com" className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50 transition-all" required />
setAddForm({ ...addForm, purchase_price: e.target.value })} placeholder="100" className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" />
setAddForm({ ...addForm, purchase_date: e.target.value })} className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" />
setAddForm({ ...addForm, registrar: e.target.value })} placeholder="Namecheap" className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" />
)} {/* Edit Modal */} {showEditModal && selectedDomain && ( setShowEditModal(false)}>
setEditForm({ ...editForm, purchase_price: e.target.value })} className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" />
setEditForm({ ...editForm, registrar: e.target.value })} className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" />
)} {/* Record Sale Modal - for tracking completed sales */} {showSellModal && selectedDomain && ( setShowSellModal(false)}>

Record a completed sale to track your profit/loss. This will mark the domain as sold in your portfolio.

Want to list it for sale instead? Use the "List" button.

setSellForm({ ...sellForm, sale_price: e.target.value })} placeholder="1000" className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" required />
setSellForm({ ...sellForm, sale_date: e.target.value })} className="w-full h-11 px-4 bg-background border border-border/50 rounded-xl text-foreground focus:outline-none focus:border-accent/50" />
)} {/* Valuation Modal */} {showValuationModal && ( { setShowValuationModal(false); setValuation(null); }}> {valuatingDomain ? (
) : valuation ? (

${valuation.estimated_value.toLocaleString()}

Pounce Score Estimate

Confidence Level {valuation.confidence}

This is an algorithmic estimate based on domain length, TLD, and market patterns. Actual market value may vary.

) : null}
)} {/* Health Report Modal */} {selectedHealthDomain && healthReports[selectedHealthDomain] && ( setSelectedHealthDomain(null)} /> )} ) } // Health Report Modal Component function HealthReportModal({ report, onClose }: { report: DomainHealthReport; onClose: () => void }) { const config = healthStatusConfig[report.status] const Icon = config.icon return (
e.stopPropagation()} > {/* Header */}

{report.domain}

{config.label}

{/* Score */}
Health Score
= 70 ? "bg-accent" : report.score >= 40 ? "bg-amber-400" : "bg-red-400" )} style={{ width: `${report.score}%` }} />
= 70 ? "text-accent" : report.score >= 40 ? "text-amber-400" : "text-red-400" )}> {report.score}/100
{/* Check Results */}
{/* DNS */} {report.dns && (

DNS Infrastructure

{report.dns.has_ns ? '✓' : '✗'} Nameservers
{report.dns.has_a ? '✓' : '✗'} A Record
{report.dns.has_mx ? '✓' : '—'} MX Record
{report.dns.is_parked && (

⚠ Parked at {report.dns.parking_provider || 'unknown provider'}

)}
)} {/* HTTP */} {report.http && (

Website Status

{report.http.is_reachable ? 'Reachable' : 'Unreachable'} {report.http.status_code && ( HTTP {report.http.status_code} )}
{report.http.is_parked && (

⚠ Parking page detected

)}
)} {/* SSL */} {report.ssl && (

SSL Certificate

{report.ssl.has_certificate ? (

{report.ssl.is_valid ? '✓ Valid certificate' : '✗ Certificate invalid/expired'}

{report.ssl.days_until_expiry !== undefined && (

30 ? "text-foreground-muted" : report.ssl.days_until_expiry > 7 ? "text-amber-400" : "text-red-400" )}> Expires in {report.ssl.days_until_expiry} days

)}
) : (

No SSL certificate

)}
)} {/* Signals & Recommendations */} {((report.signals?.length || 0) > 0 || (report.recommendations?.length || 0) > 0) && (
{(report.signals?.length || 0) > 0 && (

Signals

    {report.signals?.map((signal, i) => (
  • {signal}
  • ))}
)} {(report.recommendations?.length || 0) > 0 && (

Recommendations

    {report.recommendations?.map((rec, i) => (
  • {rec}
  • ))}
)}
)}
{/* Footer */}

Checked at {new Date(report.checked_at).toLocaleString()}

) } // Modal Component function Modal({ title, children, onClose }: { title: string; children: React.ReactNode; onClose: () => void }) { return (
e.stopPropagation()} >

{title}

{children}
) }