← back to Ken
src/app/dashboard/layout.js
97 lines
'use client';
import { useState, useEffect } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
const NAV_ITEMS = [
{ href: '/dashboard', label: 'Overview', icon: 'M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6' },
{ href: '/dashboard/markets', label: 'Markets', icon: 'M13 7h8m0 0v8m0-8l-8 8-4-4-6 6' },
{ href: '/dashboard/weather', label: 'Weather', icon: 'M3 15a4 4 0 004 4h9a5 5 0 10-.1-9.999 5.002 5.002 0 10-9.78 2.096A4.001 4.001 0 003 15z' },
{ href: '/dashboard/models', label: 'Models', icon: 'M9 3v2m6-2v2M9 19v2m6-2v2M5 9H3m2 6H3m18-6h-2m2 6h-2M7 19h10a2 2 0 002-2V7a2 2 0 00-2-2H7a2 2 0 00-2 2v10a2 2 0 002 2zM9 9h6v6H9V9z' },
{ href: '/dashboard/risk', label: 'Risk', icon: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z' },
{ href: '/dashboard/trades', label: 'Trades', icon: 'M8 7h12m0 0l-4-4m4 4l-4 4m0 6H4m0 0l4 4m-4-4l4-4' },
{ href: '/dashboard/bankroll', label: 'Bankroll', icon: 'M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z' },
{ href: '/dashboard/settings', label: 'Settings', icon: 'M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.066 2.573c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.573 1.066c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.066-2.573c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z M15 12a3 3 0 11-6 0 3 3 0 016 0z' },
];
export default function DashboardLayout({ children }) {
const pathname = usePathname();
const [collapsed, setCollapsed] = useState(false);
const [health, setHealth] = useState(null);
useEffect(() => {
fetch('/api/health').then(r => r.json()).then(d => {
if (!d.authenticated) window.location.href = '/';
setHealth(d);
}).catch(() => window.location.href = '/');
const interval = setInterval(() => {
fetch('/api/health').then(r => r.json()).then(setHealth).catch(() => {});
}, 30000);
return () => clearInterval(interval);
}, []);
return (
<div className="flex min-h-screen">
{/* Sidebar */}
<aside className={`${collapsed ? 'w-16' : 'w-56'} bg-gray-900/50 border-r border-gray-800 flex flex-col transition-all duration-200`}>
<div className="p-4 border-b border-gray-800 flex items-center gap-3">
<div className="w-8 h-8 rounded-lg flex items-center justify-center flex-shrink-0" style={{ background: 'linear-gradient(135deg, #3b82f6, #8b5cf6)' }}>
<span className="text-white font-bold text-sm">B</span>
</div>
{!collapsed && <span className="font-bold text-sm bg-gradient-to-r from-blue-400 to-purple-400 bg-clip-text text-transparent">BERTHA</span>}
</div>
<nav className="flex-1 p-2 space-y-0.5">
{NAV_ITEMS.map(item => {
const active = pathname === item.href || (item.href !== '/dashboard' && pathname?.startsWith(item.href));
return (
<Link key={item.href} href={item.href}
className={`flex items-center gap-3 px-3 py-2 rounded-lg text-sm transition-all ${
active ? 'bg-blue-500/15 text-blue-400 font-medium' : 'text-gray-400 hover:text-gray-200 hover:bg-gray-800/50'
}`}>
<svg className="w-4.5 h-4.5 flex-shrink-0" style={{width:'18px',height:'18px'}} fill="none" stroke="currentColor" strokeWidth="1.5" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" d={item.icon} />
</svg>
{!collapsed && <span>{item.label}</span>}
</Link>
);
})}
</nav>
<div className="p-3 border-t border-gray-800">
{!collapsed && health && (
<div className="space-y-1.5 text-[11px]">
<div className="flex justify-between">
<span className="text-gray-500">Mode</span>
<span className={health.paper_mode ? 'text-yellow-400' : 'text-green-400'}>
{health.paper_mode ? 'PAPER' : 'LIVE'}
</span>
</div>
<div className="flex justify-between">
<span className="text-gray-500">Kill Switch</span>
<span className={health.kill_switch ? 'text-red-400' : 'text-green-400'}>
{health.kill_switch ? 'ACTIVE' : 'OFF'}
</span>
</div>
</div>
)}
<button onClick={() => setCollapsed(!collapsed)}
className="w-full mt-2 flex items-center justify-center py-1.5 text-gray-500 hover:text-gray-300 transition">
<svg className={`w-4 h-4 transition-transform ${collapsed ? '' : 'rotate-180'}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M11 19l-7-7 7-7m8 14l-7-7 7-7" />
</svg>
</button>
</div>
</aside>
{/* Main content */}
<main className="flex-1 overflow-auto">
<div className="p-6 max-w-[1600px] mx-auto">
{children}
</div>
</main>
</div>
);
}