diff --git a/frontend/src/app/command/listings/page.tsx b/frontend/src/app/command/listings/page.tsx index ce2174a..84c7013 100644 --- a/frontend/src/app/command/listings/page.tsx +++ b/frontend/src/app/command/listings/page.tsx @@ -18,13 +18,10 @@ import { Copy, RefreshCw, DollarSign, - Globe, X, Sparkles, - Store, - List, - Search, Tag, + Store, } from 'lucide-react' import Link from 'next/link' import clsx from 'clsx' @@ -53,22 +50,6 @@ interface Listing { published_at: string | null } -interface PublicListing { - domain: string - slug: string - title: string | null - description: string | null - asking_price: number | null - currency: string - price_type: string - pounce_score: number | null - estimated_value: number | null - is_verified: boolean - allow_offers: boolean - public_url: string - seller_verified: boolean -} - interface VerificationInfo { verification_code: string dns_record_type: string @@ -78,22 +59,11 @@ interface VerificationInfo { status: string } -type TabType = 'browse' | 'my-listings' - -export default function ListingsPage() { +export default function MyListingsPage() { const { subscription } = useStore() - // Tab state - const [activeTab, setActiveTab] = useState('browse') - - // My Listings state - const [myListings, setMyListings] = useState([]) - const [loadingMy, setLoadingMy] = useState(true) - - // Browse state - const [publicListings, setPublicListings] = useState([]) - const [loadingPublic, setLoadingPublic] = useState(true) - const [searchQuery, setSearchQuery] = useState('') + const [listings, setListings] = useState([]) + const [loading, setLoading] = useState(true) // Modals const [showCreateModal, setShowCreateModal] = useState(false) @@ -116,31 +86,18 @@ export default function ListingsPage() { }) useEffect(() => { - loadMyListings() - loadPublicListings() + loadListings() }, []) - const loadMyListings = async () => { - setLoadingMy(true) + const loadListings = async () => { + setLoading(true) try { const data = await api.request('/listings/my') - setMyListings(data) + setListings(data) } catch (err: any) { - console.error('Failed to load my listings:', err) + console.error('Failed to load listings:', err) } finally { - setLoadingMy(false) - } - } - - const loadPublicListings = async () => { - setLoadingPublic(true) - try { - const data = await api.request('/listings?limit=50') - setPublicListings(data) - } catch (err: any) { - console.error('Failed to load public listings:', err) - } finally { - setLoadingPublic(false) + setLoading(false) } } @@ -164,8 +121,7 @@ export default function ListingsPage() { setSuccess('Listing created! Now verify ownership to publish.') setShowCreateModal(false) setNewListing({ domain: '', title: '', description: '', asking_price: '', price_type: 'negotiable', allow_offers: true }) - loadMyListings() - setActiveTab('my-listings') + loadListings() } catch (err: any) { setError(err.message) } finally { @@ -202,7 +158,7 @@ export default function ListingsPage() { if (result.verified) { setSuccess('Domain verified! You can now publish your listing.') setShowVerifyModal(false) - loadMyListings() + loadListings() } else { setError(result.message) } @@ -220,8 +176,7 @@ export default function ListingsPage() { body: JSON.stringify({ status: 'active' }), }) setSuccess('Listing published!') - loadMyListings() - loadPublicListings() + loadListings() } catch (err: any) { setError(err.message) } @@ -233,7 +188,7 @@ export default function ListingsPage() { try { await api.request(`/listings/${listing.id}`, { method: 'DELETE' }) setSuccess('Listing deleted') - loadMyListings() + loadListings() } catch (err: any) { setError(err.message) } @@ -261,34 +216,34 @@ export default function ListingsPage() { return {status} } - const filteredPublicListings = publicListings.filter(listing => { - if (!searchQuery) return true - return listing.domain.toLowerCase().includes(searchQuery.toLowerCase()) - }) - const tier = subscription?.tier || 'scout' const limits = { scout: 2, trader: 10, tycoon: 50 } const maxListings = limits[tier as keyof typeof limits] || 2 - const tabs = [ - { id: 'browse' as TabType, label: 'Browse Marketplace', icon: Store, count: publicListings.length }, - { id: 'my-listings' as TabType, label: 'My Listings', icon: List, count: myListings.length }, - ] - return ( setShowCreateModal(true)} - disabled={myListings.length >= maxListings} - className="flex items-center gap-2 px-4 py-2 bg-accent text-background text-sm font-medium rounded-lg - hover:bg-accent-hover transition-all disabled:opacity-50 disabled:cursor-not-allowed" - > - - List Domain - +
+ + + Browse Marketplace + + +
} > @@ -309,252 +264,135 @@ export default function ListingsPage() { )} - {/* Tabs */} -
- {tabs.map((tab) => ( - - ))} + {/* Stats */} +
+ + l.status === 'active').length} + icon={CheckCircle} + accent + /> + sum + l.view_count, 0)} + icon={Eye} + /> + sum + l.inquiry_count, 0)} + icon={MessageSquare} + />
- {/* Browse Tab */} - {activeTab === 'browse' && ( -
- {/* Search */} -
- - setSearchQuery(e.target.value)} - className="w-full pl-12 pr-4 py-3 bg-background-secondary/50 border border-border rounded-xl - text-foreground placeholder:text-foreground-subtle - focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent" - /> -
- - {/* Listings Grid */} - {loadingPublic ? ( -
- -
- ) : filteredPublicListings.length === 0 ? ( -
- -

No Domains Listed

-

- {searchQuery ? `No domains match "${searchQuery}"` : 'Be the first to list your domain!'} -

- -
- ) : ( -
- {filteredPublicListings.map((listing) => ( - -
-
-

- {listing.domain} -

- {listing.title && ( -

{listing.title}

- )} -
- {listing.is_verified && ( -
- -
- )} -
- -
- {listing.pounce_score && ( -
- Score: {listing.pounce_score} -
- )} -
-

- {formatPrice(listing.asking_price, listing.currency)} -

- {listing.price_type === 'negotiable' && ( -

Negotiable

- )} -
-
- - ))} -
- )} + {/* Listings */} + {loading ? ( +
+
- )} - - {/* My Listings Tab */} - {activeTab === 'my-listings' && ( -
- {/* Stats */} -
- - l.status === 'active').length} - icon={CheckCircle} - accent - /> - sum + l.view_count, 0)} - icon={Eye} - /> - sum + l.inquiry_count, 0)} - icon={MessageSquare} - /> -
- - {/* My Listings */} - {loadingMy ? ( -
- -
- ) : myListings.length === 0 ? ( -
- -

No Listings Yet

-

- Create your first listing to sell a domain on the Pounce marketplace. -

- -
- ) : ( -
- {myListings.map((listing) => ( -
-
- {/* Domain Info */} -
-
-

{listing.domain}

- {getStatusBadge(listing.status, listing.is_verified)} - {listing.is_verified && ( -
- -
- )} + ) : listings.length === 0 ? ( +
+ +

No Listings Yet

+

+ Create your first listing to sell a domain on the Pounce marketplace. +

+ +
+ ) : ( +
+ {listings.map((listing) => ( +
+
+ {/* Domain Info */} +
+
+

{listing.domain}

+ {getStatusBadge(listing.status, listing.is_verified)} + {listing.is_verified && ( +
+
- {listing.title && ( -

{listing.title}

- )} -
- - {/* Price */} -
-

- {formatPrice(listing.asking_price, listing.currency)} -

- {listing.pounce_score && ( -

Score: {listing.pounce_score}

- )} -
- - {/* Stats */} -
- - {listing.view_count} - - - {listing.inquiry_count} - -
- - {/* Actions */} -
- {!listing.is_verified && ( - - )} - - {listing.is_verified && listing.status === 'draft' && ( - - )} - - {listing.status === 'active' && ( - - - View - - )} - - -
+ )}
+ {listing.title && ( +

{listing.title}

+ )}
- ))} + + {/* Price */} +
+

+ {formatPrice(listing.asking_price, listing.currency)} +

+ {listing.pounce_score && ( +

Score: {listing.pounce_score}

+ )} +
+ + {/* Stats */} +
+ + {listing.view_count} + + + {listing.inquiry_count} + +
+ + {/* Actions */} +
+ {!listing.is_verified && ( + + )} + + {listing.is_verified && listing.status === 'draft' && ( + + )} + + {listing.status === 'active' && ( + + + View + + )} + + +
+
- )} + ))}
)} diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 0fdf93a..ac6cb7b 100755 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -72,8 +72,30 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S // Count available domains for notification badge const availableCount = domains?.filter(d => d.is_available).length || 0 - // Navigation items - all point to /command/* routes - const navItems = [ + // SECTION 1: Discover - External market data + const discoverItems = [ + { + href: '/command/auctions', + label: 'Auctions', + icon: Gavel, + badge: null, + }, + { + href: '/buy', + label: 'Marketplace', + icon: Tag, + badge: null, + }, + { + href: '/command/intelligence', + label: 'TLD Intelligence', + icon: TrendingUp, + badge: null, + }, + ] + + // SECTION 2: Manage - Your own assets and tools + const manageItems = [ { href: '/command/dashboard', label: 'Dashboard', @@ -92,21 +114,9 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S icon: Briefcase, badge: null, }, - { - href: '/command/auctions', - label: 'Auctions', - icon: Gavel, - badge: null, - }, - { - href: '/command/intelligence', - label: 'Intelligence', - icon: TrendingUp, - badge: null, - }, { href: '/command/listings', - label: 'Marketplace', + label: 'My Listings', icon: Tag, badge: null, }, @@ -175,64 +185,119 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
{/* Main Navigation */} -