Files
trading_bot_v3/components/Dashboard.tsx
mindesbunister de45349baa Restore working dashboard and TradingView analysis
- Fixed layout conflicts by removing minimal layout.tsx in favor of complete layout.js
- Restored original AI Analysis page with full TradingView integration
- Connected enhanced screenshot API to real TradingView automation service
- Fixed screenshot gallery to handle both string and object formats
- Added image serving API route for screenshot display
- Resolved hydration mismatch issues with suppressHydrationWarning
- All navigation pages working (Analysis, Trading, Automation, Settings)
- TradingView automation successfully capturing screenshots from AI and DIY layouts
- Docker Compose v2 compatibility ensured

Working features:
- Homepage with hero section and status cards
- Navigation menu with Trading Bot branding
- Real TradingView screenshot capture
- AI-powered chart analysis
- Multi-layout support (AI + DIY module)
- Screenshot gallery with image serving
- API endpoints for balance, status, screenshots, trading
2025-07-14 14:21:19 +02:00

298 lines
12 KiB
TypeScript
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client"
import React, { useEffect, useState } from 'react'
import AutoTradingPanel from './AutoTradingPanel'
import TradingHistory from './TradingHistory'
import DeveloperSettings from './DeveloperSettings'
import AIAnalysisPanel from './AIAnalysisPanel'
import SessionStatus from './SessionStatus'
import DriftAccountStatus from './DriftAccountStatus'
import AdvancedTradingPanel from './AdvancedTradingPanel'
export default function Dashboard() {
const [positions, setPositions] = useState<any[]>([])
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
const [stats, setStats] = useState({
totalPnL: 0,
dailyPnL: 0,
winRate: 0,
totalTrades: 0,
accountValue: 0,
netUsdValue: 0
})
useEffect(() => {
async function fetchPositions() {
try {
setLoading(true)
// Get Drift positions
const driftRes = await fetch('/api/drift/positions')
if (driftRes.ok) {
const driftData = await driftRes.json()
if (driftData.positions) {
setPositions(driftData.positions)
// Calculate stats from Drift positions
const totalPnL = driftData.positions.reduce((sum: number, pos: any) => sum + (pos.unrealizedPnl || 0), 0)
setStats(prev => ({
...prev,
totalPnL,
dailyPnL: totalPnL * 0.1, // Approximate daily as 10% of total for demo
totalTrades: driftData.positions.length
}))
// Get account balance for account value
try {
const balanceRes = await fetch('/api/drift/balance')
if (balanceRes.ok) {
const balanceData = await balanceRes.json()
setStats(prev => ({
...prev,
accountValue: balanceData.accountValue || 0,
netUsdValue: balanceData.netUsdValue || 0
}))
}
} catch (e) {
console.warn('Could not fetch balance:', e)
}
} else {
// No positions available - set empty state
setPositions([])
setStats({
totalPnL: 0,
dailyPnL: 0,
winRate: 0,
totalTrades: 0,
accountValue: 0,
netUsdValue: 0
})
}
} else {
// API failed - set empty state and show helpful message
setError('Failed to connect to Drift. Your account may not be initialized. Visit app.drift.trade to create your account.')
setPositions([])
setStats({
totalPnL: 0,
dailyPnL: 0,
winRate: 0,
totalTrades: 0,
accountValue: 0,
netUsdValue: 0
})
}
} catch (e) {
setError('Error connecting to Drift')
console.error('Error:', e)
setPositions([])
setStats({
totalPnL: 0,
dailyPnL: 0,
winRate: 0,
totalTrades: 0,
accountValue: 0,
netUsdValue: 0
})
}
setLoading(false)
}
fetchPositions()
}, [])
return (
<div className="space-y-8">
{/* Stats Cards */}
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-6 gap-6">
<div className="card card-gradient">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-400 text-sm font-medium">Net USD Value</p>
<p className="text-2xl font-bold text-emerald-400">
${stats.netUsdValue.toFixed(2)}
</p>
</div>
<div className="w-12 h-12 bg-emerald-500/20 rounded-full flex items-center justify-center">
<span className="text-emerald-400 text-xl">💎</span>
</div>
</div>
</div>
<div className="card card-gradient">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-400 text-sm font-medium">Account Value</p>
<p className="text-2xl font-bold text-blue-400">
${stats.accountValue.toFixed(2)}
</p>
</div>
<div className="w-12 h-12 bg-blue-500/20 rounded-full flex items-center justify-center">
<span className="text-blue-400 text-xl">🏦</span>
</div>
</div>
</div>
<div className="card card-gradient">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-400 text-sm font-medium">Total P&L</p>
<p className={`text-2xl font-bold ${stats.totalPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{stats.totalPnL >= 0 ? '+' : ''}${stats.totalPnL.toFixed(2)}
</p>
</div>
<div className="w-12 h-12 bg-green-500/20 rounded-full flex items-center justify-center">
<span className="text-green-400 text-xl">📈</span>
</div>
</div>
</div>
<div className="card card-gradient">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-400 text-sm font-medium">Daily P&L</p>
<p className={`text-2xl font-bold ${stats.dailyPnL >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{stats.dailyPnL >= 0 ? '+' : ''}${stats.dailyPnL.toFixed(2)}
</p>
</div>
<div className="w-12 h-12 bg-blue-500/20 rounded-full flex items-center justify-center">
<span className="text-blue-400 text-xl">💰</span>
</div>
</div>
</div>
<div className="card card-gradient">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-400 text-sm font-medium">Win Rate</p>
<p className="text-2xl font-bold text-cyan-400">{stats.winRate}%</p>
</div>
<div className="w-12 h-12 bg-cyan-500/20 rounded-full flex items-center justify-center">
<span className="text-cyan-400 text-xl">🎯</span>
</div>
</div>
</div>
<div className="card card-gradient">
<div className="flex items-center justify-between">
<div>
<p className="text-gray-400 text-sm font-medium">Total Trades</p>
<p className="text-2xl font-bold text-purple-400">{stats.totalTrades}</p>
</div>
<div className="w-12 h-12 bg-purple-500/20 rounded-full flex items-center justify-center">
<span className="text-purple-400 text-xl">🔄</span>
</div>
</div>
</div>
</div>
{/* Main Content Grid */}
<div className="grid grid-cols-1 xl:grid-cols-3 gap-8">
{/* Left Column - Controls & Account Status */}
<div className="xl:col-span-1 space-y-6">
<DriftAccountStatus />
<AdvancedTradingPanel />
<SessionStatus />
<AutoTradingPanel />
<DeveloperSettings />
</div>
{/* Right Column - Analysis & Positions */}
<div className="xl:col-span-2 space-y-6">
<AIAnalysisPanel />
{/* Open Positions */}
<div className="card card-gradient">
<div className="flex items-center justify-between mb-6">
<h2 className="text-xl font-bold text-white flex items-center">
<span className="w-2 h-2 bg-green-400 rounded-full mr-3 animate-pulse"></span>
Open Positions
</h2>
<button className="text-sm text-gray-400 hover:text-gray-300 transition-colors">
View All
</button>
</div>
{loading ? (
<div className="flex items-center justify-center py-8">
<div className="spinner"></div>
<span className="ml-2 text-gray-400">Loading positions...</span>
</div>
) : error ? (
<div className="text-center py-8">
<div className="w-16 h-16 bg-red-500/20 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-red-400 text-2xl"></span>
</div>
<p className="text-red-400 font-medium">{error}</p>
<p className="text-gray-500 text-sm mt-2">Please check your connection and try again</p>
</div>
) : positions.length === 0 ? (
<div className="text-center py-8">
<div className="w-16 h-16 bg-gray-700/50 rounded-full flex items-center justify-center mx-auto mb-4">
<span className="text-gray-400 text-2xl">📊</span>
</div>
<p className="text-gray-400 font-medium">No open positions</p>
<p className="text-gray-500 text-sm mt-2">Start trading to see your positions here</p>
</div>
) : (
<div className="overflow-hidden">
<div className="overflow-x-auto">
<table className="w-full">
<thead>
<tr className="border-b border-gray-700">
<th className="text-left py-3 px-4 text-gray-400 font-medium text-sm">Asset</th>
<th className="text-left py-3 px-4 text-gray-400 font-medium text-sm">Side</th>
<th className="text-right py-3 px-4 text-gray-400 font-medium text-sm">Size</th>
<th className="text-right py-3 px-4 text-gray-400 font-medium text-sm">Entry</th>
<th className="text-right py-3 px-4 text-gray-400 font-medium text-sm">PnL</th>
</tr>
</thead>
<tbody>
{positions.map((pos, i) => (
<tr key={i} className="border-b border-gray-800/50 hover:bg-gray-800/30 transition-colors">
<td className="py-4 px-4">
<div className="flex items-center">
<div className="w-8 h-8 bg-gradient-to-br from-orange-400 to-orange-600 rounded-full flex items-center justify-center mr-3">
<span className="text-white text-xs font-bold">
{pos.symbol?.slice(0, 2) || '--'}
</span>
</div>
<span className="font-medium text-white">{pos.symbol || '--'}</span>
</div>
</td>
<td className="py-4 px-4">
<span className={`inline-flex items-center px-2.5 py-1 rounded-full text-xs font-medium ${
(pos.side === 'LONG' || pos.side === 'long' || pos.side === 'buy')
? 'bg-green-500/20 text-green-400'
: 'bg-red-500/20 text-red-400'
}`}>
{pos.side || '--'}
</span>
</td>
<td className="py-4 px-4 text-right font-mono text-gray-300">
{typeof pos.size === 'number' ? pos.size.toFixed(4) : '--'}
</td>
<td className="py-4 px-4 text-right font-mono text-gray-300">
${typeof pos.entryPrice === 'number' ? pos.entryPrice.toFixed(2) : '--'}
</td>
<td className="py-4 px-4 text-right">
<span className={`font-mono font-medium ${
(pos.unrealizedPnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
}`}>
{(pos.unrealizedPnl || 0) >= 0 ? '+' : ''}${typeof pos.unrealizedPnl === 'number' ? pos.unrealizedPnl.toFixed(2) : '0.00'}
</span>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
)}
</div>
<TradingHistory />
</div>
</div>
</div>
)
}