'use client' import { useEffect, useState, useCallback } from 'react' import { useSearchParams } from 'next/navigation' import { useStore } from '@/lib/store' import { api } from '@/lib/api' import { Sidebar } from '@/components/Sidebar' import { Plus, Shield, Eye, MessageSquare, ExternalLink, Loader2, Trash2, CheckCircle, AlertCircle, Copy, DollarSign, X, Tag, Sparkles, TrendingUp, Gavel, Target, Menu, Settings, LogOut, Crown, Zap, Coins, Check } from 'lucide-react' import Link from 'next/link' import Image from 'next/image' import clsx from 'clsx' // ============================================================================ // TYPES // ============================================================================ interface Listing { id: number domain: string slug: string title: string | null description: string | null asking_price: number | null min_offer: number | null currency: string price_type: string pounce_score: number | null verification_status: string is_verified: boolean status: string view_count: number inquiry_count: number public_url: string created_at: string } // ============================================================================ // MAIN PAGE // ============================================================================ export default function MyListingsPage() { const { subscription, user, logout, checkAuth } = useStore() const searchParams = useSearchParams() const prefillDomain = searchParams.get('domain') const [listings, setListings] = useState([]) const [loading, setLoading] = useState(true) const [showCreateModal, setShowCreateModal] = useState(false) const [deletingId, setDeletingId] = useState(null) const [menuOpen, setMenuOpen] = useState(false) const tier = subscription?.tier || 'scout' const listingLimits: Record = { scout: 0, trader: 5, tycoon: 50 } const maxListings = listingLimits[tier] || 0 const canAddMore = listings.length < maxListings const isTycoon = tier === 'tycoon' const activeListings = listings.filter(l => l.status === 'active').length const totalViews = listings.reduce((sum, l) => sum + l.view_count, 0) const totalInquiries = listings.reduce((sum, l) => sum + l.inquiry_count, 0) useEffect(() => { checkAuth() }, [checkAuth]) useEffect(() => { if (prefillDomain) setShowCreateModal(true) }, [prefillDomain]) const loadListings = useCallback(async () => { setLoading(true) try { const data = await api.getMyListings() setListings(data) } catch (err) { console.error(err) } finally { setLoading(false) } }, []) useEffect(() => { loadListings() }, [loadListings]) const handleDelete = async (id: number, domain: string) => { if (!confirm(`Delete listing for ${domain}?`)) return setDeletingId(id) try { await api.deleteListing(id) await loadListings() } catch (err: any) { alert(err.message || 'Failed') } finally { setDeletingId(null) } } 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 tierName = subscription?.tier_name || subscription?.tier || 'Scout' const TierIcon = tierName === 'Tycoon' ? Crown : tierName === 'Trader' ? TrendingUp : Zap 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/sniper', label: 'Sniper', icon: Target }, ]}, { title: 'Monetize', items: [ { href: '/terminal/yield', label: 'Yield', icon: Coins }, { href: '/terminal/listing', label: 'For Sale', icon: Tag, active: true }, ]} ] return (
{/* MOBILE HEADER */}
For Sale
{listings.length}/{maxListings}
{activeListings}
Active
{totalViews}
Views
{totalInquiries}
Leads
{/* DESKTOP HEADER */}
Domain Marketplace

For Sale {listings.length}/{maxListings}

{activeListings}
Active
{totalViews}
Views
{totalInquiries}
Leads
{/* ADD BUTTON MOBILE */}
{/* CONTENT */}
{loading ? (
) : listings.length === 0 ? (

No listings yet

Create your first listing

) : (
{/* Header */}
Domain
Price
Status
Views
Leads
Actions
{listings.map((listing) => (
{/* Mobile */}
{listing.is_verified ? : }
{listing.domain}
{listing.status}
${listing.asking_price?.toLocaleString() || 'Negotiable'} {listing.view_count} views · {listing.inquiry_count} leads
View
{/* Desktop */}
{listing.is_verified ? : }
{listing.domain} {isTycoon && Featured}
${listing.asking_price?.toLocaleString() || '—'}
{listing.status}
{listing.view_count}
{listing.inquiry_count}
))}
)} {!canAddMore && (

Listing Limit Reached

Upgrade for more listings

Upgrade
)}
{/* 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}
)}
{/* CREATE MODAL */} {showCreateModal && ( setShowCreateModal(false)} onSuccess={() => { loadListings(); setShowCreateModal(false) }} prefillDomain={prefillDomain || ''} /> )}
) } // ============================================================================ // CREATE MODAL (simplified) // ============================================================================ function CreateListingModal({ onClose, onSuccess, prefillDomain }: { onClose: () => void; onSuccess: () => void; prefillDomain: string }) { const [domain, setDomain] = useState(prefillDomain) const [price, setPrice] = useState('') const [loading, setLoading] = useState(false) const [error, setError] = useState(null) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() if (!domain.trim()) return setLoading(true) setError(null) try { await api.createListing({ domain: domain.trim(), asking_price: price ? parseFloat(price) : null, currency: 'USD', price_type: price ? 'fixed' : 'negotiable' }) onSuccess() } catch (err: any) { setError(err.message || 'Failed') } finally { setLoading(false) } } return (
e.stopPropagation()}>
New Listing
{error &&
{error}
}
setDomain(e.target.value)} required className="w-full px-3 py-2.5 bg-white/5 border border-white/10 text-white text-sm font-mono placeholder:text-white/20 outline-none focus:border-accent/50" placeholder="example.com" />
setPrice(e.target.value)} min="0" className="w-full px-3 py-2.5 bg-white/5 border border-white/10 text-white text-sm font-mono placeholder:text-white/20 outline-none focus:border-accent/50" placeholder="Leave empty for negotiable" />
) }