add year 0 row and Nebenkosten mitfinanzieren toggle
This commit is contained in:
34
calc.js
34
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;
|
||||
|
||||
11
index.html
11
index.html
@@ -120,6 +120,17 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="input-group toggle-group">
|
||||
<label class="toggle-label" for="finance_nebenkosten">
|
||||
<span>Nebenkosten mitfinanzieren</span>
|
||||
<span class="hint">in Hypothek einrechnen</span>
|
||||
</label>
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" id="finance_nebenkosten" />
|
||||
<span class="toggle-track"><span class="toggle-thumb"></span></span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="input-group">
|
||||
<label for="closing_sell_pct">Verkaufsnebenkosten <span class="hint">(Makler)</span></label>
|
||||
<div class="slider-row">
|
||||
|
||||
45
style.css
45
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;
|
||||
|
||||
27
ui.js
27
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, [
|
||||
`<span style="color:#8b949e;font-size:0.72rem;text-transform:uppercase;letter-spacing:.05em">dieses Jahr</span>`,
|
||||
tip_row('Zinsen', d.yr_interest, '#f59e0b'),
|
||||
tip_row('Tilgung', d.yr_principal, '#f59e0b'),
|
||||
tip_row('Nebenkosten', d.yr_upkeep, '#f59e0b'),
|
||||
].join('<br>')));
|
||||
td_buy_cost.addEventListener('mouseenter', (e) => {
|
||||
const rows = i === 0
|
||||
? [
|
||||
`<span style="color:#8b949e;font-size:0.72rem;text-transform:uppercase;letter-spacing:.05em">Kauftag</span>`,
|
||||
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 ? `<span style="color:#6e7681;font-size:0.75rem">↳ mitfinanziert</span>` : null,
|
||||
].filter(Boolean)
|
||||
: [
|
||||
`<span style="color:#8b949e;font-size:0.72rem;text-transform:uppercase;letter-spacing:.05em">dieses Jahr</span>`,
|
||||
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('<br>'));
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user