← back to Bertha
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>
);
}