Auto-Recurring Subscription Payment Scheduler

Auto-Recurring Subscription Payment Scheduler

Visualize and manage all your recurring subscription payments.

${activeSubs.length}

Upcoming Payments

${paymentsHtml}
Subscription Payment Date Amount Cycle
`; document.getElementById('download-pdf-btn').addEventListener('click', generatePdf); }; const renderConfiguration = () => { const configContent = document.getElementById('content-config'); if (!configContent) return; const subsHtml = appData.subscriptions.map(sub => `
`).join(''); configContent.innerHTML = `

Manage Subscriptions

Name Amount ($) Cycle Next Bill Date Status
${subsHtml}
`; lucide.createIcons(); document.getElementById('add-sub-btn').addEventListener('click', handleAddRow); document.getElementById('config-form').addEventListener('submit', handleConfigSave); document.querySelectorAll('.remove-sub-btn').forEach(btn => btn.addEventListener('click', (e) => e.currentTarget.closest('.sub-config-row').remove())); }; const handleAddRow = () => { const container = document.getElementById('subs-list-container'); const newRow = document.createElement('div'); newRow.className = 'sub-config-row grid grid-cols-12 gap-2 sm:gap-4 items-center is-new'; newRow.innerHTML = `
`; container.appendChild(newRow); lucide.createIcons(); newRow.querySelector('.remove-sub-btn').addEventListener('click', () => newRow.remove()); }; const handleConfigSave = (e) => { e.preventDefault(); const newSubs = []; let nextId = Math.max(...appData.subscriptions.map(s => s.id), 0) + 1; document.querySelectorAll('.sub-config-row').forEach(row => { const name = row.querySelector('[data-field="name"]').value; const amount = parseFloat(row.querySelector('[data-field="amount"]').value); if (!name || isNaN(amount)) return; // Skip invalid rows const id = row.classList.contains('is-new') ? nextId++ : parseInt(row.dataset.id); newSubs.push({ id, name, amount, cycle: row.querySelector('[data-field="cycle"]').value, nextBill: row.querySelector('[data-field="nextBill"]').value, status: row.querySelector('[data-field="status"]').value, }); }); appData.subscriptions = newSubs; alert('Subscriptions saved!'); renderScheduler(); // Update dashboard with new data renderConfiguration(); // Redraw config to sync state }; const generatePdf = () => { loadingOverlay.style.display = 'flex'; const { jsPDF } = window.jspdf; const pdf = new jsPDF({ orientation: 'p', unit: 'pt', format: 'a4' }); const activeSubs = appData.subscriptions.filter(s => s.status === 'active'); let totalMonthlyCost = activeSubs.reduce((sum, s) => { if (s.cycle === 'monthly') return sum + s.amount; if (s.cycle === 'quarterly') return sum + s.amount / 3; if (s.cycle === 'annually') return sum + s.amount / 12; return sum; }, 0); let y = 40; pdf.setFontSize(18); pdf.setFont('helvetica', 'bold'); pdf.text('Subscription Payment Report', pdf.internal.pageSize.getWidth() / 2, y, { align: 'center' }); y += 20; pdf.setFontSize(10); pdf.setFont('helvetica', 'normal'); pdf.text(`Generated on: ${new Date().toLocaleDateString()}`, pdf.internal.pageSize.getWidth() / 2, y, { align: 'center' }); y += 40; pdf.autoTable({ startY: y, body: [ [ { content: 'Avg. Monthly Cost\n' + formatCurrency(totalMonthlyCost), styles: { halign: 'center', fontSize: 10, cellPadding: 10 } }, { content: 'Total Annual Cost\n' + formatCurrency(totalMonthlyCost * 12), styles: { halign: 'center', fontSize: 10, cellPadding: 10 } }, { content: 'Active Subscriptions\n' + activeSubs.length, styles: { halign: 'center', fontSize: 10, cellPadding: 10 } } ] ], theme: 'plain', styles: { font: 'helvetica', fontStyle: 'bold', lineWidth: 1, lineColor: [221, 221, 221] } }); y = pdf.autoTable.previous.finalY + 30; pdf.autoTable({ startY: y, head: [['Subscription', 'Amount', 'Cycle', 'Next Bill Date', 'Status']], body: appData.subscriptions.map(s => [s.name, formatCurrency(s.amount), s.cycle, formatDate(s.nextBill), s.status]), theme: 'grid', headStyles: { fillColor: [22, 163, 74] } // Green header }); pdf.save(`Subscription-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: 'Subscription Schedule', id: 'scheduler' }, { name: 'Manage Subscriptions', 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)); }); renderScheduler(); renderConfiguration(); switchTab(0); lucide.createIcons(); }; initializeUI(); prevTabBtn.addEventListener('click', () => { if (activeTabIndex > 0) switchTab(activeTabIndex - 1); }); nextTabBtn.addEventListener('click', () => { if (activeTabIndex < tabIdentifiers.length - 1) switchTab(activeTabIndex + 1); }); });
Scroll to Top