fix: Health check API + Portfolio UX improvements

API FIX:
- quickHealthCheck now uses POST method (was GET)
- Fixes 'int_parsing' error for domain_id

PORTFOLIO UX:
1. Health Check now works correctly with POST /domains/health-check

2. Clear separation of List vs Sell:
   - 'List' button → Opens listing form (marketplace)
   - 'Sold?' button → Records completed sale (P&L tracking)

3. Improved Valuation Modal:
   - Shows 'Pounce Score Estimate' instead of just 'Estimated Value'
   - Color-coded confidence badge (high/medium/low)
   - Clear disclaimer about algorithmic estimate

4. Record Sale Modal:
   - Clear explanation of purpose (P&L tracking)
   - Hint to use 'List' button for marketplace

LISTINGS PAGE:
- Accepts ?domain= query parameter
- Auto-opens create modal with prefilled domain
- Seamless flow from Portfolio → List on Marketplace
This commit is contained in:
yves.gugger
2025-12-10 15:17:15 +01:00
parent 05930ef441
commit cb1f009dc3
3 changed files with 50 additions and 13 deletions

View File

@ -1,6 +1,7 @@
'use client'
import { useEffect, useState } from 'react'
import { useSearchParams } from 'next/navigation'
import { useStore } from '@/lib/store'
import { api } from '@/lib/api'
import { CommandCenterLayout } from '@/components/CommandCenterLayout'
@ -61,11 +62,13 @@ interface VerificationInfo {
export default function MyListingsPage() {
const { subscription } = useStore()
const searchParams = useSearchParams()
const prefillDomain = searchParams.get('domain')
const [listings, setListings] = useState<Listing[]>([])
const [loading, setLoading] = useState(true)
// Modals
// Modals - auto-open if domain is prefilled
const [showCreateModal, setShowCreateModal] = useState(false)
const [showVerifyModal, setShowVerifyModal] = useState(false)
const [selectedListing, setSelectedListing] = useState<Listing | null>(null)
@ -89,6 +92,14 @@ export default function MyListingsPage() {
loadListings()
}, [])
// Auto-open create modal if domain is prefilled from portfolio
useEffect(() => {
if (prefillDomain) {
setNewListing(prev => ({ ...prev, domain: prefillDomain }))
setShowCreateModal(true)
}
}, [prefillDomain])
const loadListings = async () => {
setLoading(true)
try {

View File

@ -25,6 +25,7 @@ import {
Activity,
Shield,
AlertTriangle,
Tag,
} from 'lucide-react'
// Health status configuration
@ -447,11 +448,20 @@ export default function PortfolioPage() {
onClick={() => openEditModal(domain)}
title="Edit"
/>
<Link
href={`/command/listings?domain=${encodeURIComponent(domain.domain)}`}
onClick={(e) => e.stopPropagation()}
className="px-3 py-2 text-xs font-medium text-accent hover:bg-accent/10 rounded-lg transition-colors flex items-center gap-1"
>
<Tag className="w-3 h-3" />
List
</Link>
<button
onClick={(e) => { e.stopPropagation(); openSellModal(domain) }}
className="px-3 py-2 text-xs font-medium text-accent hover:bg-accent/10 rounded-lg transition-colors"
className="px-3 py-2 text-xs font-medium text-foreground-muted hover:bg-foreground/5 rounded-lg transition-colors"
title="Record a completed sale (for P&L tracking)"
>
Sell
Sold?
</button>
<TableActionButton
icon={Trash2}
@ -586,10 +596,14 @@ export default function PortfolioPage() {
</Modal>
)}
{/* Sell Modal */}
{/* Record Sale Modal - for tracking completed sales */}
{showSellModal && selectedDomain && (
<Modal title={`Sell ${selectedDomain.domain}`} onClose={() => setShowSellModal(false)}>
<Modal title={`Record Sale: ${selectedDomain.domain}`} onClose={() => setShowSellModal(false)}>
<form onSubmit={handleSellDomain} className="space-y-4">
<div className="p-3 bg-accent/10 border border-accent/20 rounded-lg text-sm text-foreground-muted">
<p>Record a completed sale to track your profit/loss. This will mark the domain as sold in your portfolio.</p>
<p className="mt-2 text-accent">Want to list it for sale instead? Use the <strong>"List"</strong> button.</p>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm text-foreground-muted mb-1.5">Sale Price *</label>
@ -647,16 +661,26 @@ export default function PortfolioPage() {
<div className="space-y-4">
<div className="text-center p-6 bg-accent/5 border border-accent/20 rounded-xl">
<p className="text-4xl font-display text-accent">${valuation.estimated_value.toLocaleString()}</p>
<p className="text-sm text-foreground-muted mt-1">Estimated Value</p>
<p className="text-sm text-foreground-muted mt-1">Pounce Score Estimate</p>
</div>
<div className="space-y-2 text-sm">
<div className="flex justify-between p-3 bg-foreground/5 rounded-lg">
<span className="text-foreground-muted">Confidence</span>
<span className="text-foreground capitalize font-medium">{valuation.confidence}</span>
<div className="flex justify-between items-center p-3 bg-foreground/5 rounded-lg">
<span className="text-foreground-muted">Confidence Level</span>
<span className={clsx(
"px-2 py-0.5 rounded text-xs font-medium capitalize",
valuation.confidence === 'high' && "bg-accent/20 text-accent",
valuation.confidence === 'medium' && "bg-amber-400/20 text-amber-400",
valuation.confidence === 'low' && "bg-foreground/10 text-foreground-muted"
)}>
{valuation.confidence}
</span>
</div>
<div className="flex justify-between p-3 bg-foreground/5 rounded-lg">
<span className="text-foreground-muted">Formula</span>
<span className="text-foreground font-mono text-xs">{valuation.valuation_formula}</span>
<div className="p-3 bg-foreground/5 rounded-lg">
<p className="text-foreground-muted mb-1">Valuation Formula</p>
<p className="text-foreground font-mono text-xs break-all">{valuation.valuation_formula}</p>
</div>
<div className="p-3 bg-amber-400/10 border border-amber-400/20 rounded-lg text-xs text-amber-400">
<p>This is an algorithmic estimate based on domain length, TLD, and market patterns. Actual market value may vary.</p>
</div>
</div>
</div>

View File

@ -385,7 +385,9 @@ class ApiClient {
// Quick health check for any domain (premium)
async quickHealthCheck(domain: string) {
return this.request<DomainHealthReport>(`/domains/health-check?domain=${encodeURIComponent(domain)}`)
return this.request<DomainHealthReport>(`/domains/health-check?domain=${encodeURIComponent(domain)}`, {
method: 'POST',
})
}
// TLD Pricing