/** * Newsletter Management - Admin Interface */ let currentPage = 1; const perPage = 50; let currentFilters = { status: 'active', verified: 'all' }; /** * Initialize page */ async function init() { // Event listeners (navbar handles admin name and logout now) document.getElementById('refresh-btn').addEventListener('click', () => loadAll()); document.getElementById('export-btn').addEventListener('click', exportSubscribers); document.getElementById('filter-status').addEventListener('change', handleFilterChange); document.getElementById('filter-verified').addEventListener('change', handleFilterChange); document.getElementById('prev-page').addEventListener('click', () => changePage(-1)); document.getElementById('next-page').addEventListener('click', () => changePage(1)); // Load data await loadAll(); } /** * Load all data */ async function loadAll() { await Promise.all([ loadStats(), loadSubscribers() ]); } /** * Load statistics */ async function loadStats() { try { const token = localStorage.getItem('admin_token'); const response = await fetch('/api/newsletter/admin/stats', { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to load stats'); const data = await response.json(); const stats = data.stats; document.getElementById('stat-total').textContent = stats.total || 0; document.getElementById('stat-active').textContent = stats.active || 0; document.getElementById('stat-verified').textContent = stats.verified || 0; document.getElementById('stat-recent').textContent = stats.recent_30_days || 0; } catch (error) { console.error('Error loading stats:', error); } } /** * Load subscribers list */ async function loadSubscribers() { try { const token = localStorage.getItem('admin_token'); const skip = (currentPage - 1) * perPage; const params = new URLSearchParams({ limit: perPage, skip, active: currentFilters.status === 'all' ? null : currentFilters.status === 'active', verified: currentFilters.verified === 'all' ? null : currentFilters.verified === 'verified' }); // Remove null values for (const [key, value] of [...params.entries()]) { if (value === 'null' || value === null) { params.delete(key); } } const response = await fetch(`/api/newsletter/admin/subscriptions?${params}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to load subscribers'); const data = await response.json(); renderSubscribers(data.subscriptions); updatePagination(data.pagination); } catch (error) { console.error('Error loading subscribers:', error); document.getElementById('subscribers-table').innerHTML = ` Error loading subscribers. Please refresh the page. `; } } /** * Render subscribers table */ function renderSubscribers(subscriptions) { const tbody = document.getElementById('subscribers-table'); if (!subscriptions || subscriptions.length === 0) { tbody.innerHTML = ` No subscribers found `; return; } tbody.innerHTML = subscriptions.map(sub => ` ${escapeHtml(sub.email)} ${escapeHtml(sub.name) || '-'} ${escapeHtml(sub.source) || 'unknown'} ${sub.active ? ` ${sub.verified ? '' : ''} ${sub.verified ? 'Active ✓' : 'Active'} ` : 'Inactive' } ${formatDate(sub.subscribed_at)} `).join(''); // Add event listeners to buttons tbody.querySelectorAll('.view-details-btn').forEach(btn => { btn.addEventListener('click', function() { const id = this.getAttribute('data-id'); viewDetails(id); }); }); tbody.querySelectorAll('.delete-subscriber-btn').forEach(btn => { btn.addEventListener('click', function() { const id = this.getAttribute('data-id'); const email = this.getAttribute('data-email'); deleteSubscriber(id, email); }); }); } /** * Update pagination UI */ function updatePagination(pagination) { document.getElementById('showing-from').textContent = pagination.skip + 1; document.getElementById('showing-to').textContent = Math.min(pagination.skip + pagination.limit, pagination.total); document.getElementById('total-count').textContent = pagination.total; document.getElementById('prev-page').disabled = currentPage === 1; document.getElementById('next-page').disabled = !pagination.has_more; } /** * Handle filter change */ function handleFilterChange() { currentFilters.status = document.getElementById('filter-status').value; currentFilters.verified = document.getElementById('filter-verified').value; currentPage = 1; loadSubscribers(); } /** * Change page */ function changePage(direction) { currentPage += direction; loadSubscribers(); } /** * View subscriber details */ async function viewDetails(id) { alert(`Subscriber details for ID: ${id}\n(Full implementation would show a modal with complete subscriber information)`); } /** * Delete subscriber */ async function deleteSubscriber(id, email) { if (!confirm(`Are you sure you want to delete subscription for ${email}?\n\nThis action cannot be undone.`)) { return; } try { const token = localStorage.getItem('admin_token'); const response = await fetch(`/api/newsletter/admin/subscriptions/${id}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to delete subscriber'); alert('Subscriber deleted successfully'); await loadAll(); } catch (error) { console.error('Error deleting subscriber:', error); alert('Failed to delete subscriber. Please try again.'); } } /** * Export subscribers as CSV */ async function exportSubscribers() { try { const token = localStorage.getItem('admin_token'); const active = currentFilters.status === 'all' ? 'all' : 'true'; const response = await fetch(`/api/newsletter/admin/export?active=${active}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!response.ok) throw new Error('Failed to export subscribers'); const blob = await response.blob(); const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `newsletter-subscribers-${Date.now()}.csv`; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); document.body.removeChild(a); } catch (error) { console.error('Error exporting subscribers:', error); alert('Failed to export subscribers. Please try again.'); } } // Logout handled by navbar component /** * Format date */ function formatDate(dateString) { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }); } /** * Escape HTML */ function escapeHtml(text) { if (!text) return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } // Initialize on page load document.addEventListener('DOMContentLoaded', init);