- Add tailwind.config.ts with proper content paths and theme config - Add postcss.config.js for Tailwind and autoprefixer processing - Downgrade tailwindcss to v3.4.17 and add missing PostCSS dependencies - Update Dockerfile to clarify build process - Fix UI styling issues in Docker environment
211 lines
9.0 KiB
TypeScript
211 lines
9.0 KiB
TypeScript
"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'
|
||
|
||
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
|
||
})
|
||
|
||
useEffect(() => {
|
||
async function fetchPositions() {
|
||
try {
|
||
const res = await fetch('/api/trading')
|
||
if (res.ok) {
|
||
const data = await res.json()
|
||
setPositions(data.positions || [])
|
||
// Calculate some mock stats for demo
|
||
setStats({
|
||
totalPnL: 1247.50,
|
||
dailyPnL: 67.25,
|
||
winRate: 73.2,
|
||
totalTrades: 156
|
||
})
|
||
} else {
|
||
setError('Failed to load positions')
|
||
}
|
||
} catch (e) {
|
||
setError('Error loading positions')
|
||
}
|
||
setLoading(false)
|
||
}
|
||
fetchPositions()
|
||
}, [])
|
||
|
||
return (
|
||
<div className="space-y-8">
|
||
{/* Stats Cards */}
|
||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||
<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 */}
|
||
<div className="xl:col-span-1 space-y-6">
|
||
<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) || 'BT'}
|
||
</span>
|
||
</div>
|
||
<span className="font-medium text-white">{pos.symbol || 'BTC/USD'}</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 === 'buy'
|
||
? 'bg-green-100 text-green-800'
|
||
: 'bg-red-100 text-red-800'
|
||
}`}>
|
||
{pos.side || 'Long'}
|
||
</span>
|
||
</td>
|
||
<td className="py-4 px-4 text-right font-mono text-gray-300">
|
||
{pos.size || '0.1 BTC'}
|
||
</td>
|
||
<td className="py-4 px-4 text-right font-mono text-gray-300">
|
||
${pos.entryPrice || '45,230.00'}
|
||
</td>
|
||
<td className="py-4 px-4 text-right">
|
||
<span className={`font-mono font-medium ${
|
||
(pos.unrealizedPnl || 125.50) >= 0 ? 'text-green-400' : 'text-red-400'
|
||
}`}>
|
||
{(pos.unrealizedPnl || 125.50) >= 0 ? '+' : ''}${(pos.unrealizedPnl || 125.50).toFixed(2)}
|
||
</span>
|
||
</td>
|
||
</tr>
|
||
))}
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
<TradingHistory />
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|