feat: Add interactive Trade Follow-up Assistant with ChatGPT integration
- Interactive chat interface to ask questions about active trades - Automatic position detection and context-aware responses - Fresh screenshot capture with updated market analysis - Smart conversation flow with trade-specific insights - Quick action buttons for common trade management questions - TradeFollowUpPanel.tsx: Full-featured chat interface with position tracking - /api/trade-followup: GPT-4o mini integration with screenshot analysis - Enhanced AIAnalysisPanel with Follow-up button integration - 'Should I exit now?' - Real-time exit recommendations - 'Update my stop loss' - SL adjustment guidance based on current conditions - 'Move to break even' - Risk-free position management - 'Current market analysis' - Fresh chart analysis with updated screenshots - 'Risk assessment' - Position risk evaluation - 'Take profit strategy' - TP optimization recommendations - Enter trade based on AI analysis → Use Follow-up for ongoing management - Ask specific questions: 'Is this still a valid setup?' - Get updated analysis: 'What do the charts look like now?' - Risk management: 'Should I move my stop loss?' - Exit timing: 'Is this a good time to take profits?' The assistant provides context-aware guidance by: Tracking your current position details (entry, size, P&L) Capturing fresh screenshots when needed for updated analysis Combining position context with current market conditions Providing specific price levels and actionable advice Maintaining conversation history for continuity Perfect for traders who want ongoing AI guidance throughout their trades!
This commit is contained in:
@@ -157,84 +157,154 @@ export default function ScreenshotGallery({
|
||||
📸
|
||||
</span>
|
||||
Chart Screenshots
|
||||
{sortedData.length === 2 && (
|
||||
<span className="ml-2 text-xs bg-purple-500/20 text-purple-300 px-2 py-1 rounded-full border border-purple-500/30">
|
||||
Side-by-Side View
|
||||
</span>
|
||||
)}
|
||||
</h4>
|
||||
<div className="text-xs text-gray-400">
|
||||
{sortedData.length} captured • Click to enlarge
|
||||
{screenshots.length} captured • Click to enlarge
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={`grid gap-4 ${
|
||||
sortedData.length === 1 ? 'grid-cols-1 max-w-md mx-auto' :
|
||||
sortedData.length === 2 ? 'grid-cols-1 md:grid-cols-2' :
|
||||
sortedData.length === 3 ? 'grid-cols-1 md:grid-cols-2 lg:grid-cols-3' :
|
||||
sortedData.length === 4 ? 'grid-cols-1 md:grid-cols-2 xl:grid-cols-4' :
|
||||
'grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'
|
||||
}`}>
|
||||
{sortedData.map((item, displayIndex) => {
|
||||
const imageUrl = formatScreenshotUrl(item.screenshot)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={displayIndex}
|
||||
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] ${
|
||||
sortedData.length === 2 ? 'lg:aspect-[4/3]' : ''
|
||||
}`}
|
||||
onClick={() => onImageClick(imageUrl)}
|
||||
>
|
||||
{/* Preview Image */}
|
||||
<div className={`bg-gray-800 flex items-center justify-center relative ${
|
||||
sortedData.length === 2 ? 'aspect-[16/10] lg:aspect-[4/3]' : 'aspect-video'
|
||||
}`}>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`${symbol} - ${item.displayLayout} - ${item.displayTimeframe} 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">{item.filename}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="space-y-6">
|
||||
{/* AI Layout Screenshots */}
|
||||
{groupedData['ai'] && groupedData['ai'].length > 0 && (
|
||||
<div>
|
||||
<h5 className="text-sm font-medium text-purple-300 mb-3 flex items-center">
|
||||
<span className="w-4 h-4 bg-gradient-to-br from-blue-400 to-blue-600 rounded mr-2 flex items-center justify-center text-xs">🤖</span>
|
||||
AI Layout
|
||||
</h5>
|
||||
<div className={`grid gap-4 ${
|
||||
groupedData['ai'].length === 1 ? 'grid-cols-1' :
|
||||
'grid-cols-1 md:grid-cols-2'
|
||||
}`}>
|
||||
{groupedData['ai'].sort((a: any, b: any) => a.sortOrder - b.sortOrder).map((item: any, displayIndex: number) => {
|
||||
const imageUrl = formatScreenshotUrl(item.screenshot)
|
||||
|
||||
{/* 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>
|
||||
return (
|
||||
<div
|
||||
key={`ai-${displayIndex}`}
|
||||
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} - ${item.displayLayout} - ${item.displayTimeframe} 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">{item.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">{item.displayLayout}</div>
|
||||
<div className="text-xs text-gray-400">{item.displayTimeframe} Timeframe</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
Click to view
|
||||
</div>
|
||||
</div>
|
||||
</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">{item.displayLayout}</div>
|
||||
<div className="text-xs text-gray-400">{item.displayTimeframe} Timeframe</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
Click to view
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* DIY Layout Screenshots */}
|
||||
{groupedData['Diy module'] && groupedData['Diy module'].length > 0 && (
|
||||
<div>
|
||||
<h5 className="text-sm font-medium text-green-300 mb-3 flex items-center">
|
||||
<span className="w-4 h-4 bg-gradient-to-br from-green-400 to-green-600 rounded mr-2 flex items-center justify-center text-xs">🔧</span>
|
||||
DIY Module
|
||||
</h5>
|
||||
<div className={`grid gap-4 ${
|
||||
groupedData['Diy module'].length === 1 ? 'grid-cols-1' :
|
||||
'grid-cols-1 md:grid-cols-2'
|
||||
}`}>
|
||||
{groupedData['Diy module'].sort((a: any, b: any) => a.sortOrder - b.sortOrder).map((item: any, displayIndex: number) => {
|
||||
const imageUrl = formatScreenshotUrl(item.screenshot)
|
||||
|
||||
return (
|
||||
<div
|
||||
key={`diy-${displayIndex}`}
|
||||
className="group relative bg-gray-800/30 rounded-lg overflow-hidden border border-gray-700 hover:border-green-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} - ${item.displayLayout} - ${item.displayTimeframe} 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">{item.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-green-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-green-300">{item.displayLayout}</div>
|
||||
<div className="text-xs text-gray-400">{item.displayTimeframe} Timeframe</div>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400">
|
||||
Click to view
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user