Files
foxhunt/web-dashboard/src/App.tsx
jgrusewski 35b7f6e27b feat(web-gateway,web-dashboard): request-id middleware, lazy routes, form validation
- Add X-Request-Id middleware with UUID v4 generation and header propagation
- Convert 6 dashboard routes to React.lazy() with Suspense for code splitting
- Add inline validation errors to OrderForm (symbol, quantity, price)
- Add date range + symbol validation to BacktestingDashboard form
- Fix !Send future issue: use .instrument(span) instead of span.entered()

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 09:09:44 +01:00

95 lines
3.1 KiB
TypeScript

import { lazy, Suspense } from 'react';
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { ErrorBoundary } from './components/common/ErrorBoundary';
import { ProtectedRoute } from './components/common/ProtectedRoute';
import { DashboardLayout } from './components/common/DashboardLayout';
import { LoginPage } from './pages/LoginPage';
const TradingDashboard = lazy(() =>
import('./pages/TradingDashboard').then(m => ({ default: m.TradingDashboard }))
);
const RiskDashboard = lazy(() =>
import('./pages/RiskDashboard').then(m => ({ default: m.RiskDashboard }))
);
const MLDashboard = lazy(() =>
import('./pages/MLDashboard').then(m => ({ default: m.MLDashboard }))
);
const PerformanceDashboard = lazy(() =>
import('./pages/PerformanceDashboard').then(m => ({ default: m.PerformanceDashboard }))
);
const BacktestingDashboard = lazy(() =>
import('./pages/BacktestingDashboard').then(m => ({ default: m.BacktestingDashboard }))
);
const ConfigDashboard = lazy(() =>
import('./pages/ConfigDashboard').then(m => ({ default: m.ConfigDashboard }))
);
function LoadingFallback() {
return (
<div style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
height: '100%',
minHeight: '200px',
}}>
<div style={{
width: '36px',
height: '36px',
borderRadius: '50%',
border: '3px solid var(--color-border, #333)',
borderTopColor: 'var(--color-accent, #3b82f6)',
animation: 'spin 0.8s linear infinite',
}} />
<style>{`@keyframes spin { to { transform: rotate(360deg); } }`}</style>
</div>
);
}
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 10_000,
refetchInterval: 15_000,
refetchOnWindowFocus: false,
},
},
});
function App() {
return (
<ErrorBoundary>
<QueryClientProvider client={queryClient}>
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
<Route
path="/*"
element={
<ProtectedRoute>
<DashboardLayout>
<Suspense fallback={<LoadingFallback />}>
<Routes>
<Route path="/trading" element={<TradingDashboard />} />
<Route path="/risk" element={<RiskDashboard />} />
<Route path="/ml" element={<MLDashboard />} />
<Route path="/performance" element={<PerformanceDashboard />} />
<Route path="/backtest" element={<BacktestingDashboard />} />
<Route path="/config" element={<ConfigDashboard />} />
<Route path="*" element={<Navigate to="/trading" replace />} />
</Routes>
</Suspense>
</DashboardLayout>
</ProtectedRoute>
}
/>
</Routes>
</BrowserRouter>
</QueryClientProvider>
</ErrorBoundary>
);
}
export default App;