Spaces:
Running
Running
File size: 17,685 Bytes
3e4cfcb |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Loan Calculator</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
/* Custom scrollbar for schedule */
.schedule-container::-webkit-scrollbar {
width: 6px;
height: 6px;
}
.schedule-container::-webkit-scrollbar-track {
background: #f1f1f1;
}
.schedule-container::-webkit-scrollbar-thumb {
background: #888;
border-radius: 3px;
}
.schedule-container::-webkit-scrollbar-thumb:hover {
background: #555;
}
/* Animation for results */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.fade-in {
animation: fadeIn 0.5s ease-out forwards;
}
</style>
</head>
<body class="bg-gray-100 min-h-screen">
<div class="flex items-center justify-center min-h-screen">
<div class="w-full max-w-2xl mx-4">
<div class="bg-white rounded-xl shadow-xl overflow-hidden">
<div class="text-center p-6 bg-indigo-700 text-white">
<h1 class="text-3xl font-bold mb-2">Loan Calculator</h1>
<p class="opacity-90">Calculate your EMI and payment schedule</p>
</div>
<div class="p-6">
<div class="mb-6">
<label for="loanAmount" class="block text-gray-700 font-medium mb-2">Loan Amount ($)</label>
<div class="relative">
<span class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">$</span>
<input type="number" id="loanAmount" class="w-full pl-8 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="10,000" min="1">
</div>
</div>
<div class="mb-6">
<label for="interestRate" class="block text-gray-700 font-medium mb-2">Interest Rate (% p.a.)</label>
<div class="relative">
<span class="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500">%</span>
<input type="number" id="interestRate" class="w-full pl-8 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="8.5" step="0.01" min="0">
</div>
</div>
<div class="mb-6">
<label for="loanTerm" class="block text-gray-700 font-medium mb-2">Loan Term</label>
<div class="flex gap-4">
<div class="flex-1">
<input type="number" id="loanTerm" class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" placeholder="5" min="1">
</div>
<select id="termType" class="flex-1 px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500">
<option value="years">Years</option>
<option value="months">Months</option>
</select>
</div>
</div>
<div class="mb-6">
<label class="block text-gray-700 font-medium mb-2">Payment Type</label>
<div class="grid grid-cols-2 gap-3">
<button id="emiBtn" class="payment-type-btn bg-indigo-600 text-white py-2 px-4 rounded-lg shadow-sm hover:bg-indigo-700 transition active:bg-indigo-800">
<i class="fas fa-calendar-alt mr-2"></i> EMI
</button>
<button id="otherBtn" class="payment-type-btn bg-gray-200 text-gray-700 py-2 px-4 rounded-lg shadow-sm hover:bg-gray-300 transition" disabled>
<i class="fas fa-clock mr-2"></i> Other (Coming Soon)
</button>
</div>
</div>
<button id="calculateBtn" class="w-full bg-indigo-600 text-white py-3 px-4 rounded-lg font-medium hover:bg-indigo-700 transition active:bg-indigo-800 shadow-md">
Calculate <i class="fas fa-calculator ml-2"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Results Modal -->
<div id="resultsModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 hidden z-50">
<div class="bg-white rounded-xl shadow-xl w-full max-w-4xl max-h-[90vh] flex flex-col">
<div class="p-6 bg-indigo-700 text-white flex justify-between items-center">
<h2 class="text-2xl font-bold">Loan Calculation Results</h2>
<button id="closeModal" class="text-white hover:text-gray-200 text-2xl">×</button>
</div>
<div class="p-6 overflow-y-auto flex-1">
<div class="grid grid-cols-2 gap-4 mb-6">
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Monthly Payment</p>
<p id="monthlyPayment" class="text-2xl font-bold text-indigo-600">$0</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Total Interest</p>
<p id="totalInterest" class="text-2xl font-bold text-indigo-600">$0</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Total Payment</p>
<p id="totalPayment" class="text-2xl font-bold text-indigo-600">$0</p>
</div>
<div class="bg-gray-50 p-4 rounded-lg">
<p class="text-sm text-gray-500">Loan Term</p>
<p id="loanTermResult" class="text-2xl font-bold text-indigo-600">0 Months</p>
</div>
</div>
<div class="border-t pt-4">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-semibold text-gray-800">Payment Schedule</h3>
<button id="downloadBtn" class="text-indigo-600 hover:text-indigo-800 flex items-center">
<i class="fas fa-download mr-1"></i> Export
</button>
</div>
<div class="schedule-container max-h-96 overflow-y-auto">
<table class="w-full text-sm">
<thead class="sticky top-0 bg-gray-100">
<tr class="text-left text-gray-600 border-b">
<th class="py-2 px-3">#</th>
<th class="py-2 px-3">Payment</th>
<th class="py-2 px-3">Principal</th>
<th class="py-2 px-3">Interest</th>
<th class="py-2 px-3">Balance</th>
</tr>
</thead>
<tbody id="scheduleBody" class="divide-y divide-gray-200">
<!-- Schedule will be populated here -->
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
const calculateBtn = document.getElementById('calculateBtn');
const emiBtn = document.getElementById('emiBtn');
const otherBtn = document.getElementById('otherBtn');
const resultsSection = document.getElementById('resultsSection');
const downloadBtn = document.getElementById('downloadBtn');
// Format currency
const formatCurrency = (amount) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2
}).format(amount);
};
// Calculate EMI
const calculateEMI = (principal, annualRate, termYears, termType) => {
let termMonths;
if (termType === 'years') {
termMonths = termYears * 12;
} else {
termMonths = termYears;
}
const monthlyRate = annualRate / 100 / 12;
const emi = principal * monthlyRate * Math.pow(1 + monthlyRate, termMonths) / (Math.pow(1 + monthlyRate, termMonths) - 1);
return {
emi: emi,
termMonths: termMonths,
totalPayment: emi * termMonths,
totalInterest: (emi * termMonths) - principal
};
};
// Generate payment schedule
const generateSchedule = (principal, annualRate, termMonths, emi) => {
const monthlyRate = annualRate / 100 / 12;
let balance = principal;
const schedule = [];
for (let i = 1; i <= termMonths; i++) {
const interest = balance * monthlyRate;
const principalPayment = emi - interest;
balance -= principalPayment;
// Ensure balance doesn't go negative due to rounding
if (i === termMonths && Math.abs(balance) < 0.01) {
balance = 0;
}
schedule.push({
month: i,
payment: emi,
principal: principalPayment,
interest: interest,
balance: balance > 0 ? balance : 0
});
}
return schedule;
};
// Modal elements
const resultsModal = document.getElementById('resultsModal');
const closeModal = document.getElementById('closeModal');
// Close modal handler
closeModal.addEventListener('click', function() {
resultsModal.classList.add('hidden');
});
// Close modal when clicking outside
resultsModal.addEventListener('click', function(e) {
if (e.target === resultsModal) {
resultsModal.classList.add('hidden');
}
});
// Calculate button click handler
calculateBtn.addEventListener('click', function() {
const loanAmount = parseFloat(document.getElementById('loanAmount').value);
const interestRate = parseFloat(document.getElementById('interestRate').value);
const loanTerm = parseFloat(document.getElementById('loanTerm').value);
const termType = document.getElementById('termType').value;
// Validate inputs
if (isNaN(loanAmount) || isNaN(interestRate) || isNaN(loanTerm) || loanAmount <= 0 || loanTerm <= 0) {
alert('Please enter valid values for all fields');
return;
}
// Calculate EMI
const result = calculateEMI(loanAmount, interestRate, loanTerm, termType);
// Update summary
document.getElementById('monthlyPayment').textContent = formatCurrency(result.emi);
document.getElementById('totalInterest').textContent = formatCurrency(result.totalInterest);
document.getElementById('totalPayment').textContent = formatCurrency(result.totalPayment);
document.getElementById('loanTermResult').textContent = `${result.termMonths} Months`;
// Generate and display schedule
const schedule = generateSchedule(loanAmount, interestRate, result.termMonths, result.emi);
const scheduleBody = document.getElementById('scheduleBody');
scheduleBody.innerHTML = '';
schedule.forEach((payment, index) => {
const row = document.createElement('tr');
row.className = index % 2 === 0 ? 'bg-white' : 'bg-gray-50';
row.innerHTML = `
<td class="py-2 px-3">${payment.month}</td>
<td class="py-2 px-3">${formatCurrency(payment.payment)}</td>
<td class="py-2 px-3">${formatCurrency(payment.principal)}</td>
<td class="py-2 px-3">${formatCurrency(payment.interest)}</td>
<td class="py-2 px-3">${formatCurrency(payment.balance)}</td>
`;
scheduleBody.appendChild(row);
});
// Show modal with results
resultsModal.classList.remove('hidden');
});
// Payment type buttons
emiBtn.addEventListener('click', function() {
emiBtn.classList.remove('bg-gray-200', 'text-gray-700');
emiBtn.classList.add('bg-indigo-600', 'text-white');
otherBtn.classList.remove('bg-indigo-600', 'text-white');
otherBtn.classList.add('bg-gray-200', 'text-gray-700');
});
otherBtn.addEventListener('click', function() {
alert('Other payment types coming soon! Currently only EMI is available.');
});
// Export schedule
downloadBtn.addEventListener('click', function() {
const loanAmount = document.getElementById('loanAmount').value;
const interestRate = document.getElementById('interestRate').value;
const loanTerm = document.getElementById('loanTerm').value;
const termType = document.getElementById('termType').value;
if (!resultsSection.classList.contains('hidden')) {
// Create CSV content
let csv = 'Payment #,Payment,Principal,Interest,Balance\n';
const rows = document.querySelectorAll('#scheduleBody tr');
rows.forEach(row => {
const cols = row.querySelectorAll('td');
const rowData = [];
cols.forEach(col => {
// Remove currency symbols and commas for CSV
const text = col.textContent.replace(/[$,]/g, '');
rowData.push(text);
});
csv += rowData.join(',') + '\n';
});
// Create download link
const blob = new Blob([csv], { type: 'text/csv' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.setAttribute('hidden', '');
a.setAttribute('href', url);
a.setAttribute('download', `loan_schedule_${loanAmount}_${interestRate}_${loanTerm}${termType === 'years' ? 'y' : 'm'}.csv`);
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
} else {
alert('Please calculate a loan first');
}
});
// Add animation to results when they appear
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('fade-in');
}
});
}, { threshold: 0.1 });
document.querySelectorAll('#resultsSection > div').forEach(el => {
observer.observe(el);
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=sombochea/loan-calculator" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html> |