Files
trading_bot_v3/components/AIAnalysisPanel.tsx
mindesbunister aa8ca9846b feat: Add trade execution and screenshot gallery to AI analysis
- Create TradeModal component for executing trades with entry, TP, SL
- Add ScreenshotGallery component with click-to-enlarge functionality
- Integrate trade buttons in both single and multi-timeframe analysis results
- Add screenshot gallery that displays captured TradingView charts
- Parse analysis data to pre-fill trade modal with AI recommendations
- Support trade execution via /api/trading endpoint
- Add visual indicators and smooth transitions for better UX

Trade button features:
- Pre-filled entry, take profit, and stop loss from AI analysis
- Configurable position size and leverage
- Real-time validation and error handling

Screenshot gallery features:
- Grid layout with hover effects
- Click to enlarge in full-screen modal
- Support for both single and multi-timeframe results
- Chart information overlay with timeframe labels
2025-07-14 00:34:13 +02:00

1383 lines
62 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import React, { useState } from 'react'
import Modal from './Modal'
import TradeModal from './TradeModal'
import ScreenshotGallery from './ScreenshotGallery'
const layouts = (process.env.NEXT_PUBLIC_TRADINGVIEW_LAYOUTS || 'ai,Diy module').split(',').map(l => l.trim())
const timeframes = [
{ label: '1m', value: '1' },
{ label: '5m', value: '5' },
{ label: '15m', value: '15' },
{ label: '1h', value: '60' },
{ label: '4h', value: '240' },
{ label: '1d', value: 'D' },
{ label: '1w', value: 'W' },
{ label: '1M', value: 'M' },
]
const popularCoins = [
{ name: 'Bitcoin', symbol: 'BTCUSD', icon: '₿', color: 'from-orange-400 to-orange-600' },
{ name: 'Ethereum', symbol: 'ETHUSD', icon: 'Ξ', color: 'from-blue-400 to-blue-600' },
{ name: 'Solana', symbol: 'SOLUSD', icon: '◎', color: 'from-purple-400 to-purple-600' },
{ name: 'Sui', symbol: 'SUIUSD', icon: '🔷', color: 'from-cyan-400 to-cyan-600' },
{ name: 'Avalanche', symbol: 'AVAXUSD', icon: '🔺', color: 'from-red-400 to-red-600' },
{ name: 'Cardano', symbol: 'ADAUSD', icon: '♠', color: 'from-indigo-400 to-indigo-600' },
{ name: 'Polygon', symbol: 'MATICUSD', icon: '🔷', color: 'from-violet-400 to-violet-600' },
{ name: 'Chainlink', symbol: 'LINKUSD', icon: '🔗', color: 'from-blue-400 to-blue-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 {
currentStep: number
totalSteps: number
steps: ProgressStep[]
timeframeProgress?: {
current: number
total: number
currentTimeframe?: string
}
}
export default function AIAnalysisPanel() {
const [symbol, setSymbol] = useState('BTCUSD')
const [selectedLayouts, setSelectedLayouts] = useState<string[]>(['ai', 'diy']) // Default to both AI and DIY
const [selectedTimeframes, setSelectedTimeframes] = useState<string[]>(['60']) // Support multiple timeframes
const [loading, setLoading] = useState(false)
const [result, setResult] = useState<any>(null)
const [error, setError] = useState<string | null>(null)
const [progress, setProgress] = useState<AnalysisProgress | null>(null)
const [modalOpen, setModalOpen] = useState(false)
const [modalData, setModalData] = useState<any>(null)
const [enlargedScreenshot, setEnlargedScreenshot] = useState<string | null>(null)
const [tradeModalOpen, setTradeModalOpen] = useState(false)
const [tradeModalData, setTradeModalData] = useState<any>(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)
}
const toggleLayout = (layout: string) => {
setSelectedLayouts(prev =>
prev.includes(layout)
? prev.filter(l => l !== layout)
: [...prev, layout]
)
}
const toggleTimeframe = (timeframe: string) => {
setSelectedTimeframes(prev =>
prev.includes(timeframe)
? prev.filter(tf => tf !== timeframe)
: [...prev, timeframe]
)
}
// Helper function to create initial progress steps
const createProgressSteps = (timeframes: string[], layouts: string[]): ProgressStep[] => {
const steps: ProgressStep[] = []
if (timeframes.length > 1) {
steps.push({
id: 'init',
title: 'Initializing Multi-Timeframe Analysis',
description: `Preparing to analyze ${timeframes.length} timeframes`,
status: 'pending'
})
} else {
steps.push({
id: 'init',
title: 'Initializing Analysis',
description: `Setting up screenshot service for ${layouts.join(', ')} layout(s)`,
status: 'pending'
})
}
steps.push({
id: 'browser',
title: 'Starting Browser Sessions',
description: `Launching ${layouts.length} browser session(s)`,
status: 'pending'
})
steps.push({
id: 'auth',
title: 'TradingView Authentication',
description: 'Logging into TradingView accounts',
status: 'pending'
})
steps.push({
id: 'navigation',
title: 'Chart Navigation',
description: 'Navigating to chart layouts and timeframes',
status: 'pending'
})
steps.push({
id: 'loading',
title: 'Chart Data Loading',
description: 'Waiting for chart data and indicators to load',
status: 'pending'
})
steps.push({
id: 'capture',
title: 'Screenshot Capture',
description: 'Capturing high-quality chart screenshots',
status: 'pending'
})
steps.push({
id: 'analysis',
title: 'AI Analysis',
description: 'Analyzing screenshots with AI for trading insights',
status: 'pending'
})
return steps
}
// Helper function to update progress
const updateProgress = (stepId: string, status: ProgressStep['status'], details?: string) => {
setProgress(prev => {
if (!prev) return null
const updatedSteps = prev.steps.map(step => {
if (step.id === stepId) {
const updatedStep = {
...step,
status,
details: details || step.details
}
if (status === 'active' && !step.startTime) {
updatedStep.startTime = Date.now()
} else if ((status === 'completed' || status === 'error') && !step.endTime) {
updatedStep.endTime = Date.now()
}
return updatedStep
}
return step
})
const currentStepIndex = updatedSteps.findIndex(step => step.status === 'active')
return {
...prev,
steps: updatedSteps,
currentStep: currentStepIndex >= 0 ? currentStepIndex + 1 : prev.currentStep
}
})
}
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframes = selectedTimeframes) => {
if (loading || selectedLayouts.length === 0 || analysisTimeframes.length === 0) return
setLoading(true)
setError(null)
setResult(null)
// Initialize progress tracking
const steps = createProgressSteps(analysisTimeframes, selectedLayouts)
setProgress({
currentStep: 0,
totalSteps: steps.length,
steps,
timeframeProgress: analysisTimeframes.length > 1 ? {
current: 0,
total: analysisTimeframes.length
} : undefined
})
try {
updateProgress('init', 'active')
if (analysisTimeframes.length === 1) {
// Single timeframe analysis
await new Promise(resolve => setTimeout(resolve, 500)) // Brief pause for UI
updateProgress('init', 'completed')
updateProgress('browser', 'active', 'Starting browser session...')
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
})
})
// Since we can't track internal API progress in real-time, we'll simulate logical progression
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('browser', 'completed')
updateProgress('auth', 'active', 'Authenticating with TradingView...')
await new Promise(resolve => setTimeout(resolve, 2000))
updateProgress('auth', 'completed')
updateProgress('navigation', 'active', `Navigating to ${analysisSymbol} chart...`)
await new Promise(resolve => setTimeout(resolve, 2000))
updateProgress('navigation', 'completed')
updateProgress('loading', 'active', 'Loading chart data and indicators...')
await new Promise(resolve => setTimeout(resolve, 3000))
updateProgress('loading', 'completed')
updateProgress('capture', 'active', 'Capturing screenshots...')
const data = await response.json()
if (!response.ok) {
updateProgress('capture', 'error', data.error || 'Screenshot capture failed')
throw new Error(data.error || 'Analysis failed')
}
updateProgress('capture', 'completed', `Captured ${data.screenshots?.length || 0} screenshot(s)`)
updateProgress('analysis', 'active', 'Running AI analysis...')
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('analysis', 'completed', 'Analysis complete!')
setResult(data)
} else {
// Multiple timeframe analysis
await new Promise(resolve => setTimeout(resolve, 500))
updateProgress('init', 'completed', `Starting analysis for ${analysisTimeframes.length} timeframes`)
const results = []
for (let i = 0; i < analysisTimeframes.length; i++) {
const tf = analysisTimeframes[i]
const timeframeLabel = timeframes.find(t => t.value === tf)?.label || tf
// Update timeframe progress
setProgress(prev => prev ? {
...prev,
timeframeProgress: {
...prev.timeframeProgress!,
current: i + 1,
currentTimeframe: timeframeLabel
}
} : null)
console.log(`🧪 Analyzing timeframe: ${timeframeLabel}`)
if (i === 0) {
updateProgress('browser', 'active', `Processing ${timeframeLabel} - Starting browser...`)
}
const response = await fetch('/api/enhanced-screenshot', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol: analysisSymbol,
timeframe: tf,
layouts: selectedLayouts,
analyze: true
})
})
if (i === 0) {
updateProgress('browser', 'completed')
updateProgress('auth', 'active', `Processing ${timeframeLabel} - Authenticating...`)
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('auth', 'completed')
}
updateProgress('navigation', 'active', `Processing ${timeframeLabel} - Navigating to chart...`)
await new Promise(resolve => setTimeout(resolve, 1000))
updateProgress('loading', 'active', `Processing ${timeframeLabel} - Loading chart data...`)
await new Promise(resolve => setTimeout(resolve, 1500))
updateProgress('capture', 'active', `Processing ${timeframeLabel} - Capturing screenshots...`)
const result = await response.json()
results.push({
timeframe: tf,
timeframeLabel,
success: response.ok,
result
})
updateProgress('analysis', 'active', `Processing ${timeframeLabel} - Running AI analysis...`)
// Small delay between requests
await new Promise(resolve => setTimeout(resolve, 2000))
}
updateProgress('navigation', 'completed')
updateProgress('loading', 'completed')
updateProgress('capture', 'completed', `Captured screenshots for all ${analysisTimeframes.length} timeframes`)
updateProgress('analysis', 'completed', `Completed analysis for all timeframes!`)
setResult({
type: 'multi_timeframe',
symbol: analysisSymbol,
summary: `Analyzed ${results.length} timeframes for ${analysisSymbol}`,
results
})
}
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to perform analysis'
setError(errorMessage)
// 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 {
setLoading(false)
}
}
const quickAnalyze = async (coinSymbol: string, quickTimeframes = selectedTimeframes) => {
setSymbol(coinSymbol)
if (!loading) {
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 && symbol && newTimeframes.length > 0) {
await performAnalysis(symbol, newTimeframes)
}
}
const testAllTimeframes = async () => {
if (loading) return
const allTimeframeValues = timeframes.map(tf => tf.value)
setSelectedTimeframes(allTimeframeValues)
if (!loading && symbol) {
await performAnalysis(symbol, allTimeframeValues)
}
}
async function handleAnalyze() {
await performAnalysis()
}
// Trade initiation handler
const handleTradeClick = (tfResult: any) => {
const analysis = tfResult?.result?.analysis || tfResult?.analysis || {}
setTradeModalData({
entry: analysis.entry?.price || analysis.entry || '',
tp: analysis.takeProfits?.tp1?.price || analysis.takeProfits?.tp1 || analysis.takeProfits || '',
sl: analysis.stopLoss?.price || analysis.stopLoss || '',
symbol: symbol,
timeframe: tfResult?.timeframeLabel || tfResult?.timeframe || '',
})
setTradeModalOpen(true)
}
// Trade execution API call
const executeTrade = async (tradeData: any) => {
try {
const response = await fetch('/api/trading', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
symbol: tradeData.symbol,
side: 'buy', // Could be derived from analysis
size: parseFloat(tradeData.size),
price: parseFloat(tradeData.entry),
stopLoss: parseFloat(tradeData.sl),
takeProfit: parseFloat(tradeData.tp),
leverage: parseInt(tradeData.leverage),
timeframe: tradeData.timeframe
})
})
const result = await response.json()
if (response.ok) {
// Show success message
alert(`Trade executed successfully! Order ID: ${result.orderId || 'N/A'}`)
} else {
alert(`Trade failed: ${result.error || 'Unknown error'}`)
}
} catch (error) {
console.error('Trade execution failed:', error)
alert('Trade execution failed. Please check your connection.')
}
setTradeModalOpen(false)
}
// Screenshot gallery modal
const handleScreenshotClick = (src: string) => {
setEnlargedScreenshot(src)
}
return (
<div className="card card-gradient">
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold text-white flex items-center">
<span className="w-8 h-8 bg-gradient-to-br from-cyan-400 to-blue-600 rounded-lg flex items-center justify-center mr-3">
🤖
</span>
AI Chart Analysis
</h2>
<div className="flex items-center space-x-2 text-sm text-gray-400">
<div className="w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></div>
<span>AI Powered</span>
</div>
</div>
{/* Quick Coin & Timeframe Analysis */}
<div className="mb-8">
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-semibold text-gray-300 flex items-center">
<span className="w-4 h-4 bg-yellow-500 rounded-full mr-2"></span>
Quick Analysis
</h3>
<span className="text-xs text-gray-500">Select coin + timeframe combo for instant analysis</span>
</div>
{/* Quick Timeframe Presets */}
<div className="mb-4 p-3 bg-gray-800/30 rounded-lg">
<label className="block text-xs font-medium text-gray-400 mb-2">Quick Timeframe Presets</label>
<div className="grid grid-cols-2 md:grid-cols-4 gap-2">
<button
onClick={() => setSelectedTimeframes(['5', '15', '60'])}
className="py-2 px-3 rounded-lg text-xs font-medium bg-purple-600/20 text-purple-300 hover:bg-purple-600/30 transition-all"
>
🕒 Scalping (5m, 15m, 1h)
</button>
<button
onClick={() => setSelectedTimeframes(['60', '240', 'D'])}
className="py-2 px-3 rounded-lg text-xs font-medium bg-blue-600/20 text-blue-300 hover:bg-blue-600/30 transition-all"
>
📊 Day Trading (1h, 4h, 1d)
</button>
<button
onClick={() => setSelectedTimeframes(['240', 'D', 'W'])}
className="py-2 px-3 rounded-lg text-xs font-medium bg-green-600/20 text-green-300 hover:bg-green-600/30 transition-all"
>
📈 Swing (4h, 1d, 1w)
</button>
<button
onClick={() => setSelectedTimeframes(['D', 'W', 'M'])}
className="py-2 px-3 rounded-lg text-xs font-medium bg-orange-600/20 text-orange-300 hover:bg-orange-600/30 transition-all"
>
🎯 Position (1d, 1w, 1m)
</button>
</div>
</div>
{/* Coin Selection */}
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
{popularCoins.map(coin => (
<button
key={coin.symbol}
onClick={() => quickAnalyze(coin.symbol)}
disabled={loading || selectedLayouts.length === 0}
className={`group relative p-3 rounded-lg border transition-all ${
loading || selectedLayouts.length === 0
? 'border-gray-700 bg-gray-800/30 cursor-not-allowed opacity-50'
: symbol === coin.symbol
? 'border-cyan-500 bg-cyan-500/10 text-cyan-300'
: 'border-gray-700 bg-gray-800/30 text-gray-300 hover:border-gray-600 hover:bg-gray-800/50 hover:text-white transform hover:scale-105'
}`}
>
<div className={`w-8 h-8 bg-gradient-to-br ${coin.color} rounded-lg flex items-center justify-center mx-auto mb-2`}>
<span className="text-white font-bold text-sm">{coin.icon}</span>
</div>
<div className="text-xs font-medium">{coin.name}</div>
<div className="text-xs text-gray-500">{coin.symbol}</div>
{symbol === coin.symbol && (
<div className="absolute top-1 right-1 w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></div>
)}
</button>
))}
</div>
</div>
{/* Advanced Analysis Section */}
<div className="mb-8">
<h3 className="text-sm font-semibold text-gray-300 flex items-center mb-4">
<span className="w-4 h-4 bg-purple-500 rounded-full mr-2"></span>
Advanced Analysis
</h3>
{/* Symbol Input */}
<div className="grid grid-cols-1 gap-4 mb-6">
<div>
<label className="block text-xs font-medium text-gray-400 mb-2">Trading Pair</label>
<input
className="w-full px-4 py-3 bg-gray-800/50 border border-gray-700 rounded-lg text-white placeholder-gray-500 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all"
value={symbol}
onChange={e => setSymbol(e.target.value.toUpperCase())}
placeholder="e.g., BTCUSD, ETHUSD"
/>
</div>
</div>
{/* Timeframe Selection */}
<div className="mb-6">
<label className="block text-xs font-medium text-gray-400 mb-3">
Analysis Timeframes
<span className="text-xs text-cyan-400 ml-2">({selectedTimeframes.length} selected)</span>
</label>
<div className="grid grid-cols-4 gap-2">
{timeframes.map(tf => (
<label key={tf.value} className="group relative cursor-pointer">
<input
type="checkbox"
checked={selectedTimeframes.includes(tf.value)}
onChange={() => toggleTimeframe(tf.value)}
className="sr-only"
/>
<div className={`flex items-center justify-center p-3 rounded-lg border transition-all ${
selectedTimeframes.includes(tf.value)
? 'border-cyan-500 bg-cyan-500/10 text-cyan-300 shadow-lg shadow-cyan-500/20'
: 'border-gray-700 bg-gray-800/30 text-gray-400 hover:border-gray-600 hover:bg-gray-800/50 hover:text-gray-300'
}`}>
<span className="text-sm font-medium">{tf.label}</span>
{selectedTimeframes.includes(tf.value) && (
<div className="absolute top-1 right-1 w-2 h-2 bg-cyan-400 rounded-full animate-pulse"></div>
)}
</div>
</label>
))}
</div>
{selectedTimeframes.length > 0 && (
<div className="mt-3 p-3 bg-gray-800/30 rounded-lg">
<div className="text-xs text-gray-400">
Selected timeframes: <span className="text-cyan-400">
{selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ')}
</span>
</div>
<div className="text-xs text-gray-500 mt-1">
💡 Multiple timeframes provide broader market outlook
</div>
</div>
)}
</div>
{/* Layout Selection */}
<div className="mb-6">
<label className="block text-xs font-medium text-gray-400 mb-3">Analysis Layouts</label>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{layouts.map(layout => (
<label key={layout} className="group relative">
<input
type="checkbox"
checked={selectedLayouts.includes(layout)}
onChange={() => toggleLayout(layout)}
className="sr-only"
/>
<div className={`flex items-center p-3 rounded-lg border cursor-pointer transition-all ${
selectedLayouts.includes(layout)
? 'border-cyan-500 bg-cyan-500/10 text-cyan-300'
: 'border-gray-700 bg-gray-800/30 text-gray-300 hover:border-gray-600 hover:bg-gray-800/50'
}`}>
<div className={`w-4 h-4 rounded border-2 mr-3 flex items-center justify-center ${
selectedLayouts.includes(layout)
? 'border-cyan-500 bg-cyan-500'
: 'border-gray-600'
}`}>
{selectedLayouts.includes(layout) && (
<svg className="w-2 h-2 text-white" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clipRule="evenodd" />
</svg>
)}
</div>
<span className="text-sm font-medium">{layout}</span>
</div>
</label>
))}
</div>
{selectedLayouts.length > 0 && (
<div className="mt-3 p-3 bg-gray-800/30 rounded-lg">
<div className="text-xs text-gray-400">
Selected layouts: <span className="text-cyan-400">{selectedLayouts.join(', ')}</span>
</div>
</div>
)}
</div>
{/* Quick Timeframe Actions */}
<div className="mb-6">
<label className="block text-xs font-medium text-gray-400 mb-3">Quick Actions</label>
<div className="grid grid-cols-4 gap-2 mb-3">
{timeframes.map(tf => (
<button
key={tf.value}
onClick={() => quickTimeframeTest(tf.value)}
disabled={loading || selectedLayouts.length === 0}
className={`py-2 px-3 rounded-lg text-xs font-medium transition-all ${
selectedTimeframes.includes(tf.value)
? 'bg-cyan-500 text-white shadow-lg'
: loading
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600 hover:text-white transform hover:scale-105'
}`}
>
{tf.label}
</button>
))}
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
<button
onClick={testAllTimeframes}
disabled={loading || selectedLayouts.length === 0 || !symbol}
className={`py-2 px-4 rounded-lg text-sm font-medium transition-all ${
loading
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
: 'bg-gradient-to-r from-purple-500 to-pink-500 text-white hover:from-purple-600 hover:to-pink-600 transform hover:scale-[1.02] active:scale-[0.98] shadow-lg'
}`}
>
🔍 Analyze All Timeframes
</button>
<button
onClick={() => setSelectedTimeframes([])}
disabled={loading || selectedTimeframes.length === 0}
className={`py-2 px-4 rounded-lg text-sm font-medium transition-all ${
loading || selectedTimeframes.length === 0
? 'bg-gray-700 text-gray-500 cursor-not-allowed'
: 'bg-gray-700 text-gray-300 hover:bg-gray-600 hover:text-white transform hover:scale-[1.02] active:scale-[0.98]'
}`}
>
🗑 Clear Selection
</button>
</div>
</div>
{/* Analyze Button */}
<button
className={`w-full py-3 px-6 rounded-lg font-semibold transition-all duration-300 ${
loading
? 'bg-gray-700 text-gray-400 cursor-not-allowed'
: 'btn-primary transform hover:scale-[1.02] active:scale-[0.98]'
}`}
onClick={handleAnalyze}
disabled={loading}
>
{loading ? (
<div className="flex items-center justify-center space-x-2">
<div className="spinner"></div>
<span>Analyzing Chart...</span>
</div>
) : (
<div className="flex items-center justify-center space-x-2">
<span>🚀</span>
<span>Start AI Analysis</span>
</div>
)}
</button>
</div>
{/* Results Section */}
{error && (
<div className="mt-6 p-4 bg-red-500/10 border border-red-500/30 rounded-lg">
<div className="flex items-start space-x-3">
<div className="w-5 h-5 text-red-400 mt-0.5"></div>
<div>
<h4 className="text-red-400 font-medium text-sm">Analysis Error</h4>
<p className="text-red-300 text-xs mt-1 opacity-90">{error}</p>
</div>
</div>
</div>
)}
{loading && progress && (
<div className="mt-6 p-6 bg-gradient-to-br from-cyan-500/10 to-blue-500/10 border border-cyan-500/30 rounded-lg">
{/* Header */}
<div className="flex items-center justify-between mb-6">
<div className="flex items-center space-x-3">
<div className="spinner border-cyan-500"></div>
<div>
<h4 className="text-cyan-400 font-medium text-lg">AI Analysis in Progress</h4>
<p className="text-cyan-300 text-sm opacity-90">
Analyzing {symbol} {selectedLayouts.join(', ')} layouts
</p>
</div>
</div>
{/* Overall Progress */}
<div className="text-right">
<div className="text-sm font-medium text-cyan-300">
Step {progress.currentStep} of {progress.totalSteps}
</div>
<div className="text-xs text-gray-400 mt-1">
{Math.round((progress.currentStep / progress.totalSteps) * 100)}% Complete
</div>
</div>
</div>
{/* Multi-timeframe progress */}
{progress.timeframeProgress && (
<div className="mb-6 p-4 bg-purple-800/20 rounded-lg border border-purple-500/30">
<div className="flex items-center justify-between mb-2">
<h5 className="text-purple-300 font-medium text-sm">Multi-Timeframe Analysis</h5>
<span className="text-xs text-purple-400">
{progress.timeframeProgress.current}/{progress.timeframeProgress.total} timeframes
</span>
</div>
<div className="w-full bg-purple-900/30 rounded-full h-2 mb-2">
<div
className="bg-gradient-to-r from-purple-500 to-purple-400 h-2 rounded-full transition-all duration-500"
style={{ width: `${(progress.timeframeProgress.current / progress.timeframeProgress.total) * 100}%` }}
/>
</div>
{progress.timeframeProgress.currentTimeframe && (
<p className="text-xs text-purple-300">
Current: {progress.timeframeProgress.currentTimeframe}
</p>
)}
</div>
)}
{/* Progress Steps */}
<div className="space-y-3">
{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 (
<div
key={step.id}
className={`flex items-center space-x-4 p-3 rounded-lg transition-all duration-300 ${
isActive ? 'bg-cyan-500/20 border border-cyan-500/50' :
isCompleted ? 'bg-green-500/10 border border-green-500/30' :
isError ? 'bg-red-500/10 border border-red-500/30' :
'bg-gray-800/30 border border-gray-700/50'
}`}
>
{/* Step Icon */}
<div className={`flex-shrink-0 w-8 h-8 rounded-full flex items-center justify-center text-sm font-medium ${
isActive ? 'bg-cyan-500 text-white animate-pulse' :
isCompleted ? 'bg-green-500 text-white' :
isError ? 'bg-red-500 text-white' :
'bg-gray-600 text-gray-300'
}`}>
{isCompleted ? '✓' :
isError ? '✗' :
isActive ? '⟳' :
index + 1}
</div>
{/* Step Content */}
<div className="flex-1 min-w-0">
<div className="flex items-center justify-between">
<h6 className={`font-medium text-sm ${
isActive ? 'text-cyan-300' :
isCompleted ? 'text-green-300' :
isError ? 'text-red-300' :
'text-gray-400'
}`}>
{step.title}
</h6>
{/* Timing */}
{(step.startTime || step.endTime) && (
<span className="text-xs text-gray-500">
{step.endTime && step.startTime ?
`${((step.endTime - step.startTime) / 1000).toFixed(1)}s` :
isActive && step.startTime ?
`${((Date.now() - step.startTime) / 1000).toFixed(0)}s` :
''
}
</span>
)}
</div>
<p className={`text-xs mt-1 ${
isActive ? 'text-cyan-400' :
isCompleted ? 'text-green-400' :
isError ? 'text-red-400' :
'text-gray-500'
}`}>
{step.details || step.description}
</p>
</div>
{/* Active indicator */}
{isActive && (
<div className="flex-shrink-0">
<div className="w-3 h-3 bg-cyan-400 rounded-full animate-ping"></div>
</div>
)}
</div>
)
})}
</div>
{/* Overall Progress Bar */}
<div className="mt-6">
<div className="w-full bg-gray-700 rounded-full h-2">
<div
className="bg-gradient-to-r from-cyan-500 to-blue-500 h-2 rounded-full transition-all duration-500"
style={{ width: `${(progress.currentStep / progress.totalSteps) * 100}%` }}
/>
</div>
</div>
</div>
)}
{result && result.type === 'multi_timeframe' && (
<div className="mt-6 space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-bold text-white flex items-center">
<span className="w-6 h-6 bg-gradient-to-br from-purple-400 to-purple-600 rounded-lg flex items-center justify-center mr-2 text-sm">
📊
</span>
Multi-Timeframe Analysis
</h3>
<div className="text-xs text-gray-400">
{result.symbol} {result.results.length} timeframes
</div>
</div>
<div className="p-4 bg-gradient-to-r from-purple-800/30 to-purple-700/30 rounded-lg border border-purple-500/30">
<h4 className="text-sm font-semibold text-purple-300 mb-2">Analysis Summary</h4>
<p className="text-white text-sm">{result.summary}</p>
</div>
<div className="grid gap-4">
{result.results.map((timeframeResult: any, index: number) => (
<div key={index} className={`p-4 rounded-lg border ${
timeframeResult.success
? 'bg-green-500/5 border-green-500/30'
: 'bg-red-500/5 border-red-500/30'
}`}>
<div className="flex items-center justify-between mb-3">
<h5 className={`font-semibold ${
timeframeResult.success ? 'text-green-400' : 'text-red-400'
}`}>
{timeframeResult.success ? '✅' : '❌'} {timeframeResult.timeframeLabel} Timeframe
</h5>
<div className="flex items-center space-x-2">
{timeframeResult.success && timeframeResult.result.analysis && (
<button
onClick={() => handleTradeClick(timeframeResult)}
className="px-3 py-1 bg-gradient-to-r from-green-500 to-green-600 text-white text-xs font-medium rounded hover:from-green-600 hover:to-green-700 transition-all transform hover:scale-105"
>
💰 Trade
</button>
)}
<span className="text-xs text-gray-400">
{timeframeResult.success ? 'Analysis Complete' : 'Failed'}
</span>
</div>
</div>
{timeframeResult.success && timeframeResult.result.analysis && (
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
<div className="text-center p-3 bg-gray-800/30 rounded">
<div className="text-xs text-gray-400">Sentiment</div>
<div className="text-sm font-medium text-white">
{safeRender(timeframeResult.result.analysis.marketSentiment)}
</div>
</div>
<div className="text-center p-3 bg-gray-800/30 rounded">
<div className="text-xs text-gray-400">Recommendation</div>
<div className="text-sm font-medium text-white">
{safeRender(timeframeResult.result.analysis.recommendation)}
</div>
</div>
<div className="text-center p-3 bg-gray-800/30 rounded">
<div className="text-xs text-gray-400">Confidence</div>
<div className="text-sm font-medium text-white">
{safeRender(timeframeResult.result.analysis.confidence)}%
</div>
</div>
</div>
)}
{timeframeResult.success && timeframeResult.result.analysis?.entry && (
<div className="mt-3 p-3 bg-yellow-500/5 border border-yellow-500/20 rounded">
<div className="text-xs text-yellow-400 font-semibold mb-1">Entry Setup</div>
<div className="text-sm text-white">
📍 ${safeRender(timeframeResult.result.analysis.entry.price)}
{timeframeResult.result.analysis.entry.buffer && (
<span className="text-yellow-400 ml-1">
{safeRender(timeframeResult.result.analysis.entry.buffer)}
</span>
)}
</div>
{timeframeResult.result.analysis.stopLoss && (
<div className="text-sm text-red-300 mt-1">
🛑 SL: ${safeRender(timeframeResult.result.analysis.stopLoss.price)}
</div>
)}
{timeframeResult.result.analysis.takeProfits && (
<div className="text-sm text-green-300 mt-1 space-y-1">
{timeframeResult.result.analysis.takeProfits.tp1 && (
<div>
🎯 TP1: ${typeof timeframeResult.result.analysis.takeProfits.tp1.price !== 'undefined'
? safeRender(timeframeResult.result.analysis.takeProfits.tp1.price)
: safeRender(timeframeResult.result.analysis.takeProfits.tp1)}
</div>
)}
{timeframeResult.result.analysis.takeProfits.tp2 && (
<div>
🎯 TP2: ${typeof timeframeResult.result.analysis.takeProfits.tp2.price !== 'undefined'
? safeRender(timeframeResult.result.analysis.takeProfits.tp2.price)
: safeRender(timeframeResult.result.analysis.takeProfits.tp2)}
</div>
)}
{/* Fallback for simple take profit format */}
{!timeframeResult.result.analysis.takeProfits.tp1 && !timeframeResult.result.analysis.takeProfits.tp2 && (
<div>
🎯 TP: {typeof timeframeResult.result.analysis.takeProfits === 'object'
? Object.values(timeframeResult.result.analysis.takeProfits).map((tp: any) => `$${safeRender(tp)}`).join(', ')
: `$${safeRender(timeframeResult.result.analysis.takeProfits)}`}
</div>
)}
</div>
)}
</div>
)}
{!timeframeResult.success && (
<div className="text-red-300 text-sm">
Analysis failed for this timeframe
</div>
)}
</div>
))}
</div>
</div>
)}
{result && result.analysis && (
<div className="mt-6 space-y-4">
<div className="flex items-center justify-between">
<h3 className="text-lg font-bold text-white flex items-center">
<span className="w-6 h-6 bg-gradient-to-br from-green-400 to-emerald-600 rounded-lg flex items-center justify-center mr-2 text-sm">
</span>
Analysis Complete
</h3>
<div className="flex items-center space-x-3">
{result.analysis && (
<button
onClick={() => handleTradeClick({ result, timeframeLabel: selectedTimeframes.map(tf => timeframes.find(t => t.value === tf)?.label).join(', ') })}
className="px-4 py-2 bg-gradient-to-r from-green-500 to-green-600 text-white text-sm font-medium rounded-lg hover:from-green-600 hover:to-green-700 transition-all transform hover:scale-105"
>
💰 Execute Trade
</button>
)}
{result.screenshots && (
<div className="text-xs text-gray-400">
Screenshots: {result.screenshots.length} captured
</div>
)}
</div>
</div>
<div className="grid gap-4">
{/* Summary */}
<div className="p-4 bg-gradient-to-r from-gray-800/50 to-gray-700/50 rounded-lg border border-gray-700">
<h4 className="text-sm font-semibold text-gray-300 mb-2 flex items-center">
<span className="w-4 h-4 bg-blue-500 rounded-full mr-2"></span>
Market Summary
</h4>
<p className="text-white text-sm leading-relaxed">{safeRender(result.analysis.summary)}</p>
</div>
{/* Key Metrics */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-gradient-to-br from-green-500/10 to-emerald-500/10 border border-green-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-green-400 mb-2">Market Sentiment</h4>
<p className="text-white font-medium">{safeRender(result.analysis.marketSentiment)}</p>
</div>
<div className="p-4 bg-gradient-to-br from-blue-500/10 to-cyan-500/10 border border-blue-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-blue-400 mb-2">Recommendation</h4>
<p className="text-white font-medium">{safeRender(result.analysis.recommendation)}</p>
{result.analysis.confidence && (
<p className="text-cyan-300 text-xs mt-1">{safeRender(result.analysis.confidence)}% confidence</p>
)}
</div>
</div>
{/* Trading Levels */}
{result.analysis.keyLevels && (
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="p-4 bg-gradient-to-br from-red-500/10 to-rose-500/10 border border-red-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-red-400 mb-2">Resistance Levels</h4>
<p className="text-red-300 font-mono text-sm">
{result.analysis.keyLevels.resistance?.join(', ') || 'None identified'}
</p>
</div>
<div className="p-4 bg-gradient-to-br from-green-500/10 to-emerald-500/10 border border-green-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-green-400 mb-2">Support Levels</h4>
<p className="text-green-300 font-mono text-sm">
{result.analysis.keyLevels.support?.join(', ') || 'None identified'}
</p>
</div>
</div>
)}
{/* Trading Setup */}
{(result.analysis.entry || result.analysis.stopLoss || result.analysis.takeProfits) && (
<div className="p-4 bg-gradient-to-br from-purple-500/10 to-violet-500/10 border border-purple-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-purple-400 mb-3">Trading Setup</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
{result.analysis.entry && (
<div>
<span className="text-xs text-gray-400">Entry Point</span>
<p className="text-yellow-300 font-mono font-semibold">
📍 ${safeRender(result.analysis.entry.price || result.analysis.entry)}
{result.analysis.entry.buffer && (
<span className="text-yellow-400 text-xs ml-1">{safeRender(result.analysis.entry.buffer)}</span>
)}
</p>
{result.analysis.entry.rationale && (
<p className="text-xs text-gray-300 mt-1">💡 {safeRender(result.analysis.entry.rationale)}</p>
)}
</div>
)}
{result.analysis.stopLoss && (
<div>
<span className="text-xs text-gray-400">Stop Loss</span>
<p className="text-red-300 font-mono font-semibold">
🛑 ${safeRender(result.analysis.stopLoss.price || result.analysis.stopLoss)}
</p>
{result.analysis.stopLoss.rationale && (
<p className="text-xs text-gray-300 mt-1">💡 {safeRender(result.analysis.stopLoss.rationale)}</p>
)}
</div>
)}
{result.analysis.takeProfits && (
<div>
<span className="text-xs text-gray-400">Take Profit Targets</span>
<div className="space-y-2 mt-2">
{result.analysis.takeProfits.tp1 && (
<div className="p-2 bg-green-500/5 rounded border border-green-500/20">
<div className="flex items-center gap-2">
<span className="text-yellow-400">🥉</span>
<span className="text-green-300 font-mono font-semibold">
TP1: ${typeof result.analysis.takeProfits.tp1.price !== 'undefined'
? result.analysis.takeProfits.tp1.price
: safeRender(result.analysis.takeProfits.tp1)}
</span>
</div>
{result.analysis.takeProfits.tp1.description && (
<p className="text-xs text-green-200 mt-1">
📋 {safeRender(result.analysis.takeProfits.tp1.description)}
</p>
)}
{result.analysis.takeProfits.tp1.rsiExpectation && (
<p className="text-xs text-blue-200 mt-1">
📊 RSI: {safeRender(result.analysis.takeProfits.tp1.rsiExpectation)}
</p>
)}
{result.analysis.takeProfits.tp1.obvExpectation && (
<p className="text-xs text-purple-200 mt-1">
📈 OBV: {safeRender(result.analysis.takeProfits.tp1.obvExpectation)}
</p>
)}
</div>
)}
{result.analysis.takeProfits.tp2 && (
<div className="p-2 bg-green-500/5 rounded border border-green-500/20">
<div className="flex items-center gap-2">
<span className="text-gray-300">🥈</span>
<span className="text-green-300 font-mono font-semibold">
TP2: ${typeof result.analysis.takeProfits.tp2.price !== 'undefined'
? result.analysis.takeProfits.tp2.price
: safeRender(result.analysis.takeProfits.tp2)}
</span>
</div>
{result.analysis.takeProfits.tp2.description && (
<p className="text-xs text-green-200 mt-1">
📋 {safeRender(result.analysis.takeProfits.tp2.description)}
</p>
)}
{result.analysis.takeProfits.tp2.rsiExpectation && (
<p className="text-xs text-blue-200 mt-1">
📊 RSI: {safeRender(result.analysis.takeProfits.tp2.rsiExpectation)}
</p>
)}
{result.analysis.takeProfits.tp2.obvExpectation && (
<p className="text-xs text-purple-200 mt-1">
📈 OBV: {safeRender(result.analysis.takeProfits.tp2.obvExpectation)}
</p>
)}
</div>
)}
{/* Fallback for simple take profit format */}
{!result.analysis.takeProfits.tp1 && !result.analysis.takeProfits.tp2 && (
<p className="text-green-300 font-mono font-semibold">
{typeof result.analysis.takeProfits === 'object'
? Object.values(result.analysis.takeProfits).map(tp => `$${safeRender(tp)}`).join(', ')
: `$${safeRender(result.analysis.takeProfits)}`}
</p>
)}
</div>
</div>
)}
</div>
</div>
)}
{/* Risk Management & Confirmation */}
{(result.analysis.riskToReward || result.analysis.confirmationTrigger) && (
<div className="p-4 bg-gradient-to-br from-amber-500/10 to-orange-500/10 border border-amber-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-amber-400 mb-3">Risk Management</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{result.analysis.riskToReward && (
<div>
<span className="text-xs text-amber-400">Risk/Reward Ratio</span>
<p className="text-amber-200 font-mono font-semibold"> {safeRender(result.analysis.riskToReward)}</p>
</div>
)}
{result.analysis.confirmationTrigger && (
<div>
<span className="text-xs text-amber-400">Confirmation Trigger</span>
<p className="text-amber-200 text-sm">🔔 {safeRender(result.analysis.confirmationTrigger)}</p>
</div>
)}
</div>
</div>
)}
{/* Timeframe Risk Assessment */}
{result.analysis.timeframeRisk && (
<div className="p-4 bg-gradient-to-br from-red-500/10 to-pink-500/10 border border-red-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-red-400 mb-3"> Timeframe Risk Assessment</h4>
<div className="grid grid-cols-1 md:grid-cols-3 gap-3">
{result.analysis.timeframeRisk.assessment && (
<div>
<span className="text-xs text-red-400">Risk Level</span>
<p className="text-red-200 font-semibold">📊 {safeRender(result.analysis.timeframeRisk.assessment)}</p>
</div>
)}
{result.analysis.timeframeRisk.positionSize && (
<div>
<span className="text-xs text-red-400">Position Size</span>
<p className="text-red-200 font-semibold">💼 {safeRender(result.analysis.timeframeRisk.positionSize)}</p>
</div>
)}
{result.analysis.timeframeRisk.leverageRecommendation && (
<div>
<span className="text-xs text-red-400">Leverage</span>
<p className="text-red-200 font-semibold">🎚 {safeRender(result.analysis.timeframeRisk.leverageRecommendation)}</p>
</div>
)}
</div>
</div>
)}
{/* Technical Indicators */}
{result.analysis.indicatorAnalysis && (
<div className="p-4 bg-gradient-to-br from-cyan-500/10 to-blue-500/10 border border-cyan-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-cyan-400 mb-3">📈 Technical Indicators</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{result.analysis.indicatorAnalysis.rsi && (
<div className="p-2 bg-cyan-500/5 rounded border border-cyan-500/20">
<span className="text-xs text-cyan-400 font-semibold">📊 RSI</span>
<p className="text-cyan-200 text-xs mt-1">{safeRender(result.analysis.indicatorAnalysis.rsi)}</p>
</div>
)}
{result.analysis.indicatorAnalysis.vwap && (
<div className="p-2 bg-blue-500/5 rounded border border-blue-500/20">
<span className="text-xs text-blue-400 font-semibold">📈 VWAP</span>
<p className="text-blue-200 text-xs mt-1">{safeRender(result.analysis.indicatorAnalysis.vwap)}</p>
</div>
)}
{result.analysis.indicatorAnalysis.obv && (
<div className="p-2 bg-purple-500/5 rounded border border-purple-500/20">
<span className="text-xs text-purple-400 font-semibold">📊 OBV</span>
<p className="text-purple-200 text-xs mt-1">{safeRender(result.analysis.indicatorAnalysis.obv)}</p>
</div>
)}
{result.analysis.indicatorAnalysis.macd && (
<div className="p-2 bg-indigo-500/5 rounded border border-indigo-500/20">
<span className="text-xs text-indigo-400 font-semibold">📉 MACD</span>
<p className="text-indigo-200 text-xs mt-1">{safeRender(result.analysis.indicatorAnalysis.macd)}</p>
</div>
)}
</div>
</div>
)}
{/* Alternatives */}
{result.analysis.alternatives && (
<div className="p-4 bg-gradient-to-br from-violet-500/10 to-purple-500/10 border border-violet-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-violet-400 mb-3">🔄 Alternative Strategies</h4>
<div className="space-y-2">
{result.analysis.alternatives.tigherStopOption && (
<div className="p-2 bg-violet-500/5 rounded border border-violet-500/20">
<span className="text-xs text-violet-400 font-semibold">🎯 Tighter Stop Option</span>
<p className="text-violet-200 text-xs mt-1">{safeRender(result.analysis.alternatives.tigherStopOption)}</p>
</div>
)}
{result.analysis.alternatives.scaledEntry && (
<div className="p-2 bg-purple-500/5 rounded border border-purple-500/20">
<span className="text-xs text-purple-400 font-semibold">📊 Scaled Entry</span>
<p className="text-purple-200 text-xs mt-1">{safeRender(result.analysis.alternatives.scaledEntry)}</p>
</div>
)}
{result.analysis.alternatives.invalidationScenario && (
<div className="p-2 bg-red-500/5 rounded border border-red-500/20">
<span className="text-xs text-red-400 font-semibold"> Invalidation Scenario</span>
<p className="text-red-200 text-xs mt-1">{safeRender(result.analysis.alternatives.invalidationScenario)}</p>
</div>
)}
</div>
</div>
)}
{/* Layout Comparison Section */}
{result.analysis.layoutComparison && (
<div className="p-4 bg-gradient-to-br from-indigo-500/10 to-blue-500/10 border border-indigo-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-indigo-400 mb-3">Multi-Layout Analysis</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
{result.analysis.layoutComparison.aiLayout && (
<div className="p-3 bg-blue-500/5 rounded border border-blue-500/20">
<span className="text-xs text-blue-400 font-semibold">AI Layout Insights</span>
<p className="text-xs text-blue-200 mt-1">{result.analysis.layoutComparison.aiLayout}</p>
</div>
)}
{result.analysis.layoutComparison.diyLayout && (
<div className="p-3 bg-green-500/5 rounded border border-green-500/20">
<span className="text-xs text-green-400 font-semibold">DIY Layout Insights</span>
<p className="text-xs text-green-200 mt-1">{result.analysis.layoutComparison.diyLayout}</p>
</div>
)}
</div>
{result.analysis.layoutComparison.consensus && (
<div className="mt-3 p-3 bg-emerald-500/5 rounded border border-emerald-500/20">
<span className="text-xs text-emerald-400 font-semibold">Layout Consensus</span>
<p className="text-xs text-emerald-200 mt-1">{result.analysis.layoutComparison.consensus}</p>
</div>
)}
{result.analysis.layoutComparison.divergences && (
<div className="mt-3 p-3 bg-amber-500/5 rounded border border-amber-500/20">
<span className="text-xs text-amber-400 font-semibold">Layout Divergences</span>
<p className="text-xs text-amber-200 mt-1">{result.analysis.layoutComparison.divergences}</p>
</div>
)}
</div>
)}
{/* Enhanced Indicator Analysis */}
{result.analysis.indicatorAnalysis?.crossLayoutConsensus && (
<div className="p-4 bg-gradient-to-br from-violet-500/10 to-purple-500/10 border border-violet-500/30 rounded-lg">
<h4 className="text-sm font-semibold text-violet-400 mb-2">Cross-Layout Consensus</h4>
<p className="text-violet-200 text-sm">{result.analysis.indicatorAnalysis.crossLayoutConsensus}</p>
</div>
)}
</div>
</div>
)}
{result && !result.analysis && result.screenshots && (
<div className="mt-6 p-4 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">
<h3 className="text-lg font-bold text-yellow-400 mb-2">Screenshots Captured</h3>
<p className="text-yellow-300 text-sm mb-2">
Screenshots were captured successfully, but AI analysis failed or was not requested.
</p>
<div className="text-xs text-gray-400">
Screenshots: {result.screenshots.length} captured
</div>
<div className="mt-2">
{result.screenshots.map((screenshot: string, index: number) => (
<div key={index} className="text-xs text-gray-500 font-mono">
{screenshot.split('/').pop()}
</div>
))}
</div>
</div>
)}
{/* Screenshot Gallery */}
{result && result.screenshots && (
<ScreenshotGallery
screenshots={result.screenshots}
symbol={symbol}
timeframes={selectedTimeframes.map(tf => 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.results && (
<ScreenshotGallery
screenshots={result.results.filter((r: any) => r.success && r.result.screenshots).flatMap((r: any) => r.result.screenshots)}
symbol={symbol}
timeframes={result.results.filter((r: any) => r.success).map((r: any) => r.timeframeLabel)}
enlargedImage={enlargedScreenshot}
onImageClick={handleScreenshotClick}
onClose={() => setEnlargedScreenshot(null)}
/>
)}
{/* Trade Modal */}
<TradeModal
isOpen={tradeModalOpen}
onClose={() => setTradeModalOpen(false)}
tradeData={tradeModalData}
onExecute={executeTrade}
/>
</div>
)
}