Refactor: Redirect dashboard to /paper for now

This commit is contained in:
2026-03-15 16:51:30 -07:00
parent 5ce2fa6924
commit 32341e76b0

View File

@@ -1,360 +1,5 @@
'use client';
import { useState, useEffect } from 'react';
const GREEN = '#28CC95';
const RED = '#FF6B6B';
import { redirect } from 'next/navigation';
export default function Dashboard() {
const [data, setData] = useState(null);
const [trades, setTrades] = useState([]);
const [loading, setLoading] = useState(true);
const [tab, setTab] = useState('market');
useEffect(() => {
const fetchState = async () => {
try {
const res = await fetch('/api/state');
const json = await res.json();
setData(json);
setLoading(false);
} catch (e) {
console.error('State fetch error:', e);
}
};
const fetchTrades = async () => {
try {
const res = await fetch('/api/trades');
const json = await res.json();
setTrades(json.trades || []);
} catch (e) {
console.error('Trades fetch error:', e);
}
};
fetchState();
fetchTrades();
const interval = setInterval(fetchState, 2000);
const tradesInterval = setInterval(fetchTrades, 10000);
return () => {
clearInterval(interval);
clearInterval(tradesInterval);
};
}, []);
if (loading) {
return (
<div className="min-h-screen bg-[#0a0a0a] flex items-center justify-center">
<div className="text-[#28CC95] text-lg animate-pulse">Loading Kalbot...</div>
</div>
);
}
const market = data?.market;
const paper = data?.paper;
const strategies = data?.strategies || [];
return (
<div className="min-h-screen bg-[#0a0a0a] text-white font-sans pb-20">
{/* Header */}
<header className="sticky top-0 z-50 bg-[#0a0a0a]/95 backdrop-blur border-b border-white/10 px-4 py-3">
<div className="flex items-center justify-between max-w-lg mx-auto">
<h1 className="text-lg font-bold" style={{ color: GREEN }}>Kalbot</h1>
<div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${data?.lastUpdate ? 'bg-green-400 animate-pulse' : 'bg-red-500'}`} />
<span className="text-xs text-gray-400">
{data?.lastUpdate ? 'Live' : 'Offline'}
</span>
</div>
</div>
</header>
<main className="max-w-lg mx-auto px-4 pt-4 space-y-4">
{/* Market Card */}
<MarketCard market={market} />
{/* Paper Stats */}
<PaperStats paper={paper} />
{/* Tab Bar */}
<div className="flex gap-1 bg-white/5 rounded-lg p-1">
{['market', 'strategies', 'trades'].map(t => (
<button
key={t}
onClick={() => setTab(t)}
className={`flex-1 py-2 px-3 rounded-md text-sm font-medium transition-all ${
tab === t ? 'bg-white/10 text-white' : 'text-gray-500 hover:text-gray-300'
}`}
>
{t.charAt(0).toUpperCase() + t.slice(1)}
</button>
))}
</div>
{/* Tab Content */}
{tab === 'market' && <MarketDetails market={market} />}
{tab === 'strategies' && <StrategiesView strategies={strategies} />}
{tab === 'trades' && <TradesView trades={trades} openPositions={paper?.openPositions || []} />}
</main>
{/* Worker Uptime */}
<div className="fixed bottom-0 left-0 right-0 bg-[#0a0a0a]/95 backdrop-blur border-t border-white/5 py-2 px-4">
<div className="max-w-lg mx-auto flex justify-between text-xs text-gray-600">
<span>Worker uptime: {formatUptime(data?.workerUptime)}</span>
<span>Updated: {data?.lastUpdate ? new Date(data.lastUpdate).toLocaleTimeString() : 'never'}</span>
</div>
</div>
</div>
);
}
function MarketCard({ market }) {
if (!market) {
return (
<div className="bg-white/5 rounded-2xl p-5 border border-white/10">
<p className="text-gray-500 text-center">No active market waiting for next 15-min window...</p>
</div>
);
}
const timeLeft = market.closeTime ? getTimeLeft(market.closeTime) : null;
return (
<div className="bg-white/5 rounded-2xl p-5 border border-white/10 space-y-4">
{/* Title */}
<div className="flex items-center justify-between">
<div>
<h2 className="font-bold text-base">BTC Up or Down</h2>
<p className="text-xs text-gray-400">15 minutes</p>
</div>
<div className="flex items-center gap-2">
{timeLeft && (
<span className="text-xs bg-white/10 px-2 py-1 rounded-full text-gray-300">
{timeLeft}
</span>
)}
<span className="text-2xl"></span>
</div>
</div>
{/* Up */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Up</span>
<div className="flex items-center gap-3">
<span className="text-sm text-gray-400">{market.yesOdds}x</span>
<span className="text-sm font-bold px-3 py-1 rounded-full border"
style={{ borderColor: GREEN, color: GREEN }}>
{market.yesPct}%
</span>
</div>
</div>
<div className="w-full bg-white/10 rounded-full h-2">
<div className="h-2 rounded-full transition-all duration-500"
style={{ width: `${market.yesPct}%`, backgroundColor: GREEN }} />
</div>
</div>
{/* Down */}
<div className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Down</span>
<div className="flex items-center gap-3">
<span className="text-sm text-gray-400">{market.noOdds}x</span>
<span className="text-sm font-bold px-3 py-1 rounded-full border"
style={{ borderColor: '#4A90D9', color: '#4A90D9' }}>
{market.noPct}%
</span>
</div>
</div>
<div className="w-full bg-white/10 rounded-full h-2">
<div className="h-2 rounded-full transition-all duration-500"
style={{ width: `${market.noPct}%`, backgroundColor: '#4A90D9' }} />
</div>
</div>
{/* Volume */}
<div className="flex justify-between text-xs text-gray-500 pt-1">
<span>${(market.volume || 0).toLocaleString()} vol</span>
<span className="font-mono text-gray-600">{market.ticker}</span>
</div>
</div>
);
}
function PaperStats({ paper }) {
if (!paper) return null;
const pnlColor = paper.totalPnL >= 0 ? GREEN : RED;
return (
<div className="grid grid-cols-4 gap-2">
<StatBox label="Balance" value={`$${paper.balance}`} />
<StatBox label="PnL" value={`${paper.totalPnL >= 0 ? '+' : ''}$${paper.totalPnL}`} color={pnlColor} />
<StatBox label="Win Rate" value={`${paper.winRate}%`} color={paper.winRate >= 50 ? GREEN : RED} />
<StatBox label="Trades" value={paper.totalTrades} />
</div>
);
}
function StatBox({ label, value, color }) {
return (
<div className="bg-white/5 rounded-xl p-3 border border-white/5 text-center">
<p className="text-[10px] text-gray-500 uppercase tracking-wider">{label}</p>
<p className="text-sm font-bold mt-0.5" style={color ? { color } : {}}>{value}</p>
</div>
);
}
function MarketDetails({ market }) {
if (!market) return <p className="text-gray-500 text-sm text-center py-8">No market data</p>;
const rows = [
['Yes Bid / Ask', `${market.yesBid || '-'}¢ / ${market.yesAsk || '-'}¢`],
['No Bid / Ask', `${market.noBid || '-'}¢ / ${market.noAsk || '-'}¢`],
['Last Price', `${market.lastPrice || '-'}¢`],
['Volume 24h', `$${(market.volume24h || 0).toLocaleString()}`],
['Open Interest', (market.openInterest || 0).toLocaleString()],
['Status', market.status || 'unknown'],
['Closes', market.closeTime ? new Date(market.closeTime).toLocaleTimeString() : '-'],
];
return (
<div className="bg-white/5 rounded-xl border border-white/5 overflow-hidden">
{rows.map(([k, v], i) => (
<div key={k} className={`flex justify-between px-4 py-3 ${i < rows.length - 1 ? 'border-b border-white/5' : ''}`}>
<span className="text-sm text-gray-400">{k}</span>
<span className="text-sm font-medium">{v}</span>
</div>
))}
</div>
);
}
function StrategiesView({ strategies }) {
if (!strategies.length) {
return <p className="text-gray-500 text-sm text-center py-8">No strategies loaded</p>;
}
return (
<div className="space-y-3">
{strategies.map((s, i) => (
<div key={i} className="bg-white/5 rounded-xl p-4 border border-white/5">
<div className="flex items-center justify-between mb-2">
<div className="flex items-center gap-2">
<span className={`w-2 h-2 rounded-full ${s.enabled && !s.paused ? 'bg-green-400' : 'bg-red-500'}`} />
<span className="font-bold text-sm capitalize">{s.name}</span>
</div>
<span className="text-xs px-2 py-0.5 rounded-full bg-white/10 text-gray-400">{s.mode}</span>
</div>
<div className="space-y-1 text-xs text-gray-400">
{s.config && Object.entries(s.config).map(([k, v]) => (
<div key={k} className="flex justify-between">
<span>{k}</span>
<span className="text-gray-300">{typeof v === 'number' ? v : String(v)}</span>
</div>
))}
{s.consecutiveLosses !== undefined && (
<div className="flex justify-between mt-1 pt-1 border-t border-white/5">
<span>Consecutive Losses</span>
<span className={s.consecutiveLosses > 0 ? 'text-red-400' : 'text-gray-300'}>
{s.consecutiveLosses}
</span>
</div>
)}
{s.currentBetSize !== undefined && (
<div className="flex justify-between">
<span>Next Bet</span>
<span className="text-gray-300">${s.currentBetSize}</span>
</div>
)}
{s.paused && (
<div className="text-red-400 font-medium mt-1"> PAUSED max losses reached</div>
)}
</div>
</div>
))}
</div>
);
}
function TradesView({ trades, openPositions }) {
return (
<div className="space-y-3">
{/* Open Positions */}
{openPositions.length > 0 && (
<div>
<h3 className="text-xs text-gray-500 uppercase tracking-wider mb-2">Open Positions</h3>
{openPositions.map((t, i) => (
<TradeRow key={i} trade={t} isOpen />
))}
</div>
)}
{/* Trade History */}
<div>
<h3 className="text-xs text-gray-500 uppercase tracking-wider mb-2">
History ({trades.length})
</h3>
{trades.length === 0 ? (
<p className="text-gray-600 text-sm text-center py-6">No trades yet. Strategies are watching...</p>
) : (
trades.map((t, i) => <TradeRow key={i} trade={t} />)
)}
</div>
</div>
);
}
function TradeRow({ trade, isOpen }) {
const won = trade.pnl > 0;
const pnlColor = trade.pnl == null ? 'text-gray-400' : won ? 'text-green-400' : 'text-red-400';
return (
<div className="bg-white/5 rounded-lg p-3 border border-white/5 mb-2">
<div className="flex items-center justify-between">
<div className="flex items-center gap-2">
{isOpen ? (
<span className="w-2 h-2 rounded-full bg-yellow-400 animate-pulse" />
) : (
<span>{won ? '✅' : '❌'}</span>
)}
<span className="text-sm font-medium capitalize">{trade.side}</span>
<span className="text-xs text-gray-500">@ {trade.price}¢</span>
</div>
<span className={`text-sm font-bold ${pnlColor}`}>
{trade.pnl != null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl}` : 'open'}
</span>
</div>
<div className="flex justify-between mt-1">
<span className="text-[10px] text-gray-600 capitalize">{trade.strategy}</span>
<span className="text-[10px] text-gray-600">
{trade.entryTime ? new Date(trade.entryTime).toLocaleTimeString() : ''}
</span>
</div>
{trade.reason && (
<p className="text-[10px] text-gray-600 mt-1 truncate">{trade.reason}</p>
)}
</div>
);
}
function getTimeLeft(closeTime) {
const diff = new Date(closeTime).getTime() - Date.now();
if (diff <= 0) return 'Closing...';
const mins = Math.floor(diff / 60000);
const secs = Math.floor((diff % 60000) / 1000);
return `${mins}:${secs.toString().padStart(2, '0')}`;
}
function formatUptime(seconds) {
if (!seconds) return '0s';
const h = Math.floor(seconds / 3600);
const m = Math.floor((seconds % 3600) / 60);
const s = Math.floor(seconds % 60);
if (h > 0) return `${h}h ${m}m`;
if (m > 0) return `${m}m ${s}s`;
return `${s}s`;
redirect('/paper');
}