diff --git a/frontend/src/app/command/listings/page.tsx b/frontend/src/app/command/listings/page.tsx
old mode 100644
new mode 100755
diff --git a/frontend/src/app/command/marketplace/page.tsx b/frontend/src/app/command/marketplace/page.tsx
new file mode 100644
index 0000000..eacc969
--- /dev/null
+++ b/frontend/src/app/command/marketplace/page.tsx
@@ -0,0 +1,311 @@
+'use client'
+
+import { useEffect, useState } from 'react'
+import { api } from '@/lib/api'
+import { CommandCenterLayout } from '@/components/CommandCenterLayout'
+import { PageContainer, StatCard, Badge } from '@/components/PremiumTable'
+import {
+ Search,
+ Shield,
+ Loader2,
+ ExternalLink,
+ Store,
+ Tag,
+ DollarSign,
+ Filter,
+ SortAsc,
+ ArrowUpDown,
+} from 'lucide-react'
+import Link from 'next/link'
+import clsx from 'clsx'
+
+interface Listing {
+ 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
+}
+
+type SortOption = 'newest' | 'price_asc' | 'price_desc' | 'score'
+
+export default function CommandMarketplacePage() {
+ const [listings, setListings] = useState
([])
+ const [loading, setLoading] = useState(true)
+ const [searchQuery, setSearchQuery] = useState('')
+ const [minPrice, setMinPrice] = useState('')
+ const [maxPrice, setMaxPrice] = useState('')
+ const [verifiedOnly, setVerifiedOnly] = useState(false)
+ const [sortBy, setSortBy] = useState('newest')
+ const [showFilters, setShowFilters] = useState(false)
+
+ useEffect(() => {
+ loadListings()
+ }, [sortBy, verifiedOnly])
+
+ const loadListings = async () => {
+ setLoading(true)
+ try {
+ const params = new URLSearchParams()
+ params.set('limit', '100')
+ if (sortBy === 'price_asc') params.set('sort', 'price_asc')
+ if (sortBy === 'price_desc') params.set('sort', 'price_desc')
+ if (verifiedOnly) params.set('verified_only', 'true')
+
+ const data = await api.request(`/listings?${params.toString()}`)
+ setListings(data)
+ } catch (err) {
+ console.error('Failed to load listings:', err)
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const formatPrice = (price: number | null, currency: string) => {
+ if (!price) return 'Make Offer'
+ return new Intl.NumberFormat('en-US', {
+ style: 'currency',
+ currency,
+ minimumFractionDigits: 0,
+ }).format(price)
+ }
+
+ const filteredListings = listings.filter(listing => {
+ // Search filter
+ if (searchQuery) {
+ const query = searchQuery.toLowerCase()
+ if (!listing.domain.toLowerCase().includes(query)) {
+ return false
+ }
+ }
+
+ // Price filters
+ if (minPrice && listing.asking_price && listing.asking_price < parseFloat(minPrice)) {
+ return false
+ }
+ if (maxPrice && listing.asking_price && listing.asking_price > parseFloat(maxPrice)) {
+ return false
+ }
+
+ return true
+ })
+
+ // Sort listings
+ const sortedListings = [...filteredListings].sort((a, b) => {
+ switch (sortBy) {
+ case 'price_asc':
+ return (a.asking_price || 0) - (b.asking_price || 0)
+ case 'price_desc':
+ return (b.asking_price || 0) - (a.asking_price || 0)
+ case 'score':
+ return (b.pounce_score || 0) - (a.pounce_score || 0)
+ default:
+ return 0
+ }
+ })
+
+ const verifiedCount = listings.filter(l => l.is_verified).length
+ const avgPrice = listings.length > 0
+ ? listings.filter(l => l.asking_price).reduce((sum, l) => sum + (l.asking_price || 0), 0) / listings.filter(l => l.asking_price).length
+ : 0
+
+ return (
+
+
+ My Listings
+
+ }
+ >
+
+ {/* Stats */}
+
+
+
+ 0 ? `$${Math.round(avgPrice).toLocaleString()}` : '—'}
+ icon={DollarSign}
+ />
+
+
+
+ {/* Search & Filters */}
+
+
+ {/* Search */}
+
+
+ setSearchQuery(e.target.value)}
+ className="w-full pl-12 pr-4 py-3 bg-background border border-border rounded-xl
+ text-foreground placeholder:text-foreground-subtle
+ focus:outline-none focus:ring-2 focus:ring-accent/30 focus:border-accent"
+ />
+
+
+ {/* Sort */}
+
+
+ {/* Filter Toggle */}
+
+
+
+ {/* Expanded Filters */}
+ {showFilters && (
+
+ )}
+
+
+ {/* Listings Grid */}
+ {loading ? (
+
+
+
+ ) : sortedListings.length === 0 ? (
+
+
+
No Domains Found
+
+ {searchQuery || minPrice || maxPrice
+ ? 'Try adjusting your filters'
+ : 'No domains are currently listed for sale'}
+
+
+
+ List Your Domain
+
+
+ ) : (
+
+ {sortedListings.map((listing) => (
+
+
+
+
+ {listing.domain}
+
+ {listing.title && (
+
{listing.title}
+ )}
+
+ {listing.is_verified && (
+
+
+
+ )}
+
+
+ {listing.description && (
+
+ {listing.description}
+
+ )}
+
+
+
+ {listing.pounce_score && (
+
+ {listing.pounce_score}
+
+ )}
+ {listing.allow_offers && (
+
Offers
+ )}
+
+
+
+ {formatPrice(listing.asking_price, listing.currency)}
+
+ {listing.price_type === 'negotiable' && (
+
Negotiable
+ )}
+
+
+
+ ))}
+
+ )}
+
+
+ )
+}
+
diff --git a/frontend/src/app/pricing/page.tsx b/frontend/src/app/pricing/page.tsx
index 8d1f5b5..eb413cd 100644
--- a/frontend/src/app/pricing/page.tsx
+++ b/frontend/src/app/pricing/page.tsx
@@ -23,8 +23,9 @@ const tiers = [
{ text: 'Daily availability scans', highlight: false, available: true },
{ text: 'Email alerts', highlight: false, available: true },
{ text: 'Raw auction feed', highlight: false, available: true, sublabel: 'Unfiltered' },
- { text: 'Curated auction list', highlight: false, available: false },
+ { text: '2 domain listings', highlight: false, available: true, sublabel: 'For Sale' },
{ text: 'Deal scores & valuations', highlight: false, available: false },
+ { text: 'Sniper Alerts', highlight: false, available: false },
],
cta: 'Start Free',
highlighted: false,
@@ -43,8 +44,9 @@ const tiers = [
{ text: 'Hourly scans', highlight: true, available: true, sublabel: '24x faster' },
{ text: 'Smart spam filter', highlight: true, available: true, sublabel: 'Curated list' },
{ text: 'Deal scores & valuations', highlight: true, available: true },
+ { text: '10 domain listings', highlight: true, available: true, sublabel: 'For Sale' },
+ { text: '5 Sniper Alerts', highlight: true, available: true },
{ text: 'Portfolio tracking (25)', highlight: true, available: true },
- { text: '90-day price history', highlight: false, available: true },
{ text: 'Expiry date tracking', highlight: true, available: true },
],
cta: 'Upgrade to Trader',
@@ -62,10 +64,11 @@ const tiers = [
features: [
{ text: '500 domains to track', highlight: true, available: true },
{ text: 'Real-time scans', highlight: true, available: true, sublabel: 'Every 10 min' },
- { text: 'Priority alerts', highlight: true, available: true },
+ { text: '50 domain listings', highlight: true, available: true, sublabel: 'For Sale' },
+ { text: 'Unlimited Sniper Alerts', highlight: true, available: true },
+ { text: 'SEO Juice Detector', highlight: true, available: true, sublabel: 'Backlinks' },
{ text: 'Unlimited portfolio', highlight: true, available: true },
{ text: 'Full price history', highlight: true, available: true },
- { text: 'Advanced valuation', highlight: true, available: true },
{ text: 'API access', highlight: true, available: true, sublabel: 'Coming soon' },
],
cta: 'Go Tycoon',
@@ -80,8 +83,10 @@ const comparisonFeatures = [
{ name: 'Check Frequency', scout: 'Daily', trader: 'Hourly', tycoon: '10 min' },
{ name: 'Auction Feed', scout: 'Raw (unfiltered)', trader: 'Curated (spam-free)', tycoon: 'Curated (spam-free)' },
{ name: 'Deal Scores', scout: '—', trader: 'check', tycoon: 'check' },
+ { name: 'For Sale Listings', scout: '2', trader: '10', tycoon: '50' },
+ { name: 'Sniper Alerts', scout: '—', trader: '5', tycoon: 'Unlimited' },
{ name: 'Portfolio Domains', scout: '—', trader: '25', tycoon: 'Unlimited' },
- { name: 'Domain Valuation', scout: '—', trader: 'check', tycoon: 'Advanced' },
+ { name: 'SEO Juice Detector', scout: '—', trader: '—', tycoon: 'check' },
{ name: 'Price History', scout: '—', trader: '90 days', tycoon: 'Unlimited' },
{ name: 'Expiry Tracking', scout: '—', trader: 'check', tycoon: 'check' },
]
diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx
index ac6cb7b..72118b6 100755
--- a/frontend/src/components/Sidebar.tsx
+++ b/frontend/src/components/Sidebar.tsx
@@ -72,6 +72,8 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
// Count available domains for notification badge
const availableCount = domains?.filter(d => d.is_available).length || 0
+ const isTycoon = tierName.toLowerCase() === 'tycoon'
+
// SECTION 1: Discover - External market data
const discoverItems = [
{
@@ -81,7 +83,7 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
badge: null,
},
{
- href: '/buy',
+ href: '/command/marketplace',
label: 'Marketplace',
icon: Tag,
badge: null,
@@ -95,7 +97,13 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
]
// SECTION 2: Manage - Your own assets and tools
- const manageItems = [
+ const manageItems: Array<{
+ href: string
+ label: string
+ icon: any
+ badge: number | null
+ tycoonOnly?: boolean
+ }> = [
{
href: '/command/dashboard',
label: 'Dashboard',
@@ -130,7 +138,8 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
href: '/command/seo',
label: 'SEO Juice',
icon: Link2,
- badge: 'Tycoon',
+ badge: null,
+ tycoonOnly: true,
},
]
@@ -246,56 +255,67 @@ export function Sidebar({ collapsed: controlledCollapsed, onCollapsedChange }: S
{collapsed && }
- {manageItems.map((item) => (
-
setMobileOpen(false)}
- className={clsx(
- "group relative flex items-center gap-3 px-3 py-3 rounded-xl transition-all duration-300",
- isActive(item.href)
- ? "bg-gradient-to-r from-accent/20 to-accent/5 text-foreground border border-accent/20 shadow-[0_0_20px_-5px_rgba(16,185,129,0.2)]"
- : "text-foreground-muted hover:text-foreground hover:bg-foreground/5 border border-transparent"
- )}
- title={collapsed ? item.label : undefined}
- >
- {isActive(item.href) && (
-
- )}
-
-
- {item.badge && typeof item.badge === 'number' && (
-
- {item.badge > 9 ? '9+' : item.badge}
+ {manageItems.map((item) => {
+ const isDisabled = item.tycoonOnly && !isTycoon
+ const ItemWrapper = isDisabled ? 'div' : Link
+
+ return (
+ !isDisabled && setMobileOpen(false)}
+ className={clsx(
+ "group relative flex items-center gap-3 px-3 py-3 rounded-xl transition-all duration-300",
+ isDisabled
+ ? "opacity-50 cursor-not-allowed border border-transparent"
+ : isActive(item.href)
+ ? "bg-gradient-to-r from-accent/20 to-accent/5 text-foreground border border-accent/20 shadow-[0_0_20px_-5px_rgba(16,185,129,0.2)]"
+ : "text-foreground-muted hover:text-foreground hover:bg-foreground/5 border border-transparent"
+ )}
+ title={
+ isDisabled
+ ? "SEO Juice Detector: Analyze backlinks, domain authority & find hidden SEO value. Upgrade to Tycoon to unlock."
+ : collapsed ? item.label : undefined
+ }
+ >
+ {!isDisabled && isActive(item.href) && (
+
+ )}
+
+
+ {item.badge && typeof item.badge === 'number' && !isDisabled && (
+
+ {item.badge > 9 ? '9+' : item.badge}
+
+ )}
+
+ {!collapsed && (
+
+ {item.label}
)}
- {item.badge && typeof item.badge === 'string' && !collapsed && (
-
- {item.badge}
-
+ {/* Lock icon for disabled items */}
+ {isDisabled && !collapsed && (
+
)}
-
- {!collapsed && (
-
- {item.label}
-
- )}
- {!isActive(item.href) && (
-
- )}
-
- ))}
+ {!isDisabled && !isActive(item.href) && (
+
+ )}
+
+ )
+ })}