feat: Implement Jupiter-style trading interface with token selection

- Add 'You're paying' and 'You're receiving' sections with proper token dropdowns
- Implement balance display and MAX button functionality
- Add automatic receiving amount calculation based on paying amount
- Enhance token selector with icons, names, and balance information
- Improve leverage position value calculations and risk warnings
- Update trade execution to use new paying/receiving token structure
- Maintain all existing functionality including stop loss, take profit, and position management

This creates a more intuitive and professional trading interface that matches Jupiter's UX patterns.
This commit is contained in:
mindesbunister
2025-07-16 14:56:53 +02:00
parent db6a020028
commit 0e3a2d7255
14 changed files with 1047 additions and 458 deletions

View File

@@ -1,6 +1,7 @@
'use client'
import React, { useState } from 'react'
import AIAnalysisPanel from '../../components/AIAnalysisPanel.tsx'
import TradeExecutionPanel from '../../components/TradeExecutionPanel.js'
export default function AnalysisPage() {
const [analysisResult, setAnalysisResult] = useState(null)
@@ -15,13 +16,70 @@ export default function AnalysisPage() {
<div className="space-y-8">
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold text-white">AI Analysis</h1>
<p className="text-gray-400 mt-2">Get comprehensive market insights powered by AI analysis</p>
<h1 className="text-3xl font-bold text-white">AI Analysis & Trading</h1>
<p className="text-gray-400 mt-2">Get comprehensive market insights powered by AI analysis and execute trades</p>
</div>
</div>
<div className="max-w-6xl mx-auto">
<AIAnalysisPanel onAnalysisComplete={handleAnalysisComplete} />
<div className="grid grid-cols-1 xl:grid-cols-2 gap-8">
{/* Left Column - AI Analysis */}
<div className="space-y-6">
<div className="bg-gray-900/50 rounded-lg p-6 border border-gray-800">
<h2 className="text-xl font-semibold text-white mb-4">📊 AI Market Analysis</h2>
<AIAnalysisPanel onAnalysisComplete={handleAnalysisComplete} />
</div>
</div>
{/* Right Column - Trading Panel */}
<div className="space-y-6">
<div className="bg-gray-900/50 rounded-lg p-6 border border-gray-800">
<h2 className="text-xl font-semibold text-white mb-4">💰 Execute Trade</h2>
<TradeExecutionPanel
analysis={analysisResult}
symbol={currentSymbol}
/>
</div>
{/* Analysis Summary */}
{analysisResult && (
<div className="bg-blue-900/20 rounded-lg p-6 border border-blue-800">
<h3 className="text-lg font-semibold text-blue-400 mb-3">🎯 Analysis Summary</h3>
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span className="text-gray-400">Symbol:</span>
<span className="text-white font-medium">{currentSymbol}</span>
</div>
<div className="flex justify-between">
<span className="text-gray-400">Sentiment:</span>
<span className={`font-medium ${
analysisResult.sentiment === 'BULLISH' ? 'text-green-400' :
analysisResult.sentiment === 'BEARISH' ? 'text-red-400' : 'text-yellow-400'
}`}>
{analysisResult.sentiment}
</span>
</div>
{analysisResult.entryPrice && (
<div className="flex justify-between">
<span className="text-gray-400">Entry Price:</span>
<span className="text-white font-medium">${analysisResult.entryPrice}</span>
</div>
)}
{analysisResult.stopLoss && (
<div className="flex justify-between">
<span className="text-gray-400">Stop Loss:</span>
<span className="text-red-400 font-medium">${analysisResult.stopLoss}</span>
</div>
)}
{analysisResult.takeProfit && (
<div className="flex justify-between">
<span className="text-gray-400">Take Profit:</span>
<span className="text-green-400 font-medium">${analysisResult.takeProfit}</span>
</div>
)}
</div>
</div>
)}
</div>
</div>
</div>
)

View File

@@ -1,14 +0,0 @@
'use client'
import React from 'react'
import TradingChart from '../../components/TradingChart'
export default function SimpleChartTest() {
return (
<div className="min-h-screen bg-gray-900 p-4">
<div className="max-w-7xl mx-auto">
<h1 className="text-2xl font-bold text-white mb-6">Chart Test</h1>
<TradingChart />
</div>
</div>
)
}

View File

@@ -0,0 +1,595 @@
'use client'
import React, { useState, useRef, useEffect } from 'react'
import SimpleChart from '../../components/SimpleChart'
interface Position {
id: string
symbol: string
side: 'BUY' | 'SELL'
amount: number
entryPrice: number
stopLoss: number
takeProfit: number
currentPrice: number
unrealizedPnl: number
leverage: number
}
export default function ChartTradingDemo() {
const [selectedSymbol, setSelectedSymbol] = useState('SOL')
const [leverage, setLeverage] = useState(1)
const [payingAmount, setPayingAmount] = useState('')
const [payingToken, setPayingToken] = useState('USDC')
const [receivingAmount, setReceivingAmount] = useState('')
const [stopLoss, setStopLoss] = useState('')
const [takeProfit, setTakeProfit] = useState('')
const [isSymbolDropdownOpen, setIsSymbolDropdownOpen] = useState(false)
const [isPayingTokenDropdownOpen, setIsPayingTokenDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const payingTokenDropdownRef = useRef<HTMLDivElement>(null)
const [positions, setPositions] = useState<Position[]>([
// 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,
leverage: 2,
}
])
const symbols = [
{ symbol: 'SOL', name: 'Solana', price: 166.21, change: 2.45, icon: '🟣' },
{ symbol: 'BTC', name: 'Bitcoin', price: 42150.00, change: -1.23, icon: '🟠' },
{ symbol: 'ETH', name: 'Ethereum', price: 2580.50, change: 3.12, icon: '⬜' },
{ symbol: 'BONK', name: 'Bonk', price: 0.00003456, change: 15.67, icon: '🐕' },
{ symbol: 'JUP', name: 'Jupiter', price: 0.8234, change: 5.43, icon: '🪐' },
{ symbol: 'WIF', name: 'dogwifhat', price: 3.42, change: -8.21, icon: '🧢' },
]
const payingTokens = [
{ symbol: 'USDC', name: 'USD Coin', balance: 1234.56, icon: '💵' },
{ symbol: 'USDT', name: 'Tether', balance: 892.34, icon: '💰' },
{ symbol: 'SOL', name: 'Solana', balance: 5.42, icon: '🟣' },
{ symbol: 'BTC', name: 'Bitcoin', balance: 0.025, icon: '🟠' },
{ symbol: 'ETH', name: 'Ethereum', balance: 1.85, icon: '⬜' },
]
// Close dropdown when clicking outside
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsSymbolDropdownOpen(false)
}
if (payingTokenDropdownRef.current && !payingTokenDropdownRef.current.contains(event.target as Node)) {
setIsPayingTokenDropdownOpen(false)
}
}
document.addEventListener('mousedown', handleClickOutside)
return () => {
document.removeEventListener('mousedown', handleClickOutside)
}
}, [])
const getCurrentSymbolData = () => {
return symbols.find(s => s.symbol === selectedSymbol) || symbols[0]
}
const getCurrentPayingTokenData = () => {
return payingTokens.find(t => t.symbol === payingToken) || payingTokens[0]
}
// Calculate receiving amount based on paying amount
useEffect(() => {
if (payingAmount && payingToken === 'USDC') {
const symbolPrice = getCurrentSymbolData().price
const receiving = parseFloat(payingAmount) / symbolPrice
setReceivingAmount(receiving.toFixed(6))
} else if (payingAmount && payingToken === selectedSymbol) {
setReceivingAmount(payingAmount)
} else {
setReceivingAmount('')
}
}, [payingAmount, payingToken, selectedSymbol])
const handleTrade = (side: 'BUY' | 'SELL') => {
const amount = parseFloat(payingAmount)
const symbolData = getCurrentSymbolData()
if (!amount || amount <= 0) {
alert('Please enter a valid amount')
return
}
const newPosition: Position = {
id: `pos_${Date.now()}`,
symbol: `${selectedSymbol}/USDC`,
side,
amount,
entryPrice: symbolData.price,
stopLoss: stopLoss ? parseFloat(stopLoss) : symbolData.price * (side === 'BUY' ? 0.95 : 1.05),
takeProfit: takeProfit ? parseFloat(takeProfit) : symbolData.price * (side === 'BUY' ? 1.05 : 0.95),
currentPrice: symbolData.price,
unrealizedPnl: 0,
leverage,
}
setPositions(prev => [...prev, newPosition])
setPayingAmount('')
setReceivingAmount('')
setStopLoss('')
setTakeProfit('')
alert(`${side} order placed (demo)`)
}
const handleClosePosition = (positionId: string) => {
setPositions(prev => prev.filter(pos => pos.id !== positionId))
alert('Position closed (demo)')
}
return (
<>
<style jsx>{`
.slider::-webkit-slider-thumb {
appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: #eab308;
border: 2px solid #1f2937;
cursor: pointer;
box-shadow: 0 0 0 1px #374151;
}
.slider::-moz-range-thumb {
height: 20px;
width: 20px;
border-radius: 50%;
background: #eab308;
border: 2px solid #1f2937;
cursor: pointer;
box-shadow: 0 0 0 1px #374151;
}
.slider::-webkit-slider-track {
height: 8px;
border-radius: 4px;
}
.slider::-moz-range-track {
height: 8px;
border-radius: 4px;
}
`}</style>
<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 Dropdown */}
<div className="relative" ref={dropdownRef}>
<button
onClick={() => setIsSymbolDropdownOpen(!isSymbolDropdownOpen)}
className="flex items-center space-x-3 bg-gray-700 hover:bg-gray-600 rounded-lg px-4 py-2 text-white transition-all border border-gray-600"
>
<span className="text-lg">{getCurrentSymbolData().icon}</span>
<div className="text-left">
<div className="font-medium">{selectedSymbol}/USDC</div>
<div className="text-xs text-gray-400">{getCurrentSymbolData().name}</div>
</div>
<div className="text-right">
<div className="text-sm font-mono">${getCurrentSymbolData().price.toLocaleString()}</div>
<div className={`text-xs ${getCurrentSymbolData().change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{getCurrentSymbolData().change >= 0 ? '+' : ''}{getCurrentSymbolData().change}%
</div>
</div>
<svg className={`w-4 h-4 transition-transform ${isSymbolDropdownOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Dropdown Menu */}
{isSymbolDropdownOpen && (
<div className="absolute top-full left-0 mt-2 w-80 bg-gray-800 border border-gray-600 rounded-lg shadow-xl z-50 max-h-80 overflow-y-auto">
<div className="p-2">
<div className="text-xs text-gray-400 uppercase tracking-wide px-3 py-2 border-b border-gray-700">
Select Trading Pair
</div>
{symbols.map(({ symbol, name, price, change, icon }) => (
<button
key={symbol}
onClick={() => {
setSelectedSymbol(symbol)
setIsSymbolDropdownOpen(false)
}}
className={`w-full flex items-center space-x-3 px-3 py-3 text-left rounded-lg transition-all ${
selectedSymbol === symbol
? 'bg-blue-600/20 border border-blue-500/30'
: 'hover:bg-gray-700'
}`}
>
<span className="text-lg">{icon}</span>
<div className="flex-1">
<div className="flex items-center justify-between">
<div>
<div className="font-medium text-white">{symbol}/USDC</div>
<div className="text-xs text-gray-400">{name}</div>
</div>
<div className="text-right">
<div className="text-sm font-mono text-white">
${price < 1 ? price.toFixed(8) : price.toLocaleString()}
</div>
<div className={`text-xs ${change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{change >= 0 ? '+' : ''}{change}%
</div>
</div>
</div>
</div>
{selectedSymbol === symbol && (
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
)}
</button>
))}
</div>
</div>
)}
</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">
<SimpleChart symbol={selectedSymbol} positions={positions} />
</div>
{/* Trading Panel (30% width) */}
<div className="w-96 border-l border-gray-700 p-4 space-y-4">
<div className="bg-gray-800 rounded-lg p-4">
<h3 className="text-white text-lg font-semibold mb-4">{selectedSymbol}/USDC</h3>
<div className="text-2xl font-bold text-white mb-2">
${getCurrentSymbolData().price.toLocaleString()}
</div>
<div className={`text-sm ${getCurrentSymbolData().change >= 0 ? 'text-green-400' : 'text-red-400'}`}>
{getCurrentSymbolData().change >= 0 ? '+' : ''}{getCurrentSymbolData().change}%
</div>
{/* Leverage Selector */}
<div className="mt-6">
<div className="flex items-center justify-between mb-3">
<label className="block text-gray-400 text-sm">Leverage</label>
<div className="text-sm text-yellow-400 font-medium">
{leverage}x
</div>
</div>
{/* Leverage Slider */}
<div className="relative">
<input
type="range"
min="1"
max="100"
value={leverage}
onChange={(e) => setLeverage(parseInt(e.target.value))}
className="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer slider"
style={{
background: `linear-gradient(to right, #eab308 0%, #eab308 ${((leverage - 1) / 99) * 100}%, #374151 ${((leverage - 1) / 99) * 100}%, #374151 100%)`
}}
/>
{/* Leverage Marks */}
<div className="flex justify-between mt-2 px-1">
{[1, 2, 5, 10, 20, 50, 100].map((mark) => (
<button
key={mark}
onClick={() => setLeverage(mark)}
className={`text-xs transition-all ${
leverage === mark
? 'text-yellow-400 font-medium'
: 'text-gray-500 hover:text-gray-300'
}`}
>
{mark}x
</button>
))}
</div>
</div>
{/* Leverage Warning */}
{leverage > 10 && (
<div className="mt-2 flex items-center space-x-2 text-xs text-orange-400">
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clipRule="evenodd" />
</svg>
<span>High leverage increases liquidation risk</span>
</div>
)}
</div>
{/* Quick Trade Buttons */}
<div className="mt-6 space-y-3">
<button
onClick={() => handleTrade('BUY')}
disabled={!payingAmount}
className="w-full bg-green-600 hover:bg-green-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white py-3 px-4 rounded-lg font-medium transition-all"
>
Long/Buy {leverage > 1 && `(${leverage}x)`}
</button>
<button
onClick={() => handleTrade('SELL')}
disabled={!payingAmount}
className="w-full bg-red-600 hover:bg-red-700 disabled:bg-gray-600 disabled:cursor-not-allowed text-white py-3 px-4 rounded-lg font-medium transition-all"
>
Short/Sell {leverage > 1 && `(${leverage}x)`}
</button>
</div>
{/* Trade Form */}
<div className="mt-6 space-y-4">
{/* You're Paying Section */}
<div>
<label className="block text-gray-400 text-sm mb-2">You're paying</label>
<div className="bg-gray-700 rounded-lg p-3 border border-gray-600">
<div className="flex items-center justify-between">
<input
type="number"
value={payingAmount}
onChange={(e) => setPayingAmount(e.target.value)}
placeholder="0.00"
step="0.01"
min="0"
className="bg-transparent text-white text-lg font-medium focus:outline-none flex-1 mr-3"
/>
{/* Paying Token Selector */}
<div className="relative" ref={payingTokenDropdownRef}>
<button
onClick={() => setIsPayingTokenDropdownOpen(!isPayingTokenDropdownOpen)}
className="flex items-center space-x-2 bg-gray-600 hover:bg-gray-500 rounded-lg px-3 py-2 transition-all"
>
<span className="text-sm">{getCurrentPayingTokenData().icon}</span>
<span className="text-white font-medium">{payingToken}</span>
<svg className={`w-4 h-4 text-gray-300 transition-transform ${isPayingTokenDropdownOpen ? 'rotate-180' : ''}`} fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
</svg>
</button>
{/* Paying Token Dropdown */}
{isPayingTokenDropdownOpen && (
<div className="absolute top-full right-0 mt-2 w-64 bg-gray-800 border border-gray-600 rounded-lg shadow-xl z-50">
<div className="p-2">
{payingTokens.map(({ symbol, name, balance, icon }) => (
<button
key={symbol}
onClick={() => {
setPayingToken(symbol)
setIsPayingTokenDropdownOpen(false)
}}
className={`w-full flex items-center justify-between px-3 py-2 text-left rounded-lg transition-all ${
payingToken === symbol
? 'bg-blue-600/20 border border-blue-500/30'
: 'hover:bg-gray-700'
}`}
>
<div className="flex items-center space-x-2">
<span className="text-sm">{icon}</span>
<div>
<div className="text-white font-medium">{symbol}</div>
<div className="text-xs text-gray-400">{name}</div>
</div>
</div>
<div className="text-xs text-gray-400">
{balance.toLocaleString()}
</div>
</button>
))}
</div>
</div>
)}
</div>
</div>
{/* Balance and MAX button */}
<div className="flex items-center justify-between mt-2 text-xs text-gray-400">
<span>Balance: {getCurrentPayingTokenData().balance.toLocaleString()} {payingToken}</span>
<button
onClick={() => setPayingAmount(getCurrentPayingTokenData().balance.toString())}
className="text-blue-400 hover:text-blue-300"
>
MAX
</button>
</div>
</div>
{leverage > 1 && payingAmount && (
<div className="text-xs text-yellow-400 mt-1">
Position value: ${(parseFloat(payingAmount) * leverage).toLocaleString()} ({leverage}x leverage)
</div>
)}
</div>
{/* You're Receiving Section */}
<div>
<label className="block text-gray-400 text-sm mb-2">You're receiving</label>
<div className="bg-gray-700 rounded-lg p-3 border border-gray-600">
<div className="flex items-center justify-between">
<div className="text-lg font-medium text-white">
{receivingAmount || '0.00'}
</div>
<div className="flex items-center space-x-2 bg-gray-600 rounded-lg px-3 py-2">
<span className="text-sm">{getCurrentSymbolData().icon}</span>
<span className="text-white font-medium">{selectedSymbol}</span>
</div>
</div>
<div className="text-xs text-gray-400 mt-2">
Rate: 1 {payingToken} = {payingToken === 'USDC' ? (1 / getCurrentSymbolData().price).toFixed(8) : '1.00'} {selectedSymbol}
</div>
</div>
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-gray-400 text-sm mb-2">Stop Loss</label>
<input
type="number"
value={stopLoss}
onChange={(e) => setStopLoss(e.target.value)}
placeholder="Optional"
step="0.01"
min="0"
className="w-full bg-gray-700 text-white rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
<div>
<label className="block text-gray-400 text-sm mb-2">Take Profit</label>
<input
type="number"
value={takeProfit}
onChange={(e) => setTakeProfit(e.target.value)}
placeholder="Optional"
step="0.01"
min="0"
className="w-full bg-gray-700 text-white rounded-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
</div>
</div>
{/* Risk Information */}
{payingAmount && leverage > 1 && (
<div className="bg-yellow-900/20 border border-yellow-600/20 rounded-lg p-3">
<div className="text-yellow-400 text-xs font-medium mb-1">⚠️ Leveraged Position Risk</div>
<div className="text-xs text-gray-300">
Position value: ${(parseFloat(payingAmount) * leverage).toLocaleString()} ({leverage}x leverage)
</div>
<div className="text-xs text-gray-300">
Liquidation risk: High with {leverage}x leverage
</div>
</div>
)}
</div>
</div>
</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
? 'text-green-400' : 'text-red-400'
}`}>
{positions.reduce((sum, pos) => sum + pos.unrealizedPnl, 0) >= 0 ? '+' : ''}
${positions.reduce((sum, pos) => sum + pos.unrealizedPnl, 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: Position) => (
<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}
{position.leverage > 1 && (
<span className="ml-2 text-yellow-400 text-sm">
{position.leverage}x
</span>
)}
</div>
<div className="text-sm text-gray-400">
Size: {position.amount} • Entry: ${position.entryPrice?.toFixed(2)}
</div>
<div className="text-xs text-red-400">
SL: ${position.stopLoss.toFixed(2)}
</div>
<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 ? 'text-green-400' : 'text-red-400'
}`}>
{position.unrealizedPnl >= 0 ? '+' : ''}${(position.unrealizedPnl || 0).toFixed(2)}
</div>
{position.leverage > 1 && (
<div className="text-xs text-yellow-400">
{position.leverage}x Leveraged
</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 transition-all"
>
Close
</button>
</div>
</div>
))}
</div>
)}
</div>
</div>
</div>
</div>
</>
)
}

25
app/error.tsx Normal file
View File

@@ -0,0 +1,25 @@
'use client'
import React from 'react'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="text-center">
<h1 className="text-4xl font-bold text-red-400 mb-4">Something went wrong!</h1>
<p className="text-gray-400 mb-8">An error occurred while loading this page.</p>
<button
onClick={reset}
className="bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-colors"
>
Try again
</button>
</div>
</div>
)
}

21
app/not-found.tsx Normal file
View File

@@ -0,0 +1,21 @@
'use client'
import React from 'react'
import Link from 'next/link'
export default function NotFound() {
return (
<div className="min-h-screen bg-gray-900 flex items-center justify-center">
<div className="text-center">
<h1 className="text-6xl font-bold text-white mb-4">404</h1>
<h2 className="text-2xl font-semibold text-gray-300 mb-8">Page Not Found</h2>
<p className="text-gray-400 mb-8">The page you're looking for doesn't exist.</p>
<Link
href="/"
className="bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg transition-colors"
>
Go Back Home
</Link>
</div>
</div>
)
}

View File

@@ -54,6 +54,59 @@ export default function HomePage() {
</a>
</div>
</div>
{/* Featured: Chart Trading Demo */}
<div className="card card-gradient">
<div className="flex items-center justify-between mb-6">
<div>
<h2 className="text-xl font-semibold text-white mb-2">🚀 Professional Chart Trading</h2>
<p className="text-gray-400">Advanced trading interface with leverage, charting, and position management</p>
</div>
<div className="text-right">
<span className="inline-block px-3 py-1 bg-yellow-600/20 text-yellow-400 rounded-full text-sm font-medium">NEW</span>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div className="flex items-center space-x-3">
<div className="w-8 h-8 bg-green-500/20 rounded-full flex items-center justify-center">
<span className="text-green-400 text-sm">📈</span>
</div>
<div>
<div className="text-white font-medium">Real-time Charts</div>
<div className="text-gray-400 text-sm">Professional candlestick visualization</div>
</div>
</div>
<div className="flex items-center space-x-3">
<div className="w-8 h-8 bg-yellow-500/20 rounded-full flex items-center justify-center">
<span className="text-yellow-400 text-sm"></span>
</div>
<div>
<div className="text-white font-medium">Leverage Trading</div>
<div className="text-gray-400 text-sm">1x to 100x leverage options</div>
</div>
</div>
<div className="flex items-center space-x-3">
<div className="w-8 h-8 bg-blue-500/20 rounded-full flex items-center justify-center">
<span className="text-blue-400 text-sm">🎯</span>
</div>
<div>
<div className="text-white font-medium">Position Management</div>
<div className="text-gray-400 text-sm">Stop loss & take profit</div>
</div>
</div>
</div>
<a
href="/chart-trading-demo"
className="inline-flex items-center px-6 py-3 bg-gradient-to-r from-blue-600 to-purple-600 text-white rounded-lg hover:from-blue-700 hover:to-purple-700 transition-all transform hover:scale-105 font-medium"
>
<span className="mr-2">🚀</span>
Launch Chart Trading Terminal
</a>
</div>
</div>
)
}

View File

@@ -1,115 +0,0 @@
'use client'
import React, { useEffect, useRef, useState } from 'react'
export default function SimpleChart() {
const chartContainerRef = useRef<HTMLDivElement>(null)
const [status, setStatus] = useState('Initializing...')
useEffect(() => {
if (!chartContainerRef.current) return
const initChart = async () => {
try {
setStatus('Loading lightweight-charts...')
console.log('Importing lightweight-charts...')
const LightweightCharts = await import('lightweight-charts')
console.log('Lightweight charts imported successfully')
setStatus('Creating chart...')
const { createChart, ColorType, CandlestickSeries } = LightweightCharts
const chart = createChart(chartContainerRef.current!, {
layout: {
background: { type: ColorType.Solid, color: '#1a1a1a' },
textColor: '#ffffff',
},
width: chartContainerRef.current!.clientWidth || 800,
height: 400,
grid: {
vertLines: { color: 'rgba(42, 46, 57, 0.5)' },
horzLines: { color: 'rgba(42, 46, 57, 0.5)' },
},
})
setStatus('Adding candlestick series...')
console.log('Chart created, adding candlestick series...')
const candlestickSeries = chart.addSeries(CandlestickSeries, {
upColor: '#26a69a',
downColor: '#ef5350',
borderDownColor: '#ef5350',
borderUpColor: '#26a69a',
wickDownColor: '#ef5350',
wickUpColor: '#26a69a',
})
// Generate sample data
const data = []
const baseTime = new Date(Date.now() - 100 * 60 * 1000) // 100 minutes ago
let price = 166.5
for (let i = 0; i < 100; i++) {
const currentTime = new Date(baseTime.getTime() + i * 60 * 1000) // 1 minute intervals
const timeString = currentTime.toISOString().split('T')[0] // YYYY-MM-DD format
const change = (Math.random() - 0.5) * 2 // Random price change
const open = price
const close = price + change
const high = Math.max(open, close) + Math.random() * 1
const low = Math.min(open, close) - Math.random() * 1
data.push({
time: timeString,
open: Number(open.toFixed(2)),
high: Number(high.toFixed(2)),
low: Number(low.toFixed(2)),
close: Number(close.toFixed(2)),
})
price = close
}
console.log('Setting chart data...', data.length, 'points')
candlestickSeries.setData(data)
setStatus('Chart loaded successfully!')
console.log('Chart created successfully!')
// Handle resize
const handleResize = () => {
if (chartContainerRef.current) {
chart.applyOptions({
width: chartContainerRef.current.clientWidth,
})
}
}
window.addEventListener('resize', handleResize)
return () => {
window.removeEventListener('resize', handleResize)
chart.remove()
}
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error)
console.error('Error creating chart:', error)
setStatus(`Error: ${errorMessage}`)
}
}
initChart()
}, [])
return (
<div className="min-h-screen bg-gray-900 p-8">
<h1 className="text-white text-2xl mb-4">Lightweight Charts Test</h1>
<div className="text-gray-400 mb-4">Status: {status}</div>
<div
ref={chartContainerRef}
className="bg-gray-800 rounded w-full h-96"
style={{ minHeight: '400px' }}
/>
</div>
)
}