/** * Admin Listmonk Management Functions * Handles admin interface for email list synchronization */ // Global variables for admin Listmonk functionality let syncInProgress = false; let syncProgressInterval = null; /** * Initialize Listmonk admin section */ async function initListmonkAdmin() { await refreshListmonkStatus(); await loadListmonkStats(); } /** * Refresh the Listmonk sync status display */ async function refreshListmonkStatus() { console.log('🔄 Refreshing Listmonk status...'); try { const response = await fetch('/api/listmonk/status', { credentials: 'include' }); console.log('📡 Status response:', response.status, response.statusText); if (!response.ok) throw new Error(`HTTP ${response.status}`); const status = await response.json(); console.log('📊 Status data:', status); updateStatusDisplay(status); // Update global status if available if (window.listmonkStatus) { window.listmonkStatus.currentStatus = status; } } catch (error) { console.error('Failed to refresh Listmonk status:', error); updateStatusDisplay({ enabled: false, connected: false, lastError: `Status check failed: ${error.message}` }); } } /** * Update the status display in the admin panel */ function updateStatusDisplay(status) { console.log('🎨 Updating status display with:', status); const connectionStatus = document.getElementById('connection-status'); const autosyncStatus = document.getElementById('autosync-status'); const lastError = document.getElementById('last-error'); console.log('🔍 Status elements found:', { connectionStatus: !!connectionStatus, autosyncStatus: !!autosyncStatus, lastError: !!lastError }); if (connectionStatus) { if (status.enabled && status.connected) { connectionStatus.innerHTML = '✅ Connected'; } else if (status.enabled && !status.connected) { connectionStatus.innerHTML = '❌ Failed'; } else { connectionStatus.innerHTML = '⭕ Disabled'; } console.log('✅ Connection status updated:', connectionStatus.innerHTML); } if (autosyncStatus) { if (status.enabled) { autosyncStatus.innerHTML = '✅ Enabled'; } else { autosyncStatus.innerHTML = '⭕ Disabled'; } console.log('✅ Auto-sync status updated:', autosyncStatus.innerHTML); } if (lastError) { if (status.lastError) { lastError.innerHTML = `${escapeHtml(status.lastError)}`; } else { lastError.innerHTML = 'None'; } console.log('✅ Last error updated:', lastError.innerHTML); } } /** * Load and display Listmonk list statistics */ async function loadListmonkStats() { try { const response = await fetch('/api/listmonk/stats', { credentials: 'include' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const data = await response.json(); console.log('📊 Stats API response:', data); // Debug log if (data.success && data.stats) { // Ensure stats is an array const statsArray = Array.isArray(data.stats) ? data.stats : []; console.log('📊 Stats array:', statsArray); // Debug log displayListStats(statsArray); } else { console.warn('No Listmonk stats available:', data.error || 'Unknown error'); displayListStats([]); } } catch (error) { console.error('Failed to load Listmonk stats:', error); } } /** * Display list statistics in the admin panel */ function displayListStats(stats) { const statusContent = document.getElementById('sync-status-display'); if (!statusContent) return; console.log('📊 displayListStats called with:', stats, 'Type:', typeof stats); // Debug log // Ensure stats is an array const statsArray = Array.isArray(stats) ? stats : []; console.log('📊 Stats array after conversion:', statsArray, 'Length:', statsArray.length); // Debug log // Add stats section if it doesn't exist let statsSection = document.getElementById('listmonk-stats-section'); if (!statsSection) { statsSection = document.createElement('div'); statsSection.id = 'listmonk-stats-section'; statsSection.innerHTML = '

Email Lists

'; statusContent.appendChild(statsSection); } // Clear existing stats const existingStats = statsSection.querySelector('.stats-list'); if (existingStats) { existingStats.remove(); } // Create stats display const statsList = document.createElement('div'); statsList.className = 'stats-list'; if (statsArray.length === 0) { statsList.innerHTML = '

No email lists found

'; } else { statsArray.forEach(list => { const statRow = document.createElement('div'); statRow.className = 'status-row'; statRow.innerHTML = ` ${escapeHtml(list.name)} ${list.subscriberCount} subscribers `; statsList.appendChild(statRow); }); } statsSection.appendChild(statsList); } /** * Sync data to Listmonk * @param {string} type - 'locations', 'users', or 'all' */ async function syncToListmonk(type) { if (syncInProgress) { showNotification('Sync already in progress', 'warning'); return; } syncInProgress = true; const progressSection = document.getElementById('sync-progress'); const resultsDiv = document.getElementById('sync-results'); const progressBar = document.getElementById('sync-progress-bar'); // Show progress section if (progressSection) { progressSection.style.display = 'block'; } // Reset progress if (progressBar) { progressBar.style.width = '0%'; } if (resultsDiv) { resultsDiv.innerHTML = '
Starting sync...
'; } // Disable sync buttons const buttons = document.querySelectorAll('.sync-buttons .btn'); buttons.forEach(btn => { btn.disabled = true; btn.style.opacity = '0.6'; }); try { const response = await fetch(`/api/listmonk/sync/${type}`, { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); // Animate progress to 100% if (progressBar) { progressBar.style.width = '100%'; } // Display results displaySyncResults(result, resultsDiv); // Refresh stats after successful sync setTimeout(() => { loadListmonkStats(); }, 1000); } catch (error) { console.error('Sync failed:', error); if (resultsDiv) { resultsDiv.innerHTML = `
Sync Failed: ${escapeHtml(error.message)}
`; } showNotification('Sync failed: ' + error.message, 'error'); } finally { syncInProgress = false; // Re-enable sync buttons buttons.forEach(btn => { btn.disabled = false; btn.style.opacity = '1'; }); } } /** * Display sync results in the admin panel */ function displaySyncResults(result, resultsDiv) { if (!resultsDiv) return; let html = ''; if (result.success) { html += `
✅ ${escapeHtml(result.message)}
`; // Show detailed results for different sync types if (result.results) { if (result.results.locations) { html += formatSyncResults('Locations', result.results.locations); } if (result.results.users) { html += formatSyncResults('Users', result.results.users); } // For single type syncs if (result.results.total !== undefined) { html += formatSyncResults('Items', result.results); } } showNotification('Sync completed successfully!', 'success'); } else { html += `
❌ Sync Failed: ${escapeHtml(result.error)}
`; showNotification('Sync failed', 'error'); } resultsDiv.innerHTML = html; } /** * Format sync results for display */ function formatSyncResults(type, results) { let html = `
${type}: ${results.success} succeeded, ${results.failed} failed (${results.total} total)
`; // Show errors if any if (results.errors && results.errors.length > 0) { html += `
Errors:
`; } return html; } /** * Test Listmonk connection */ async function testListmonkConnection() { try { const response = await fetch('/api/listmonk/test-connection', { credentials: 'include' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const result = await response.json(); if (result.success && result.connected) { showNotification('✅ Listmonk connection successful!', 'success'); } else { showNotification('❌ Connection failed: ' + (result.message || 'Unknown error'), 'error'); } // Refresh status after test setTimeout(refreshListmonkStatus, 1000); } catch (error) { console.error('Connection test failed:', error); showNotification('❌ Connection test failed: ' + error.message, 'error'); } } /** * Reinitialize Listmonk lists */ async function reinitializeListmonk() { if (!confirm('This will recreate all email lists. Are you sure?')) { return; } try { const response = await fetch('/api/listmonk/reinitialize', { method: 'POST', credentials: 'include' }); if (!response.ok) throw new Error(`HTTP ${response.status}`); const result = await response.json(); if (result.success) { showNotification('✅ Lists reinitialized successfully!', 'success'); setTimeout(() => { refreshListmonkStatus(); loadListmonkStats(); }, 1000); } else { showNotification('❌ Failed to reinitialize: ' + result.message, 'error'); } } catch (error) { console.error('Reinitialize failed:', error); showNotification('❌ Reinitialize failed: ' + error.message, 'error'); } } /** * Utility function to escape HTML */ function escapeHtml(text) { if (typeof text !== 'string') return text; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Fallback notification function if showNotification is not available */ function showNotification(message, type = 'info') { // Check if a global showNotification function exists if (typeof window.showNotification === 'function' && window.showNotification !== showNotification) { window.showNotification(message, type); } else { // Simple fallback - log to console and show alert for errors console.log(`[${type.toUpperCase()}] ${message}`); if (type === 'error') { console.error(message); alert(`ERROR: ${message}`); } else if (type === 'warning') { console.warn(message); alert(`WARNING: ${message}`); } else if (type === 'success') { console.info(`SUCCESS: ${message}`); } } } // Auto-initialize when admin page loads function initializeListmonkEventListeners() { console.log('🔧 Initializing Listmonk event listeners...'); // Sync Locations button const syncLocationsBtn = document.getElementById('sync-locations-btn'); if (syncLocationsBtn) { syncLocationsBtn.addEventListener('click', () => { console.log('📍 Sync Locations clicked'); syncToListmonk('locations'); }); console.log('✅ Sync Locations button listener added'); } else { console.warn('❌ Sync Locations button not found'); } // Sync Users button const syncUsersBtn = document.getElementById('sync-users-btn'); if (syncUsersBtn) { syncUsersBtn.addEventListener('click', () => { console.log('👤 Sync Users clicked'); syncToListmonk('users'); }); console.log('✅ Sync Users button listener added'); } else { console.warn('❌ Sync Users button not found'); } // Sync All button const syncAllBtn = document.getElementById('sync-all-btn'); if (syncAllBtn) { syncAllBtn.addEventListener('click', () => { console.log('🔄 Sync All clicked'); syncToListmonk('all'); }); console.log('✅ Sync All button listener added'); } else { console.warn('❌ Sync All button not found'); } // Refresh Status button const refreshStatusBtn = document.getElementById('refresh-status-btn'); if (refreshStatusBtn) { refreshStatusBtn.addEventListener('click', () => { console.log('🔍 Refresh Status clicked'); refreshListmonkStatus(); }); console.log('✅ Refresh Status button listener added'); } else { console.warn('❌ Refresh Status button not found'); } // Test Connection button const testConnectionBtn = document.getElementById('test-connection-btn'); if (testConnectionBtn) { testConnectionBtn.addEventListener('click', () => { console.log('🔗 Test Connection clicked'); testListmonkConnection(); }); console.log('✅ Test Connection button listener added'); } else { console.warn('❌ Test Connection button not found'); } // Reinitialize Lists button const reinitializeListsBtn = document.getElementById('reinitialize-lists-btn'); if (reinitializeListsBtn) { reinitializeListsBtn.addEventListener('click', () => { console.log('🛠️ Reinitialize Lists clicked'); reinitializeListmonk(); }); console.log('✅ Reinitialize Lists button listener added'); } else { console.warn('❌ Reinitialize Lists button not found'); } } function attemptInitialization(attempt = 1) { console.log(`🔄 Attempting Listmonk initialization (attempt ${attempt})`); // Try to initialize admin and event listeners directly try { initListmonkAdmin(); initializeListmonkEventListeners(); console.log('✅ Listmonk initialization completed successfully'); return true; } catch (error) { console.warn(`⚠️ Attempt ${attempt} failed:`, error.message); if (attempt < 5) { console.log(`⏱️ Retrying in ${attempt * 500}ms...`); setTimeout(() => attemptInitialization(attempt + 1), attempt * 500); return false; } else { console.error('❌ All initialization attempts failed'); // Still try to initialize event listeners as a fallback try { initializeListmonkEventListeners(); console.log('✅ Event listeners initialized as fallback'); } catch (fallbackError) { console.error('❌ Even fallback initialization failed:', fallbackError); } return false; } } } // Also try a more direct approach - just initialize when the page is ready function directInitialization() { console.log('🚀 Direct Listmonk initialization...'); try { initListmonkAdmin(); initializeListmonkEventListeners(); console.log('✅ Direct initialization successful'); } catch (error) { console.error('Direct initialization failed:', error); // Try just the event listeners try { initializeListmonkEventListeners(); console.log('✅ Event listeners initialized directly'); } catch (listenerError) { console.error('❌ Even direct event listener initialization failed:', listenerError); } } } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { attemptInitialization(); // Also try direct initialization after a delay setTimeout(directInitialization, 2000); }); } else { attemptInitialization(); // Also try direct initialization after a delay setTimeout(directInitialization, 1000); } // Export functions for global use window.syncToListmonk = syncToListmonk; window.refreshListmonkStatus = refreshListmonkStatus; window.testListmonkConnection = testListmonkConnection; window.reinitializeListmonk = reinitializeListmonk;