| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- import { getDashboardSummary, getRecentSignals, getRecentTrades } from '@/lib/queries';
- import { RecentSignalsSection } from './RecentSignals';
- import { Badge } from '@/components/ui/badge';
- import { TradeTableClient } from '@/components/TradeTableClient';
- function StatusIndicator({ running }: { running: boolean }) {
- return (
- <span className="flex items-center gap-2">
- <span
- className={`w-2.5 h-2.5 rounded-full ${
- running ? 'bg-green-500 animate-pulse' : 'bg-red-500'
- }`}
- />
- <span className={running ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}>
- {running ? 'Running' : 'Stopped'}
- </span>
- </span>
- );
- }
- function SummaryCard({
- label,
- value,
- subtext,
- color = 'text-heading',
- }: {
- label: string;
- value: string;
- subtext?: string;
- color?: string;
- }) {
- return (
- <div className="card">
- <p className="text-sub text-sm mb-1">{label}</p>
- <p className={`text-2xl font-bold ${color}`}>{value}</p>
- {subtext && <p className="text-dim text-xs mt-1">{subtext}</p>}
- </div>
- );
- }
- export const dynamic = 'force-dynamic';
- export default async function DashboardPage() {
- const [summary, recentSignals, recentTrades] = await Promise.all([
- getDashboardSummary(),
- getRecentSignals(20),
- getRecentTrades(20),
- ]);
- return (
- <div className="space-y-8">
- <div className="flex items-center justify-between">
- <div>
- <h1 className="text-2xl font-bold text-heading">Dashboard</h1>
- <p className="text-sm text-sub mt-1">Weather arbitrage bot overview</p>
- </div>
- <div className="flex items-center gap-4">
- {summary.dryRun && (
- <Badge variant="yellow">DRY RUN</Badge>
- )}
- <StatusIndicator running={summary.botStatus === 'running'} />
- </div>
- </div>
- {/* Bot Status Card */}
- <div className="card flex items-center justify-between">
- <div className="flex items-center gap-6">
- <div>
- <p className="text-sm text-sub">Bot Status</p>
- <StatusIndicator running={summary.botStatus === 'running'} />
- </div>
- <div className="h-10 w-px divider" />
- <div>
- <p className="text-sm text-sub">Uptime</p>
- <p className="text-lg font-mono text-heading">{summary.uptime}</p>
- </div>
- <div className="h-10 w-px divider" />
- <div>
- <p className="text-sm text-sub">Mode</p>
- <p className="text-lg font-medium text-yellow-600 dark:text-yellow-400">
- {summary.dryRun ? 'Dry Run' : 'Live'}
- </p>
- </div>
- </div>
- </div>
- {/* Summary Cards */}
- <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
- <SummaryCard
- label="Active Markets"
- value={String(summary.activeMarkets)}
- subtext="Weather prediction markets"
- />
- <SummaryCard
- label="Open Positions"
- value={String(summary.openPositions)}
- subtext="Across all markets"
- />
- <SummaryCard
- label="Today's PnL"
- value={`$${summary.todayPnl.toFixed(2)}`}
- color={summary.todayPnl >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}
- subtext="Unrealized + realized"
- />
- <SummaryCard
- label="Win Rate"
- value={`${(summary.winRate * 100).toFixed(1)}%`}
- color="text-blue-600 dark:text-blue-400"
- subtext="Last 30 days"
- />
- </div>
- {/* Recent Trades */}
- {recentTrades.length > 0 && (
- <div className="card">
- <TradeTableClient trades={recentTrades} />
- </div>
- )}
- {/* Recent Signals */}
- <div className="card">
- <div className="flex items-center justify-between mb-4">
- <h2 className="text-lg font-semibold text-heading">Recent Signals</h2>
- <span className="text-sm text-sub">
- {recentSignals.length > 0 ? `Last ${recentSignals.length} signals` : 'No signals yet'}
- </span>
- </div>
- {recentSignals.length > 0 ? (
- <RecentSignalsSection signals={recentSignals} />
- ) : (
- <p className="text-dim text-center py-8">
- No signals yet. Start the bot to begin scanning markets.
- </p>
- )}
- </div>
- </div>
- );
- }
|