export function debtPayoffCalculator(form: FormTs) {
form.addRow(row => {
row.addTextPanel('header', {
computedValue: () => 'Debt Payoff Calculator',
customStyles: { 'font-size': '1.5rem', 'font-weight': '600', 'color': '#1e293b' }
});
});
form.addSpacer({ height: 20 });
// Debt Details Section
const debtSection = form.addSubform('debt', { title: '๐ณ Debt Details' });
debtSection.addRow(row => {
row.addDecimal('balance', {
label: 'Current Balance',
min: 0,
max: 1000000,
defaultValue: 15000,
placeholder: 'e.g. 15000',
prefix: '$',
isRequired: true,
tooltip: 'Total amount you currently owe'
}, '1fr');
row.addDecimal('interestRate', {
label: 'Annual Interest Rate (APR)',
min: 0,
max: 40,
defaultValue: 18.9,
placeholder: 'e.g. 18.9',
suffix: '%',
isRequired: true
}, '1fr');
});
debtSection.addRow(row => {
row.addDropdown('debtType', {
label: 'Debt Type',
options: [
{ id: 'credit-card', name: 'Credit Card' },
{ id: 'personal-loan', name: 'Personal Loan' },
{ id: 'auto-loan', name: 'Auto Loan' },
{ id: 'student-loan', name: 'Student Loan' },
{ id: 'medical-debt', name: 'Medical Debt' },
{ id: 'other', name: 'Other Debt' }
],
defaultValue: 'credit-card'
});
});
// Payment Section
const paymentSection = form.addSubform('payment', { title: '๐ต Payment Plan' });
paymentSection.addRow(row => {
row.addDecimal('monthlyPayment', {
label: 'Monthly Payment',
min: 0,
max: 100000,
defaultValue: 400,
placeholder: 'e.g. 400',
prefix: '$',
isRequired: true,
tooltip: 'Amount you pay each month'
}, '1fr');
row.addDecimal('extraPayment', {
label: 'Extra Monthly Payment',
min: 0,
max: 50000,
defaultValue: 0,
placeholder: 'e.g. 100',
prefix: '$',
tooltip: 'Additional amount to pay down principal faster'
}, '1fr');
});
paymentSection.addRow(row => {
row.addTextPanel('minimumPaymentWarning', {
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const monthlyRate = rate / 12;
const monthlyInterest = balance * monthlyRate;
if (monthlyPayment <= monthlyInterest) {
return `Your payment must be higher than $${(Number(monthlyInterest) || 0).toFixed(2)} (monthly interest) to pay down debt.`;
}
return '';
},
customStyles: { 'font-size': '0.9rem', 'color': '#dc2626', 'text-align': 'center', 'font-weight': '500' }
});
});
form.addSpacer({ height: 20, showLine: true, lineStyle: 'dashed' });
// Results Section
const resultsSection = form.addSubform('results', { title: '๐ Payoff Timeline', isCollapsible: false });
resultsSection.addRow(row => {
row.addPriceDisplay('payoffMonths', {
label: 'Time to Payoff',
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const totalPayment = monthlyPayment + extraPayment;
const monthlyRate = rate / 12;
if (totalPayment <= balance * monthlyRate) {
return 999; // Never pays off
}
// Calculate months to payoff
const months = Math.ceil(
Math.log(totalPayment / (totalPayment - balance * monthlyRate)) / Math.log(1 + monthlyRate)
);
return months;
},
variant: 'large',
prefix: '',
suffix: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const totalPayment = monthlyPayment + extraPayment;
const monthlyRate = rate / 12;
if (totalPayment <= balance * monthlyRate) {
return ' (never)';
}
const months = Math.ceil(
Math.log(totalPayment / (totalPayment - balance * monthlyRate)) / Math.log(1 + monthlyRate)
);
const years = Math.floor(months / 12);
const remainingMonths = months % 12;
if (years > 0 && remainingMonths > 0) {
return ` months (${years}y ${remainingMonths}m)`;
} else if (years > 0) {
return ` months (${years} years)`;
}
return ' months';
}
}, '1fr');
row.addTextPanel('payoffDate', {
label: 'Debt-Free Date',
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const totalPayment = monthlyPayment + extraPayment;
const monthlyRate = rate / 12;
if (totalPayment <= balance * monthlyRate) {
return 'Never (increase payments)';
}
const months = Math.ceil(
Math.log(totalPayment / (totalPayment - balance * monthlyRate)) / Math.log(1 + monthlyRate)
);
const payoffDate = new Date();
payoffDate.setMonth(payoffDate.getMonth() + months);
return payoffDate.toLocaleDateString('en-US', { month: 'long', year: 'numeric' });
},
customStyles: { 'font-size': '1.1rem', 'font-weight': '600', 'text-align': 'center', 'color': '#059669' }
}, '1fr');
});
resultsSection.addRow(row => {
row.addPriceDisplay('totalInterest', {
label: 'Total Interest Paid',
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const totalPayment = monthlyPayment + extraPayment;
const monthlyRate = rate / 12;
if (totalPayment <= balance * monthlyRate) {
return 0;
}
let remainingBalance = balance;
let totalInterestPaid = 0;
while (remainingBalance > 0) {
const interestCharge = remainingBalance * monthlyRate;
totalInterestPaid += interestCharge;
const principalPayment = Math.min(totalPayment - interestCharge, remainingBalance);
remainingBalance -= principalPayment;
if (remainingBalance < 0.01) break;
if (totalInterestPaid > balance * 10) break; // Safety limit
}
return Math.round(totalInterestPaid);
},
variant: 'warning'
}, '1fr');
row.addPriceDisplay('totalPaid', {
label: 'Total Amount Paid',
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const totalPayment = monthlyPayment + extraPayment;
const monthlyRate = rate / 12;
if (totalPayment <= balance * monthlyRate) {
return 0;
}
let remainingBalance = balance;
let totalPaid = 0;
while (remainingBalance > 0) {
const interestCharge = remainingBalance * monthlyRate;
const payment = Math.min(totalPayment, remainingBalance + interestCharge);
totalPaid += payment;
remainingBalance -= (payment - interestCharge);
if (remainingBalance < 0.01) break;
if (totalPaid > balance * 10) break;
}
return Math.round(totalPaid);
},
variant: 'default'
}, '1fr');
});
// Savings Comparison Section
const savingsSection = form.addSubform('savings', { title: '๐ฐ Extra Payment Savings', isCollapsible: false });
savingsSection.addRow(row => {
row.addPriceDisplay('interestSaved', {
label: 'Interest Saved',
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const monthlyRate = rate / 12;
// Calculate interest without extra payment
let remainingBalance = balance;
let interestWithoutExtra = 0;
if (monthlyPayment > balance * monthlyRate) {
while (remainingBalance > 0) {
const interestCharge = remainingBalance * monthlyRate;
interestWithoutExtra += interestCharge;
remainingBalance -= (monthlyPayment - interestCharge);
if (remainingBalance < 0.01 || interestWithoutExtra > balance * 10) break;
}
}
// Calculate interest with extra payment
remainingBalance = balance;
let interestWithExtra = 0;
const totalPayment = monthlyPayment + extraPayment;
if (totalPayment > balance * monthlyRate) {
while (remainingBalance > 0) {
const interestCharge = remainingBalance * monthlyRate;
interestWithExtra += interestCharge;
remainingBalance -= (totalPayment - interestCharge);
if (remainingBalance < 0.01 || interestWithExtra > balance * 10) break;
}
}
return Math.round(interestWithoutExtra - interestWithExtra);
},
variant: 'success',
prefix: '+$'
}, '1fr');
row.addPriceDisplay('monthsSaved', {
label: 'Months Saved',
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = (debtSection.decimal('interestRate')?.value() || 18.9) / 100;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const monthlyRate = rate / 12;
if (monthlyPayment <= balance * monthlyRate) return 0;
const monthsWithoutExtra = Math.ceil(
Math.log(monthlyPayment / (monthlyPayment - balance * monthlyRate)) / Math.log(1 + monthlyRate)
);
const totalPayment = monthlyPayment + extraPayment;
if (totalPayment <= balance * monthlyRate) return 0;
const monthsWithExtra = Math.ceil(
Math.log(totalPayment / (totalPayment - balance * monthlyRate)) / Math.log(1 + monthlyRate)
);
return monthsWithoutExtra - monthsWithExtra;
},
variant: 'success',
prefix: '',
suffix: ' months'
}, '1fr');
});
savingsSection.addRow(row => {
row.addTextPanel('savingsNote', {
computedValue: () => {
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
if (extraPayment === 0) {
return 'Add an extra payment above to see how much you could save!';
}
return `By adding $${extraPayment}/month extra, you accelerate your debt freedom!`;
},
customStyles: { 'font-size': '0.95rem', 'color': '#475569', 'text-align': 'center', 'font-weight': '500' }
});
});
// Summary Section
const summarySection = form.addSubform('summary', {
title: '๐งพ Summary',
isCollapsible: false,
sticky: 'bottom'
});
summarySection.addRow(row => {
row.addTextPanel('summaryText', {
computedValue: () => {
const balance = debtSection.decimal('balance')?.value() || 15000;
const rate = debtSection.decimal('interestRate')?.value() || 18.9;
const monthlyPayment = paymentSection.decimal('monthlyPayment')?.value() || 400;
const extraPayment = paymentSection.decimal('extraPayment')?.value() || 0;
const totalPayment = monthlyPayment + extraPayment;
return `Paying $${totalPayment.toLocaleString()}/month on $${balance.toLocaleString()} at ${rate}% APR`;
},
customStyles: { 'font-size': '1rem', 'font-weight': '600', 'text-align': 'center', 'color': '#1e293b' }
});
});
summarySection.addRow(row => {
row.addTextPanel('disclaimer', {
computedValue: () => 'Estimates for planning purposes. Actual payoff may vary based on interest rate changes, fees, and payment timing.',
customStyles: { 'font-size': '0.8rem', 'color': '#64748b', 'text-align': 'center' }
});
});
form.configureSubmitButton({
label: 'Save My Plan'
});
}