'use client'; import { useState, useEffect } from 'react'; const GREEN = '#28CC95'; const RED = '#FF6B6B'; 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 (
Loading Kalbot...
); } const market = data?.market; const paper = data?.paper; const strategies = data?.strategies || []; return (
{/* Header */}

Kalbot

{data?.lastUpdate ? 'Live' : 'Offline'}
{/* Market Card */} {/* Paper Stats */} {/* Tab Bar */}
{['market', 'strategies', 'trades'].map(t => ( ))}
{/* Tab Content */} {tab === 'market' && } {tab === 'strategies' && } {tab === 'trades' && }
{/* Worker Uptime */}
Worker uptime: {formatUptime(data?.workerUptime)} Updated: {data?.lastUpdate ? new Date(data.lastUpdate).toLocaleTimeString() : 'never'}
); } function MarketCard({ market }) { if (!market) { return (

No active market — waiting for next 15-min window...

); } const timeLeft = market.closeTime ? getTimeLeft(market.closeTime) : null; return (
{/* Title */}

BTC Up or Down

15 minutes

{timeLeft && ( ⏱ {timeLeft} )}
{/* Up */}
Up
{market.yesOdds}x {market.yesPct}%
{/* Down */}
Down
{market.noOdds}x {market.noPct}%
{/* Volume */}
${(market.volume || 0).toLocaleString()} vol {market.ticker}
); } function PaperStats({ paper }) { if (!paper) return null; const pnlColor = paper.totalPnL >= 0 ? GREEN : RED; return (
= 0 ? '+' : ''}$${paper.totalPnL}`} color={pnlColor} /> = 50 ? GREEN : RED} />
); } function StatBox({ label, value, color }) { return (

{label}

{value}

); } function MarketDetails({ market }) { if (!market) return

No market data

; 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 (
{rows.map(([k, v], i) => (
{k} {v}
))}
); } function StrategiesView({ strategies }) { if (!strategies.length) { return

No strategies loaded

; } return (
{strategies.map((s, i) => (
{s.name}
{s.mode}
{s.config && Object.entries(s.config).map(([k, v]) => (
{k} {typeof v === 'number' ? v : String(v)}
))} {s.consecutiveLosses !== undefined && (
Consecutive Losses 0 ? 'text-red-400' : 'text-gray-300'}> {s.consecutiveLosses}
)} {s.currentBetSize !== undefined && (
Next Bet ${s.currentBetSize}
)} {s.paused && (
⚠️ PAUSED — max losses reached
)}
))}
); } function TradesView({ trades, openPositions }) { return (
{/* Open Positions */} {openPositions.length > 0 && (

Open Positions

{openPositions.map((t, i) => ( ))}
)} {/* Trade History */}

History ({trades.length})

{trades.length === 0 ? (

No trades yet. Strategies are watching...

) : ( trades.map((t, i) => ) )}
); } 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 (
{isOpen ? ( ) : ( {won ? '✅' : '❌'} )} {trade.side} @ {trade.price}¢
{trade.pnl != null ? `${trade.pnl >= 0 ? '+' : ''}$${trade.pnl}` : 'open'}
{trade.strategy} {trade.entryTime ? new Date(trade.entryTime).toLocaleTimeString() : ''}
{trade.reason && (

{trade.reason}

)}
); } 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`; }