perf(web): lazy-load app routes and heavy surfaces

This commit is contained in:
root 2026-05-06 17:12:22 +00:00
parent e853ffc0c5
commit 7de6b236c0
3 changed files with 59 additions and 27 deletions

View File

@ -1,10 +1,9 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { Suspense, lazy, useState, useEffect, useCallback, useRef } from 'react';
import { BrowserRouter } from 'react-router-dom';
import { useWebSocket } from './hooks/useWebSocket';
import { useAuth } from './components/AuthContext';
import { Login } from './components/Login';
import { ResetPassword } from './components/ResetPassword';
import { ChatControl } from './components/ChatControl';
import { AppContext } from './context/AppContext';
import { AppShell } from './components/layout/AppShell';
import { useBacktestFeatureGate } from './backtest/useBacktestFeatureGate';
@ -12,6 +11,8 @@ import { useTabFeatureFlags } from './hooks/useTabFeatureFlags';
import { tradingRuntime, tradingTelemetry } from './lib/runtime';
import { createTradeProfile, fetchTradeProfiles, updateTradeProfile } from './lib/profileApi';
const ChatControl = lazy(() => import('./components/ChatControl').then((mod) => ({ default: mod.ChatControl })));
// ─── Helpers (preserved from original App.tsx) ───────────────────────────────
export const resolveProfileNameForAction = (
@ -262,7 +263,9 @@ function App() {
)}
{/* Floating AI strategy assistant */}
<ChatControl profiles={chatProfiles} onApplyProfile={handleChatApply} />
<Suspense fallback={null}>
<ChatControl profiles={chatProfiles} onApplyProfile={handleChatApply} />
</Suspense>
</AppContext.Provider>
</BrowserRouter>
);

View File

@ -1,17 +1,41 @@
import { Suspense, lazy } from 'react';
import { Link, Routes, Route, useLocation } from 'react-router-dom';
import { Sidebar } from './Sidebar';
import { Header } from './Header';
import { RightPanel } from './RightPanel';
import { Button } from '../ui/button';
import { HomeView } from '../../views/HomeView';
import { PortfolioView } from '../../views/PortfolioView';
import { ResearchView } from '../../views/ResearchView';
import { SimpleView } from '../../views/SimpleView';
import { MarketsView } from '../../views/MarketsView';
import { ScreenerView } from '../../views/ScreenerView';
import { WatchlistView } from '../../views/WatchlistView';
import { AlertsView } from '../../views/AlertsView';
import { SettingsView } from '../../views/SettingsView';
const HomeView = lazy(() => import('../../views/HomeView').then((mod) => ({ default: mod.HomeView })));
const PortfolioView = lazy(() => import('../../views/PortfolioView').then((mod) => ({ default: mod.PortfolioView })));
const ResearchView = lazy(() => import('../../views/ResearchView').then((mod) => ({ default: mod.ResearchView })));
const SimpleView = lazy(() => import('../../views/SimpleView').then((mod) => ({ default: mod.SimpleView })));
const MarketsView = lazy(() => import('../../views/MarketsView').then((mod) => ({ default: mod.MarketsView })));
const ScreenerView = lazy(() => import('../../views/ScreenerView').then((mod) => ({ default: mod.ScreenerView })));
const WatchlistView = lazy(() => import('../../views/WatchlistView').then((mod) => ({ default: mod.WatchlistView })));
const AlertsView = lazy(() => import('../../views/AlertsView').then((mod) => ({ default: mod.AlertsView })));
const SettingsView = lazy(() => import('../../views/SettingsView').then((mod) => ({ default: mod.SettingsView })));
function RouteFallback() {
return (
<section
aria-live="polite"
style={{
minHeight: 320,
borderRadius: 24,
border: '1px solid var(--border)',
background: 'var(--card)',
color: 'var(--foreground)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: 14,
fontWeight: 700,
}}
>
Loading workspace
</section>
);
}
function NotFoundView() {
const location = useLocation();
@ -64,18 +88,20 @@ export function AppShell() {
<div className="dashboard-content-row">
{/* Scrollable main content */}
<main className="dashboard-content">
<Routes>
<Route path="/" element={<HomeView />} />
<Route path="/portfolio" element={<PortfolioView />} />
<Route path="/research" element={<ResearchView />} />
<Route path="/simple" element={<SimpleView />} />
<Route path="/markets" element={<MarketsView />} />
<Route path="/screener" element={<ScreenerView />} />
<Route path="/watchlist" element={<WatchlistView />} />
<Route path="/alerts" element={<AlertsView />} />
<Route path="/settings" element={<SettingsView />} />
<Route path="*" element={<NotFoundView />} />
</Routes>
<Suspense fallback={<RouteFallback />}>
<Routes>
<Route path="/" element={<HomeView />} />
<Route path="/portfolio" element={<PortfolioView />} />
<Route path="/research" element={<ResearchView />} />
<Route path="/simple" element={<SimpleView />} />
<Route path="/markets" element={<MarketsView />} />
<Route path="/screener" element={<ScreenerView />} />
<Route path="/watchlist" element={<WatchlistView />} />
<Route path="/alerts" element={<AlertsView />} />
<Route path="/settings" element={<SettingsView />} />
<Route path="*" element={<NotFoundView />} />
</Routes>
</Suspense>
</main>
{/* Fixed right panel */}

View File

@ -1,10 +1,11 @@
import { useState } from 'react';
import { Suspense, lazy, useState } from 'react';
import { useAppContext } from '../context/AppContext';
import { MarketplaceTab } from '../tabs/MarketplaceTab';
import { TopVolatile, AISetups } from '../components/MarketOpportunities';
import type { StrategyPreset } from '../lib/PresetRegistry';
import { PageHeader } from '../components/ui/page-header';
const MarketplaceTab = lazy(() => import('../tabs/MarketplaceTab').then((mod) => ({ default: mod.MarketplaceTab })));
export function MarketsView() {
const { botState, showMarketplaceTab } = useAppContext();
const [, setClonedPreset] = useState<StrategyPreset | null>(null);
@ -22,7 +23,9 @@ export function MarketsView() {
<AISetups botState={botState} />
</div>
{showMarketplaceTab && (
<MarketplaceTab onClone={handleClone} botState={botState} />
<Suspense fallback={null}>
<MarketplaceTab onClone={handleClone} botState={botState} />
</Suspense>
)}
</div>
);