import { useApp } from "../context/AppContext.jsx"; import { fmt, pct, fmtDatum } from "../utils/format.js"; import { PURPLE, PURPLE_LIGHT, GREEN, RED } from "../constants/index.js"; import { VCard, VBadge } from "../components/ui/index.jsx"; export default function DashboardTab() { const { ev, totEV, evRows, cats, T, darkMode, currentUser } = useApp(); const hour = new Date().getHours(); const dagdeel = hour < 12 ? "Goedemorgen" : hour < 18 ? "Goedemiddag" : "Goedenavond"; const rawNaam = currentUser?.naam?.trim().split(/\s+/)[0] || ""; const voornaam = rawNaam ? rawNaam.charAt(0).toUpperCase() + rawNaam.slice(1) : ""; const catSum = (row) => cats.reduce((a, c) => a + (row[c.id] || 0), 0); const startEV = evRows.length > 0 ? (evRows[evRows.length - 1]?.nettoWaarde || 0) : 0; const extraFiat = evRows.length > 1 ? evRows.slice(0, -1).reduce((acc, row) => acc + catSum(row), 0) : 0; const totaalFiat = startEV + extraFiat; const winst = totEV - totaalFiat; const roi = totaalFiat > 0 ? winst / totaalFiat : 0; const doel = ev.doel || 1; const progressPct = Math.min(totEV / doel, 1); const milestones = [0, 0.25, 0.5, 0.75, 1].map((f) => f * doel); const kpis = [ { label: "Start eigen vermogen", val: fmt(startEV), accent: PURPLE }, { label: "Extra geΓ―nvesteerde fiat", val: fmt(extraFiat), accent: PURPLE }, { label: "Totaal geΓ―nvesteerde fiat", val: fmt(totaalFiat), accent: PURPLE }, { label: "Totaal eigen vermogen", val: fmt(totEV), accent: GREEN }, { label: "Winst op investering", val: fmt(winst), accent: winst >= 0 ? GREEN : RED }, { label: "ROI %", val: totaalFiat > 0 ? pct(roi, 2) : "β€”", accent: roi >= 0 ? GREEN : RED }, ]; // Minigrafiek data const vData = [...evRows].sort((a, b) => a.datum.localeCompare(b.datum)); return (
{dagdeel}{voornaam ? `, ${voornaam}` : ""} πŸ‘‹

The Road naar {fmt(doel)}

Persoonlijk vermogensdashboard
{/* KPI grid */}
{kpis.map(({ label, val, accent }) => (
{label}
{val}
))}
{/* Doel progressie */}
🎯 Doel & Bijbehorende Progressie
{pct(progressPct)} behaald
{[0.25, 0.5, 0.75].map((f) => (
))}
{milestones.map((m) => ( {fmt(m)} ))}
{/* EV Grafiek */}
Ontwikkeling van Eigen Vermogen
{vData.length < 2 ? (
πŸ“ˆ
Leg minimaal 2 voortgangsmomenten vast in het Eigen Vermogen tabblad om de grafiek te tonen.
) : ( )}
); } // ── Mini inline SVG grafiek ──────────────────────────────────────────────────── function MiniLineChart({ vData, darkMode }) { const [hovered, setHovered] = useState(null); const W = 700, H = 230, pl = 58, pr = 16, pt = 12, pb = 62; const vals = vData.map((d) => d.nettoWaarde || 0); const mn = Math.min(...vals) * 0.95; const mx = Math.max(...vals) * 1.05; const rng = mx - mn || 1; const iW = W - pl - pr, iH = H - pt - pb; const xs = vData.map((_, i) => pl + (i / Math.max(vData.length - 1, 1)) * iW); const ys = vals.map((v) => pt + (1 - (v - mn) / rng) * iH); const line = xs.map((x, i) => `${x},${ys[i]}`).join(" "); const area = `${xs[0]},${pt + iH} ${line} ${xs[xs.length - 1]},${pt + iH}`; const gc = darkMode ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.06)"; const lc = darkMode ? "#6b7280" : "#94a3b8"; return ( {[0, 0.25, 0.5, 0.75, 1].map((f) => { const gy = pt + (1 - f) * iH; return ( {fmt(mn + f * rng)} ); })} {xs.map((x, i) => { const isHov = hovered === i; const tipW = 90, tipH = 22; const tipX = Math.min(Math.max(x - tipW / 2, pl), W - tipW - 4); const tipY = ys[i] - tipH - 8 < pt ? ys[i] + 10 : ys[i] - tipH - 8; return ( setHovered(i)} onMouseLeave={() => setHovered(null)} style={{ cursor: "pointer" }} > {/* Vergroot klikgebied */} {fmtDatum(vData[i].datum)} {/* Tooltip */} {isHov && ( {fmt(vals[i])} )} ); })} ); }