diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 54621a2..39bfba8 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,4 +1,4 @@ -import { BrowserRouter, Routes, Route, useLocation } from 'react-router-dom' +import { BrowserRouter, Routes, Route, useLocation, Navigate } from 'react-router-dom' import { useEffect, ComponentType } from 'react' import Sidebar from './components/layout/Sidebar' import TabBar from './components/layout/TabBar' @@ -35,21 +35,48 @@ import AIDesks from './pages/AIDesks' import MacroSeriesPage from './pages/MacroSeriesPage' import EuroSimulator from './pages/EuroSimulator' import CausalLab from './pages/CausalLab' -import { Navigate } from 'react-router-dom' import { useCycleWatcher } from './hooks/useApi' -// ── Keep-alive component definitions ───────────────────────────────────────── -// KEEP_ALIVE_META provides metadata (path/label/icon); components added here. -// Routes listed here are REMOVED from below to avoid double-render. +// ── Keep-alive pages ────────────────────────────────────────────────────────── +// All pages except InstrumentDashboard (uses URL :id param — would break when hidden). +// Order must match KEEP_ALIVE_META in keepAliveMeta.ts. const KEEP_ALIVE_DEFS: { path: string; component: ComponentType }[] = [ - { path: '/market-events', component: MarketEvents }, - { path: '/causal-lab', component: CausalLab }, + { path: '/', component: Dashboard }, + { path: '/geo', component: GeoRadar }, + { path: '/markets', component: Markets }, + { path: '/macro', component: MacroRegime }, + { path: '/options', component: OptionsLab }, + { path: '/patterns', component: PatternExplorer }, + { path: '/pattern-lab', component: PatternLab }, + { path: '/portfolio', component: Portfolio }, + { path: '/journal', component: JournalDeBord }, + { path: '/rapport', component: RapportIA }, + { path: '/super-contexte', component: SuperContexte }, + { path: '/analytics', component: Analytics }, + { path: '/analytics-advanced', component: AnalyticsAdvanced }, + { path: '/risk', component: RiskDashboard }, + { path: '/var', component: VaRAnalysis }, + { path: '/position-history', component: PositionHistory }, + { path: '/backtest', component: Backtest }, + { path: '/calendar', component: CalendarPage }, + { path: '/simulator', component: EuroSimulator }, + { path: '/causal-lab', component: CausalLab }, + { path: '/macro-series', component: MacroSeriesPage }, + { path: '/institutional', component: InstitutionalReports }, + { path: '/specialist-desks', component: SpecialistDesks }, + { path: '/market-events', component: MarketEvents }, + { path: '/ai-desks', component: AIDesks }, + { path: '/cycle-actions', component: CycleActions }, + { path: '/snapshot', component: ExternalSnapshot }, + { path: '/logs', component: SystemLogs }, + { path: '/config', component: Config }, + { path: '/patterns/edit', component: PatternEditor }, ] const KEEP_ALIVE_PATHS = new Set(KEEP_ALIVE_DEFS.map(d => d.path)) -// ── KeepAlivePage — mounts once, hides/shows on route change ───────────────── +// ── KeepAlivePage ───────────────────────────────────────────────────────────── function KeepAlivePage({ path, component: Component }: { path: string; component: ComponentType }) { const { kept, keepRoute } = useTabs() @@ -60,7 +87,6 @@ function KeepAlivePage({ path, component: Component }: { path: string; component if (isActive) keepRoute(path) }, [isActive, path, keepRoute]) - // Don't mount until the user visits for the first time if (!kept.has(path) && !isActive) return null return ( @@ -70,48 +96,21 @@ function KeepAlivePage({ path, component: Component }: { path: string; component ) } -// ── NormalRoutes — handles all non-keep-alive routes ───────────────────────── +// ── NormalRoutes — only InstrumentDashboard (uses :id param) + redirects ───── function NormalRoutes() { const location = useLocation() - // Yield to KeepAlive when on a keep-alive route if (KEEP_ALIVE_PATHS.has(location.pathname)) return null return (
- } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> + {/* InstrumentDashboard kept here: useParams() reads :id from URL */} } /> } /> - } /> - } /> - } /> - } /> - } /> + {/* Legacy redirects */} + } /> + } />
) diff --git a/frontend/src/components/layout/TabBar.tsx b/frontend/src/components/layout/TabBar.tsx index eccd390..75f44cd 100644 --- a/frontend/src/components/layout/TabBar.tsx +++ b/frontend/src/components/layout/TabBar.tsx @@ -13,40 +13,42 @@ export default function TabBar() { if (visible.length === 0) return null return ( -
- {visible.map(({ path, label, icon: Icon }) => { - const isActive = location.pathname === path || location.pathname.startsWith(path + '/') - return ( - - ) - })} + + {label} + { + e.stopPropagation() + const remaining = visible.filter(m => m.path !== path) + forgetRoute(path) + if (isActive && remaining.length > 0) { + navigate(remaining[remaining.length - 1].path) + } + }} + className="ml-1 opacity-0 group-hover:opacity-100 text-slate-600 hover:text-white rounded hover:bg-slate-600/40 p-0.5 transition-opacity leading-none" + > + + + + ) + })} +
) } diff --git a/frontend/src/config/keepAliveMeta.ts b/frontend/src/config/keepAliveMeta.ts index 75cebd0..f4eeccb 100644 --- a/frontend/src/config/keepAliveMeta.ts +++ b/frontend/src/config/keepAliveMeta.ts @@ -1,13 +1,46 @@ -import { Radio, FlaskConical } from 'lucide-react' +import { + LayoutDashboard, Globe, BarChart2, Activity, TrendingUp, Zap, FlaskConical, + DollarSign, BookOpen, FileBarChart, Brain, Microscope, ShieldAlert, Gauge, + GitCompare, History, Calendar, Sliders, TrendingUp as MacroSeriesIcon, + Building2, Users, ScanEye, Radio, Bot, PlayCircle, ScrollText, Settings, +} from 'lucide-react' import type { LucideIcon } from 'lucide-react' export interface KeepAliveMeta { - path: string + path: string label: string - icon: LucideIcon + icon: LucideIcon } export const KEEP_ALIVE_META: KeepAliveMeta[] = [ - { path: '/market-events', label: 'Market Events', icon: Radio }, - { path: '/causal-lab', label: 'Lab Causal', icon: FlaskConical }, + { path: '/', label: 'Cockpit', icon: LayoutDashboard }, + { path: '/geo', label: 'Geo Radar', icon: Globe }, + { path: '/markets', label: 'Markets', icon: BarChart2 }, + { path: '/macro', label: 'Macro Regime', icon: Activity }, + { path: '/options', label: 'Options Lab', icon: TrendingUp }, + { path: '/patterns', label: 'Patterns', icon: Zap }, + { path: '/pattern-lab', label: 'Pattern Lab', icon: FlaskConical }, + { path: '/portfolio', label: 'Portfolio', icon: DollarSign }, + { path: '/journal', label: 'Journal', icon: BookOpen }, + { path: '/rapport', label: 'Cycle Report', icon: FileBarChart }, + { path: '/super-contexte', label: 'Super Context', icon: Brain }, + { path: '/analytics', label: 'Analytics', icon: FlaskConical }, + { path: '/analytics-advanced',label: 'Adv. Analytics', icon: Microscope }, + { path: '/risk', label: 'Risk', icon: ShieldAlert }, + { path: '/var', label: 'VaR', icon: Gauge }, + { path: '/position-history', label: 'Positions', icon: GitCompare }, + { path: '/backtest', label: 'Backtest', icon: History }, + { path: '/calendar', label: 'Calendar', icon: Calendar }, + { path: '/simulator', label: 'Simulator', icon: Sliders }, + { path: '/causal-lab', label: 'Lab Causal', icon: FlaskConical }, + { path: '/macro-series', label: 'Macro Series', icon: MacroSeriesIcon }, + { path: '/institutional', label: 'Inst. Reports', icon: Building2 }, + { path: '/specialist-desks', label: 'Specialist Desks', icon: Users }, + { path: '/market-events', label: 'Market Events', icon: Radio }, + { path: '/ai-desks', label: 'AI Desks', icon: Bot }, + { path: '/cycle-actions', label: 'Cycle Actions', icon: PlayCircle }, + { path: '/snapshot', label: 'Snapshot', icon: ScanEye }, + { path: '/logs', label: 'Logs', icon: ScrollText }, + { path: '/config', label: 'Config', icon: Settings }, + { path: '/patterns/edit', label: 'Pattern Editor', icon: Zap }, ] diff --git a/frontend/src/index.css b/frontend/src/index.css index 1c00883..8285c52 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -14,6 +14,16 @@ ::-webkit-scrollbar-thumb:hover { @apply bg-slate-600; } } +@layer utilities { + .scrollbar-none { + scrollbar-width: none; + -ms-overflow-style: none; + } + .scrollbar-none::-webkit-scrollbar { + display: none; + } +} + @layer components { .card { @apply bg-dark-800 border border-slate-700/40 rounded-lg p-4;