diff --git a/links/templates/links/mini_apps/fire_planning.html b/links/templates/links/mini_apps/fire_planning.html index 6382265..1533545 100644 --- a/links/templates/links/mini_apps/fire_planning.html +++ b/links/templates/links/mini_apps/fire_planning.html @@ -322,6 +322,11 @@ + +
+ + +
@@ -408,7 +413,7 @@ Quick Adjustments - See Impact Instantly -
+
@@ -468,6 +473,21 @@
+ + +
+
+ + 0.0% +
+
+ + +
+
@@ -486,21 +506,6 @@
- -
-

- - FIRE Progress Over Time -

-

- - This shows your progress toward FIRE as a percentage. The calculation is: - (Your Net Worth ÷ Inflation-Adjusted FIRE Target) × 100%. - When you hit 100%, you've reached FIRE! -

- -
-

@@ -573,7 +578,6 @@ let netWorthChart = null; let monteCarloChart = null; - let progressChart = null; // Custom plugin to draw vertical line at retirement age const verticalLinePlugin = { @@ -649,13 +653,14 @@ function generateProjections(inputs) { const projections = []; const yearsToProject = inputs.targetAge - inputs.currentAge; - const annualSavings = inputs.annualIncome * inputs.savingsRate; + let currentIncome = inputs.annualIncome; let netWorth = inputs.currentNetWorth; for (let year = 0; year <= yearsToProject; year++) { const age = inputs.currentAge + year; const currentYear = new Date().getFullYear() + year; const fireTarget = inputs.fireTarget * Math.pow(1 + inputs.inflationRate, year); + const annualSavings = currentIncome * inputs.savingsRate; const progress = (netWorth / fireTarget) * 100; projections.push({ @@ -667,8 +672,9 @@ annualSavings: annualSavings }); - // Project next year's net worth + // Project next year's net worth and income netWorth = (netWorth + annualSavings) * (1 + inputs.returnRate); + currentIncome = currentIncome * (1 + inputs.incomeGrowthRate); } return projections; @@ -772,7 +778,7 @@ newValue = Math.max(min, Math.min(max, newValue)); // Round to appropriate precision - if (fieldId === 'returnRate' || fieldId === 'inflationRate') { + if (fieldId === 'returnRate' || fieldId === 'inflationRate' || fieldId === 'incomeGrowthRate') { newValue = Math.round(newValue * 10) / 10; // 1 decimal place } else { newValue = Math.round(newValue); @@ -793,11 +799,13 @@ const returnRate = parseFloat(document.getElementById('returnRate').value) || 0; const annualIncome = parseFloat(document.getElementById('annualIncome').value) || 0; const targetAge = parseInt(document.getElementById('targetAge').value) || 0; + const incomeGrowthRate = parseFloat(document.getElementById('incomeGrowthRate').value) || 0; document.getElementById('quickExpensesValue').textContent = formatCurrency(annualExpenses); document.getElementById('quickReturnValue').textContent = returnRate.toFixed(1) + '%'; document.getElementById('quickIncomeValue').textContent = formatCurrency(annualIncome); document.getElementById('quickTargetAgeValue').textContent = targetAge; + document.getElementById('quickIncomeGrowthValue').textContent = incomeGrowthRate.toFixed(1) + '%'; } // Get input values @@ -816,6 +824,7 @@ savingsRate: savingsRate, returnRate: parseFloat(document.getElementById('returnRate').value) / 100 || 0.08, inflationRate: parseFloat(document.getElementById('inflationRate').value) / 100 || 0.025, + incomeGrowthRate: parseFloat(document.getElementById('incomeGrowthRate').value) / 100 || 0, // Calculate FIRE target using 4% rule fireTarget: expenses * 25 }; @@ -828,17 +837,19 @@ for (let sim = 0; sim < numSimulations; sim++) { let netWorth = inputs.currentNetWorth; + let currentIncome = inputs.annualIncome; let yearToFire = null; for (let year = 0; year < maxYears; year++) { const age = inputs.currentAge + year; - const annualSavings = inputs.annualIncome * inputs.savingsRate; + const annualSavings = currentIncome * inputs.savingsRate; // Add randomness to returns (normal distribution) const volatility = 0.15; // 15% standard deviation const randomReturn = inputs.returnRate + (Math.random() - 0.5) * 2 * volatility; netWorth = (netWorth + annualSavings) * (1 + randomReturn); + currentIncome = currentIncome * (1 + inputs.incomeGrowthRate); // Adjust FIRE target for inflation const inflationAdjustedTarget = inputs.fireTarget * Math.pow(1 + inputs.inflationRate, year); @@ -898,7 +909,6 @@ // Update all charts updateNetWorthChart(projections, inputs); updateMonteCarloChart(inputs, p10, p25, p50, p75, p90); - updateProgressChart(projections); } // Show results @@ -1122,82 +1132,6 @@ }); } - // Update progress chart - function updateProgressChart(projections) { - if (progressChart) { - progressChart.destroy(); - } - - const ages = projections.map(p => p.age); - const progress = projections.map(p => Math.min(p.progress, 150)); // Cap at 150% for chart - - const ctx = document.getElementById('progressChart').getContext('2d'); - progressChart = new Chart(ctx, { - type: 'line', - data: { - labels: ages, - datasets: [{ - label: 'Progress to FIRE', - data: progress, - borderColor: '#3498db', - backgroundColor: 'rgba(52, 152, 219, 0.2)', - borderWidth: 3, - fill: true, - tension: 0.4, - pointRadius: 0 - }, { - label: '100% (FIRE Achieved)', - data: Array(ages.length).fill(100), - borderColor: '#27ae60', - backgroundColor: 'transparent', - borderWidth: 2, - borderDash: [5, 5], - fill: false, - pointRadius: 0 - }] - }, - options: { - responsive: true, - maintainAspectRatio: true, - plugins: { - legend: { - display: true, - position: 'top' - }, - tooltip: { - mode: 'index', - intersect: false, - callbacks: { - label: function(context) { - if (context.datasetIndex === 0) { - return 'Progress: ' + context.parsed.y.toFixed(1) + '%'; - } - return context.dataset.label; - } - } - } - }, - scales: { - y: { - beginAtZero: true, - max: 150, - ticks: { - callback: function(value) { - return value + '%'; - } - } - }, - x: { - title: { - display: true, - text: 'Age' - } - } - } - } - }); - } - // Reset form function resetForm() { document.getElementById('currentAge').value = 35; @@ -1209,7 +1143,9 @@ document.getElementById('savingsRate').value = 40; document.getElementById('returnRate').value = 8; document.getElementById('inflationRate').value = 2.5; + document.getElementById('incomeGrowthRate').value = 0; + updateQuickAdjustmentDisplays(); document.getElementById('resultsSection').classList.remove('show'); } @@ -1250,7 +1186,8 @@ 'annualIncome', 'savingsRate', 'returnRate', - 'inflationRate' + 'inflationRate', + 'incomeGrowthRate' ]; // Add input event listeners for auto-calculation