🔒 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:
mindesbunister
2025-07-17 14:00:24 +02:00
parent 8372b271cb
commit 4ff35b8e04
9 changed files with 65 additions and 52 deletions

View File

@@ -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>
) )

View File

@@ -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,15 +39,30 @@ 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))
upColor: '#26a69a', addLog('Chart methods: ' + chartMethods.slice(0, 10).join(', ') + '...')
downColor: '#ef5350',
borderDownColor: '#ef5350', // Try to add a candlestick series using the modern API
borderUpColor: '#26a69a', let candlestickSeries;
wickDownColor: '#ef5350', if ('addCandlestickSeries' in chart) {
wickUpColor: '#26a69a', addLog('Using addCandlestickSeries method')
}) candlestickSeries = (chart as any).addCandlestickSeries({
upColor: '#26a69a',
downColor: '#ef5350',
borderDownColor: '#ef5350',
borderUpColor: '#26a69a',
wickDownColor: '#ef5350',
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

View File

@@ -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',
}) })

View File

@@ -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',
}) })

View File

@@ -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')

View File

@@ -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',

View File

@@ -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',
}) })

View File

@@ -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',
}) })

View File

@@ -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,24 +422,26 @@ 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() {
await performAnalysis() if (!analysisInProgress.current) {
await performAnalysis()
}
} }
// Trade initiation handler // Trade initiation handler
@@ -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>