Fix ScreenshotGallery and improve trade execution feedback
- Add keyboard ESC listener for closing enlarged screenshots - Fix screenshot URL formatting to use /screenshots/[filename] route - Improve trade execution error handling with detailed messages - Show specific feedback for insufficient funds, auth issues, etc. - Remove unused Modal import that was causing build errors - Add click-outside-to-close functionality for enlarged images
This commit is contained in:
@@ -1,6 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import Modal from './Modal'
|
|
||||||
import TradeModal from './TradeModal'
|
import TradeModal from './TradeModal'
|
||||||
import ScreenshotGallery from './ScreenshotGallery'
|
import ScreenshotGallery from './ScreenshotGallery'
|
||||||
|
|
||||||
@@ -420,27 +419,50 @@ export default function AIAnalysisPanel() {
|
|||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
symbol: tradeData.symbol,
|
symbol: tradeData.symbol,
|
||||||
side: 'buy', // Could be derived from analysis
|
side: 'BUY', // Could be derived from analysis
|
||||||
size: parseFloat(tradeData.size),
|
amount: parseFloat(tradeData.size), // Changed from 'size' to 'amount'
|
||||||
price: parseFloat(tradeData.entry),
|
price: parseFloat(tradeData.entry),
|
||||||
stopLoss: parseFloat(tradeData.sl),
|
stopLoss: parseFloat(tradeData.sl),
|
||||||
takeProfit: parseFloat(tradeData.tp),
|
takeProfit: parseFloat(tradeData.tp),
|
||||||
leverage: parseInt(tradeData.leverage),
|
leverage: parseInt(tradeData.leverage),
|
||||||
timeframe: tradeData.timeframe
|
timeframe: tradeData.timeframe,
|
||||||
|
orderType: 'MARKET' // Default to market order
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const result = await response.json()
|
const result = await response.json()
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok && result.success) {
|
||||||
// Show success message
|
// Show detailed success message
|
||||||
alert(`Trade executed successfully! Order ID: ${result.orderId || 'N/A'}`)
|
let message = `✅ Trade executed successfully!\n\n`
|
||||||
|
message += `📊 Order ID: ${result.txId}\n`
|
||||||
|
message += `💰 Symbol: ${tradeData.symbol}\n`
|
||||||
|
message += `📈 Size: ${tradeData.size}\n`
|
||||||
|
message += `💵 Entry: $${tradeData.entry}\n`
|
||||||
|
|
||||||
|
if (tradeData.sl) message += `🛑 Stop Loss: $${tradeData.sl}\n`
|
||||||
|
if (tradeData.tp) message += `🎯 Take Profit: $${tradeData.tp}\n`
|
||||||
|
|
||||||
|
if (result.conditionalOrders && result.conditionalOrders.length > 0) {
|
||||||
|
message += `\n🔄 Conditional orders: ${result.conditionalOrders.length} placed`
|
||||||
|
}
|
||||||
|
|
||||||
|
alert(message)
|
||||||
} else {
|
} else {
|
||||||
alert(`Trade failed: ${result.error || 'Unknown error'}`)
|
// Show detailed error message
|
||||||
|
const errorMsg = result.error || 'Unknown error occurred'
|
||||||
|
|
||||||
|
if (errorMsg.includes('insufficient funds') || errorMsg.includes('balance')) {
|
||||||
|
alert(`❌ Trade Failed: Insufficient Balance\n\nPlease deposit funds to your Drift account before placing trades.\n\nError: ${errorMsg}`)
|
||||||
|
} else if (errorMsg.includes('not logged in') || errorMsg.includes('Cannot execute trade')) {
|
||||||
|
alert(`❌ Trade Failed: Authentication Issue\n\nPlease check your Drift connection in the settings.\n\nError: ${errorMsg}`)
|
||||||
|
} else {
|
||||||
|
alert(`❌ Trade Failed\n\nError: ${errorMsg}`)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Trade execution failed:', error)
|
console.error('Trade execution failed:', error)
|
||||||
alert('Trade execution failed. Please check your connection.')
|
alert('❌ Trade execution failed due to network error.\n\nPlease check your connection and try again.')
|
||||||
}
|
}
|
||||||
|
|
||||||
setTradeModalOpen(false)
|
setTradeModalOpen(false)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
import React from 'react'
|
import React, { useEffect } from 'react'
|
||||||
|
|
||||||
interface ScreenshotGalleryProps {
|
interface ScreenshotGalleryProps {
|
||||||
screenshots: string[]
|
screenshots: string[]
|
||||||
@@ -18,8 +18,30 @@ export default function ScreenshotGallery({
|
|||||||
onImageClick,
|
onImageClick,
|
||||||
onClose
|
onClose
|
||||||
}: ScreenshotGalleryProps) {
|
}: ScreenshotGalleryProps) {
|
||||||
|
// Handle ESC key to close enlarged image
|
||||||
|
useEffect(() => {
|
||||||
|
const handleKeyDown = (event: KeyboardEvent) => {
|
||||||
|
if (event.key === 'Escape' && enlargedImage) {
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (enlargedImage) {
|
||||||
|
document.addEventListener('keydown', handleKeyDown)
|
||||||
|
return () => document.removeEventListener('keydown', handleKeyDown)
|
||||||
|
}
|
||||||
|
}, [enlargedImage, onClose])
|
||||||
|
|
||||||
if (screenshots.length === 0) return null
|
if (screenshots.length === 0) return null
|
||||||
|
|
||||||
|
// Helper function to format screenshot URL
|
||||||
|
const formatScreenshotUrl = (screenshot: string) => {
|
||||||
|
// Extract just the filename from the full path
|
||||||
|
const filename = screenshot.split('/').pop() || screenshot
|
||||||
|
// Return the Next.js API route format
|
||||||
|
return `/screenshots/${filename}`
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* Gallery Grid */}
|
{/* Gallery Grid */}
|
||||||
@@ -40,23 +62,25 @@ export default function ScreenshotGallery({
|
|||||||
{screenshots.map((screenshot, index) => {
|
{screenshots.map((screenshot, index) => {
|
||||||
const filename = screenshot.split('/').pop() || ''
|
const filename = screenshot.split('/').pop() || ''
|
||||||
const timeframe = timeframes[index] || 'Unknown'
|
const timeframe = timeframes[index] || 'Unknown'
|
||||||
|
const imageUrl = formatScreenshotUrl(screenshot)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={index}
|
key={index}
|
||||||
className="group relative bg-gray-800/30 rounded-lg overflow-hidden border border-gray-700 hover:border-purple-500/50 transition-all cursor-pointer transform hover:scale-[1.02]"
|
className="group relative bg-gray-800/30 rounded-lg overflow-hidden border border-gray-700 hover:border-purple-500/50 transition-all cursor-pointer transform hover:scale-[1.02]"
|
||||||
onClick={() => onImageClick(screenshot)}
|
onClick={() => onImageClick(imageUrl)}
|
||||||
>
|
>
|
||||||
{/* Preview Image */}
|
{/* Preview Image */}
|
||||||
<div className="aspect-video bg-gray-800 flex items-center justify-center relative">
|
<div className="aspect-video bg-gray-800 flex items-center justify-center relative">
|
||||||
<img
|
<img
|
||||||
src={screenshot}
|
src={imageUrl}
|
||||||
alt={`${symbol} - ${timeframe} chart`}
|
alt={`${symbol} - ${timeframe} chart`}
|
||||||
className="w-full h-full object-cover"
|
className="w-full h-full object-cover"
|
||||||
onError={(e) => {
|
onError={(e: any) => {
|
||||||
const target = e.target as HTMLImageElement
|
const target = e.target as HTMLImageElement
|
||||||
target.style.display = 'none'
|
target.style.display = 'none'
|
||||||
target.nextElementSibling?.classList.remove('hidden')
|
const fallback = target.nextElementSibling as HTMLElement
|
||||||
|
if (fallback) fallback.classList.remove('hidden')
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<div className="hidden absolute inset-0 flex items-center justify-center text-gray-400">
|
<div className="hidden absolute inset-0 flex items-center justify-center text-gray-400">
|
||||||
@@ -97,8 +121,11 @@ export default function ScreenshotGallery({
|
|||||||
|
|
||||||
{/* Enlarged Image Modal */}
|
{/* Enlarged Image Modal */}
|
||||||
{enlargedImage && (
|
{enlargedImage && (
|
||||||
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 z-50">
|
<div
|
||||||
<div className="relative max-w-6xl max-h-[90vh] w-full">
|
className="fixed inset-0 bg-black/80 backdrop-blur-sm flex items-center justify-center p-4 z-50"
|
||||||
|
onClick={onClose}
|
||||||
|
>
|
||||||
|
<div className="relative max-w-6xl max-h-[90vh] w-full" onClick={(e: any) => e.stopPropagation()}>
|
||||||
{/* Close Button */}
|
{/* Close Button */}
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
@@ -112,6 +139,11 @@ export default function ScreenshotGallery({
|
|||||||
src={enlargedImage}
|
src={enlargedImage}
|
||||||
alt="Enlarged chart"
|
alt="Enlarged chart"
|
||||||
className="w-full h-full object-contain rounded-lg border border-gray-600"
|
className="w-full h-full object-contain rounded-lg border border-gray-600"
|
||||||
|
onError={(e: any) => {
|
||||||
|
console.error('Failed to load enlarged image:', enlargedImage)
|
||||||
|
const target = e.target as HTMLImageElement
|
||||||
|
target.alt = 'Failed to load image'
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Image Info Overlay */}
|
{/* Image Info Overlay */}
|
||||||
@@ -122,7 +154,7 @@ export default function ScreenshotGallery({
|
|||||||
<div className="text-gray-300 text-sm">AI analyzed screenshot • High resolution view</div>
|
<div className="text-gray-300 text-sm">AI analyzed screenshot • High resolution view</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-xs text-gray-400">
|
<div className="text-xs text-gray-400">
|
||||||
ESC to close
|
ESC to close • Click outside to close
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user