import { useState, useEffect, useCallback } 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'; import { useTabFeatureFlags } from './hooks/useTabFeatureFlags'; import { tradingRuntime, tradingTelemetry } from './lib/runtime'; import { createTradeProfile, fetchTradeProfiles, updateTradeProfile } from './lib/profileApi'; // ─── Helpers (preserved from original App.tsx) ─────────────────────────────── export const resolveProfileNameForAction = ( action: string, requestedName: string | undefined, chatProfiles: Array<{ name?: string }>, suffixProvider: () => string = () => new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', second: '2-digit' }).replace(/:/g, '') ) => { let profileName = requestedName || 'AI Profile'; if (action === 'create_profile') { const existing = chatProfiles.find(p => p.name === profileName); if (existing) profileName = `${profileName} (${suffixProvider()})`; } return profileName; }; export const buildChatApplyPayload = ( profileData: any, currentUserId: string, profileName: string ) => ({ name: profileName, user_id: currentUserId, allocated_capital: Number(profileData.allocated_capital || 1000), risk_per_trade_percent: Number(profileData.risk_per_trade_percent || 1), symbols: profileData.symbols || 'BTC/USDT', is_active: profileData.is_active ?? true, strategy_config: profileData.strategy_config, }); // ─── App ───────────────────────────────────────────────────────────────────── function App() { const { user, profile, loading, signOut } = useAuth(); const { socket, botState, connected } = useWebSocket(tradingRuntime.tradingApiUrl); const [activeSymbol, setActiveSymbol] = useState(''); const [chatProfiles, setChatProfiles] = useState([]); const [previewAsCustomer] = useState(false); const { enabled: backtestEnabledForView, loading: backtestGateLoading } = useBacktestFeatureGate({ previewAsCustomer }); const { flags: tabFlags } = useTabFeatureFlags(); // Feature gates const isAdminAccount = profile?.role === 'admin'; const isAdmin = isAdminAccount && !previewAsCustomer; const showBacktestTab = isAdmin || (!backtestGateLoading && backtestEnabledForView); const showMarketplaceTab = isAdmin || tabFlags.marketplace; // Critical system events (for the alert banner) const recentCriticalEvents = (botState.operationalEvents ?? []).filter(e => (e.severity === 'ERROR' || e.severity === 'WARN') && Date.now() - e.timestamp < 600_000 ); const hasCriticalEvents = recentCriticalEvents.length > 0; // Chat profile management const fetchChatProfiles = useCallback(async () => { const data = await fetchTradeProfiles(); setChatProfiles(data ?? []); }, []); useEffect(() => { if (user) { fetchChatProfiles(); const id = setInterval(fetchChatProfiles, 30_000); return () => clearInterval(id); } }, [user, fetchChatProfiles]); const handleChatApply = async ( action: string, profileData: any ): Promise<{ success: boolean; error?: string }> => { const currentUserId = user?.id; if (!currentUserId) return { success: false, error: 'Not authenticated' }; const profileName = resolveProfileNameForAction(action, profileData.name, chatProfiles); const payload = buildChatApplyPayload(profileData, currentUserId, profileName); if (action === 'create_profile') { try { await createTradeProfile(payload); fetchChatProfiles(); window.dispatchEvent(new Event('profiles-updated')); return { success: true }; } catch (err: any) { return { success: false, error: err.message }; } } if (action === 'update_profile' && profileData.id) { try { await updateTradeProfile(profileData.id, payload); fetchChatProfiles(); window.dispatchEvent(new Event('profiles-updated')); return { success: true }; } catch (err: any) { return { success: false, error: err.message }; } } return { success: false, error: 'Unknown action' }; }; const handleSignOut = async () => { tradingTelemetry.client.trackEvent('info', 'auth', 'trading_web_sign_out', { userId: user?.id ?? 'anonymous', feature: 'sign_out', tags: { surface: 'web' }, }); await signOut(); }; // ── Auth gates ────────────────────────────────────────────────────────────── if (window.location.pathname === '/reset-callback') return ; if (loading) { return (
Loading…
); } if (!user) return ; // ── Render ────────────────────────────────────────────────────────────────── return ( {/* Critical system alert banner */} {hasCriticalEvents && (
⚠️ SYSTEM ALERT: {recentCriticalEvents.length} CRITICAL ISSUES DETECTED — GO TO SETTINGS › ADMIN PANEL ⚠️
)}
{/* Floating AI strategy assistant */}
); } export default App;