import React, { useRef } from 'react'; import { Chart as ChartJS, LineElement, PointElement, LinearScale, CategoryScale, Tooltip, Legend, Filler, } from 'chart.js'; import { Line } from 'react-chartjs-2'; ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale, Tooltip, Legend, Filler); const styles = { card: { background: '#1a1a1a', padding: '1.5rem', borderRadius: '12px', border: '1px solid #333', marginBottom: '1.5rem' }, title: { fontSize: '1.1rem', fontWeight: 700, marginBottom: '1rem', color: '#f7931a' }, saveBtn: { marginTop: '0.75rem', background: 'none', border: '1px solid #555', color: '#aaa', borderRadius: '6px', padding: '0.4rem 1rem', cursor: 'pointer', fontSize: '0.85rem' }, }; function toDateKey(date) { return date.toISOString().split('T')[0]; } function priceOn(date, priceMap, currentPrice, isToday, sortedPurchases) { if (isToday) return currentPrice || 0; // Try candle history (walk back up to 7 days) for (let i = 0; i <= 7; i++) { const d = new Date(date); d.setDate(d.getDate() - i); const p = priceMap[toDateKey(d)]; if (p) return p; } // Fall back to most recent purchase price up to this date let fallback = null; for (const p of sortedPurchases) { const pd = new Date(p.created_at); pd.setHours(0, 0, 0, 0); if (pd <= date) fallback = p.price_eur; } return fallback; } export default function PortfolioChart({ purchases, stats, btcHistory }) { const chartRef = useRef(null); if (!purchases || purchases.length === 0) return null; const sorted = [...purchases].sort((a, b) => new Date(a.created_at) - new Date(b.created_at)); const today = new Date(); today.setHours(0, 0, 0, 0); // Build price lookup from candle history const priceMap = {}; (btcHistory || []).forEach(({ date, close }) => { priceMap[date] = close; }); const firstDate = new Date(sorted[0].created_at); firstDate.setHours(0, 0, 0, 0); // Generate biweekly dates from first purchase to today const dates = []; const cursor = new Date(firstDate); while (cursor <= today) { dates.push(new Date(cursor)); cursor.setDate(cursor.getDate() + 7); } if (toDateKey(dates[dates.length - 1]) !== toDateKey(today)) { dates.push(new Date(today)); } const labels = []; const portfolioValues = []; const investedValues = []; dates.forEach(date => { const isToday = toDateKey(date) === toDateKey(today); const price = priceOn(date, priceMap, stats?.current_price, isToday, sorted); if (price === null) return; // no price data available, skip // Cumulative BTC and invested up to this date let cumBtc = 0; let cumInvested = 0; sorted.forEach(p => { const pDate = new Date(p.created_at); pDate.setHours(0, 0, 0, 0); if (pDate <= date) { cumBtc += p.amount_eur / p.price_eur; cumInvested += p.amount_eur; } }); if (cumBtc === 0) return; // no purchases yet at this date labels.push(date.toLocaleDateString('en-GB')); portfolioValues.push(parseFloat((cumBtc * price).toFixed(2))); investedValues.push(parseFloat(cumInvested.toFixed(2))); }); const data = { labels, datasets: [ { label: 'Portfolio Value (€)', data: portfolioValues, borderColor: '#f7931a', backgroundColor: 'rgba(247,147,26,0.1)', fill: true, tension: 0.3, }, { label: 'Total Invested (€)', data: investedValues, borderColor: '#4fc3f7', backgroundColor: 'transparent', borderDash: [6, 3], tension: 0.3, }, ], }; const options = { responsive: true, plugins: { legend: { labels: { color: '#aaa' } }, tooltip: { mode: 'index', intersect: false }, }, scales: { x: { ticks: { color: '#666' }, grid: { color: '#2a2a2a' } }, y: { ticks: { color: '#666' }, grid: { color: '#2a2a2a' } }, }, }; const handleSave = () => { const chart = chartRef.current; if (!chart) return; const url = chart.toBase64Image(); const a = document.createElement('a'); a.href = url; a.download = 'btc-portfolio.png'; a.click(); }; return (