From b4954bf6954ca388ce188539b97147b6d3c09f56 Mon Sep 17 00:00:00 2001 From: Yves Gugger Date: Mon, 15 Dec 2025 17:30:32 +0100 Subject: [PATCH] feat: rebuild HUNT page with radar-style mobile layout --- frontend/src/app/terminal/hunt/page.tsx | 372 ++++++++++++++++++++++-- 1 file changed, 344 insertions(+), 28 deletions(-) diff --git a/frontend/src/app/terminal/hunt/page.tsx b/frontend/src/app/terminal/hunt/page.tsx index 627ed36..ddf721a 100644 --- a/frontend/src/app/terminal/hunt/page.tsx +++ b/frontend/src/app/terminal/hunt/page.tsx @@ -1,58 +1,374 @@ 'use client' -import { useEffect, useState } from 'react' +import { useEffect, useState, useCallback } from 'react' +import { useStore } from '@/lib/store' import { Sidebar } from '@/components/Sidebar' import { Toast, useToast } from '@/components/Toast' -import { useStore } from '@/lib/store' import { HuntStrategyChips, type HuntTab } from '@/components/hunt/HuntStrategyChips' import { SniperTab } from '@/components/hunt/SniperTab' import { TrendSurferTab } from '@/components/hunt/TrendSurferTab' import { BrandableForgeTab } from '@/components/hunt/BrandableForgeTab' +import { + Eye, + Gavel, + Crosshair, + Zap, + Target, + TrendingUp, + Settings, + Sparkles, + Menu, + Tag, + Coins, + Shield, + LogOut, + Crown, + X, + Briefcase, +} from 'lucide-react' +import clsx from 'clsx' +import Link from 'next/link' +import Image from 'next/image' + +// ============================================================================ +// MAIN PAGE +// ============================================================================ export default function HuntPage() { - const { checkAuth } = useStore() - const { toast, hideToast, showToast } = useToast() + const { isAuthenticated, isLoading: authLoading, user, subscription, logout, checkAuth, domains } = useStore() + const { toast, showToast, hideToast } = useToast() const [tab, setTab] = useState('sniper') + // Mobile Menu State + const [menuOpen, setMenuOpen] = useState(false) + + // Check auth on mount useEffect(() => { checkAuth() }, [checkAuth]) + // Computed + const availableDomains = domains?.filter(d => d.is_available) || [] + const totalDomains = domains?.length || 0 + + // Nav Items for Mobile Bottom Bar + const mobileNavItems = [ + { href: '/terminal/radar', label: 'Radar', icon: Target, active: false }, + { href: '/terminal/hunt', label: 'Hunt', icon: Crosshair, active: true }, + { href: '/terminal/watchlist', label: 'Watch', icon: Eye, active: false }, + { href: '/terminal/market', label: 'Market', icon: Gavel, active: false }, + ] + + // Full Navigation for Drawer + 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/hunt', label: 'Hunt', icon: Crosshair }, + { 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/portfolio', label: 'Portfolio', icon: Briefcase }, + { href: '/terminal/cfo', label: 'CFO', icon: Shield }, + { href: '/terminal/sniper', label: 'Sniper', icon: Target }, + ] + }, + { + title: 'Monetize', + items: [ + { href: '/terminal/yield', label: 'Yield', icon: Coins, isNew: true }, + { href: '/terminal/listing', label: 'For Sale', icon: Tag }, + ] + } + ] + + // Tab labels for header + const tabLabels: Record = { + sniper: 'Closeout Sniper', + trends: 'Trend Surfer', + forge: 'Brandable Forge', + } + return (
- + {/* Desktop Sidebar */} +
+ +
-
- {/* Header */} -
-
-
-

PHASE 1

-

HUNT

-

- Find → Analyze → Decide. Strategy-first discovery for domainers. -

+ {/* Main Content */} +
+ + {/* ═══════════════════════════════════════════════════════════════════════ */} + {/* MOBILE HEADER - Techy Angular */} + {/* ═══════════════════════════════════════════════════════════════════════ */} +
+
+ {/* Top Row: Brand + Stats */} +
+
+
+ Domain Hunt +
+
+ {tabLabels[tab]} +
+ + {/* Stats Grid */} +
+
+
{totalDomains}
+
Tracked
+
+
+
{availableDomains.length}
+
Available
+
+ +
+
+
+ + {/* ═══════════════════════════════════════════════════════════════════════ */} + {/* DESKTOP HERO + STRATEGY CHIPS */} + {/* ═══════════════════════════════════════════════════════════════════════ */} +
+ {/* Desktop Hero Text */} +
+
+
+ Phase 1 · Discovery +
+

+ Domain Hunt +

+

+ Find → Analyze → Decide. Strategy-first discovery for domainers. +

+
+ + {/* Strategy Chips - Desktop */} +
+ + {/* Strategy Chips - Mobile (horizontal scroll) */} +
+
+ {(['sniper', 'trends', 'forge'] as HuntTab[]).map((t) => { + const active = tab === t + const labels: Record = { + sniper: { label: 'SNIPER', hint: '< $10 · 5y+' }, + trends: { label: 'TRENDS', hint: 'Keywords + Typos' }, + forge: { label: 'FORGE', hint: 'Brandables' }, + } + return ( + + ) + })} +
+
- {/* Content */} -
- {tab === 'sniper' ? : null} - {tab === 'trends' ? : null} - {tab === 'forge' ? : null} + {/* ═══════════════════════════════════════════════════════════════════════ */} + {/* TAB CONTENT */} + {/* ═══════════════════════════════════════════════════════════════════════ */} +
+ {tab === 'sniper' && } + {tab === 'trends' && } + {tab === 'forge' && }
+ + {/* ═══════════════════════════════════════════════════════════════════════ */} + {/* MOBILE BOTTOM NAV */} + {/* ═══════════════════════════════════════════════════════════════════════ */} + + + {/* ═══════════════════════════════════════════════════════════════════════ */} + {/* MOBILE DRAWER */} + {/* ═══════════════════════════════════════════════════════════════════════ */} + {menuOpen && ( +
+ {/* Backdrop */} +
setMenuOpen(false)} + /> + + {/* Drawer Panel */} +
+ + {/* Drawer Header */} +
+
+ Pounce +
+

POUNCE

+

Terminal v1.0

+
+
+ +
+ + {/* Navigation Sections */} +
+ {drawerNavSections.map((section) => ( +
+
+
+ {section.title} +
+
+ {section.items.map((item: any) => ( + setMenuOpen(false)} + 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 + )} + + ))} +
+
+ ))} + + {/* Settings */} +
+ setMenuOpen(false)} + className="flex items-center gap-3 py-2.5 text-white/50 active:text-white transition-colors" + > + + Settings + + + {user?.is_admin && ( + setMenuOpen(false)} + className="flex items-center gap-3 py-2.5 text-amber-500/70 active:text-amber-400 transition-colors" + > + + Admin + + )} +
+
+ + {/* User Card */} +
+
+
+ +
+
+

+ {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 tracking-wider active:scale-[0.98] transition-all mb-2" + > + + Upgrade + + )} + + +
+
+
+ )}
- {toast && ( - - )} + {/* Toast */} + {toast && }
) } -