← back to Ken

src/app/dashboard/bankroll/page.js

105 lines

'use client';
import { useState, useEffect } from 'react';

export default function BankrollPage() {
  const [ledger, setLedger] = useState([]);
  const [summary, setSummary] = useState({});
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function load() {
      try {
        const res = await fetch('/api/bankroll');
        const data = await res.json();
        setLedger(data.ledger || []);
        setSummary(data.summary || {});
      } catch (err) { console.error(err); }
      setLoading(false);
    }
    load();
  }, []);

  if (loading) return <div className="flex items-center justify-center h-64"><div className="animate-spin w-8 h-8 border-2 border-blue-500 border-t-transparent rounded-full" /></div>;

  const eventColors = {
    deposit: 'badge-green',
    withdrawal: 'badge-red',
    trade_pnl: 'badge-blue',
    fee: 'badge-yellow',
    adjustment: 'badge-purple'
  };

  return (
    <div className="space-y-6">
      <div>
        <h1 className="text-2xl font-bold">Bankroll</h1>
        <p className="text-gray-500 text-sm">Ledger tracking every balance change</p>
      </div>

      <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
        <div className="card">
          <p className="text-xs text-gray-500 uppercase mb-1">Current Balance</p>
          <p className="stat-value text-green-400">${parseFloat(summary.current_balance || 50).toFixed(2)}</p>
        </div>
        <div className="card">
          <p className="text-xs text-gray-500 uppercase mb-1">Total Deposits</p>
          <p className="stat-value text-blue-400">${parseFloat(summary.total_deposits || 50).toFixed(2)}</p>
        </div>
        <div className="card">
          <p className="text-xs text-gray-500 uppercase mb-1">Total P&L</p>
          <p className={`stat-value ${parseFloat(summary.total_pnl || 0) >= 0 ? 'text-green-400' : 'text-red-400'}`}>
            ${parseFloat(summary.total_pnl || 0).toFixed(2)}
          </p>
        </div>
        <div className="card">
          <p className="text-xs text-gray-500 uppercase mb-1">Total Fees</p>
          <p className="stat-value text-yellow-400">${parseFloat(summary.total_fees || 0).toFixed(2)}</p>
        </div>
      </div>

      {/* Bankroll Chart Placeholder */}
      <div className="card">
        <h2 className="font-semibold mb-3">Balance History</h2>
        {ledger.length > 1 ? (
          <div className="h-48 flex items-end gap-1">
            {ledger.slice(-60).map((entry, i) => {
              const bal = parseFloat(entry.balance_after);
              const maxBal = Math.max(...ledger.slice(-60).map(e => parseFloat(e.balance_after)));
              const minBal = Math.min(...ledger.slice(-60).map(e => parseFloat(e.balance_after)));
              const range = maxBal - minBal || 1;
              const height = ((bal - minBal) / range) * 160 + 20;
              return (
                <div key={i} className="flex-1 rounded-t transition-all hover:opacity-80" title={`$${bal.toFixed(2)} - ${entry.event_type}`}
                  style={{ height: `${height}px`, background: bal >= 50 ? 'linear-gradient(to top, #10b981, #059669)' : 'linear-gradient(to top, #ef4444, #dc2626)', minWidth: '3px' }} />
              );
            })}
          </div>
        ) : (
          <p className="text-gray-500 text-sm text-center py-8">Balance chart will appear after trades begin.</p>
        )}
      </div>

      {/* Ledger Table */}
      <div className="card overflow-x-auto">
        <h2 className="font-semibold mb-3">Ledger Entries</h2>
        <table>
          <thead><tr><th>Time</th><th>Event</th><th>Amount</th><th>Balance After</th><th>Note</th></tr></thead>
          <tbody>
            {ledger.map((entry, i) => (
              <tr key={i}>
                <td className="text-gray-400 text-sm">{new Date(entry.ts).toLocaleString()}</td>
                <td><span className={`badge ${eventColors[entry.event_type] || 'badge-blue'}`}>{entry.event_type}</span></td>
                <td className={`font-mono ${parseFloat(entry.amount) >= 0 ? 'text-green-400' : 'text-red-400'}`}>
                  {parseFloat(entry.amount) >= 0 ? '+' : ''}${parseFloat(entry.amount).toFixed(2)}
                </td>
                <td className="font-mono font-medium">${parseFloat(entry.balance_after).toFixed(2)}</td>
                <td className="text-gray-400 text-sm max-w-[200px] truncate">{entry.note || '-'}</td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>
    </div>
  );
}