🔒 Implement concurrency protection and remove marketing text
- Add analysisInProgress useRef to prevent multiple simultaneous analyses - Protect all analysis entry points (performAnalysis, quickAnalyze, etc.) - Update button disabled state to include concurrency check - Remove marketing text from analysis page and AIAnalysisPanel - Fix remaining TypeScript compilation errors in chart components - Ensure clean UI without promotional content Fixes sync issues caused by overlapping analysis sessions
This commit is contained in:
@@ -5,16 +5,6 @@ import AIAnalysisPanel from '../../components/AIAnalysisPanel'
|
|||||||
export default function AnalysisPage() {
|
export default function AnalysisPage() {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
<div className="text-center mb-8">
|
|
||||||
<h1 className="text-3xl font-bold text-white mb-4">
|
|
||||||
🤖 AI-Powered Market Analysis
|
|
||||||
</h1>
|
|
||||||
<p className="text-gray-400 max-w-2xl mx-auto">
|
|
||||||
Get professional trading insights with multi-timeframe analysis, precise entry/exit levels,
|
|
||||||
and institutional-quality recommendations powered by OpenAI.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<AIAnalysisPanel />
|
<AIAnalysisPanel />
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -21,12 +21,15 @@ export default function ChartDebug() {
|
|||||||
try {
|
try {
|
||||||
addLog('Starting chart initialization...')
|
addLog('Starting chart initialization...')
|
||||||
|
|
||||||
// Import lightweight-charts
|
// Dynamic import to avoid SSR issues
|
||||||
const LightweightCharts = await import('lightweight-charts')
|
addLog('Importing lightweight-charts...')
|
||||||
addLog('Lightweight charts imported successfully')
|
const LightweightChartsModule = await import('lightweight-charts')
|
||||||
|
addLog('Import successful')
|
||||||
|
|
||||||
const { createChart } = LightweightCharts
|
addLog('Available exports: ' + Object.keys(LightweightChartsModule).join(', '))
|
||||||
addLog('createChart extracted')
|
|
||||||
|
const { createChart } = LightweightChartsModule
|
||||||
|
addLog('Extracted createChart')
|
||||||
|
|
||||||
// Create chart with minimal options
|
// Create chart with minimal options
|
||||||
const chart = createChart(chartContainerRef.current!, {
|
const chart = createChart(chartContainerRef.current!, {
|
||||||
@@ -36,8 +39,15 @@ export default function ChartDebug() {
|
|||||||
addLog('Chart created successfully')
|
addLog('Chart created successfully')
|
||||||
setChartCreated(true)
|
setChartCreated(true)
|
||||||
|
|
||||||
// Add candlestick series with the correct v5 API
|
// Check what methods are available on the chart
|
||||||
const candlestickSeries = chart.addCandlestickSeries({
|
const chartMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(chart))
|
||||||
|
addLog('Chart methods: ' + chartMethods.slice(0, 10).join(', ') + '...')
|
||||||
|
|
||||||
|
// Try to add a candlestick series using the modern API
|
||||||
|
let candlestickSeries;
|
||||||
|
if ('addCandlestickSeries' in chart) {
|
||||||
|
addLog('Using addCandlestickSeries method')
|
||||||
|
candlestickSeries = (chart as any).addCandlestickSeries({
|
||||||
upColor: '#26a69a',
|
upColor: '#26a69a',
|
||||||
downColor: '#ef5350',
|
downColor: '#ef5350',
|
||||||
borderDownColor: '#ef5350',
|
borderDownColor: '#ef5350',
|
||||||
@@ -45,6 +55,14 @@ export default function ChartDebug() {
|
|||||||
wickDownColor: '#ef5350',
|
wickDownColor: '#ef5350',
|
||||||
wickUpColor: '#26a69a',
|
wickUpColor: '#26a69a',
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
addLog('Trying alternative API')
|
||||||
|
candlestickSeries = (chart as any).addAreaSeries({
|
||||||
|
lineColor: '#26a69a',
|
||||||
|
topColor: 'rgba(38, 166, 154, 0.4)',
|
||||||
|
bottomColor: 'rgba(38, 166, 154, 0.0)',
|
||||||
|
})
|
||||||
|
}
|
||||||
addLog('Candlestick series added')
|
addLog('Candlestick series added')
|
||||||
|
|
||||||
// Very simple test data
|
// Very simple test data
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ export default function DebugChart() {
|
|||||||
|
|
||||||
addLog('Available exports: ' + Object.keys(LightweightChartsModule).join(', '))
|
addLog('Available exports: ' + Object.keys(LightweightChartsModule).join(', '))
|
||||||
|
|
||||||
const { createChart, ColorType, CrosshairMode } = LightweightChartsModule
|
const { createChart } = LightweightChartsModule
|
||||||
addLog('Extracted createChart and other components')
|
addLog('Extracted createChart')
|
||||||
|
|
||||||
addLog('Creating chart...')
|
addLog('Creating chart...')
|
||||||
const chart = createChart(chartContainerRef.current!, {
|
const chart = createChart(chartContainerRef.current!, {
|
||||||
@@ -46,7 +46,7 @@ export default function DebugChart() {
|
|||||||
addLog('Chart created successfully')
|
addLog('Chart created successfully')
|
||||||
|
|
||||||
addLog('Adding candlestick series...')
|
addLog('Adding candlestick series...')
|
||||||
const candlestickSeries = chart.addCandlestickSeries({
|
const candlestickSeries = (chart as any).addCandlestickSeries({
|
||||||
upColor: '#26a69a',
|
upColor: '#26a69a',
|
||||||
downColor: '#ef5350',
|
downColor: '#ef5350',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ export default function DirectChart() {
|
|||||||
})
|
})
|
||||||
console.log('Chart created')
|
console.log('Chart created')
|
||||||
|
|
||||||
const series = chart.addCandlestickSeries({
|
const series = (chart as any).addCandlestickSeries({
|
||||||
upColor: '#26a69a',
|
upColor: '#26a69a',
|
||||||
downColor: '#ef5350',
|
downColor: '#ef5350',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function MinimalChartTest() {
|
|||||||
setStatus('Chart created')
|
setStatus('Chart created')
|
||||||
|
|
||||||
setStatus('Adding series...')
|
setStatus('Adding series...')
|
||||||
const series = chart.addCandlestickSeries({})
|
const series = (chart as any).addCandlestickSeries({})
|
||||||
console.log('Series created:', series)
|
console.log('Series created:', series)
|
||||||
setStatus('Series added')
|
setStatus('Series added')
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function SimpleChart() {
|
|||||||
setStatus('Adding candlestick series...')
|
setStatus('Adding candlestick series...')
|
||||||
console.log('Chart created, adding candlestick series...')
|
console.log('Chart created, adding candlestick series...')
|
||||||
|
|
||||||
const candlestickSeries = chart.addCandlestickSeries({
|
const candlestickSeries = (chart as any).addCandlestickSeries({
|
||||||
upColor: '#26a69a',
|
upColor: '#26a69a',
|
||||||
downColor: '#ef5350',
|
downColor: '#ef5350',
|
||||||
borderDownColor: '#ef5350',
|
borderDownColor: '#ef5350',
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function SimpleTest() {
|
|||||||
setStatus('Chart created')
|
setStatus('Chart created')
|
||||||
|
|
||||||
// Add series
|
// Add series
|
||||||
const series = chart.addCandlestickSeries({
|
const series = (chart as any).addCandlestickSeries({
|
||||||
upColor: '#00ff00',
|
upColor: '#00ff00',
|
||||||
downColor: '#ff0000',
|
downColor: '#ff0000',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ export default function WorkingChart() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const candlestickSeries = chart.addCandlestickSeries({
|
const candlestickSeries = (chart as any).addCandlestickSeries({
|
||||||
upColor: '#26a69a',
|
upColor: '#26a69a',
|
||||||
downColor: '#ef5350',
|
downColor: '#ef5350',
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import React, { useState, useEffect } from 'react'
|
import React, { useState, useEffect, useRef } from 'react'
|
||||||
import TradeModal from './TradeModal'
|
import TradeModal from './TradeModal'
|
||||||
import ScreenshotGallery from './ScreenshotGallery'
|
import ScreenshotGallery from './ScreenshotGallery'
|
||||||
|
|
||||||
@@ -83,6 +83,9 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
const [modalOpen, setModalOpen] = useState(false)
|
const [modalOpen, setModalOpen] = useState(false)
|
||||||
const [modalData, setModalData] = useState<any>(null)
|
const [modalData, setModalData] = useState<any>(null)
|
||||||
const [enlargedScreenshot, setEnlargedScreenshot] = useState<string | null>(null)
|
const [enlargedScreenshot, setEnlargedScreenshot] = useState<string | null>(null)
|
||||||
|
|
||||||
|
// Ref to prevent concurrent analysis calls
|
||||||
|
const analysisInProgress = useRef(false)
|
||||||
const [tradeModalOpen, setTradeModalOpen] = useState(false)
|
const [tradeModalOpen, setTradeModalOpen] = useState(false)
|
||||||
const [tradeModalData, setTradeModalData] = useState<any>(null)
|
const [tradeModalData, setTradeModalData] = useState<any>(null)
|
||||||
|
|
||||||
@@ -123,6 +126,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
setEventSource(null)
|
setEventSource(null)
|
||||||
// Reset UI state when analysis completes
|
// Reset UI state when analysis completes
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
analysisInProgress.current = false
|
||||||
setProgress(null)
|
setProgress(null)
|
||||||
} else if (progressData.type === 'connected') {
|
} else if (progressData.type === 'connected') {
|
||||||
console.log(`🔍 EventSource connected for ${sessionId}`)
|
console.log(`🔍 EventSource connected for ${sessionId}`)
|
||||||
@@ -138,6 +142,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
console.log(`🔍 All steps completed for ${sessionId}, resetting UI state`)
|
console.log(`🔍 All steps completed for ${sessionId}, resetting UI state`)
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
analysisInProgress.current = false
|
||||||
setProgress(null)
|
setProgress(null)
|
||||||
es.close()
|
es.close()
|
||||||
setEventSource(null)
|
setEventSource(null)
|
||||||
@@ -205,8 +210,13 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
// const updateProgress = ...removed for real-time implementation
|
// const updateProgress = ...removed for real-time implementation
|
||||||
|
|
||||||
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframes = selectedTimeframes) => {
|
const performAnalysis = async (analysisSymbol = symbol, analysisTimeframes = selectedTimeframes) => {
|
||||||
if (loading || selectedLayouts.length === 0 || analysisTimeframes.length === 0) return
|
// 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)
|
setLoading(true)
|
||||||
setError(null)
|
setError(null)
|
||||||
setResult(null)
|
setResult(null)
|
||||||
@@ -368,6 +378,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
|
|
||||||
// Reset loading state immediately on error
|
// Reset loading state immediately on error
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
analysisInProgress.current = false
|
||||||
setProgress(null)
|
setProgress(null)
|
||||||
|
|
||||||
// Mark current active step as error
|
// Mark current active step as error
|
||||||
@@ -391,13 +402,14 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
// Single timeframe analysis loading state is managed by progress tracking
|
// Single timeframe analysis loading state is managed by progress tracking
|
||||||
if (analysisTimeframes.length > 1) {
|
if (analysisTimeframes.length > 1) {
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
analysisInProgress.current = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const quickAnalyze = async (coinSymbol: string, quickTimeframes = selectedTimeframes) => {
|
const quickAnalyze = async (coinSymbol: string, quickTimeframes = selectedTimeframes) => {
|
||||||
setSymbol(coinSymbol)
|
setSymbol(coinSymbol)
|
||||||
if (!loading) {
|
if (!loading && !analysisInProgress.current) {
|
||||||
await performAnalysis(coinSymbol, quickTimeframes)
|
await performAnalysis(coinSymbol, quickTimeframes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -410,25 +422,27 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
|
|
||||||
setSelectedTimeframes(newTimeframes)
|
setSelectedTimeframes(newTimeframes)
|
||||||
|
|
||||||
if (!loading && symbol && newTimeframes.length > 0) {
|
if (!loading && !analysisInProgress.current && symbol && newTimeframes.length > 0) {
|
||||||
await performAnalysis(symbol, newTimeframes)
|
await performAnalysis(symbol, newTimeframes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const testAllTimeframes = async () => {
|
const testAllTimeframes = async () => {
|
||||||
if (loading) return
|
if (loading || analysisInProgress.current) return
|
||||||
|
|
||||||
const allTimeframeValues = timeframes.map(tf => tf.value)
|
const allTimeframeValues = timeframes.map(tf => tf.value)
|
||||||
setSelectedTimeframes(allTimeframeValues)
|
setSelectedTimeframes(allTimeframeValues)
|
||||||
|
|
||||||
if (!loading && symbol) {
|
if (!loading && !analysisInProgress.current && symbol) {
|
||||||
await performAnalysis(symbol, allTimeframeValues)
|
await performAnalysis(symbol, allTimeframeValues)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleAnalyze() {
|
async function handleAnalyze() {
|
||||||
|
if (!analysisInProgress.current) {
|
||||||
await performAnalysis()
|
await performAnalysis()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Trade initiation handler
|
// Trade initiation handler
|
||||||
const handleTradeClick = (tfResult: any) => {
|
const handleTradeClick = (tfResult: any) => {
|
||||||
@@ -593,16 +607,7 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
return (
|
return (
|
||||||
<div className="card card-gradient">
|
<div className="card card-gradient">
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h2 className="text-xl font-bold text-white flex items-center">
|
{/* Analysis Controls Area */}
|
||||||
<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>
|
</div>
|
||||||
|
|
||||||
{/* Quick Coin & Timeframe Analysis */}
|
{/* Quick Coin & Timeframe Analysis */}
|
||||||
@@ -794,14 +799,14 @@ export default function AIAnalysisPanel({ onAnalysisComplete }: AIAnalysisPanelP
|
|||||||
{/* Analyze Button */}
|
{/* Analyze Button */}
|
||||||
<button
|
<button
|
||||||
className={`w-full py-3 px-6 rounded-lg font-semibold transition-all duration-300 ${
|
className={`w-full py-3 px-6 rounded-lg font-semibold transition-all duration-300 ${
|
||||||
loading
|
(loading || analysisInProgress.current)
|
||||||
? 'bg-gray-700 text-gray-400 cursor-not-allowed'
|
? 'bg-gray-700 text-gray-400 cursor-not-allowed'
|
||||||
: 'btn-primary transform hover:scale-[1.02] active:scale-[0.98]'
|
: 'btn-primary transform hover:scale-[1.02] active:scale-[0.98]'
|
||||||
}`}
|
}`}
|
||||||
onClick={handleAnalyze}
|
onClick={handleAnalyze}
|
||||||
disabled={loading}
|
disabled={loading || analysisInProgress.current}
|
||||||
>
|
>
|
||||||
{loading ? (
|
{(loading || analysisInProgress.current) ? (
|
||||||
<div className="flex items-center justify-center space-x-2">
|
<div className="flex items-center justify-center space-x-2">
|
||||||
<div className="spinner"></div>
|
<div className="spinner"></div>
|
||||||
<span>Analyzing Chart...</span>
|
<span>Analyzing Chart...</span>
|
||||||
|
|||||||
Reference in New Issue
Block a user