/**
* 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:
`;
// Show only first 5 errors to avoid overwhelming the UI
const errorsToShow = results.errors.slice(0, 5);
errorsToShow.forEach(error => {
html += `- ${escapeHtml(error.email || error.id)}: ${escapeHtml(error.error)}
`;
});
if (results.errors.length > 5) {
html += `- ... and ${results.errors.length - 5} more errors
`;
}
html += `
`;
}
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;