Remove one chart

This commit is contained in:
2025-10-11 19:52:31 +11:00
parent 675660d343
commit 4157ecb5e6
@@ -322,6 +322,11 @@
<label class="input-label" for="inflationRate">Inflation Rate (%)</label>
<input type="number" id="inflationRate" class="input-field" value="2.5" min="0" max="20" step="0.1">
</div>
<div class="input-group">
<label class="input-label" for="incomeGrowthRate">Income Growth (%)</label>
<input type="number" id="incomeGrowthRate" class="input-field" value="0" min="0" max="20" step="0.1">
</div>
</div>
<div class="flex gap-2 mt-3 items-center">
@@ -408,7 +413,7 @@
Quick Adjustments - See Impact Instantly
</h3>
<div class="grid grid-cols-2 md:grid-cols-4 gap-2">
<div class="grid grid-cols-2 md:grid-cols-5 gap-2">
<!-- Annual Expenses Adjustment -->
<div class="bg-gradient-to-br from-blue-50 to-blue-100 p-2 rounded border border-blue-200">
<div class="flex items-center justify-between mb-1">
@@ -468,6 +473,21 @@
<button onclick="adjustValue('targetAge', 1)" class="px-2 py-0.5 bg-white text-gray-700 rounded text-xs hover:bg-orange-200 transition border border-orange-300">+1y</button>
</div>
</div>
<!-- Income Growth Rate Adjustment -->
<div class="bg-gradient-to-br from-teal-50 to-teal-100 p-2 rounded border border-teal-200">
<div class="flex items-center justify-between mb-1">
<label class="text-xs font-semibold text-gray-700">
<i class="fas fa-arrow-trend-up mr-1 text-teal-600"></i>
Income Growth
</label>
<span id="quickIncomeGrowthValue" class="text-xs font-mono bg-white px-1 py-0.5 rounded">0.0%</span>
</div>
<div class="flex gap-1 flex-wrap">
<button onclick="adjustValue('incomeGrowthRate', -1)" class="px-2 py-0.5 bg-white text-gray-700 rounded text-xs hover:bg-teal-200 transition border border-teal-300">-1%</button>
<button onclick="adjustValue('incomeGrowthRate', 1)" class="px-2 py-0.5 bg-white text-gray-700 rounded text-xs hover:bg-teal-200 transition border border-teal-300">+1%</button>
</div>
</div>
</div>
</div>
@@ -486,21 +506,6 @@
<canvas id="netWorthChart" style="max-height: 400px;"></canvas>
</div>
<!-- Progress Chart -->
<div class="chart-container">
<h3 class="chart-title">
<i class="fas fa-percentage mr-2"></i>
FIRE Progress Over Time
</h3>
<p class="text-sm text-gray-600 mb-3">
<i class="fas fa-info-circle mr-1"></i>
This shows your progress toward FIRE as a percentage. The calculation is:
<strong>(Your Net Worth ÷ Inflation-Adjusted FIRE Target) × 100%</strong>.
When you hit 100%, you've reached FIRE!
</p>
<canvas id="progressChart" style="max-height: 300px;"></canvas>
</div>
<!-- Year-by-Year Data Table -->
<div class="chart-container">
<h3 class="chart-title">
@@ -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