"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 // Utility function to convert timeframe to sortable number const timeframeToMinutes = (timeframe: string): number => { const tf = timeframe.toLowerCase() if (tf.includes('5m') || tf === '5') return 5 if (tf.includes('15m') || tf === '15') return 15 if (tf.includes('30m') || tf === '30') return 30 if (tf.includes('1h') || tf === '60') return 60 if (tf.includes('2h') || tf === '120') return 120 if (tf.includes('4h') || tf === '240') return 240 if (tf.includes('1d') || tf === 'D') return 1440 // Default fallback return parseInt(tf) || 999 } // Extract layout and timeframe from filename const extractInfoFromFilename = (filename: string) => { // Pattern: SYMBOL_TIMEFRAME_LAYOUT_TIMESTAMP.png // e.g., SOLUSD_5_ai_1752749431435.png or SOLUSD_15_Diy module_1752749479893.png const parts = filename.replace('.png', '').split('_') if (parts.length >= 4) { const timeframe = parts[1] const layout = parts.slice(2, -1).join('_') // Handle "Diy module" with space return { timeframe, layout } } // Fallback: try to extract from anywhere in filename const timeframeMatch = filename.match(/_(\d+|D)_/) const layoutMatch = filename.match(/_(ai|Diy module|diy)_/) return { timeframe: timeframeMatch ? timeframeMatch[1] : 'Unknown', layout: layoutMatch ? layoutMatch[1] : 'Unknown' } } // Format timeframe for display const formatTimeframe = (tf: string): string => { if (tf === 'D') return '1D' if (tf === '5') return '5m' if (tf === '15') return '15m' if (tf === '30') return '30m' if (tf === '60') return '1h' if (tf === '120') return '2h' if (tf === '240') return '4h' return `${tf}m` } // Format layout name for display const formatLayoutName = (layout: string): string => { if (layout === 'ai') return 'AI Layout' if (layout === 'Diy module') return 'DIY Module' return layout } // Create screenshot data with extracted info const screenshotData = screenshots.map((screenshot, index) => { const screenshotUrl = typeof screenshot === 'string' ? screenshot : (screenshot as any)?.url || String(screenshot) const filename = screenshotUrl.split('/').pop() || '' const { timeframe, layout } = extractInfoFromFilename(filename) return { screenshot, screenshotUrl, filename, timeframe, layout, displayTimeframe: formatTimeframe(timeframe), displayLayout: formatLayoutName(layout), index, sortOrder: timeframeToMinutes(formatTimeframe(timeframe)) } }) // Group by layout and sort within each group const groupedData = screenshotData.reduce((acc: any, item) => { if (!acc[item.layout]) { acc[item.layout] = [] } acc[item.layout].push(item) return acc }, {}) // Sort each layout group by timeframe and combine // First layout (ai), then second layout (Diy module) const layoutOrder = ['ai', 'Diy module'] const sortedData = layoutOrder.reduce((result: any[], layoutKey) => { if (groupedData[layoutKey]) { const sortedGroup = groupedData[layoutKey].sort((a: any, b: any) => a.sortOrder - b.sortOrder) result.push(...sortedGroup) } return result }, []) // Add any remaining layouts not in the predefined order Object.keys(groupedData).forEach(layoutKey => { if (!layoutOrder.includes(layoutKey)) { const sortedGroup = groupedData[layoutKey].sort((a: any, b: any) => a.sortOrder - b.sortOrder) sortedData.push(...sortedGroup) } }) // Helper function to format screenshot URL const formatScreenshotUrl = (screenshot: string | any) => { // Handle both string URLs and screenshot objects const screenshotUrl = typeof screenshot === 'string' ? screenshot : screenshot.url || screenshot // Extract just the filename from the full path const filename = screenshotUrl.split('/').pop() || screenshotUrl // Use the new API route with query parameter return `/api/image?file=${filename}` } return ( <> {/* Gallery Grid */}

📸 Chart Screenshots

{sortedData.length} captured • Click to enlarge
{sortedData.map((item, displayIndex) => { const imageUrl = formatScreenshotUrl(item.screenshot) return (
onImageClick(imageUrl)} > {/* Preview Image */}
{`${symbol} { const target = e.target as HTMLImageElement target.style.display = 'none' const fallback = target.nextElementSibling as HTMLElement if (fallback) fallback.classList.remove('hidden') }} />
📊
Chart Preview
{item.filename}
{/* Overlay */}
🔍
{/* Image Info */}
{symbol}
{item.displayLayout}
{item.displayTimeframe} Timeframe
Click to view
) })}
{/* Enlarged Image Modal */} {enlargedImage && (
e.stopPropagation()}> {/* Close Button */} {/* Image */} Enlarged chart { console.error('Failed to load enlarged image:', enlargedImage) const target = e.target as HTMLImageElement target.alt = 'Failed to load image' }} /> {/* Image Info Overlay */}
{symbol} Chart Analysis
AI analyzed screenshot • High resolution view
ESC to close • Click outside to close
)} ) }