Files
trading_bot_v3/components/ScreenshotGallery.tsx
mindesbunister 23cab77200 🎉 FIXED: Screenshot gallery preview and enlargement functionality
Major Issues Resolved:
- Screenshot gallery images now load and display correctly
- Click-to-enlarge modal functionality working
- ESC key closes enlarged images (confirmed working)
- Click-outside-to-close functionality added

- Created new /api/image route to serve screenshots (bypasses Next.js 15 route issue)
- Fixed screenshot URL formatting to use query parameter format
- Added proper keyboard event handling for ESC key
- Improved timeframe extraction from screenshot filenames
- Enhanced trade execution error handling with detailed feedback

- Fixed Next.js build cache issues causing route problems
- Cleaned up debugging console logs
- Restored normal conditional gallery rendering
- Proper error handling for image loading failures

The screenshot gallery now fully works:
1. Images display in grid layout 
2. Click any image to enlarge 
3. ESC key closes enlarged view 
4. Click outside modal to close 
5. Proper timeframe labeling 
6. Trade execution shows detailed error messages 
2025-07-14 01:22:30 +02:00

180 lines
7.0 KiB
TypeScript

"use client"
import React, { useEffect } from 'react'
interface ScreenshotGalleryProps {
screenshots: string[]
symbol: string
timeframes: string[]
enlargedImage: string | null
onImageClick: (src: string) => void
onClose: () => void
}
export default function ScreenshotGallery({
screenshots,
symbol,
timeframes,
enlargedImage,
onImageClick,
onClose
}: 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
// Helper function to format screenshot URL
const formatScreenshotUrl = (screenshot: string) => {
// Extract just the filename from the full path
const filename = screenshot.split('/').pop() || screenshot
// Use the new API route with query parameter
return `/api/image?file=${filename}`
}
return (
<>
{/* Gallery Grid */}
<div className="mt-6 p-4 bg-gradient-to-br from-purple-500/10 to-indigo-500/10 border border-purple-500/30 rounded-lg">
<div className="flex items-center justify-between mb-4">
<h4 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>
Chart Screenshots
</h4>
<div className="text-xs text-gray-400">
{screenshots.length} captured Click to enlarge
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{screenshots.map((screenshot, index) => {
const filename = screenshot.split('/').pop() || ''
// Extract timeframe from filename (e.g., SOLUSD_5_ai_timestamp.png -> "5m")
const extractTimeframeFromFilename = (filename: string) => {
const match = filename.match(/_(\d+|D)_/)
if (!match) return 'Unknown'
const tf = match[1]
if (tf === 'D') return '1D'
if (tf === '5') return '5m'
if (tf === '15') return '15m'
if (tf === '60') return '1h'
if (tf === '240') return '4h'
return `${tf}m`
}
const timeframe = timeframes[index] || extractTimeframeFromFilename(filename)
const imageUrl = formatScreenshotUrl(screenshot)
return (
<div
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]"
onClick={() => onImageClick(imageUrl)}
>
{/* Preview Image */}
<div className="aspect-video bg-gray-800 flex items-center justify-center relative">
<img
src={imageUrl}
alt={`${symbol} - ${timeframe} chart`}
className="w-full h-full object-cover"
onError={(e: any) => {
const target = e.target as HTMLImageElement
target.style.display = 'none'
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="text-center">
<div className="text-3xl mb-2">📊</div>
<div className="text-sm">Chart Preview</div>
<div className="text-xs text-gray-500">{filename}</div>
</div>
</div>
{/* Overlay */}
<div className="absolute inset-0 bg-black/0 group-hover:bg-black/20 transition-all flex items-center justify-center">
<div className="opacity-0 group-hover:opacity-100 transition-opacity">
<div className="w-12 h-12 bg-purple-500/80 rounded-full flex items-center justify-center">
<span className="text-white text-xl">🔍</span>
</div>
</div>
</div>
</div>
{/* Image Info */}
<div className="p-3">
<div className="flex items-center justify-between">
<div>
<div className="text-sm font-medium text-white">{symbol}</div>
<div className="text-xs text-purple-300">{timeframe} Timeframe</div>
</div>
<div className="text-xs text-gray-400">
Click to view
</div>
</div>
</div>
</div>
)
})}
</div>
</div>
{/* Enlarged Image Modal */}
{enlargedImage && (
<div
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 */}
<button
onClick={onClose}
className="absolute top-4 right-4 w-10 h-10 bg-black/50 hover:bg-black/70 rounded-full flex items-center justify-center text-white z-10 transition-colors"
>
</button>
{/* Image */}
<img
src={enlargedImage}
alt="Enlarged chart"
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 */}
<div className="absolute bottom-4 left-4 right-4 bg-black/70 backdrop-blur-sm rounded-lg p-4">
<div className="flex items-center justify-between">
<div>
<div className="text-white font-medium">{symbol} Chart Analysis</div>
<div className="text-gray-300 text-sm">AI analyzed screenshot High resolution view</div>
</div>
<div className="text-xs text-gray-400">
ESC to close Click outside to close
</div>
</div>
</div>
</div>
</div>
)}
</>
)
}