From 183e69f0b717369c2549052ebd1b41f2e70802d5 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 20 Mar 2026 02:33:17 +0100 Subject: [PATCH] add year 0 row and Nebenkosten mitfinanzieren toggle --- calc.js | 34 ++++++++++++++++++++++++++++++---- index.html | 11 +++++++++++ style.css | 45 +++++++++++++++++++++++++++++++++++++++++++++ ui.js | 27 ++++++++++++++++++++------- 4 files changed, 106 insertions(+), 11 deletions(-) diff --git a/calc.js b/calc.js index 67ff2d3..01d6647 100644 --- a/calc.js +++ b/calc.js @@ -13,7 +13,16 @@ function run_simulation(p) { const months = p.years * 12; // --- mortgage setup --- - const loan = p.home_price * (1 - p.down_pct / 100); + const down_payment = p.home_price * (p.down_pct / 100); + const closing_costs = p.home_price * (p.closing_buy_pct / 100); + // if financed: closing costs rolled into loan, buyer only needs down payment upfront + const loan = p.finance_nebenkosten + ? p.home_price * (1 - p.down_pct / 100) + closing_costs + : p.home_price * (1 - p.down_pct / 100); + const upfront_buy = p.finance_nebenkosten + ? down_payment + : down_payment + closing_costs; + const r_m = p.interest_rate / 100 / 12; const n = p.loan_years * 12; let monthly_payment; @@ -23,9 +32,6 @@ function run_simulation(p) { monthly_payment = loan * (r_m * Math.pow(1 + r_m, n)) / (Math.pow(1 + r_m, n) - 1); } - // upfront costs for buyer - const down_payment = p.home_price * (p.down_pct / 100); - const upfront_buy = down_payment + p.home_price * (p.closing_buy_pct / 100); const start_capital = p.start_capital || 0; // --- state --- @@ -53,6 +59,26 @@ function run_simulation(p) { let breakeven_month = null; + // --- year 0 snapshot (day of purchase) --- + const selling_costs_0 = home_value * (p.closing_sell_pct / 100); + labels.push('Jahr 0'); + buyer_nw_arr.push(Math.round(home_value - selling_costs_0 - balance + buyer_portfolio)); + renter_nw_arr.push(Math.round(start_capital)); + cum_buy_arr.push(Math.round(upfront_buy)); + cum_rent_arr.push(0); + detail_arr.push({ + house_value: Math.round(home_value), + mortgage_balance: Math.round(balance), + buyer_portfolio: Math.round(buyer_portfolio), + yr_interest: 0, + yr_principal: 0, + yr_upkeep: 0, + yr_rent: 0, + yr_renters_ins: 0, + kaufnebenkosten: Math.round(closing_costs), + finance_nebenkosten: p.finance_nebenkosten, + }); + const invest_r_m = p.invest_return / 100 / 12; const app_r_m = p.appreciation / 100 / 12; const rent_inc_m = p.rent_increase / 100 / 12; diff --git a/index.html b/index.html index 75eaa7b..c867509 100644 --- a/index.html +++ b/index.html @@ -120,6 +120,17 @@ +
+ + +
+
diff --git a/style.css b/style.css index 1e7bd1f..6abce0c 100644 --- a/style.css +++ b/style.css @@ -201,6 +201,51 @@ label { .unit.prefix { color: var(--text-muted); } +/* ===== Toggle switch ===== */ +.toggle-group { + flex-direction: row; + align-items: center; + justify-content: space-between; +} + +.toggle-label { + display: flex; + flex-direction: column; + gap: 0.1rem; + color: var(--text-muted); + font-size: 0.82rem; + font-weight: 500; + cursor: pointer; +} + +.toggle-switch { cursor: pointer; flex-shrink: 0; } +.toggle-switch input { display: none; } + +.toggle-track { + display: block; + width: 40px; + height: 22px; + background: rgba(255,255,255,0.1); + border-radius: 99px; + position: relative; + transition: background 0.2s; +} + +.toggle-thumb { + position: absolute; + top: 3px; left: 3px; + width: 16px; height: 16px; + background: var(--text-muted); + border-radius: 50%; + transition: transform 0.2s, background 0.2s; +} + +.toggle-switch input:checked + .toggle-track { background: var(--buy-muted); } +.toggle-switch input:checked + .toggle-track .toggle-thumb { + transform: translateX(18px); + background: var(--buy); +} + /* ===== Range slider styling ===== */ input[type="range"] { -webkit-appearance: none; diff --git a/ui.js b/ui.js index d0deb0b..0afdb46 100644 --- a/ui.js +++ b/ui.js @@ -24,7 +24,8 @@ const get_params = () => ({ monthly_rent: +document.getElementById('monthly_rent').value, rent_increase: +document.getElementById('rent_increase').value, renters_ins: +document.getElementById('renters_ins').value, - start_capital: +document.getElementById('start_capital').value, + start_capital: +document.getElementById('start_capital').value, + finance_nebenkosten: document.getElementById('finance_nebenkosten').checked, }); // ===== Charts ===== @@ -206,12 +207,22 @@ const build_table = (result) => { const td_buy_cost = document.createElement('td'); td_buy_cost.textContent = fmt_eur(result.cum_buy_arr[i]); td_buy_cost.classList.add('has-tip'); - td_buy_cost.addEventListener('mouseenter', (e) => show_tooltip(e, [ - `dieses Jahr`, - tip_row('Zinsen', d.yr_interest, '#f59e0b'), - tip_row('Tilgung', d.yr_principal, '#f59e0b'), - tip_row('Nebenkosten', d.yr_upkeep, '#f59e0b'), - ].join('
'))); + td_buy_cost.addEventListener('mouseenter', (e) => { + const rows = i === 0 + ? [ + `Kauftag`, + tip_row('Eigenkapital', result.cum_buy_arr[0] - (d.finance_nebenkosten ? 0 : d.kaufnebenkosten), '#f59e0b'), + tip_row('Kaufnebenkosten', d.kaufnebenkosten, d.finance_nebenkosten ? '#8b949e' : '#f59e0b'), + d.finance_nebenkosten ? `↳ mitfinanziert` : null, + ].filter(Boolean) + : [ + `dieses Jahr`, + tip_row('Zinsen', d.yr_interest, '#f59e0b'), + tip_row('Tilgung', d.yr_principal, '#f59e0b'), + tip_row('Nebenkosten', d.yr_upkeep, '#f59e0b'), + ]; + show_tooltip(e, rows.join('
')); + }); const td_rent_cost = document.createElement('td'); td_rent_cost.textContent = fmt_eur(result.cum_rent_arr[i]); @@ -304,6 +315,8 @@ const SLIDER_IDS = [ SLIDER_IDS.forEach(wire_slider); // wire plain number inputs (no slider) +document.getElementById('finance_nebenkosten').addEventListener('change', recalc); + ['home_price', 'insurance', 'monthly_rent', 'renters_ins', 'start_capital'].forEach((id) => { document.getElementById(id).addEventListener('input', recalc); });