040e1df685
- 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>
153 lines
4.7 KiB
TypeScript
153 lines
4.7 KiB
TypeScript
'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>
|
||
)
|
||
}
|