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:
mindesbunister
2025-07-14 00:50:39 +02:00
parent aa8ca9846b
commit 045d4a41e3
2 changed files with 71 additions and 17 deletions

View File

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

View File

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