// src/components/TradingDashboard.tsx
import React, { useEffect } from 'react';
import { useMiniApp, usePositions, useWalletAddress } from '@basedone/miniapp-sdk/react';
import { PricePanel } from './PricePanel';
import { TradingForm } from './TradingForm';
import { PositionsPanel } from './PositionsPanel';
import { OrdersPanel } from './OrdersPanel';
export function TradingDashboard() {
const {
client,
connected,
sessionId,
loading,
error
} = useMiniApp();
if (error) {
return (
<div style={{
padding: '20px',
background: '#ff4444',
borderRadius: '8px',
color: '#fff'
}}>
Error: {error.message}
</div>
);
}
if (!connected || loading) {
return (
<div style={{
padding: '40px',
textAlign: 'center',
fontSize: '18px'
}}>
Connecting to Based Terminal...
</div>
);
}
return (
<div style={{
maxWidth: '1400px',
margin: '0 auto',
padding: '20px'
}}>
<Header sessionId={sessionId} />
<div style={{
display: 'grid',
gridTemplateColumns: '1fr 1fr',
gap: '20px',
marginTop: '20px'
}}>
<PricePanel />
<TradingForm />
<PositionsPanel />
<OrdersPanel />
</div>
</div>
);
}
function Header({ sessionId }: { sessionId?: string }) {
const { address } = useWalletAddress();
return (
<div style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
padding: '20px',
background: '#1a1a1a',
borderRadius: '12px',
border: '1px solid #333'
}}>
<h1 style={{ margin: 0 }}>Trading Dashboard</h1>
<div style={{ textAlign: 'right' }}>
<div style={{ color: '#00ff88', fontSize: '14px' }}>
Session: {sessionId}
</div>
{address && (
<div style={{ color: '#aaa', fontSize: '12px', marginTop: '5px' }}>
Wallet: {address.slice(0, 6)}...{address.slice(-4)}
</div>
)}
</div>
</div>
);
}
// src/components/PricePanel.tsx
export function PricePanel() {
const marketData = useMarketData(['ETH', 'BTC', 'SOL']);
return (
<div style={{
background: '#1a1a1a',
padding: '20px',
borderRadius: '12px',
border: '1px solid #333'
}}>
<h2 style={{ marginTop: 0 }}>Market Prices</h2>
{Object.entries(marketData).map(([symbol, data]) => (
<div key={symbol} style={{
padding: '15px',
background: '#000',
borderRadius: '8px',
marginBottom: '10px'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ fontSize: '18px', fontWeight: 'bold' }}>
{symbol}
</span>
<span style={{ fontSize: '24px', color: '#00ff88' }}>
${data.price?.toFixed(2) || '--'}
</span>
</div>
</div>
))}
</div>
);
}
// src/components/PositionsPanel.tsx
export function PositionsPanel() {
const { client } = useMiniApp();
const { positions, refresh, loading } = usePositions(client);
useEffect(() => {
refresh();
}, [refresh]);
return (
<div style={{
background: '#1a1a1a',
padding: '20px',
borderRadius: '12px',
border: '1px solid #333'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<h2 style={{ margin: 0 }}>Positions</h2>
<button
onClick={refresh}
disabled={loading}
style={{
padding: '8px 16px',
background: '#00ff88',
color: '#000',
border: 'none',
borderRadius: '4px',
cursor: loading ? 'not-allowed' : 'pointer',
fontWeight: 'bold'
}}
>
{loading ? 'Loading...' : 'Refresh'}
</button>
</div>
{positions.length === 0 ? (
<div style={{ textAlign: 'center', padding: '40px', color: '#666' }}>
No open positions
</div>
) : (
<div>
{positions.map((pos: any) => {
const pnl = pos.unrealizedPnl || 0;
const pnlColor = pnl >= 0 ? '#00ff88' : '#ff4444';
return (
<div key={pos.symbol} style={{
padding: '15px',
background: '#000',
borderRadius: '8px',
marginBottom: '10px'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '10px' }}>
<span style={{ fontSize: '18px', fontWeight: 'bold' }}>
{pos.symbol}
</span>
<span>Size: {pos.size}</span>
</div>
<div style={{ fontSize: '14px', color: '#aaa' }}>
Entry: ${pos.entryPrice.toFixed(2)}
</div>
<div style={{ fontSize: '18px', color: pnlColor, fontWeight: 'bold', marginTop: '8px' }}>
PnL: ${pnl.toFixed(2)}
</div>
</div>
);
})}
</div>
)}
</div>
);
}
// src/components/OrdersPanel.tsx
export function OrdersPanel() {
const { client, orders, getOpenOrders, refreshOrders, loading } = useMiniApp();
const { execute: cancelOrder } = useCommand('order.cancel');
useEffect(() => {
getOpenOrders();
}, [getOpenOrders]);
const handleCancel = async (orderId: string, symbol: string) => {
try {
await cancelOrder({ orderId, symbol });
await refreshOrders();
} catch (error: any) {
alert(`Cancel failed: ${error.message}`);
}
};
return (
<div style={{
background: '#1a1a1a',
padding: '20px',
borderRadius: '12px',
border: '1px solid #333'
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '15px' }}>
<h2 style={{ margin: 0 }}>Open Orders</h2>
<button
onClick={refreshOrders}
disabled={loading}
style={{
padding: '8px 16px',
background: '#00ff88',
color: '#000',
border: 'none',
borderRadius: '4px',
cursor: loading ? 'not-allowed' : 'pointer',
fontWeight: 'bold'
}}
>
{loading ? 'Loading...' : 'Refresh'}
</button>
</div>
{orders.length === 0 ? (
<div style={{ textAlign: 'center', padding: '40px', color: '#666' }}>
No open orders
</div>
) : (
<div>
{orders.map((order: any) => (
<div key={order.orderId} style={{
padding: '15px',
background: '#000',
borderRadius: '8px',
marginBottom: '10px',
borderLeft: `3px solid ${order.side === 'buy' ? '#00ff88' : '#ff4444'}`
}}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: '10px' }}>
<span style={{ fontSize: '18px', fontWeight: 'bold' }}>
{order.symbol}
</span>
<span style={{
padding: '4px 8px',
background: order.side === 'buy' ? '#00ff8822' : '#ff444422',
color: order.side === 'buy' ? '#00ff88' : '#ff4444',
borderRadius: '4px',
fontSize: '12px',
fontWeight: 'bold'
}}>
{order.side.toUpperCase()}
</span>
</div>
<div style={{ fontSize: '14px', color: '#aaa', marginBottom: '10px' }}>
{order.size} @ {order.price ? `$${order.price}` : 'Market'}
</div>
<button
onClick={() => handleCancel(order.orderId, order.symbol)}
style={{
padding: '6px 12px',
background: '#ff4444',
color: '#fff',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '12px',
fontWeight: 'bold'
}}
>
Cancel Order
</button>
</div>
))}
</div>
)}
</div>
);
}