Files
trumpsignal-frontend/components/nav/Navbar.tsx
T
k 040e1df685 feat: revamp dashboard, trades, and add landing/legal pages
- Major UI updates across dashboard, analytics, posts, trades, settings
- New landing page, robots/sitemap, contact/privacy/terms pages
- Updated globals.css with extensive styling and new landing.css
- Refactor signedRequest, realtime data hook, and dashboard store

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-25 16:04:57 +08:00

153 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use client'
import { useState, useEffect } from 'react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useAccount, useConnect, useDisconnect } from 'wagmi'
function BrandMark() {
return <span className="brand-mark">α</span>
}
function ThemeToggle() {
const [theme, setTheme] = useState<'light' | 'dark'>('light')
useEffect(() => {
const stored = localStorage.getItem('theme') as 'light' | 'dark' | null
const t = stored || 'light'
setTheme(t)
document.documentElement.dataset.theme = t
}, [])
function toggle() {
const next = theme === 'dark' ? 'light' : 'dark'
setTheme(next)
localStorage.setItem('theme', next)
document.documentElement.dataset.theme = next
}
return (
<button className="icon-btn theme-toggle" onClick={toggle}>
{theme === 'dark' ? (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<circle cx="12" cy="12" r="4" stroke="currentColor" strokeWidth="2" />
<path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" stroke="currentColor" strokeWidth="2" strokeLinecap="round"/>
</svg>
) : (
<svg width="16" height="16" viewBox="0 0 24 24" fill="none">
<path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z" stroke="currentColor" strokeWidth="2" strokeLinejoin="round"/>
</svg>
)}
</button>
)
}
export default function Navbar() {
const pathname = usePathname()
const { address, isConnected } = useAccount()
const { connect, connectors } = useConnect()
const { disconnect } = useDisconnect()
const [mounted, setMounted] = useState(false)
const [walletMenuOpen, setWalletMenuOpen] = useState(false)
useEffect(() => { setMounted(true) }, [])
useEffect(() => { setWalletMenuOpen(false) }, [pathname, address])
const locale = pathname.split('/')[1] || 'en'
const path = '/' + pathname.split('/').slice(2).join('/')
const tabs = [
{ key: '/', label: 'Overview' },
{ key: '/posts', label: 'Signals' },
{ key: '/trades', label: 'Trades' },
{ key: '/analytics', label: 'Analytics' },
{ key: '/settings', label: 'Settings' },
]
function isActive(key: string) {
if (key === '/') return path === '/' || path === '//'
return path.startsWith(key)
}
const shortAddr = address ? `${address.slice(0, 6)}${address.slice(-4)}` : null
return (
<nav className="nav">
<div className="brand">
<BrandMark />
<span>Trump Alpha</span>
</div>
<div className="nav-tabs">
{tabs.map(t => (
<Link
key={t.key}
href={`/${locale}${t.key === '/' ? '' : t.key}`}
className={`nav-tab ${isActive(t.key) ? 'active' : ''}`}
>
{t.label}
</Link>
))}
</div>
<div className="nav-spacer" />
<div className="nav-right">
<ThemeToggle />
{!mounted ? (
<button className="connect-btn lg" suppressHydrationWarning>Connect wallet</button>
) : isConnected && shortAddr ? (
<div className="wallet-menu-wrap" style={{ position: 'relative' }}>
<button
className="wallet-chip"
onClick={() => setWalletMenuOpen(open => !open)}
onBlur={(e) => {
if (!e.currentTarget.parentElement?.contains(e.relatedTarget as Node | null)) {
setWalletMenuOpen(false)
}
}}
aria-expanded={walletMenuOpen}
aria-haspopup="menu"
>
<span className="ava" />
<span className="mono">{shortAddr}</span>
</button>
<div className={`wallet-menu ${walletMenuOpen ? 'open' : ''}`} role="menu">
<button
role="menuitem"
onMouseDown={(e) => {
e.preventDefault()
if (address) navigator.clipboard?.writeText(address)
setWalletMenuOpen(false)
}}
>
Copy address
</button>
<button
role="menuitem"
className="danger"
onMouseDown={(e) => {
e.preventDefault()
disconnect()
setWalletMenuOpen(false)
}}
>
Disconnect
</button>
</div>
</div>
) : (
<button
className="connect-btn lg"
onClick={() => {
const connector = connectors[0]
if (connector) connect({ connector })
}}
>
Connect wallet
</button>
)}
</div>
</nav>
)
}