/** * Admin Listmonk Management Functions for Influence System * 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}: ${response.statusText}`); } const status = await response.json(); console.log('📊 Status data:', status); updateStatusDisplay(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) { connectionStatus.innerHTML = '❌ Connection 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.style.display = 'block'; lastError.innerHTML = `⚠️ Last Error: ${escapeHtml(status.lastError)}`; } else { lastError.style.display = '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}: ${response.statusText}`); } const data = await response.json(); console.log('📊 Stats API response:', data); if (data.success && data.stats) { displayListStats(data.stats); } else { console.error('Stats API returned unsuccessful response:', data); displayListStats([]); } } catch (error) { console.error('Failed to load Listmonk stats:', error); } } /** * Display list statistics in the admin panel */ function displayListStats(stats) { const statsSection = document.getElementById('listmonk-stats-section'); if (!statsSection) return; console.log('📊 displayListStats called with:', stats, 'Type:', typeof stats); // Ensure stats is an array const statsArray = Array.isArray(stats) ? stats : []; console.log('📊 Stats array after conversion:', statsArray, 'Length:', statsArray.length); // 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'; statsList.style.cssText = 'display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 1rem;'; if (statsArray.length === 0) { statsList.innerHTML = '

No email lists found or sync is disabled

'; } else { statsArray.forEach(list => { const statCard = document.createElement('div'); statCard.style.cssText = 'background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);'; statCard.innerHTML = `

${escapeHtml(list.name)}

${list.subscriberCount || 0}

subscribers

`; statsList.appendChild(statCard); }); } statsSection.appendChild(statsList); } /** * Sync data to Listmonk * @param {string} type - 'participants', 'recipients', 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%'; progressBar.textContent = '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'; }); // Simulate progress let progress = 0; const progressInterval = setInterval(() => { if (progress < 90) { progress += Math.random() * 10; if (progressBar) { progressBar.style.width = `${Math.min(progress, 90)}%`; progressBar.textContent = `${Math.floor(Math.min(progress, 90))}%`; } } }, 200); try { let endpoint = '/api/listmonk/sync/'; let syncName = ''; switch(type) { case 'participants': endpoint += 'participants'; syncName = 'Campaign Participants'; break; case 'recipients': endpoint += 'recipients'; syncName = 'Custom Recipients'; break; case 'all': endpoint += 'all'; syncName = 'All Data'; break; default: throw new Error('Invalid sync type'); } const response = await fetch(endpoint, { method: 'POST', credentials: 'include' }); clearInterval(progressInterval); if (progressBar) { progressBar.style.width = '100%'; progressBar.textContent = '100%'; } if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); displaySyncResults(result, resultsDiv); // Refresh stats after successful sync await loadListmonkStats(); } catch (error) { clearInterval(progressInterval); console.error('Sync failed:', error); if (resultsDiv) { resultsDiv.innerHTML = `
❌ Sync Failed

${escapeHtml(error.message)}

`; } } 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 || 'Sync completed successfully')}
`; if (result.results) { // Handle different result structures if (result.results.participants || result.results.customRecipients) { // Multi-type sync (all) if (result.results.participants) { html += formatSyncResults('Campaign Participants', result.results.participants); } if (result.results.customRecipients) { html += formatSyncResults('Custom Recipients', result.results.customRecipients); } } else { // Single type sync html += formatSyncResults('Results', result.results); } } } else { html += `
❌ Sync Failed

${escapeHtml(result.error || result.message || 'Unknown error')}

`; } resultsDiv.innerHTML = html; } /** * Format sync results for display */ function formatSyncResults(type, results) { let html = `
📊 ${escapeHtml(type)}: ${results.success} succeeded, ${results.failed} failed (${results.total} total)
`; // Show errors if any if (results.errors && results.errors.length > 0) { const maxErrors = 5; // Show max 5 errors const errorCount = results.errors.length; html += `
⚠️ Errors (showing ${Math.min(errorCount, maxErrors)} of ${errorCount}):
'; } return html; } /** * Test Listmonk connection */ async function testListmonkConnection() { try { const response = await fetch('/api/listmonk/test-connection', { method: 'POST', credentials: 'include' }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const result = await response.json(); if (result.success) { showNotification('✅ Listmonk connection successful!', 'success'); await refreshListmonkStatus(); } else { showNotification(`❌ Connection failed: ${result.message}`, 'error'); } } 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}: ${response.statusText}`); } const result = await response.json(); if (result.success) { showNotification('✅ Listmonk lists reinitialized successfully!', 'success'); await refreshListmonkStatus(); await loadListmonkStats(); } else { showNotification(`❌ Reinitialization failed: ${result.message}`, 'error'); } } catch (error) { console.error('Reinitialization failed:', error); showNotification(`❌ Reinitialization failed: ${error.message}`, 'error'); } } /** * Utility function to escape HTML */ function escapeHtml(text) { if (typeof text !== 'string') return ''; const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } /** * Show notification message */ function showNotification(message, type = 'info') { const messageContainer = document.getElementById('message-container'); if (!messageContainer) { // Fallback to alert if container not found alert(message); return; } const alertClass = type === 'success' ? 'message-success' : type === 'error' ? 'message-error' : 'message-info'; messageContainer.className = alertClass; messageContainer.textContent = message; messageContainer.classList.remove('hidden'); // Auto-hide after 5 seconds setTimeout(() => { messageContainer.classList.add('hidden'); }, 5000); } // Initialize Listmonk admin when tab is activated document.addEventListener('DOMContentLoaded', () => { // Watch for tab changes const listmonkTab = document.querySelector('[data-tab="listmonk"]'); if (listmonkTab) { listmonkTab.addEventListener('click', () => { // Initialize on first load if (!listmonkTab.dataset.initialized) { initListmonkAdmin(); listmonkTab.dataset.initialized = 'true'; } }); } }); // Export functions for global use window.syncToListmonk = syncToListmonk; window.refreshListmonkStatus = refreshListmonkStatus; window.testListmonkConnection = testListmonkConnection; window.reinitializeListmonk = reinitializeListmonk; window.initListmonkAdmin = initListmonkAdmin;