Fix blank page issue and implement working chart
## Fixed Issues: - ✅ Resolved blank page caused by problematic chart component imports - ✅ Removed broken chart components that had library compatibility issues - ✅ Created SimpleChart component using HTML5 Canvas that works reliably - ✅ Cleaned up test pages and unused components ## Working Features: - ✅ Trading page loads correctly without blank screen - ✅ Professional candlestick chart with grid lines and price labels - ✅ Clean trading interface with all panels visible - ✅ No more loading errors or component failures ## Technical Implementation: - Used native HTML5 Canvas API for chart rendering - Proper TypeScript types and error handling - Responsive design that works in Docker environment - No external library dependencies to cause conflicts The trading dashboard is now stable and functional.
This commit is contained in:
@@ -1,220 +0,0 @@
|
||||
'use client'
|
||||
import React, { useState, useEffect } from 'react'
|
||||
import TradingChart from '../../components/TradingChart'
|
||||
import CompactTradingPanel from '../../components/CompactTradingPanel'
|
||||
|
||||
export default function ChartTradingPageSimple() {
|
||||
const [currentPrice, setCurrentPrice] = useState(166.21)
|
||||
const [positions, setPositions] = useState([
|
||||
// Mock position data for demo
|
||||
{
|
||||
id: 'demo_pos_1',
|
||||
symbol: 'SOL/USDC',
|
||||
side: 'BUY' as 'BUY' | 'SELL',
|
||||
amount: 0.5,
|
||||
entryPrice: 165.50,
|
||||
stopLoss: 160.00,
|
||||
takeProfit: 170.00,
|
||||
currentPrice: 166.21,
|
||||
unrealizedPnl: 0.355,
|
||||
}
|
||||
])
|
||||
const [selectedSymbol, setSelectedSymbol] = useState('SOL')
|
||||
|
||||
const handleTrade = async (tradeData: any) => {
|
||||
try {
|
||||
console.log('Trade executed (demo):', tradeData)
|
||||
|
||||
// Create a mock position for demo
|
||||
const newPosition = {
|
||||
id: `demo_${Date.now()}`,
|
||||
symbol: `${tradeData.symbol}/USDC`,
|
||||
side: tradeData.side,
|
||||
amount: tradeData.amount,
|
||||
entryPrice: tradeData.price,
|
||||
stopLoss: tradeData.stopLoss,
|
||||
takeProfit: tradeData.takeProfit,
|
||||
currentPrice: currentPrice,
|
||||
unrealizedPnl: 0,
|
||||
}
|
||||
|
||||
setPositions(prev => [...prev, newPosition])
|
||||
alert(`Demo trade executed: ${tradeData.side} ${tradeData.amount} ${tradeData.symbol}`)
|
||||
} catch (error) {
|
||||
console.error('Trade execution error:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const handlePriceUpdate = (price: number) => {
|
||||
setCurrentPrice(price)
|
||||
|
||||
// Update position P&L based on new price
|
||||
setPositions(prev => prev.map(pos => ({
|
||||
...pos,
|
||||
currentPrice: price,
|
||||
unrealizedPnl: pos.side === 'BUY'
|
||||
? (price - pos.entryPrice) * pos.amount
|
||||
: (pos.entryPrice - price) * pos.amount
|
||||
})))
|
||||
}
|
||||
|
||||
const handleClosePosition = (positionId: string) => {
|
||||
setPositions(prev => prev.filter(pos => pos.id !== positionId))
|
||||
alert('Position closed (demo)')
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="h-screen bg-gray-900 flex flex-col">
|
||||
{/* Top Bar */}
|
||||
<div className="bg-gray-800 border-b border-gray-700 p-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center space-x-6">
|
||||
<h1 className="text-xl font-bold text-white">Chart Trading Terminal</h1>
|
||||
|
||||
{/* Symbol Selector */}
|
||||
<div className="flex space-x-2">
|
||||
{['SOL', 'BTC', 'ETH'].map(symbol => (
|
||||
<button
|
||||
key={symbol}
|
||||
onClick={() => setSelectedSymbol(symbol)}
|
||||
className={`px-4 py-2 rounded-lg text-sm font-medium transition-all ${
|
||||
selectedSymbol === symbol
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'bg-gray-700 text-gray-300 hover:bg-gray-600'
|
||||
}`}
|
||||
>
|
||||
{symbol}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Market Status */}
|
||||
<div className="flex items-center space-x-4 text-sm">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-400 rounded-full animate-pulse"></div>
|
||||
<span className="text-gray-300">Demo Mode</span>
|
||||
</div>
|
||||
<div className="text-gray-400">
|
||||
{new Date().toLocaleTimeString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Trading Interface */}
|
||||
<div className="flex-1 flex">
|
||||
{/* Chart Area (70% width) */}
|
||||
<div className="flex-1 p-4">
|
||||
<TradingChart
|
||||
symbol={selectedSymbol}
|
||||
positions={positions}
|
||||
onPriceUpdate={handlePriceUpdate}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Trading Panel (30% width) */}
|
||||
<div className="w-96 border-l border-gray-700 p-4 space-y-4">
|
||||
<CompactTradingPanel
|
||||
symbol={selectedSymbol}
|
||||
currentPrice={currentPrice}
|
||||
onTrade={handleTrade}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Panel - Positions */}
|
||||
<div className="border-t border-gray-700 bg-gray-800">
|
||||
<div className="p-4">
|
||||
<div className="flex items-center justify-between mb-4">
|
||||
<div className="flex space-x-6">
|
||||
<button className="text-white font-medium border-b-2 border-blue-500 pb-2">
|
||||
Positions ({positions.length})
|
||||
</button>
|
||||
<button className="text-gray-400 hover:text-white pb-2">
|
||||
Orders (0)
|
||||
</button>
|
||||
<button className="text-gray-400 hover:text-white pb-2">
|
||||
History
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{positions.length > 0 && (
|
||||
<div className="text-sm text-gray-400">
|
||||
Total P&L: <span className={`${
|
||||
positions.reduce((sum, pos) => sum + (pos.unrealizedPnl || 0), 0) >= 0
|
||||
? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
{positions.reduce((sum, pos) => sum + (pos.unrealizedPnl || 0), 0) >= 0 ? '+' : ''}$
|
||||
{positions.reduce((sum, pos) => sum + (pos.unrealizedPnl || 0), 0).toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Positions Table */}
|
||||
<div className="max-h-48 overflow-y-auto">
|
||||
{positions.length === 0 ? (
|
||||
<div className="text-center py-8 text-gray-400">
|
||||
No open positions
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
{positions.map((position: any) => (
|
||||
<div
|
||||
key={position.id}
|
||||
className="bg-gray-900 rounded-lg p-4 flex items-center justify-between"
|
||||
>
|
||||
<div className="flex items-center space-x-4">
|
||||
<div className={`w-3 h-3 rounded-full ${
|
||||
position.side === 'BUY' ? 'bg-green-400' : 'bg-red-400'
|
||||
}`}></div>
|
||||
<div>
|
||||
<div className="text-white font-medium">
|
||||
{position.symbol} • {position.side}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
Size: {position.amount} • Entry: ${position.entryPrice?.toFixed(2)}
|
||||
</div>
|
||||
{position.stopLoss && (
|
||||
<div className="text-xs text-red-400">
|
||||
SL: ${position.stopLoss.toFixed(2)}
|
||||
</div>
|
||||
)}
|
||||
{position.takeProfit && (
|
||||
<div className="text-xs text-green-400">
|
||||
TP: ${position.takeProfit.toFixed(2)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="text-right">
|
||||
<div className="text-white font-medium">
|
||||
${(position.amount * position.currentPrice).toFixed(2)}
|
||||
</div>
|
||||
<div className={`text-sm ${
|
||||
(position.unrealizedPnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'
|
||||
}`}>
|
||||
{(position.unrealizedPnl || 0) >= 0 ? '+' : ''}${(position.unrealizedPnl || 0).toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex space-x-2">
|
||||
<button
|
||||
onClick={() => handleClosePosition(position.id)}
|
||||
className="px-3 py-1 bg-red-600 text-white rounded text-sm hover:bg-red-700"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user