export function compoundInterestCalculator(form: FormTs) {
// Compounding frequency periods per year
const compoundingPeriods: Record<string, number> = {
'annually': 1,
'semi-annually': 2,
'quarterly': 4,
'monthly': 12,
'weekly': 52,
'daily': 365
};
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Compound Interest Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Initial Investment Section
const investmentSection = form.addSubform('investment', { title: '๐ต Initial Investment' });
investmentSection.addRow(row => {
row.addDecimal('principal', {
label: 'Starting Amount',
min: 0,
max: 10000000,
defaultValue: 10000,
placeholder: 'e.g. 10000',
prefix: '$',
isRequired: true
}, '1fr');
row.addDecimal('monthlyContribution', {
label: 'Monthly Contribution',
min: 0,
max: 100000,
defaultValue: 500,
placeholder: 'e.g. 500',
prefix: '$',
tooltip: 'Additional amount added each month'
}, '1fr');
});
// Interest & Time Section
const interestSection = form.addSubform('interest', { title: '๐ Interest & Time' });
interestSection.addRow(row => {
row.addDecimal('interestRate', {
label: 'Annual Interest Rate',
min: 0,
max: 30,
defaultValue: 7,
placeholder: 'e.g. 7',
suffix: '%',
isRequired: true
}, '1fr');
row.addInteger('years', {
label: 'Investment Period',
min: 1,
max: 50,
defaultValue: 20,
suffix: 'years',
isRequired: true
}, '1fr');
});
interestSection.addRow(row => {
row.addDropdown('compoundingFrequency', {
label: 'Compounding Frequency',
options: [
{ id: 'annually', name: 'Annually (1x/year)' },
{ id: 'semi-annually', name: 'Semi-Annually (2x/year)' },
{ id: 'quarterly', name: 'Quarterly (4x/year)' },
{ id: 'monthly', name: 'Monthly (12x/year)' },
{ id: 'weekly', name: 'Weekly (52x/year)' },
{ id: 'daily', name: 'Daily (365x/year)' }
],
defaultValue: 'monthly',
isRequired: true
}, '1fr');
row.addDropdown('contributionTiming', {
label: 'Contribution Timing',
options: [
{ id: 'end', name: 'End of Period' },
{ id: 'beginning', name: 'Beginning of Period' }
],
defaultValue: 'end',
tooltip: 'When contributions are made each month'
}, '1fr');
});
// Inflation Adjustment (Optional)
const optionsSection = form.addSubform('options', { title: 'โ๏ธ Advanced Options' });
optionsSection.addRow(row => {
row.addCheckbox('adjustForInflation', {
label: 'Adjust for Inflation',
defaultValue: false
}, '1fr');
row.addDecimal('inflationRate', {
label: 'Expected Inflation Rate',
min: 0,
max: 15,
defaultValue: 3,
suffix: '%',
isVisible: () => optionsSection.checkbox('adjustForInflation')?.value() === true
}, '1fr');
});
optionsSection.addRow(row => {
row.addCheckbox('accountForTaxes', {
label: 'Account for Taxes',
defaultValue: false
}, '1fr');
row.addDecimal('taxRate', {
label: 'Tax Rate on Gains',
min: 0,
max: 50,
defaultValue: 25,
suffix: '%',
isVisible: () => optionsSection.checkbox('accountForTaxes')?.value() === true,
tooltip: 'Applied to interest earnings only'
}, '1fr');
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Results Section
const summarySection = form.addSubform('summary', { title: '๐ฐ Investment Growth', isCollapsible: false });
summarySection.addRow(row => {
row.addPriceDisplay('totalContributions', {
label: 'Total Contributions',
computedValue: () => {
const principal = investmentSection.decimal('principal')?.value() || 10000;
const monthly = investmentSection.decimal('monthlyContribution')?.value() || 500;
const years = interestSection.integer('years')?.value() || 20;
return Math.round(principal + (monthly * 12 * years));
},
variant: 'default'
}, '1fr');
row.addPriceDisplay('totalInterest', {
label: 'Interest Earned',
computedValue: () => {
const principal = investmentSection.decimal('principal')?.value() || 10000;
const monthly = investmentSection.decimal('monthlyContribution')?.value() || 500;
const rate = (interestSection.decimal('interestRate')?.value() || 7) / 100;
const years = interestSection.integer('years')?.value() || 20;
const frequency = interestSection.dropdown('compoundingFrequency')?.value() || 'monthly';
const timing = interestSection.dropdown('contributionTiming')?.value() || 'end';
const n = compoundingPeriods[frequency] || 12;
const r = rate / n;
const t = years * n;
// Future value of principal
const fvPrincipal = principal * Math.pow(1 + r, t);
// Future value of contributions (annuity)
const monthlyRate = rate / 12;
const totalMonths = years * 12;
let fvContributions = 0;
if (monthly > 0 && monthlyRate > 0) {
fvContributions = monthly * ((Math.pow(1 + monthlyRate, totalMonths) - 1) / monthlyRate);
if (timing === 'beginning') {
fvContributions *= (1 + monthlyRate);
}
} else if (monthly > 0) {
fvContributions = monthly * totalMonths;
}
const totalValue = fvPrincipal + fvContributions;
const totalContributions = principal + (monthly * 12 * years);
return Math.round(totalValue - totalContributions);
},
variant: 'success',
prefix: '+'
}, '1fr');
});
summarySection.addRow(row => {
row.addTextPanel('growthMultiple', {
computedValue: () => {
const principal = investmentSection.decimal('principal')?.value() || 10000;
const monthly = investmentSection.decimal('monthlyContribution')?.value() || 500;
const rate = (interestSection.decimal('interestRate')?.value() || 7) / 100;
const years = interestSection.integer('years')?.value() || 20;
const frequency = interestSection.dropdown('compoundingFrequency')?.value() || 'monthly';
const n = compoundingPeriods[frequency] || 12;
const r = rate / n;
const t = years * n;
const fvPrincipal = principal * Math.pow(1 + r, t);
const monthlyRate = rate / 12;
const totalMonths = years * 12;
let fvContributions = 0;
if (monthly > 0 && monthlyRate > 0) {
fvContributions = monthly * ((Math.pow(1 + monthlyRate, totalMonths) - 1) / monthlyRate);
} else if (monthly > 0) {
fvContributions = monthly * totalMonths;
}
const totalValue = fvPrincipal + fvContributions;
const totalContributions = principal + (monthly * 12 * years);
const multiple = totalValue / totalContributions;
return `Your money grows ${(Number(multiple) || 0).toFixed(2)}x over ${years} years`;
},
customStyles: { 'font-size': '0.95rem', 'color': '#475569', 'text-align': 'center', 'font-weight': '500' }
});
});
// Year-by-Year Breakdown hint
summarySection.addSpacer({ height: 15 });
summarySection.addRow(row => {
row.addTextPanel('yearlyBreakdown', {
computedValue: () => {
const principal = investmentSection.decimal('principal')?.value() || 10000;
const monthly = investmentSection.decimal('monthlyContribution')?.value() || 500;
const rate = (interestSection.decimal('interestRate')?.value() || 7) / 100;
const years = interestSection.integer('years')?.value() || 20;
const milestones = [5, 10, 15, 20, 25, 30].filter(y => y <= years);
if (milestones.length === 0) return '';
let result = 'Milestones: ';
milestones.forEach((year, index) => {
const monthlyRate = rate / 12;
const months = year * 12;
const fvPrincipal = principal * Math.pow(1 + monthlyRate, months);
let fvContributions = 0;
if (monthly > 0 && monthlyRate > 0) {
fvContributions = monthly * ((Math.pow(1 + monthlyRate, months) - 1) / monthlyRate);
}
const value = Math.round(fvPrincipal + fvContributions);
result += `Year ${year}: $${value.toLocaleString()}`;
if (index < milestones.length - 1) result += ' | ';
});
return result;
},
customStyles: { 'font-size': '0.8rem', 'color': '#64748b', 'text-align': 'center' }
});
});
const finalSection = form.addSubform('final', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
finalSection.addRow(row => {
row.addPriceDisplay('futureValue', {
label: 'Future Value',
computedValue: () => {
const principal = investmentSection.decimal('principal')?.value() || 10000;
const monthly = investmentSection.decimal('monthlyContribution')?.value() || 500;
const rate = (interestSection.decimal('interestRate')?.value() || 7) / 100;
const years = interestSection.integer('years')?.value() || 20;
const frequency = interestSection.dropdown('compoundingFrequency')?.value() || 'monthly';
const timing = interestSection.dropdown('contributionTiming')?.value() || 'end';
const adjustInflation = optionsSection.checkbox('adjustForInflation')?.value() || false;
const inflationRate = (optionsSection.decimal('inflationRate')?.value() || 3) / 100;
const accountTaxes = optionsSection.checkbox('accountForTaxes')?.value() || false;
const taxRate = (optionsSection.decimal('taxRate')?.value() || 25) / 100;
const n = compoundingPeriods[frequency] || 12;
const r = rate / n;
const t = years * n;
// Future value of principal
const fvPrincipal = principal * Math.pow(1 + r, t);
// Future value of contributions
const monthlyRate = rate / 12;
const totalMonths = years * 12;
let fvContributions = 0;
if (monthly > 0 && monthlyRate > 0) {
fvContributions = monthly * ((Math.pow(1 + monthlyRate, totalMonths) - 1) / monthlyRate);
if (timing === 'beginning') {
fvContributions *= (1 + monthlyRate);
}
} else if (monthly > 0) {
fvContributions = monthly * totalMonths;
}
let totalValue = fvPrincipal + fvContributions;
const totalContributions = principal + (monthly * 12 * years);
const interestEarned = totalValue - totalContributions;
// Apply taxes to interest only
if (accountTaxes && interestEarned > 0) {
const taxAmount = interestEarned * taxRate;
totalValue = totalValue - taxAmount;
}
// Adjust for inflation
if (adjustInflation) {
totalValue = totalValue / Math.pow(1 + inflationRate, years);
}
return Math.round(totalValue);
},
variant: 'large'
});
});
finalSection.addRow(row => {
row.addTextPanel('inflationNote', {
computedValue: () => {
const adjustInflation = optionsSection.checkbox('adjustForInflation')?.value() || false;
if (adjustInflation) {
return "Value shown in today's dollars (adjusted for inflation)";
}
return 'Estimates for educational purposes. Actual returns may vary.';
},
customStyles: { 'font-size': '0.85rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Save My Calculation'
});
}