"use client" import React, { useState, useEffect, useRef } from 'react' import TradeModal from './TradeModal' import ScreenshotGallery from './ScreenshotGallery' const layouts = (process.env.NEXT_PUBLIC_TRADINGVIEW_LAYOUTS || 'ai,Diy module').split(',').map(l => l.trim()) // Layout display names mapping const layoutDisplayNames: { [key: string]: string } = { 'ai': 'ai', 'Diy module': 'Diy module' } const timeframes = [ { label: '1m', value: '1' }, { label: '5m', value: '5' }, { label: '15m', value: '15' }, { label: '30m', value: '30' }, { label: '1h', value: '60' }, { label: '2h', value: '120' }, { label: '4h', value: '240' }, { label: '1d', value: 'D' }, { label: '1w', value: 'W' }, { label: '1M', value: 'M' }, ] const popularCoins = [ { name: 'Bitcoin', symbol: 'BTCUSD', icon: 'https://assets.coingecko.com/coins/images/1/large/bitcoin.png', color: 'from-orange-400 to-orange-600' }, { name: 'Ethereum', symbol: 'ETHUSD', icon: 'https://assets.coingecko.com/coins/images/279/large/ethereum.png', color: 'from-blue-400 to-blue-600' }, { name: 'Solana', symbol: 'SOLUSD', icon: 'https://assets.coingecko.com/coins/images/4128/large/solana.png', color: 'from-purple-400 to-purple-600' } ] // Progress tracking interfaces interface ProgressStep { id: string title: string description: string status: 'pending' | 'active' | 'completed' | 'error' startTime?: number endTime?: number details?: string } interface AnalysisProgress { sessionId: string currentStep: number totalSteps: number steps: ProgressStep[] timeframeProgress?: { current: number total: number currentTimeframe?: string } } interface AIAnalysisPanelProps { onAnalysisComplete?: (analysis: any, symbol: string) => void } export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelProps = {}) { const [symbol, setSymbol] = useState('BTCUSD') const [selectedLayouts, setSelectedLayouts] = useState(['ai', 'Diy module']) // Default to both layouts const [selectedTimeframes, setSelectedTimeframes] = useState(['60']) // Support multiple timeframes const [loading, setLoading] = useState(false) const [result, setResult] = useState(null) const [error, setError] = useState(null) const [progress, setProgress] = useState(null) const [eventSource, setEventSource] = useState(null) const [modalOpen, setModalOpen] = useState(false) const [modalData, setModalData] = useState(null) const [enlargedScreenshot, setEnlargedScreenshot] = useState(null) // Ref to prevent concurrent analysis calls const analysisInProgress = useRef(false) const [tradeModalOpen, setTradeModalOpen] = useState(false) const [tradeModalData, setTradeModalData] = useState(null) // Helper function to safely render any value const safeRender = (value: any): string => { if (typeof value === 'string') return value if (typeof value === 'number') return value.toString() if (Array.isArray(value)) return value.join(', ') if (typeof value === 'object' && value !== null) { return JSON.stringify(value) } return String(value) } // Real-time progress tracking const startProgressTracking = (sessionId: string) => { console.log(`šŸ” Starting progress tracking for session: ${sessionId}`) // Close existing connection if (eventSource) { eventSource.close() } const es = new EventSource(`/api/progress/${sessionId}/stream`) es.onopen = () => { console.log(`šŸ” EventSource connection opened for ${sessionId}`) } es.onmessage = (event) => { try { const progressData = JSON.parse(event.data) console.log(`šŸ” Received progress update for ${sessionId}:`, progressData) if (progressData.type === 'complete') { console.log(`šŸ” Analysis complete for ${sessionId}`) es.close() setEventSource(null) // Reset UI state when analysis completes setLoading(false) setProgress(null) } else if (progressData.type === 'connected') { console.log(`šŸ” EventSource connected for ${sessionId}`) } else { // Update progress state immediately setProgress(progressData) // Check if analysis is complete based on steps if (progressData.steps && progressData.steps.length > 0) { const allCompleted = progressData.steps.every((step: any) => step.status === 'completed' || step.status === 'error' ) if (allCompleted) { console.log(`šŸ” All steps completed for ${sessionId}, resetting UI state`) setTimeout(() => { setLoading(false) setProgress(null) es.close() setEventSource(null) }, 2000) // Give 2 seconds to show final state } } } } catch (error) { console.error('Error parsing progress data:', error) } } es.onerror = (error) => { console.error('EventSource error:', error) es.close() setEventSource(null) } setEventSource(es) } // Cleanup event source on unmount React.useEffect(() => { return () => { if (eventSource) { eventSource.close() } } }, [eventSource]) // Normalize layout key to match backend expectations const normalizeLayoutKey = (layout: string): string => { // Keep the exact names as they appear in TradingView if (layout === 'ai') return 'ai' if (layout === 'Diy module') return 'Diy module' return layout } // Get display name for layout (keep exact TradingView names) const getLayoutDisplayName = (layout: string): string => { return layoutDisplayNames[layout] || layout } const toggleLayout = (layout: string) => { const normalizedLayout = normalizeLayoutKey(layout) setSelectedLayouts(prev => prev.includes(normalizedLayout) ? prev.filter(l => l !== normalizedLayout) : [...prev, normalizedLayout] ) } const toggleTimeframe = (timeframe: string) => { setSelectedTimeframes(prev => prev.includes(timeframe) ? prev.filter(tf => tf !== timeframe) : [...prev, timeframe] ) } // Helper function to create initial progress steps (no longer used - using real-time progress) // const createProgressSteps = ...removed for real-time implementation // Helper function to update progress (no longer used - using real-time progress) // const updateProgress = ...removed for real-time implementation const performAnalysis = async (analysisSymbol = symbol, analysisTimeframes = selectedTimeframes) => { // Prevent concurrent analysis calls if (loading || analysisInProgress.current || selectedLayouts.length === 0 || analysisTimeframes.length === 0) { console.log('āš ļø Analysis blocked:', { loading, analysisInProgress: analysisInProgress.current, selectedLayouts: selectedLayouts.length, analysisTimeframes: analysisTimeframes.length }) return } analysisInProgress.current = true setLoading(true) setError(null) setResult(null) // Set initial progress state to show animation immediately setProgress({ sessionId: 'initializing', currentStep: 1, totalSteps: 6, steps: [ { id: 'init', title: 'Initializing Analysis', description: 'Starting AI-powered trading analysis...', status: 'active', startTime: Date.now() }, { id: 'auth', title: 'TradingView Authentication', description: 'Logging into TradingView accounts', status: 'pending' }, { id: 'navigation', title: 'Chart Navigation', description: 'Navigating to chart layouts', status: 'pending' }, { id: 'loading', title: 'Chart Data Loading', description: 'Waiting for chart data and indicators', status: 'pending' }, { id: 'capture', title: 'Screenshot Capture', description: 'Capturing high-quality screenshots', status: 'pending' }, { id: 'analysis', title: 'AI Analysis', description: 'Analyzing screenshots with AI', status: 'pending' } ], timeframeProgress: analysisTimeframes.length > 1 ? { current: 0, total: analysisTimeframes.length } : undefined }) try { if (analysisTimeframes.length === 1) { // Single timeframe analysis with real-time progress // Pre-generate sessionId and start progress tracking BEFORE making the API call const sessionId = `analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` startProgressTracking(sessionId) const response = await fetch('/api/enhanced-screenshot', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: analysisSymbol, timeframe: analysisTimeframes[0], layouts: selectedLayouts, analyze: true, sessionId: sessionId // Pass pre-generated sessionId }) }) const data = await response.json() if (!response.ok) { throw new Error(data.error || 'Analysis failed') } setResult(data) // For single timeframe analysis, don't set loading to false yet // Let the progress tracking handle UI state reset console.log(`šŸ” API call completed for ${sessionId}, progress tracking will handle UI reset`) // Call the callback with analysis result if provided if (onAnalysisComplete && data.analysis) { onAnalysisComplete(data.analysis, analysisSymbol) } } else { // Multiple timeframe analysis - use batch comparative analysis console.log(`šŸŽÆ Starting batch comparative analysis for ${analysisTimeframes.length} timeframes`) // Pre-generate sessionId for batch analysis const sessionId = `batch_analysis_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` startProgressTracking(sessionId) const response = await fetch('/api/batch-analysis', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: analysisSymbol, timeframes: analysisTimeframes, layouts: selectedLayouts, sessionId: sessionId }) }) const data = await response.json() if (!response.ok) { throw new Error(data.error || 'Batch analysis failed') } console.log(`āœ… Batch analysis completed: ${data.totalScreenshots} screenshots, ${data.timeframes.length} timeframes`) // Transform batch result to match expected format for UI compatibility const multiResult = { type: 'batch_comparative', symbol: analysisSymbol, summary: data.summary || `Batch comparative analysis of ${data.timeframes?.length || analysisTimeframes.length} timeframes`, analysis: data.analysis, screenshots: data.screenshots, timeframeBreakdown: data.timeframeBreakdown, totalScreenshots: data.totalScreenshots, timeframes: analysisTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label || tf), // Create results array for UI compatibility results: analysisTimeframes.map(tf => ({ timeframe: tf, timeframeLabel: timeframes.find(t => t.value === tf)?.label || tf, success: true, result: { analysis: data.analysis, // Single comparative analysis for all timeframes screenshots: data.screenshots.filter((s: string) => s.includes(`_${tf}_`)), sessionId: sessionId } })) } setResult(multiResult) // Call the callback with analysis result if provided if (onAnalysisComplete && data.analysis) { onAnalysisComplete(data.analysis, analysisSymbol) } } } catch (err) { const errorMessage = err instanceof Error ? err.message : 'Failed to perform analysis' setError(errorMessage) // Reset loading state immediately on error setLoading(false) setProgress(null) // Mark current active step as error setProgress(prev => { if (!prev) return null const activeStepIndex = prev.steps.findIndex(step => step.status === 'active') if (activeStepIndex >= 0) { const updatedSteps = [...prev.steps] updatedSteps[activeStepIndex] = { ...updatedSteps[activeStepIndex], status: 'error', details: errorMessage, endTime: Date.now() } return { ...prev, steps: updatedSteps } } return prev }) } finally { // Always reset concurrency flag, but only reset loading for multi-timeframe analysis // Single timeframe analysis loading state is managed by progress tracking analysisInProgress.current = false if (analysisTimeframes.length > 1) { setLoading(false) } } } const quickAnalyze = async (coinSymbol: string, quickTimeframes = selectedTimeframes) => { setSymbol(coinSymbol) if (!loading && !analysisInProgress.current) { await performAnalysis(coinSymbol, quickTimeframes) } } const quickTimeframeTest = async (testTimeframe: string) => { // Toggle the timeframe in selection instead of replacing const newTimeframes = selectedTimeframes.includes(testTimeframe) ? selectedTimeframes.filter(tf => tf !== testTimeframe) : [...selectedTimeframes, testTimeframe] setSelectedTimeframes(newTimeframes) if (!loading && !analysisInProgress.current && symbol && newTimeframes.length > 0) { await performAnalysis(symbol, newTimeframes) } } const testAllTimeframes = async () => { if (loading || analysisInProgress.current) return const allTimeframeValues = timeframes.map(tf => tf.value) setSelectedTimeframes(allTimeframeValues) if (!loading && !analysisInProgress.current && symbol) { await performAnalysis(symbol, allTimeframeValues) } } async function handleAnalyze() { if (!analysisInProgress.current) { await performAnalysis() } } // Trade initiation handler const handleTradeClick = (tfResult: any) => { console.log('šŸ”„ AIAnalysisPanel handleTradeClick called with:', tfResult) const analysis = tfResult?.result?.analysis || tfResult?.analysis || {} console.log('šŸ”„ Extracted analysis:', analysis) // Enhanced data extraction with better fallbacks let entryPrice = '' let takeProfit1 = '' let takeProfit2 = '' let stopLoss = '' // Extract entry price with multiple fallback options if (analysis.entry?.price) { entryPrice = analysis.entry.price.toString() } else if (analysis.entry && typeof analysis.entry === 'number') { entryPrice = analysis.entry.toString() } else if (analysis.entry && typeof analysis.entry === 'string') { entryPrice = analysis.entry } // Extract take profit 1 with multiple fallback options if (analysis.takeProfits?.tp1?.price) { takeProfit1 = analysis.takeProfits.tp1.price.toString() } else if (analysis.takeProfits?.tp1 && typeof analysis.takeProfits.tp1 === 'number') { takeProfit1 = analysis.takeProfits.tp1.toString() } else if (analysis.takeProfits && typeof analysis.takeProfits === 'number') { takeProfit1 = analysis.takeProfits.toString() } else if (analysis.takeProfit?.price) { takeProfit1 = analysis.takeProfit.price.toString() } else if (analysis.takeProfit && typeof analysis.takeProfit === 'number') { takeProfit1 = analysis.takeProfit.toString() } // Extract take profit 2 if available if (analysis.takeProfits?.tp2?.price) { takeProfit2 = analysis.takeProfits.tp2.price.toString() } else if (analysis.takeProfits?.tp2 && typeof analysis.takeProfits.tp2 === 'number') { takeProfit2 = analysis.takeProfits.tp2.toString() } // Extract stop loss with multiple fallback options if (analysis.stopLoss?.price) { stopLoss = analysis.stopLoss.price.toString() } else if (analysis.stopLoss && typeof analysis.stopLoss === 'number') { stopLoss = analysis.stopLoss.toString() } else if (analysis.stopLoss && typeof analysis.stopLoss === 'string') { stopLoss = analysis.stopLoss } const tradeData = { entry: entryPrice, tp: takeProfit1, // This maps to tp1 in the modal tp2: takeProfit2, // This will be handled in the modal sl: stopLoss, symbol: symbol, timeframe: tfResult?.timeframeLabel || tfResult?.timeframe || '', } console.log('šŸ”„ Enhanced trade data extraction:', { originalAnalysis: analysis, extractedData: tradeData }) setTradeModalData(tradeData) console.log('šŸ”„ Opening trade modal...') setTradeModalOpen(true) } // Trade execution API call const executeTrade = async (tradeData: any) => { try { // Determine if this is a leveraged position or spot trade const leverage = parseFloat(tradeData.leverage) || 1 const isLeveraged = leverage > 1 // Route to appropriate API based on leverage const apiEndpoint = isLeveraged ? '/api/trading/execute-drift' : '/api/trading/execute-dex' const tradingMode = isLeveraged ? 'PERP' : 'SPOT' console.log(`šŸŽÆ Executing ${tradingMode} trade with ${leverage}x leverage via ${apiEndpoint}`) const response = await fetch(apiEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ symbol: tradeData.symbol || symbol, side: 'BUY', // Could be derived from analysis amount: parseFloat(tradeData.positionSize) || parseFloat(tradeData.size), amountUSD: parseFloat(tradeData.amountUSD || tradeData.positionSize || tradeData.size), leverage: leverage, stopLoss: parseFloat(tradeData.sl), takeProfit: parseFloat(tradeData.tp1), // Use TP1 as primary target useRealDEX: true, // Enable real trading for manual execution tradingMode: tradingMode, tradingPair: `${tradeData.symbol || symbol}/USDC`, quickSwap: false }) }) const result = await response.json() if (response.ok && result.success) { // Show detailed success message based on trading type const leverage = parseFloat(tradeData.leverage) || 1 const isLeveraged = leverage > 1 const tradeType = isLeveraged ? 'Leveraged Perpetual Position' : 'Spot Trade' const platform = isLeveraged ? 'Drift Protocol' : 'Jupiter DEX' let message = `āœ… ${tradeType} executed successfully!\n\n` message += `šŸ“Š Transaction ID: ${result.trade?.txId || result.txId}\n` message += `šŸ’° Symbol: ${tradeData.symbol || symbol}\n` message += `šŸ“ˆ Size: ${tradeData.positionSize || tradeData.size} USDC\n` if (isLeveraged) { message += `⚔ Leverage: ${leverage}x (via increased position size)\n` message += `ļæ½ Actual Trade Size: $${(parseFloat(tradeData.positionSize || tradeData.size) * leverage).toFixed(2)}\n` } message += `ļæ½šŸŖ Platform: ${platform}\n` if (tradeData.sl) message += `šŸ›‘ Stop Loss: $${tradeData.sl} (Jupiter Trigger Order)\n` if (tradeData.tp1) message += `šŸŽÆ Take Profit: $${tradeData.tp1} (Jupiter Trigger Order)\n` if (result.triggerOrders?.status === 'CREATED') { message += `\nšŸ”„ Trigger Orders: ACTIVE\n` if (result.triggerOrders.stopLossOrderId) message += `šŸ›‘ SL Order: ${result.triggerOrders.stopLossOrderId.substring(0, 8)}...\n` if (result.triggerOrders.takeProfitOrderId) message += `šŸŽÆ TP Order: ${result.triggerOrders.takeProfitOrderId.substring(0, 8)}...\n` } else if (result.trade?.monitoring || result.position) { message += `\nšŸ”„ Position monitoring: ACTIVE` } alert(message) } else { // Show detailed error message const errorMsg = result.error || 'Unknown error occurred' if (errorMsg.includes('not configured') || errorMsg.includes('Wallet not initialized')) { alert(`āŒ Trade Failed: Jupiter DEX Not Configured\n\nPlease configure your Jupiter DEX wallet in the settings before executing real trades.\n\nError: ${errorMsg}`) } else if (errorMsg.includes('insufficient') || errorMsg.includes('balance')) { alert(`āŒ Trade Failed: Insufficient Balance\n\nPlease ensure you have enough tokens in your wallet.\n\nError: ${errorMsg}`) } else if (errorMsg.includes('Real Jupiter Perpetuals trading not yet implemented')) { alert(`āŒ Real Trading Not Available\n\nReal Jupiter Perpetuals trading is still in development. This trade will be simulated instead.\n\nTo use real spot trading, reduce the leverage to 1x.`) } else if (errorMsg.includes('Trigger API error') || errorMsg.includes('trigger orders failed')) { alert(`āš ļø Trade Executed, But Trigger Orders Failed\n\nYour main trade was successful, but stop loss/take profit orders could not be created.\n\nError: ${errorMsg}\n\nPlease monitor your position manually.`) } else { alert(`āŒ Trade Failed\n\nError: ${errorMsg}`) } } } catch (error) { console.error('Trade execution failed:', error) alert('āŒ Trade execution failed due to network error.\n\nPlease check your connection and try again.') } setTradeModalOpen(false) } // Screenshot gallery modal const handleScreenshotClick = (src: string) => { setEnlargedScreenshot(src) } return (
{/* Analysis Controls Area */}
{/* Quick Coin & Timeframe Analysis */}

Quick Analysis

Select coin + timeframe combo for instant analysis
{/* Quick Timeframe Presets */}
{/* Timeframe Selection */}
{timeframes.map(tf => ( ))}
{selectedTimeframes.length > 0 && (
Selected timeframes: {selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ')}
šŸ’” Multiple timeframes provide broader market outlook
)}
{/* Coin Selection */}
{popularCoins.map(coin => ( ))}
{/* Advanced Analysis Section */}

Advanced Analysis

{/* Symbol Input */}
setSymbol(e.target.value.toUpperCase())} placeholder="e.g., BTCUSD, ETHUSD" />
{/* Layout Selection */}
{layouts.map(layout => { const normalizedLayout = normalizeLayoutKey(layout) const displayName = getLayoutDisplayName(layout) const isSelected = selectedLayouts.includes(normalizedLayout) return ( ) })}
{selectedLayouts.length > 0 && (
Selected layouts: {selectedLayouts.map(layout => getLayoutDisplayName(layout)).join(', ')}
)}
{/* Analyze Button */}
{/* Results Section */} {error && (
āš ļø

Analysis Error

{error}

)} {loading && progress && (
{/* Header */}

AI Analysis in Progress

Analyzing {symbol} • {selectedLayouts.map(layout => getLayoutDisplayName(layout)).join(', ')} layouts

{/* Overall Progress */}
Step {progress.currentStep} of {progress.totalSteps}
{Math.round((progress.currentStep / progress.totalSteps) * 100)}% Complete
{/* Multi-timeframe progress */} {progress.timeframeProgress && (
Multi-Timeframe Analysis
{progress.timeframeProgress.current}/{progress.timeframeProgress.total} timeframes
{progress.timeframeProgress.currentTimeframe && (

Current: {progress.timeframeProgress.currentTimeframe}

)}
)} {/* Progress Steps */}
{progress.steps.map((step, index) => { const isActive = step.status === 'active' const isCompleted = step.status === 'completed' const isError = step.status === 'error' const isPending = step.status === 'pending' return (
{/* Step Icon */}
{isCompleted ? 'āœ“' : isError ? 'āœ—' : isActive ? '⟳' : index + 1}
{/* Step Content */}
{step.title}
{/* Timing */} {(step.startTime || step.endTime) && ( {step.endTime && step.startTime ? `${((step.endTime - step.startTime) / 1000).toFixed(1)}s` : isActive && step.startTime ? `${((Date.now() - step.startTime) / 1000).toFixed(0)}s` : '' } )}

{step.details || step.description}

{/* Active indicator */} {isActive && (
)}
) })}
{/* Overall Progress Bar */}
)} {result && (result.type === 'multi_timeframe' || result.type === 'batch_comparative') && (

šŸ“Š Multi-Timeframe Analysis

{result.symbol} • {result.results.length} timeframes

Analysis Summary

{result.summary}

{result.results .sort((a: any, b: any) => { // Sort by timeframe order: 5m, 15m, 30m, 1h, 2h, 4h, 1D const timeframeOrder: {[key: string]: number} = { '5': 1, '5m': 1, '15': 2, '15m': 2, '30': 3, '30m': 3, '60': 4, '1h': 4, '120': 5, '2h': 5, '240': 6, '4h': 6, 'D': 7, '1D': 7 } const orderA = timeframeOrder[a.timeframe] || timeframeOrder[a.timeframeLabel] || 999 const orderB = timeframeOrder[b.timeframe] || timeframeOrder[b.timeframeLabel] || 999 return orderA - orderB }) .map((timeframeResult: any, index: number) => (
{timeframeResult.success ? 'āœ…' : 'āŒ'} {timeframeResult.timeframeLabel} Timeframe
{timeframeResult.success && timeframeResult.result.analysis && ( )} {timeframeResult.success ? 'Analysis Complete' : 'Failed'}
{timeframeResult.success && timeframeResult.result.analysis && (
Sentiment
{safeRender(timeframeResult.result.analysis.marketSentiment)}
Recommendation
{safeRender(timeframeResult.result.analysis.recommendation)}
Confidence
{safeRender(timeframeResult.result.analysis.confidence)}%
)} {timeframeResult.success && timeframeResult.result.analysis?.entry && (
Entry Setup
šŸ“ ${safeRender(timeframeResult.result.analysis.entry.price)} {timeframeResult.result.analysis.entry.buffer && ( {safeRender(timeframeResult.result.analysis.entry.buffer)} )}
{timeframeResult.result.analysis.stopLoss && (
šŸ›‘ SL: ${safeRender(timeframeResult.result.analysis.stopLoss.price)}
)} {timeframeResult.result.analysis.takeProfits && (
{timeframeResult.result.analysis.takeProfits.tp1 && (
šŸŽÆ TP1: ${typeof timeframeResult.result.analysis.takeProfits.tp1.price !== 'undefined' ? safeRender(timeframeResult.result.analysis.takeProfits.tp1.price) : safeRender(timeframeResult.result.analysis.takeProfits.tp1)}
)} {timeframeResult.result.analysis.takeProfits.tp2 && (
šŸŽÆ TP2: ${typeof timeframeResult.result.analysis.takeProfits.tp2.price !== 'undefined' ? safeRender(timeframeResult.result.analysis.takeProfits.tp2.price) : safeRender(timeframeResult.result.analysis.takeProfits.tp2)}
)} {/* Fallback for simple take profit format */} {!timeframeResult.result.analysis.takeProfits.tp1 && !timeframeResult.result.analysis.takeProfits.tp2 && (
šŸŽÆ TP: {typeof timeframeResult.result.analysis.takeProfits === 'object' ? Object.values(timeframeResult.result.analysis.takeProfits).map((tp: any) => `$${safeRender(tp)}`).join(', ') : `$${safeRender(timeframeResult.result.analysis.takeProfits)}`}
)}
)}
)} {!timeframeResult.success && (
Analysis failed for this timeframe
)}
))}
)} {result && result.analysis && (

āœ… Analysis Complete

{result.analysis && ( )} {result.screenshots && (
Screenshots: {result.screenshots.length} captured
)}
{/* Summary */}

Market Summary

{safeRender(result.analysis.summary)}

{/* Key Metrics */}

Market Sentiment

{safeRender(result.analysis.marketSentiment)}

Recommendation

{safeRender(result.analysis.recommendation)}

{result.analysis.confidence && (

{safeRender(result.analysis.confidence)}% confidence

)}
{/* Trading Levels */} {result.analysis.keyLevels && (

Resistance Levels

{result.analysis.keyLevels.resistance?.join(', ') || 'None identified'}

Support Levels

{result.analysis.keyLevels.support?.join(', ') || 'None identified'}

)} {/* Trading Setup */} {(result.analysis.entry || result.analysis.stopLoss || result.analysis.takeProfits) && (

Trading Setup

{result.analysis.entry && (
Entry Point

šŸ“ ${safeRender(result.analysis.entry.price || result.analysis.entry)} {result.analysis.entry.buffer && ( {safeRender(result.analysis.entry.buffer)} )}

{result.analysis.entry.rationale && (

šŸ’” {safeRender(result.analysis.entry.rationale)}

)}
)} {result.analysis.stopLoss && (
Stop Loss

šŸ›‘ ${safeRender(result.analysis.stopLoss.price || result.analysis.stopLoss)}

{result.analysis.stopLoss.rationale && (

šŸ’” {safeRender(result.analysis.stopLoss.rationale)}

)}
)} {result.analysis.takeProfits && (
Take Profit Targets
{result.analysis.takeProfits.tp1 && (
šŸ„‰ TP1: ${typeof result.analysis.takeProfits.tp1.price !== 'undefined' ? result.analysis.takeProfits.tp1.price : safeRender(result.analysis.takeProfits.tp1)}
{result.analysis.takeProfits.tp1.description && (

šŸ“‹ {safeRender(result.analysis.takeProfits.tp1.description)}

)} {result.analysis.takeProfits.tp1.rsiExpectation && (

šŸ“Š RSI: {safeRender(result.analysis.takeProfits.tp1.rsiExpectation)}

)} {result.analysis.takeProfits.tp1.obvExpectation && (

šŸ“ˆ OBV: {safeRender(result.analysis.takeProfits.tp1.obvExpectation)}

)}
)} {result.analysis.takeProfits.tp2 && (
🄈 TP2: ${typeof result.analysis.takeProfits.tp2.price !== 'undefined' ? result.analysis.takeProfits.tp2.price : safeRender(result.analysis.takeProfits.tp2)}
{result.analysis.takeProfits.tp2.description && (

šŸ“‹ {safeRender(result.analysis.takeProfits.tp2.description)}

)} {result.analysis.takeProfits.tp2.rsiExpectation && (

šŸ“Š RSI: {safeRender(result.analysis.takeProfits.tp2.rsiExpectation)}

)} {result.analysis.takeProfits.tp2.obvExpectation && (

šŸ“ˆ OBV: {safeRender(result.analysis.takeProfits.tp2.obvExpectation)}

)}
)} {/* Fallback for simple take profit format */} {!result.analysis.takeProfits.tp1 && !result.analysis.takeProfits.tp2 && (

{typeof result.analysis.takeProfits === 'object' ? Object.values(result.analysis.takeProfits).map(tp => `$${safeRender(tp)}`).join(', ') : `$${safeRender(result.analysis.takeProfits)}`}

)}
)}
)} {/* Risk Management & Confirmation */} {(result.analysis.riskToReward || result.analysis.confirmationTrigger) && (

Risk Management

{result.analysis.riskToReward && (
Risk/Reward Ratio

āš–ļø {safeRender(result.analysis.riskToReward)}

)} {result.analysis.confirmationTrigger && (
Confirmation Trigger

šŸ”” {safeRender(result.analysis.confirmationTrigger)}

)}
)} {/* Timeframe Risk Assessment */} {result.analysis.timeframeRisk && (

ā° Timeframe Risk Assessment

{result.analysis.timeframeRisk.assessment && (
Risk Level

šŸ“Š {safeRender(result.analysis.timeframeRisk.assessment)}

)} {result.analysis.timeframeRisk.positionSize && (
Position Size

šŸ’¼ {safeRender(result.analysis.timeframeRisk.positionSize)}

)} {result.analysis.timeframeRisk.leverageRecommendation && (
Leverage

šŸŽšļø {safeRender(result.analysis.timeframeRisk.leverageRecommendation)}

)}
)} {/* Technical Indicators */} {result.analysis.indicatorAnalysis && (

šŸ“ˆ Technical Indicators

{result.analysis.indicatorAnalysis.rsi && (
šŸ“Š RSI

{safeRender(result.analysis.indicatorAnalysis.rsi)}

)} {result.analysis.indicatorAnalysis.vwap && (
šŸ“ˆ VWAP

{safeRender(result.analysis.indicatorAnalysis.vwap)}

)} {result.analysis.indicatorAnalysis.obv && (
šŸ“Š OBV

{safeRender(result.analysis.indicatorAnalysis.obv)}

)} {result.analysis.indicatorAnalysis.macd && (
šŸ“‰ MACD

{safeRender(result.analysis.indicatorAnalysis.macd)}

)}
)} {/* Alternatives */} {result.analysis.alternatives && (

šŸ”„ Alternative Strategies

{result.analysis.alternatives.tigherStopOption && (
šŸŽÆ Tighter Stop Option

{safeRender(result.analysis.alternatives.tigherStopOption)}

)} {result.analysis.alternatives.scaledEntry && (
šŸ“Š Scaled Entry

{safeRender(result.analysis.alternatives.scaledEntry)}

)} {result.analysis.alternatives.invalidationScenario && (
āŒ Invalidation Scenario

{safeRender(result.analysis.alternatives.invalidationScenario)}

)}
)} {/* Layout Comparison Section */} {result.analysis.layoutComparison && (

Multi-Layout Analysis

{result.analysis.layoutComparison.aiLayout && (
AI Layout Insights

{result.analysis.layoutComparison.aiLayout}

)} {result.analysis.layoutComparison.diyLayout && (
DIY Layout Insights

{result.analysis.layoutComparison.diyLayout}

)}
{result.analysis.layoutComparison.consensus && (
Layout Consensus

{result.analysis.layoutComparison.consensus}

)} {result.analysis.layoutComparison.divergences && (
Layout Divergences

{result.analysis.layoutComparison.divergences}

)}
)} {/* Enhanced Indicator Analysis */} {result.analysis.indicatorAnalysis?.crossLayoutConsensus && (

Cross-Layout Consensus

{result.analysis.indicatorAnalysis.crossLayoutConsensus}

)}
)} {result && !result.analysis && result.screenshots && (

Screenshots Captured

Screenshots were captured successfully, but AI analysis failed or was not requested.

Screenshots: {result.screenshots.length} captured
{result.screenshots.map((screenshot: string, index: number) => (
{screenshot.split('/').pop()}
))}
)} {/* Screenshot Gallery */} {result && result.screenshots && ( timeframes.find(t => t.value === tf)?.label || tf)} enlargedImage={enlargedScreenshot} onImageClick={handleScreenshotClick} onClose={() => setEnlargedScreenshot(null)} /> )} {/* Multi-timeframe Screenshot Gallery */} {result && (result.type === 'multi_timeframe' || result.type === 'batch_comparative') && result.results && ( r.success && r.result.screenshots) .sort((a: any, b: any) => { // Sort by timeframe order: 5m, 15m, 30m, 1h, 2h, 4h, 1D const timeframeOrder: {[key: string]: number} = { '5': 1, '5m': 1, '15': 2, '15m': 2, '30': 3, '30m': 3, '60': 4, '1h': 4, '120': 5, '2h': 5, '240': 6, '4h': 6, 'D': 7, '1D': 7 } const orderA = timeframeOrder[a.timeframe] || timeframeOrder[a.timeframeLabel] || 999 const orderB = timeframeOrder[b.timeframe] || timeframeOrder[b.timeframeLabel] || 999 return orderA - orderB }) .flatMap((r: any) => r.result.screenshots)} symbol={symbol} timeframes={result.results .filter((r: any) => r.success) .sort((a: any, b: any) => { // Sort by timeframe order: 5m, 15m, 30m, 1h, 2h, 4h, 1D const timeframeOrder: {[key: string]: number} = { '5': 1, '5m': 1, '15': 2, '15m': 2, '30': 3, '30m': 3, '60': 4, '1h': 4, '120': 5, '2h': 5, '240': 6, '4h': 6, 'D': 7, '1D': 7 } const orderA = timeframeOrder[a.timeframe] || timeframeOrder[a.timeframeLabel] || 999 const orderB = timeframeOrder[b.timeframe] || timeframeOrder[b.timeframeLabel] || 999 return orderA - orderB }) .map((r: any) => r.timeframeLabel)} enlargedImage={enlargedScreenshot} onImageClick={handleScreenshotClick} onClose={() => setEnlargedScreenshot(null)} /> )} {/* Trade Modal */} {console.log('šŸ”„ About to render TradeModal with:', { isOpen: tradeModalOpen, tradeData: tradeModalData })} { console.log('šŸ”„ TradeModal onClose called') setTradeModalOpen(false) }} tradeData={tradeModalData} onExecute={executeTrade} />
) }