From a5a9f408661494da955f1b514d0e97ddc24e1b84 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Sat, 13 Dec 2025 14:03:43 +0100 Subject: [PATCH] Market: Techy angular mobile design matching Radar --- frontend/src/app/terminal/market/page.tsx | 644 ++++++++++++---------- 1 file changed, 339 insertions(+), 305 deletions(-) diff --git a/frontend/src/app/terminal/market/page.tsx b/frontend/src/app/terminal/market/page.tsx index 58f73d7..9d249d6 100644 --- a/frontend/src/app/terminal/market/page.tsx +++ b/frontend/src/app/terminal/market/page.tsx @@ -30,7 +30,8 @@ import { Crown, Sparkles, Coins, - Tag + Tag, + Filter } from 'lucide-react' import clsx from 'clsx' import Link from 'next/link' @@ -111,7 +112,7 @@ function isSpam(domain: string): boolean { // ============================================================================ export default function MarketPage() { - const { subscription, user, logout } = useStore() + const { subscription, user, logout, checkAuth } = useStore() const { toast, showToast, hideToast } = useToast() const [items, setItems] = useState([]) @@ -134,8 +135,14 @@ export default function MarketPage() { const [totalPages, setTotalPages] = useState(1) const ITEMS_PER_PAGE = 50 - // Mobile Menu + // Mobile Menu & Filters const [menuOpen, setMenuOpen] = useState(false) + const [filtersOpen, setFiltersOpen] = useState(false) + + // Check auth on mount + useEffect(() => { + checkAuth() + }, [checkAuth]) const loadData = useCallback(async (currentPage = 1) => { setLoading(true) @@ -250,6 +257,14 @@ export default function MarketPage() { return filtered }, [items, searchQuery, loading, hideSpam]) + // Active filters count + const activeFiltersCount = [ + sourceFilter !== 'all', + priceRange !== 'all', + tldFilter !== 'all', + hideSpam + ].filter(Boolean).length + // Mobile Nav const mobileNavItems = [ { href: '/terminal/radar', label: 'Radar', icon: Target, active: false }, @@ -300,50 +315,63 @@ export default function MarketPage() { {/* MOBILE HEADER */} {/* ═══════════════════════════════════════════════════════════════════════ */}
-
-
-
-
- -
+
+ {/* Top Row */} +
+
+
+ Live Market
-
-

Market

-

Live Auctions

+
+
-
-
-
{stats.total}
+ {/* Stats Grid */} +
+
+
{stats.total}
+
Total
+
+
+
{stats.pounceCount}
+
Pounce
+
+
+
{stats.auctionCount}
+
External
+
+
+
{stats.highScore}
+
Score 80+
-
{/* ═══════════════════════════════════════════════════════════════════════ */} - {/* SEARCH - Mobile */} + {/* MOBILE SEARCH & FILTERS */} {/* ═══════════════════════════════════════════════════════════════════════ */} -
+
+ {/* Search */}
setSearchQuery(e.target.value)} onFocus={() => setSearchFocused(true)} onBlur={() => setSearchFocused(false)} - placeholder="Search auctions..." - className="flex-1 bg-transparent px-3 py-3.5 text-base text-white placeholder:text-white/20 outline-none font-medium" + placeholder="Search domains..." + className="flex-1 bg-transparent px-3 py-3 text-sm text-white placeholder:text-white/20 outline-none font-mono" /> {searchQuery && ( )}
-
- - {/* ═══════════════════════════════════════════════════════════════════════ */} - {/* FILTERS - Mobile Horizontal Scroll */} - {/* ═══════════════════════════════════════════════════════════════════════ */} -
-
- - - - - {['com', 'ai', 'io', 'net'].map((tld) => ( +
+ + + + {/* Filters Panel */} + {filtersOpen && ( +
+ {/* Source */} +
+
Source
+
+ {[ + { value: 'all', label: 'All' }, + { value: 'pounce', label: 'Pounce', icon: Diamond }, + { value: 'external', label: 'External' }, + ].map((item) => ( + + ))} +
+
+ + {/* TLD */} +
+
TLD
+
+ {['all', 'com', 'ai', 'io', 'net'].map((tld) => ( + + ))} +
+
+ + {/* Price */} +
+
Price
+
+ {[ + { value: 'all', label: 'All' }, + { value: 'low', label: '< $100' }, + { value: 'mid', label: '< $1k' }, + { value: 'high', label: '$1k+' }, + ].map((item) => ( + + ))} +
+
+ + {/* Spam Filter */} - ))} - - {[ - { value: 'low', label: '< $100' }, - { value: 'mid', label: '< $1k' }, - { value: 'high', label: '$1k+' }, - ].map((item) => ( - - ))} -
+ + )}
{/* ═══════════════════════════════════════════════════════════════════════ */} @@ -440,29 +517,29 @@ export default function MarketPage() {
-
- - Live Auctions +
+
+ Live Auctions
-

+

Market - {stats.total} + {stats.total}

-
+
-
{stats.pounceCount}
-
Pounce Direct
+
{stats.pounceCount}
+
Pounce Direct
-
{stats.auctionCount}
-
External
+
{stats.auctionCount}
+
External
-
{stats.highScore}
-
Score 80+
+
{stats.highScore}
+
Score 80+
@@ -476,8 +553,8 @@ export default function MarketPage() {
- + {['com', 'ai', 'io', 'net'].map((tld) => ( + + ))}
@@ -516,10 +596,10 @@ export default function MarketPage() { ].map((item) => (
+
setSearchQuery(e.target.value)} - placeholder="Search domains..." - className="bg-[#050505] border border-white/10 pl-9 pr-4 py-2 text-sm text-white placeholder:text-white/25 outline-none focus:border-accent/40 w-48 lg:w-64" + placeholder="Search..." + className="bg-white/[0.02] border border-white/10 pl-9 pr-4 py-2 text-sm text-white placeholder:text-white/25 outline-none focus:border-accent/40 w-48 font-mono" />
@@ -564,23 +645,21 @@ export default function MarketPage() { {/* ═══════════════════════════════════════════════════════════════════════ */} {/* CONTENT */} {/* ═══════════════════════════════════════════════════════════════════════ */} -
+
{loading ? (
) : filteredItems.length === 0 ? ( -
-
- -
-

No domains found

-

Try adjusting your filters

+
+ +

No domains found

+

Try adjusting filters

) : ( <> - {/* Mobile Cards */} -
+ {/* Results List */} +
{filteredItems.map((item) => { const timeLeftSec = getSecondsUntilEnd(item.end_time) const isUrgent = timeLeftSec > 0 && timeLeftSec < 3600 @@ -593,25 +672,33 @@ export default function MarketPage() {
-
-
-
- {isPounce ? ( -
- + {/* Mobile Row */} +
+
+
+
+ {isPounce ? ( + + ) : ( + {item.source.substring(0, 2).toUpperCase()} + )} +
+
+
{item.domain}
+
+ {item.source} + | + + {isPounce ? 'Instant' : displayTime || 'N/A'} +
- ) : ( -
- {item.source.substring(0, 2).toUpperCase()} -
- )} -
-
{item.domain}
-
{item.source}
@@ -623,54 +710,36 @@ export default function MarketPage() { {formatPrice(item.price)}
= 80 ? "text-accent bg-accent/10" : + item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/10" : + "text-white/30 bg-white/5" )}> - {isPounce ? ( - - - Instant - - ) : ( - - - {displayTime || 'N/A'} - - )} + Score {item.pounce_score}
- {/* Score & Actions */} -
- = 80 ? "text-accent bg-accent/10" : - item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/10" : - "text-white/40 bg-white/5" - )}> - Score {item.pounce_score} - - -
- + {/* Actions */} +
{isPounce ? 'Buy' : 'Bid'} - {!isPounce && } + {!isPounce && }
-
- ) - })} -
- - {/* Desktop Table */} -
-
-
Domain
-
Score
-
Price
-
Time
-
Actions
-
- - {filteredItems.map((item) => { - const timeLeftSec = getSecondsUntilEnd(item.end_time) - const isUrgent = timeLeftSec > 0 && timeLeftSec < 3600 - const isPounce = item.is_pounce - const displayTime = item.status === 'auction' ? calcTimeRemaining(item.end_time) : null - const isTracked = trackedDomains.has(item.domain) - const isTracking = trackingInProgress === item.domain - - return ( -
-
-
- {isPounce ? ( -
+ + {/* Desktop Row */} +
+
+
+ {isPounce ? ( -
- ) : ( -
- {item.source.substring(0, 2).toUpperCase()} -
- )} + ) : ( + {item.source.substring(0, 2).toUpperCase()} + )} +
-
{item.domain}
-
- {item.source} +
{item.domain}
+
+ {item.source} {isPounce && item.verified && ( <> - · + | Verified )} - {item.num_bids ? <>·{item.num_bids} bids : null} + {item.num_bids ? <>|{item.num_bids} bids : null}
-
+ {/* Score */} +
= 80 ? "text-accent bg-accent/10" : item.pounce_score >= 50 ? "text-amber-400 bg-amber-400/10" : "text-white/40 bg-white/5" @@ -760,21 +802,23 @@ export default function MarketPage() {
-
+ {/* Price */} +
{formatPrice(item.price)}
-
+
{item.price_type === 'bid' ? 'Bid' : 'Buy Now'}
-
+ {/* Time */} +
{isPounce ? ( - + Instant @@ -788,11 +832,11 @@ export default function MarketPage() { )}
-
+ {/* Actions */} +
@@ -876,13 +920,13 @@ export default function MarketPage() {
- {page} / {totalPages} + {page}/{totalPages} @@ -897,33 +941,33 @@ export default function MarketPage() { {/* MOBILE BOTTOM NAV */} {/* ═══════════════════════════════════════════════════════════════════════ */} @@ -934,64 +978,61 @@ export default function MarketPage() { {menuOpen && (
setMenuOpen(false)} /> -
+
-
+
- Pounce + Pounce
-

POUNCE

-

Terminal

+

POUNCE

+

Terminal v1.0

-
+
{drawerNavSections.map((section) => ( -
-
-
- {section.title} +
+
+
+ {section.title}
-
+
{section.items.map((item: any) => ( setMenuOpen(false)} - className="flex items-center gap-4 px-6 py-3.5 text-white/70 active:text-white active:bg-white/[0.03] transition-colors border-l-2 border-transparent active:border-accent" + className="flex items-center gap-3 px-4 py-2.5 text-white/60 active:text-white active:bg-white/[0.03] transition-colors border-l-2 border-transparent active:border-accent" > - + {item.label} {item.isNew && ( - - NEW - + NEW )} - ))}
))} -
+
setMenuOpen(false)} - className="flex items-center gap-3 py-3 text-white/60 active:text-white transition-colors" + className="flex items-center gap-3 py-2.5 text-white/50 active:text-white transition-colors" > - + Settings @@ -999,32 +1040,25 @@ export default function MarketPage() { setMenuOpen(false)} - className="flex items-center gap-3 py-3 text-amber-500/80 active:text-amber-400 transition-colors" + className="flex items-center gap-3 py-2.5 text-amber-500/70 active:text-amber-400 transition-colors" > - - Admin Panel + + Admin )}
-
-
-
- +
+
+
+

{user?.name || user?.email?.split('@')[0] || 'User'}

-

- {tierName} Plan -

+

{tierName}

@@ -1032,18 +1066,18 @@ export default function MarketPage() { setMenuOpen(false)} - className="flex items-center justify-center gap-2 w-full py-3 bg-white text-black text-sm font-bold rounded-lg active:scale-[0.98] transition-all mb-3 shadow-lg" + className="flex items-center justify-center gap-2 w-full py-2.5 bg-accent text-black text-xs font-bold uppercase tracking-wider active:scale-[0.98] transition-all mb-2" > - - Upgrade Now + + Upgrade )}