`;
document.getElementById('calculator-form').addEventListener('submit', handleCalculation);
};
const handleCalculation = (e) => {
e.preventDefault();
// 1. Gather Inputs
const purchaseAmount = parseFloat(document.getElementById('purchase-amount').value);
const downPayment = parseFloat(document.getElementById('down-payment').value);
const annualRate = parseFloat(document.getElementById('interest-rate').value);
let loanTerm = parseInt(document.getElementById('loan-term').value);
const termUnit = document.getElementById('term-unit').value;
const startDate = new Date(document.getElementById('start-date').value);
if (isNaN(purchaseAmount) || isNaN(annualRate) || isNaN(loanTerm) || !startDate) {
alert('Please fill in all fields with valid values.');
return;
}
// Store inputs for PDF
inputs = { purchaseAmount, downPayment, annualRate, loanTerm, termUnit, startDate };
// 2. Calculate Principal
let basePrincipal = purchaseAmount - downPayment;
let totalFees = 0;
appData.fees.forEach(fee => {
if (fee.enabled) {
if (fee.type === 'fixed') {
totalFees += fee.value;
} else if (fee.type === 'percent') {
totalFees += basePrincipal * (fee.value / 100);
}
}
});
const principal = basePrincipal + totalFees;
// 3. Calculate Payment
const monthlyRate = (annualRate / 100) / 12;
const numberOfMonths = termUnit === 'years' ? loanTerm * 12 : loanTerm;
if(monthlyRate === 0) { // Simple interest-free calculation
monthlyPayment = principal / numberOfMonths;
} else {
monthlyPayment = principal * (monthlyRate * Math.pow(1 + monthlyRate, numberOfMonths)) / (Math.pow(1 + monthlyRate, numberOfMonths) - 1);
}
if (!isFinite(monthlyPayment)) {
alert("Could not calculate payment. Please check your inputs.");
return;
}
// 4. Generate Amortization Schedule
let balance = principal;
let totalInterest = 0;
const schedule = [];
for (let i = 1; i <= numberOfMonths; i++) {
const interestPaid = balance * monthlyRate;
const principalPaid = monthlyPayment - interestPaid;
balance -= principalPaid;
totalInterest += interestPaid;
let paymentDate = new Date(startDate);
paymentDate.setMonth(startDate.getMonth() + i - 1);
schedule.push({
paymentNumber: i,
paymentDate: formatDate(paymentDate),
paymentAmount: monthlyPayment,
principalPaid,
interestPaid,
remainingBalance: Math.abs(balance) < 0.01 ? 0 : balance, // handle floating point inaccuracies
});
}
const payoffDate = new Date(startDate);
payoffDate.setMonth(startDate.getMonth() + numberOfMonths - 1);
calculationResult = {
monthlyPayment,
totalPrincipal: principal,
totalInterest,
totalCost: principal + totalInterest,
payoffDate: formatDate(payoffDate),
schedule,
totalFees
};
renderResults();
};
const renderResults = () => {
const resultsContainer = document.getElementById('results-container');
if (!calculationResult) return;
resultsContainer.innerHTML = `
Payment Summary
Monthly Payment
${formatCurrency(calculationResult.monthlyPayment)}
Total Interest Paid
${formatCurrency(calculationResult.totalInterest)}
Final Payoff Date
${calculationResult.payoffDate}
Amortization Schedule
| # |
Date |
Principal |
Interest |
Balance |
${calculationResult.schedule.map(row => `
| ${row.paymentNumber} |
${row.paymentDate} |
${formatCurrency(row.principalPaid)} |
${formatCurrency(row.interestPaid)} |
${formatCurrency(row.remainingBalance)} |
`).join('')}
`;
document.getElementById('download-pdf-btn').addEventListener('click', generatePdf);
};
const renderConfig = () => {
const configContent = document.getElementById('content-config');
if (!configContent) return;
const feesHtml = appData.fees.map((fee, index) => `
`).join('');
configContent.innerHTML = `
Configure Additional Fees
`;
document.getElementById('fee-config-form').addEventListener('submit', handleConfigUpdate);
};
const handleConfigUpdate = (e) => {
e.preventDefault();
const newFees = [];
document.querySelectorAll('.fee-row').forEach(row => {
newFees.push({
name: row.querySelector('[data-field="name"]').value,
type: row.querySelector('[data-field="type"]').value,
value: parseFloat(row.querySelector('[data-field="value"]').value),
enabled: row.querySelector('[data-field="enabled"]').checked
});
});
appData.fees = newFees;
alert('Fee configuration saved! Your next calculation will include these changes.');
};
const generatePdf = () => {
if (!calculationResult) return;
loadingOverlay.style.display = 'flex';
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' });
// --- Add Content to PDF ---
let y = 40;
// Title
pdf.setFontSize(18);
pdf.setFont('helvetica', 'bold');
pdf.text('Installment Payment Plan Report', pdf.internal.pageSize.getWidth() / 2, y, { align: 'center' });
y += 30;
// Summary section
pdf.setFontSize(10);
pdf.setFont('helvetica', 'normal');
pdf.text(`Generated on: ${new Date().toLocaleDateString()}`, 40, y);
y += 30;
pdf.setFontSize(12);
pdf.setFont('helvetica', 'bold');
pdf.text('Loan Summary', 40, y);
y += 20;
pdf.setFontSize(10);
pdf.setFont('helvetica', 'normal');
const summaryData = [
['Purchase Amount:', formatCurrency(inputs.purchaseAmount)],
['Down Payment:', formatCurrency(inputs.downPayment)],
['Additional Fees:', formatCurrency(calculationResult.totalFees)],
['Total Principal:', formatCurrency(calculationResult.totalPrincipal)],
['Annual Interest Rate:', `${inputs.annualRate.toFixed(2)}%`],
['Loan Term:', `${inputs.loanTerm} ${inputs.termUnit}`],
['Monthly Payment:', formatCurrency(calculationResult.monthlyPayment)],
['Total Interest:', formatCurrency(calculationResult.totalInterest)],
['Payoff Date:', calculationResult.payoffDate]
];
summaryData.forEach(row => {
pdf.setFont('helvetica', 'bold');
pdf.text(row[0], 50, y);
pdf.setFont('helvetica', 'normal');
pdf.text(row[1], 200, y);
y += 15;
});
y += 20;
// Amortization Table
pdf.setFontSize(12);
pdf.setFont('helvetica', 'bold');
pdf.text('Amortization Schedule', 40, y);
y += 20;
pdf.autoTable({
startY: y,
head: [['#', 'Date', 'Principal', 'Interest', 'Balance']],
body: calculationResult.schedule.map(row => [
row.paymentNumber,
row.paymentDate,
formatCurrency(row.principalPaid),
formatCurrency(row.interestPaid),
formatCurrency(row.remainingBalance)
]),
theme: 'grid',
headStyles: { fillColor: [22, 163, 74] }, // Green header
styles: { fontSize: 8 },
});
pdf.save(`Payment-Plan-Report.pdf`);
loadingOverlay.style.display = 'none';
};
// --- TAB NAVIGATION & INITIALIZATION ---
const switchTab = (tabIndex) => {
activeTabIndex = tabIndex;
document.querySelectorAll('.tab-btn').forEach((btn, i) => btn.classList.toggle('active', i === tabIndex));
document.querySelectorAll('.tab-content').forEach((content, i) => content.classList.toggle('hidden', i !== tabIndex));
updateNavButtons();
};
const updateNavButtons = () => {
prevTabBtn.disabled = activeTabIndex === 0;
nextTabBtn.disabled = activeTabIndex === tabIdentifiers.length - 1;
};
const initializeUI = () => {
const tabs = [
{ name: 'Payment Calculator', id: 'calculator' },
{ name: 'Fee Configuration', id: 'config' }
];
tabIdentifiers = tabs.map(t => t.id);
tabsContainer.innerHTML = tabs.map(tab => `
`).join('');
mainContent.innerHTML = tabs.map(tab => `
`).join('');
tabs.forEach((tab, index) => {
document.getElementById(`tab-${tab.id}`).addEventListener('click', () => switchTab(index));
});
renderCalculator();
renderConfig();
switchTab(0);
lucide.createIcons();
};
initializeUI();
// Load jsPDF autoTable plugin
const autoTableScript = document.createElement('script');
autoTableScript.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.5.23/jspdf.plugin.autotable.min.js';
document.head.appendChild(autoTableScript);
prevTabBtn.addEventListener('click', () => { if (activeTabIndex > 0) switchTab(activeTabIndex - 1); });
nextTabBtn.addEventListener('click', () => { if (activeTabIndex < tabIdentifiers.length - 1) switchTab(activeTabIndex + 1); });
});