diff --git a/app/dashboard/page.js b/app/dashboard/page.js index 0e54909..ceccb05 100644 --- a/app/dashboard/page.js +++ b/app/dashboard/page.js @@ -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 ( -
-
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`; + redirect('/paper'); }