page.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. import { getDashboardSummary, getRecentSignals, getRecentTrades } from '@/lib/queries';
  2. import { RecentSignalsSection } from './RecentSignals';
  3. import { Badge } from '@/components/ui/badge';
  4. import { TradeTableClient } from '@/components/TradeTableClient';
  5. function StatusIndicator({ running }: { running: boolean }) {
  6. return (
  7. <span className="flex items-center gap-2">
  8. <span
  9. className={`w-2.5 h-2.5 rounded-full ${
  10. running ? 'bg-green-500 animate-pulse' : 'bg-red-500'
  11. }`}
  12. />
  13. <span className={running ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}>
  14. {running ? 'Running' : 'Stopped'}
  15. </span>
  16. </span>
  17. );
  18. }
  19. function SummaryCard({
  20. label,
  21. value,
  22. subtext,
  23. color = 'text-heading',
  24. }: {
  25. label: string;
  26. value: string;
  27. subtext?: string;
  28. color?: string;
  29. }) {
  30. return (
  31. <div className="card">
  32. <p className="text-sub text-sm mb-1">{label}</p>
  33. <p className={`text-2xl font-bold ${color}`}>{value}</p>
  34. {subtext && <p className="text-dim text-xs mt-1">{subtext}</p>}
  35. </div>
  36. );
  37. }
  38. export const dynamic = 'force-dynamic';
  39. export default async function DashboardPage() {
  40. const [summary, recentSignals, recentTrades] = await Promise.all([
  41. getDashboardSummary(),
  42. getRecentSignals(20),
  43. getRecentTrades(20),
  44. ]);
  45. return (
  46. <div className="space-y-8">
  47. <div className="flex items-center justify-between">
  48. <div>
  49. <h1 className="text-2xl font-bold text-heading">Dashboard</h1>
  50. <p className="text-sm text-sub mt-1">Weather arbitrage bot overview</p>
  51. </div>
  52. <div className="flex items-center gap-4">
  53. {summary.dryRun && (
  54. <Badge variant="yellow">DRY RUN</Badge>
  55. )}
  56. <StatusIndicator running={summary.botStatus === 'running'} />
  57. </div>
  58. </div>
  59. {/* Bot Status Card */}
  60. <div className="card flex items-center justify-between">
  61. <div className="flex items-center gap-6">
  62. <div>
  63. <p className="text-sm text-sub">Bot Status</p>
  64. <StatusIndicator running={summary.botStatus === 'running'} />
  65. </div>
  66. <div className="h-10 w-px divider" />
  67. <div>
  68. <p className="text-sm text-sub">Uptime</p>
  69. <p className="text-lg font-mono text-heading">{summary.uptime}</p>
  70. </div>
  71. <div className="h-10 w-px divider" />
  72. <div>
  73. <p className="text-sm text-sub">Mode</p>
  74. <p className="text-lg font-medium text-yellow-600 dark:text-yellow-400">
  75. {summary.dryRun ? 'Dry Run' : 'Live'}
  76. </p>
  77. </div>
  78. </div>
  79. </div>
  80. {/* Summary Cards */}
  81. <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
  82. <SummaryCard
  83. label="Active Markets"
  84. value={String(summary.activeMarkets)}
  85. subtext="Weather prediction markets"
  86. />
  87. <SummaryCard
  88. label="Open Positions"
  89. value={String(summary.openPositions)}
  90. subtext="Across all markets"
  91. />
  92. <SummaryCard
  93. label="Today's PnL"
  94. value={`$${summary.todayPnl.toFixed(2)}`}
  95. color={summary.todayPnl >= 0 ? 'text-green-600 dark:text-green-400' : 'text-red-600 dark:text-red-400'}
  96. subtext="Unrealized + realized"
  97. />
  98. <SummaryCard
  99. label="Win Rate"
  100. value={`${(summary.winRate * 100).toFixed(1)}%`}
  101. color="text-blue-600 dark:text-blue-400"
  102. subtext="Last 30 days"
  103. />
  104. </div>
  105. {/* Recent Trades */}
  106. {recentTrades.length > 0 && (
  107. <div className="card">
  108. <TradeTableClient trades={recentTrades} />
  109. </div>
  110. )}
  111. {/* Recent Signals */}
  112. <div className="card">
  113. <div className="flex items-center justify-between mb-4">
  114. <h2 className="text-lg font-semibold text-heading">Recent Signals</h2>
  115. <span className="text-sm text-sub">
  116. {recentSignals.length > 0 ? `Last ${recentSignals.length} signals` : 'No signals yet'}
  117. </span>
  118. </div>
  119. {recentSignals.length > 0 ? (
  120. <RecentSignalsSection signals={recentSignals} />
  121. ) : (
  122. <p className="text-dim text-center py-8">
  123. No signals yet. Start the bot to begin scanning markets.
  124. </p>
  125. )}
  126. </div>
  127. </div>
  128. );
  129. }