From 26daad68cf4a9e599e247ba2215107be04d9438b Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Sat, 13 Dec 2025 17:46:07 +0100 Subject: [PATCH] feat: Mobile-optimized + left-aligned public pages (acquire, discover, yield, pricing, landing) --- frontend/src/app/acquire/page.tsx | 10 +- frontend/src/app/discover/page.tsx | 20 +- frontend/src/app/page.tsx | 4 +- frontend/src/app/pricing/page.tsx | 46 +- .../src/app/terminal/intel/[tld]/page.tsx | 4 +- frontend/src/app/terminal/intel/page.tsx | 4 +- frontend/src/app/terminal/listing/page.tsx | 3 +- frontend/src/app/terminal/market/page.tsx | 4 +- frontend/src/app/terminal/portfolio/page.tsx | 1654 ++++++++--------- frontend/src/app/terminal/radar/page.tsx | 4 +- frontend/src/app/terminal/settings/page.tsx | 2 + frontend/src/app/terminal/sniper/page.tsx | 4 +- frontend/src/app/terminal/watchlist/page.tsx | 4 +- frontend/src/app/terminal/yield/page.tsx | 3 +- frontend/src/app/yield/page.tsx | 34 +- frontend/src/components/Sidebar.tsx | 7 + 16 files changed, 845 insertions(+), 962 deletions(-) diff --git a/frontend/src/app/acquire/page.tsx b/frontend/src/app/acquire/page.tsx index 1774d9d..63ad8b5 100644 --- a/frontend/src/app/acquire/page.tsx +++ b/frontend/src/app/acquire/page.tsx @@ -288,20 +288,20 @@ export default function MarketPage() {
-
+
{/* Hero Header - High Tech */} -
+
- +
Live Liquidity Pool -

+

Acquire Assets.

-

+

Global liquidity pool. Verified assets only. Aggregated from GoDaddy, Sedo, and Pounce Direct.

diff --git a/frontend/src/app/discover/page.tsx b/frontend/src/app/discover/page.tsx index 927aa6a..a84b0b7 100644 --- a/frontend/src/app/discover/page.tsx +++ b/frontend/src/app/discover/page.tsx @@ -147,23 +147,23 @@ export default function DiscoverPage() {
-
+
- {/* Header Section - Matched with Acquire */} -
-
+ {/* Header Section */} +
+
- +
Global Intelligence -

+

Market Pulse.

-

- Real-time inflation monitor. Track pricing, renewal risks, and breakout trends. - Aggregated intelligence across 886+ TLDs. +

+ Real-time inflation monitor. Track pricing and breakout trends. + 886+ TLDs monitored.

@@ -182,7 +182,7 @@ export default function DiscoverPage() { {/* Top Movers Cards */} {topGainer && topVolume && highRisk && ( -
+
{/* Left: Typography & Brand */} -
+
{/* Brand Seal - Mobile */}
@@ -187,7 +187,7 @@ export default function HomePage() { { value: '10s', label: 'Latency' }, { value: '$1B+', label: 'Assets Tracked' }, ].map((stat, i) => ( -
+
{stat.value}
{stat.label}
diff --git a/frontend/src/app/pricing/page.tsx b/frontend/src/app/pricing/page.tsx index 25a1f40..941e3c2 100644 --- a/frontend/src/app/pricing/page.tsx +++ b/frontend/src/app/pricing/page.tsx @@ -175,7 +175,7 @@ export default function PricingPage() {
-
+
{/* Cancelled Banner */} {showCancelledBanner && ( @@ -198,28 +198,28 @@ export default function PricingPage() { )} {/* Hero */} -
- +
+
Clearance Levels -

+

Pick your weapon.

-

- Start free. Scale when you're ready. All plans include core features. +

+ Start free. Scale when you're ready.

- {/* Pricing Cards - AWARD WINNING STYLE */} -
+ {/* Pricing Cards */} +
{tiers.map((tier, index) => (
{/* Header */} -
-

{tier.name}

-
+
+

{tier.name}

+
{tier.price === '0' ? ( - Free + Free ) : ( <> - ${tier.price} - {tier.period} + ${tier.price} + {tier.period} )}
-

{tier.description}

+

{tier.description}

{/* Features */} -
    +
      {tier.features.map((feature) => (
    • {feature.available ? ( @@ -319,8 +319,8 @@ export default function PricingPage() { ))}
- {/* Comparison Table - TECH STYLE */} -
+ {/* Comparison Table */} +

Spec Sheet

@@ -354,9 +354,9 @@ export default function PricingPage() {
- {/* FAQ - TERMINAL STYLE */} -
-

Mission Support

+ {/* FAQ */} +
+

Mission Support

{faqs.map((faq, i) => (
-
- -
-
-
- - {label} -
-
- {value} - {subValue && {subValue}} -
- {trend && ( -
- {trend === 'up' ? : trend === 'down' ? : null} - {trend === 'up' ? 'PROFIT' : trend === 'down' ? 'LOSS' : 'NEUTRAL'} -
- )} -
-
- ) +function getDaysUntilRenewal(renewalDate: string | null): number | null { + if (!renewalDate) return null + const renDate = new Date(renewalDate) + const now = new Date() + const diffTime = renDate.getTime() - now.getTime() + return Math.ceil(diffTime / (1000 * 60 * 60 * 24)) } -// ============================================================================ -// TYPES -// ============================================================================ - -interface PortfolioDomain { - id: number - domain: string - purchase_date: string | null - purchase_price: number | null - purchase_registrar: string | null - registrar: string | null - renewal_date: string | null - renewal_cost: number | null - auto_renew: boolean - estimated_value: number | null - value_updated_at: string | null - is_sold: boolean - sale_date: string | null - sale_price: number | null - status: string - notes: string | null - tags: string | null - roi: number | null - created_at: string - updated_at: string +function formatDate(date: string | null): string { + if (!date) return '—' + return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) } -interface PortfolioSummary { - total_domains: number - active_domains: number - sold_domains: number - total_invested: number - total_value: number - total_sold_value: number - unrealized_profit: number - realized_profit: number - overall_roi: number +function formatCurrency(value: number | null): string { + if (value === null || value === undefined) return '—' + return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD', maximumFractionDigits: 0 }).format(value) +} + +function formatROI(roi: number | null): string { + if (roi === null || roi === undefined) return '—' + const sign = roi >= 0 ? '+' : '' + return `${sign}${roi.toFixed(0)}%` } // ============================================================================ @@ -131,857 +73,775 @@ interface PortfolioSummary { // ============================================================================ export default function PortfolioPage() { - const { subscription } = useStore() + const { subscription, user, logout, checkAuth } = useStore() + const { toast, showToast, hideToast } = useToast() const [domains, setDomains] = useState([]) const [summary, setSummary] = useState(null) const [loading, setLoading] = useState(true) - - // Modals + const [refreshingId, setRefreshingId] = useState(null) + const [deletingId, setDeletingId] = useState(null) const [showAddModal, setShowAddModal] = useState(false) - const [showEditModal, setShowEditModal] = useState(false) - const [showSellModal, setShowSellModal] = useState(false) - const [showListModal, setShowListModal] = useState(false) const [selectedDomain, setSelectedDomain] = useState(null) - const [saving, setSaving] = useState(false) - const [error, setError] = useState(null) - const [success, setSuccess] = useState(null) + const [filter, setFilter] = useState<'all' | 'active' | 'sold'>('all') - // List for sale form - const [listData, setListData] = useState({ - asking_price: '', - price_type: 'negotiable', - }) + // Sorting + const [sortField, setSortField] = useState<'domain' | 'value' | 'roi' | 'renewal'>('domain') + const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc') - // Form state - const [formData, setFormData] = useState({ - domain: '', - purchase_date: '', - purchase_price: '', - registrar: '', - renewal_date: '', - renewal_cost: '', - notes: '', - tags: '', - }) + // Mobile Menu + const [menuOpen, setMenuOpen] = useState(false) - const [sellData, setSellData] = useState({ - sale_date: new Date().toISOString().split('T')[0], - sale_price: '', - }) + // Tier-based access for listing (same as listing page) + const tier = subscription?.tier || 'scout' + const isScout = tier === 'scout' + const canListForSale = !isScout // Only Trader & Tycoon can list + + useEffect(() => { checkAuth() }, [checkAuth]) const loadData = useCallback(async () => { setLoading(true) try { const [domainsData, summaryData] = await Promise.all([ - api.request('/portfolio'), - api.request('/portfolio/summary'), + api.getPortfolio(), + api.getPortfolioSummary() ]) setDomains(domainsData) setSummary(summaryData) - } catch (err: any) { + } catch (err) { console.error('Failed to load portfolio:', err) } finally { setLoading(false) } }, []) - useEffect(() => { - loadData() - }, [loadData]) + useEffect(() => { loadData() }, [loadData]) - const handleAdd = async (e: React.FormEvent) => { - e.preventDefault() - setSaving(true) - setError(null) - - try { - await api.request('/portfolio', { - method: 'POST', - body: JSON.stringify({ - domain: formData.domain, - purchase_date: formData.purchase_date || null, - purchase_price: formData.purchase_price ? parseFloat(formData.purchase_price) : null, - registrar: formData.registrar || null, - renewal_date: formData.renewal_date || null, - renewal_cost: formData.renewal_cost ? parseFloat(formData.renewal_cost) : null, - notes: formData.notes || null, - tags: formData.tags || null, - }), - }) - setSuccess('Domain added to portfolio!') - setShowAddModal(false) - setFormData({ domain: '', purchase_date: '', purchase_price: '', registrar: '', renewal_date: '', renewal_cost: '', notes: '', tags: '' }) - loadData() - } catch (err: any) { - setError(err.message) - } finally { - setSaving(false) - } - } + // Stats + const stats = useMemo(() => { + const active = domains.filter(d => !d.is_sold).length + const sold = domains.filter(d => d.is_sold).length + const renewingSoon = domains.filter(d => { + if (d.is_sold || !d.renewal_date) return false + const days = getDaysUntilRenewal(d.renewal_date) + return days !== null && days <= 30 && days > 0 + }).length + return { total: domains.length, active, sold, renewingSoon } + }, [domains]) - const handleEdit = async (e: React.FormEvent) => { - e.preventDefault() - if (!selectedDomain) return - setSaving(true) - setError(null) - - try { - await api.request(`/portfolio/${selectedDomain.id}`, { - method: 'PUT', - body: JSON.stringify({ - purchase_date: formData.purchase_date || null, - purchase_price: formData.purchase_price ? parseFloat(formData.purchase_price) : null, - registrar: formData.registrar || null, - renewal_date: formData.renewal_date || null, - renewal_cost: formData.renewal_cost ? parseFloat(formData.renewal_cost) : null, - notes: formData.notes || null, - tags: formData.tags || null, - }), - }) - setSuccess('Domain updated!') - setShowEditModal(false) - loadData() - } catch (err: any) { - setError(err.message) - } finally { - setSaving(false) - } - } - - const handleSell = async (e: React.FormEvent) => { - e.preventDefault() - if (!selectedDomain) return - setSaving(true) - setError(null) - - try { - await api.request(`/portfolio/${selectedDomain.id}/sell`, { - method: 'POST', - body: JSON.stringify({ - sale_date: sellData.sale_date, - sale_price: parseFloat(sellData.sale_price), - }), - }) - setSuccess(`🎉 Congratulations! ${selectedDomain.domain} marked as sold!`) - setShowSellModal(false) - loadData() - } catch (err: any) { - setError(err.message) - } finally { - setSaving(false) - } - } - - const handleDelete = async (domain: PortfolioDomain) => { - if (!confirm(`Remove ${domain.domain} from portfolio?`)) return - - try { - await api.request(`/portfolio/${domain.id}`, { method: 'DELETE' }) - setSuccess('Domain removed from portfolio') - loadData() - } catch (err: any) { - setError(err.message) - } - } - - const handleRefreshValue = async (domain: PortfolioDomain) => { - try { - await api.request(`/portfolio/${domain.id}/refresh-value`, { method: 'POST' }) - setSuccess(`Value refreshed for ${domain.domain}`) - loadData() - } catch (err: any) { - setError(err.message) - } - } - - const openEditModal = (domain: PortfolioDomain) => { - setSelectedDomain(domain) - setFormData({ - domain: domain.domain, - purchase_date: domain.purchase_date?.split('T')[0] || '', - purchase_price: domain.purchase_price?.toString() || '', - registrar: domain.registrar || '', - renewal_date: domain.renewal_date?.split('T')[0] || '', - renewal_cost: domain.renewal_cost?.toString() || '', - notes: domain.notes || '', - tags: domain.tags || '', + // Filtered & Sorted + const filteredDomains = useMemo(() => { + let filtered = domains.filter(d => { + if (filter === 'active') return !d.is_sold + if (filter === 'sold') return d.is_sold + return true }) - setShowEditModal(true) - } - - const openSellModal = (domain: PortfolioDomain) => { - setSelectedDomain(domain) - setSellData({ - sale_date: new Date().toISOString().split('T')[0], - sale_price: domain.estimated_value?.toString() || '', - }) - setShowSellModal(true) - } - - const openListModal = (domain: PortfolioDomain) => { - setSelectedDomain(domain) - setListData({ - asking_price: domain.estimated_value?.toString() || '', - price_type: 'negotiable', - }) - setShowListModal(true) - } - - const handleListForSale = async (e: React.FormEvent) => { - e.preventDefault() - if (!selectedDomain) return - setSaving(true) - setError(null) - try { - // Create a listing for this domain - await api.request('/listings', { - method: 'POST', - body: JSON.stringify({ - domain: selectedDomain.domain, - asking_price: listData.asking_price ? parseFloat(listData.asking_price) : null, - price_type: listData.price_type, - allow_offers: true, - }), - }) - setSuccess(`${selectedDomain.domain} is now listed for sale! Go to "For Sale" to verify ownership and publish.`) - setShowListModal(false) - } catch (err: any) { - setError(err.message) - } finally { - setSaving(false) + const mult = sortDirection === 'asc' ? 1 : -1 + filtered.sort((a, b) => { + switch (sortField) { + case 'domain': return mult * a.domain.localeCompare(b.domain) + case 'value': return mult * ((a.estimated_value || 0) - (b.estimated_value || 0)) + case 'roi': return mult * ((a.roi || 0) - (b.roi || 0)) + case 'renewal': + const aDate = a.renewal_date ? new Date(a.renewal_date).getTime() : Infinity + const bDate = b.renewal_date ? new Date(b.renewal_date).getTime() : Infinity + return mult * (aDate - bDate) + default: return 0 + } + }) + + return filtered + }, [domains, filter, sortField, sortDirection]) + + const handleSort = useCallback((field: typeof sortField) => { + if (sortField === field) { + setSortDirection(d => d === 'asc' ? 'desc' : 'asc') + } else { + setSortField(field) + setSortDirection('asc') } - } + }, [sortField]) - const formatCurrency = (value: number | null) => { - if (value === null) return '—' - return new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', - minimumFractionDigits: 0, - }).format(value) - } + const handleRefreshValue = useCallback(async (id: number) => { + setRefreshingId(id) + try { + const updated = await api.refreshDomainValue(id) + setDomains(prev => prev.map(d => d.id === id ? updated : d)) + showToast('Value updated', 'success') + } catch { showToast('Update failed', 'error') } + finally { setRefreshingId(null) } + }, [showToast]) - const formatDate = (date: string | null) => { - if (!date) return '—' - return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) - } + const handleDelete = useCallback(async (id: number, name: string) => { + if (!confirm(`Remove ${name} from portfolio?`)) return + setDeletingId(id) + try { + await api.deletePortfolioDomain(id) + setDomains(prev => prev.filter(d => d.id !== id)) + showToast('Domain removed', 'success') + loadData() // Refresh summary + } catch { showToast('Failed', 'error') } + finally { setDeletingId(null) } + }, [showToast, loadData]) - // Tier check - const tier = subscription?.tier || 'scout' - const canUsePortfolio = tier !== 'scout' + const tierName = subscription?.tier_name || subscription?.tier || 'Scout' + const TierIcon = tierName === 'Tycoon' ? Crown : tierName === 'Trader' ? TrendingUp : Zap + + const mobileNavItems = [ + { href: '/terminal/radar', 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 drawerNavSections = [ + { title: 'Discover', items: [ + { href: '/terminal/radar', 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, active: true }, + { href: '/terminal/sniper', label: 'Sniper', icon: Target }, + ]}, + { title: 'Monetize', items: [ + { href: '/terminal/yield', label: 'Yield', icon: Coins, isNew: true }, + { href: '/terminal/listing', label: 'For Sale', icon: Tag }, + ]} + ] return ( - -
- - {/* Ambient Background Glow */} -
-
-
-
- -
- - {/* Header Section */} -
-
-
-
-

Portfolio

+
+
+ +
+ {/* MOBILE HEADER */} +
+
+
+
+
+ My Portfolio +
+
+ {stats.total} domains
-

- Track your domain investments, valuations, and ROI. Your personal domain asset manager. -

- {canUsePortfolio && ( - - )} -
- - {/* Messages */} - {error && ( -
- -

{error}

- -
- )} - - {success && ( -
- -

{success}

- + {/* Stats Grid */} +
+
+
{stats.active}
+
Active
+
+
+
{formatCurrency(summary?.total_value || 0).replace('$', '')}
+
Value
+
+
+
= 0 ? "text-accent" : "text-rose-400")}> + {formatROI(summary?.overall_roi || 0)} +
+
ROI
+
+
+
{stats.renewingSoon}
+
Renewing
+
- )} +
+
- {/* Paywall */} - {!canUsePortfolio && ( -
-
-
- -

Unlock Portfolio Management

-

- Track your domain investments, monitor valuations, and calculate ROI. Know exactly how your portfolio is performing. -

- +
+
+
+
+ Domain Assets +
+

+ Portfolio + {stats.total} +

+
+ +
+
+
{formatCurrency(summary?.total_invested || 0)}
+
Invested
+
+
+
{formatCurrency(summary?.total_value || 0)}
+
Value
+
+
+
= 0 ? "text-accent" : "text-rose-400")}> + {formatROI(summary?.overall_roi || 0)} +
+
ROI
+
+
+
{stats.renewingSoon}
+
Renewing
+
+
+
+ + + {/* ADD DOMAIN + FILTERS */} +
+
+
-
- )} - - {/* Stats Grid */} - {canUsePortfolio && summary && ( -
- 0 ? 'up' : summary.unrealized_profit < 0 ? 'down' : 'neutral'} - /> - - = 0 ? 'emerald' : 'rose'} - trend={summary.unrealized_profit > 0 ? 'up' : summary.unrealized_profit < 0 ? 'down' : 'neutral'} - /> - 0 ? '+' : ''}${summary.overall_roi.toFixed(1)}%`} - subValue={`${summary.sold_domains} sold`} - icon={Target} - color={summary.overall_roi >= 0 ? 'emerald' : 'rose'} - trend={summary.overall_roi > 0 ? 'up' : summary.overall_roi < 0 ? 'down' : 'neutral'} - /> + Add Domain + + + {/* Filters */} +
+ {[ + { value: 'all', label: 'All', count: stats.total }, + { value: 'active', label: 'Active', count: stats.active }, + { value: 'sold', label: 'Sold', count: stats.sold }, + ].map((item) => ( + + ))}
- )} +
+ - {/* Domains Table */} - {canUsePortfolio && ( -
- {/* Table Header */} -
-
Domain
-
Cost
-
Value
-
ROI
-
Status
-
Actions
-
- - {loading ? ( -
- -
- ) : domains.length === 0 ? ( -
-
- -
-

No domains in portfolio

-

- Add your first domain to start tracking your investments. -

- -
- ) : ( -
- {domains.map((domain) => ( -
- - {/* Domain */} -
-
-
- {domain.domain.charAt(0).toUpperCase()} -
-
-
{domain.domain}
-
- {domain.registrar || 'No registrar'} - {domain.renewal_date && ( - • Renews {formatDate(domain.renewal_date)} - )} -
-
+ {/* DOMAIN LIST */} +
+ {loading ? ( +
+ +
+ ) : !filteredDomains.length ? ( +
+ +

No domains in your portfolio

+

Add your first domain to start tracking

+ +
+ ) : ( +
+ {/* Desktop Table Header */} +
+ +
Purchase
+ + + +
Actions
+
+ + {filteredDomains.map((domain) => { + const daysUntilRenewal = getDaysUntilRenewal(domain.renewal_date) + const isRenewingSoon = daysUntilRenewal !== null && daysUntilRenewal <= 30 && daysUntilRenewal > 0 + const roiPositive = (domain.roi || 0) >= 0 + + return ( +
+ {/* Mobile Row */} +
+
+
+
+ {domain.is_sold ? : } +
+
+
{domain.domain}
+
+ {domain.registrar || 'Unknown registrar'} +
+
+
+ +
+
{formatCurrency(domain.estimated_value)}
+
+ {formatROI(domain.roi)} +
- - {/* Cost */} -
-
{formatCurrency(domain.purchase_price)}
- {domain.purchase_date && ( -
{formatDate(domain.purchase_date)}
- )} -
- - {/* Value */} -
-
- {domain.is_sold ? formatCurrency(domain.sale_price) : formatCurrency(domain.estimated_value)} -
- {domain.is_sold ? ( -
Sold {formatDate(domain.sale_date)}
- ) : domain.value_updated_at && ( -
Updated {formatDate(domain.value_updated_at)}
- )} -
- - {/* ROI */} -
- {domain.roi !== null ? ( -
= 0 ? "text-emerald-400" : "text-rose-400" - )}> - {domain.roi > 0 ? '+' : ''}{domain.roi.toFixed(1)}% -
- ) : ( -
- )} -
- - {/* Status */} -
- - {domain.is_sold ? 'Sold' : domain.status} - -
- - {/* Actions */} -
- {!domain.is_sold && ( - <> + +
+ {!domain.is_sold && canListForSale && ( + + Sell + + )} - - - )} - -
-
- ))} -
- )} -
+ + {/* Desktop Row */} +
+
+
+ {domain.is_sold ? : } +
+
+
{domain.domain}
+
+ {domain.registrar || 'Unknown'} + {domain.is_sold && SOLD} +
+
+ + + +
+ + {/* Purchase */} +
+ {formatCurrency(domain.purchase_price)} +
+ + {/* Value */} +
+ {formatCurrency(domain.estimated_value)} +
+ + {/* ROI */} +
+ + {roiPositive ? : } + {formatROI(domain.roi)} + +
+ + {/* Renewal */} +
+ {domain.is_sold ? ( + + ) : isRenewingSoon ? ( + {daysUntilRenewal}d + ) : ( + {formatDate(domain.renewal_date)} + )} +
+ + {/* Actions */} +
+ {!domain.is_sold && canListForSale && ( + + Sell + + )} + + + +
+
+
+ ) + })} +
)} + -
- - {/* Add Modal */} - {showAddModal && ( -
-
-
-

Add to Portfolio

-

Track a domain you own

-
- -
-
- - setFormData({ ...formData, domain: e.target.value })} - placeholder="example.com" - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-zinc-600 focus:outline-none focus:border-blue-500/50 transition-all font-mono" - /> -
- -
-
- - setFormData({ ...formData, purchase_date: e.target.value })} - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white focus:outline-none focus:border-blue-500/50 transition-all" - /> -
-
- -
- - setFormData({ ...formData, purchase_price: e.target.value })} - placeholder="0.00" - className="w-full pl-9 pr-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-zinc-600 focus:outline-none focus:border-blue-500/50 transition-all font-mono" - /> -
-
-
- -
-
- - setFormData({ ...formData, registrar: e.target.value })} - placeholder="Namecheap, GoDaddy..." - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white placeholder:text-zinc-600 focus:outline-none focus:border-blue-500/50 transition-all" - /> -
-
- - setFormData({ ...formData, renewal_date: e.target.value })} - className="w-full px-4 py-3 bg-white/5 border border-white/10 rounded-xl text-white focus:outline-none focus:border-blue-500/50 transition-all" - /> -
-
- -
- -