import { useState, useEffect, useCallback } from 'react'; import { useWebSocket } from './hooks/useWebSocket'; import { LivePulseTicker } from './components/LivePulseTicker'; import { OverviewTab } from './tabs/OverviewTab'; import { SignalsTab } from './tabs/SignalsTab'; import { PositionsTab } from './tabs/PositionsTab'; import { HistoryTab } from './tabs/HistoryTab'; import { SettingsTab } from './tabs/SettingsTab'; import { EntriesTab } from './tabs/EntriesTab'; import { AdminTab } from './tabs/AdminTab'; import { TradeProfileManager } from './components/TradeProfileManager'; import { StrategyWizard } from './components/StrategyWizard'; import { MyStrategiesTab } from './tabs/MyStrategiesTab'; import { MarketplaceTab } from './tabs/MarketplaceTab'; import { MembershipTab } from './tabs/MembershipTab'; import { BacktestTab } from './tabs/BacktestTab'; import { ChatControl } from './components/ChatControl'; import type { StrategyPreset } from './lib/PresetRegistry'; import { RISK_STYLE_TEMPLATES } from './lib/RiskStyleTemplates'; import './App.css'; import { useAuth } from './components/AuthContext'; import { Login } from './components/Login'; import { ResetPassword } from './components/ResetPassword'; import { useBacktestFeatureGate } from './backtest/useBacktestFeatureGate'; import { tradingRuntime, tradingTelemetry } from './lib/runtime'; import { createTradeProfile, fetchTradeProfiles, updateTradeProfile } from './lib/profileApi'; 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, }); function App() { const { user, profile, loading, signOut } = useAuth(); const { botState, connected } = useWebSocket(tradingRuntime.tradingApiUrl); const [activeTab, setActiveTab] = useState('overview'); const [wizardSeed, setWizardSeed] = useState(null); const [chatProfiles, setChatProfiles] = useState([]); const [previewAsCustomer, setPreviewAsCustomer] = useState(false); const { enabled: backtestEnabledForView, loading: backtestGateLoading } = useBacktestFeatureGate({ previewAsCustomer }); const systemHealthData = botState.health || {}; const hasCapitalViolation = ((systemHealthData as any)?.capitalInvariantViolations || 0) > 0; const anyLoopUnhealthy = (systemHealthData as any)?.tradingLoopHealthy === false || (systemHealthData as any)?.reconciliationLoopHealthy === false; const systemHealthState = hasCapitalViolation ? 'Unhealthy' : anyLoopUnhealthy ? 'Degraded' : 'Healthy'; const systemHealthColor = hasCapitalViolation ? '#ff6657' : anyLoopUnhealthy ? '#facc15' : '#34c759'; const systemHealthTooltip = hasCapitalViolation ? 'Capital invariant violation detected—resolve ledger divergence before trading resumes.' : anyLoopUnhealthy ? 'Degraded indicates a trading or reconciliation loop is lagging or experiencing lock contention.' : 'All monitored loops are healthy.'; const recentCriticalEvents = (botState.operationalEvents || []).filter(e => (e.severity === 'ERROR' || e.severity === 'WARN') && (Date.now() - e.timestamp < 600000) ); const hasCriticalEvents = recentCriticalEvents.length > 0; const fetchChatProfiles = useCallback(async () => { const data = await fetchTradeProfiles(); setChatProfiles(data || []); }, []); useEffect(() => { if (user) { fetchChatProfiles(); const interval = setInterval(fetchChatProfiles, 30000); return () => clearInterval(interval); } }, [user, fetchChatProfiles]); const handleChatApply = async (action: string, profileData: any): Promise<{ success: boolean; error?: string }> => { const profileName = resolveProfileNameForAction(action, profileData.name, chatProfiles); // IMPORTANT: Use the authenticated user's ID (auth.uid()) for RLS compliance const currentUserId = user?.id; if (!currentUserId) { console.error('[ChatApply] No authenticated user found'); return { success: false, error: 'Not authenticated' }; } const payload = buildChatApplyPayload(profileData, currentUserId, profileName); console.log('[ChatApply] Action:', action, 'user_id:', currentUserId, 'Payload:', payload); if (action === 'create_profile') { try { await createTradeProfile(payload); } catch (error: any) { console.error('[ChatApply] Insert error:', error); return { success: false, error: error.message }; } console.log('[ChatApply] Profile created successfully:', profileName); fetchChatProfiles(); window.dispatchEvent(new Event('profiles-updated')); return { success: true }; } else if (action === 'update_profile' && profileData.id) { try { await updateTradeProfile(profileData.id, payload); } catch (error: any) { console.error('[ChatApply] Update error:', error); return { success: false, error: error.message }; } console.log('[ChatApply] Profile updated successfully:', profileName); fetchChatProfiles(); window.dispatchEvent(new Event('profiles-updated')); return { success: true }; } return { success: false, error: 'Unknown action' }; }; const handleClonePreset = (preset: StrategyPreset) => { const style = RISK_STYLE_TEMPLATES.find(t => t.id === preset.riskStyleId); const dummyProfile = { name: `${preset.name} (Clone)`, symbols: preset.recommendedAssets.join(','), allocated_capital: 1000, is_active: false, strategy_config: { execution: { minRulePassRatio: style?.minRulePassRatio || 1.0 }, riskLimits: { dailyProfitTargetUsd: 100 }, rules: [ { ruleId: 'RiskManagementRule', enabled: true, ruleType: 'mandatory' }, { ruleId: 'SessionRule', enabled: true, ruleType: 'mandatory', params: { sessions: 'LDN,NY' } }, ...(style?.mandatoryRules || []).map(r => ({ ruleId: r, enabled: true, ruleType: 'mandatory' })), ...(style?.votingRules || []).map(r => ({ ruleId: r, enabled: true, ruleType: 'voting' })) ] } }; setWizardSeed(dummyProfile); setActiveTab('wizard'); }; // Handle Password Reset Route if (window.location.pathname === '/reset-callback') { return ; } if (loading) { return
Loading...
; } if (!user) { return ; } const isAdminAccount = profile?.role === 'admin'; const isAdmin = isAdminAccount && !previewAsCustomer; const showBacktestTab = isAdmin || (!backtestGateLoading && backtestEnabledForView); const renderTab = () => { switch (activeTab) { case 'overview': return ; case 'signals': if (!isAdmin) return ; return ; case 'entries': if (!isAdmin) return ; return ; case 'positions': return ; case 'history': return ; case 'strategy_clusters': return ; case 'profiles': return ; case 'marketplace': return ; case 'membership': return ; case 'wizard': return { setWizardSeed(null); setActiveTab('profiles'); }} />; case 'backtest': if (!showBacktestTab) return ; return ; case 'settings': return ; case 'admin': if (!isAdmin) return ; return ; default: return ; } }; const handleSignOut = async () => { tradingTelemetry.client.trackEvent('info', 'auth', 'trading_web_sign_out', { userId: user?.id ?? 'anonymous', feature: 'sign_out', tags: { surface: 'web' }, }); await signOut(); }; return (
{hasCriticalEvents && (
setActiveTab('admin')} > ⚠️ SYSTEM ALERT: {recentCriticalEvents.length} CRITICAL ISSUES DETECTED. CLICK TO REVIEW IN ADMIN PANEL. ⚠️
)}

Trading Bot Dashboard v2.3

{connected ? 'Connected' : 'Reconnecting...'}
{botState.settings.isAlgoEnabled ? '🟢' : '⏸️'}
{botState.settings.isAlgoEnabled ? 'Bot Active' : 'Bot Monitoring'} Mode: {botState.settings.executionMode}
{/* Trading Control Status Badge */} {botState.health?.tradingControl && (
{botState.health.tradingControl.mode === 'PAUSED' ? '⏸️' : '▶️'}
{botState.health.tradingControl.mode === 'PAUSED' ? 'Trading Paused' : 'Trading Active'} {botState.health.tradingControl.mode === 'PAUSED' ? 'No new entries' : 'Entries allowed'}
)} {/* Preview as Customer Toggle — Admin Only */} {isAdminAccount && ( )}
System Health {systemHealthState}
👤 {user?.email}
{renderTab()}
{/* Global AI Strategy Assistant - floating robot icon on all pages */}
); } export default App;