// Admin Panel JavaScript class AdminPanel { constructor() { this.currentCampaign = null; this.campaigns = []; this.authManager = null; } async init() { // Check authentication first if (typeof authManager !== 'undefined') { this.authManager = authManager; const isAuth = await this.authManager.checkSession(); if (!isAuth || !this.authManager.user?.isAdmin) { window.location.href = '/login.html'; return; } this.setupUserInterface(); } else { // Fallback if authManager not loaded window.location.href = '/login.html'; return; } this.setupEventListeners(); this.setupFormInteractions(); this.loadCampaigns(); } setupUserInterface() { // Add user info to header const adminHeader = document.querySelector('.admin-header .admin-container'); if (adminHeader && this.authManager.user) { const userInfo = document.createElement('div'); userInfo.style.cssText = 'position: absolute; top: 1rem; right: 2rem; color: white; font-size: 0.9rem;'; userInfo.innerHTML = ` Welcome, ${this.authManager.user.name || this.authManager.user.email} `; adminHeader.style.position = 'relative'; adminHeader.appendChild(userInfo); // Add logout event listener document.getElementById('logout-btn').addEventListener('click', () => { this.authManager.logout(); }); } } setupEventListeners() { } setupEventListeners() { // Tab navigation document.querySelectorAll('.nav-btn').forEach(btn => { btn.addEventListener('click', (e) => { const tab = e.target.dataset.tab; this.switchTab(tab); }); }); // Form submissions document.getElementById('create-campaign-form').addEventListener('submit', (e) => { this.handleCreateCampaign(e); }); document.getElementById('edit-campaign-form').addEventListener('submit', (e) => { this.handleUpdateCampaign(e); }); // Cancel buttons - using event delegation for proper handling document.addEventListener('click', (e) => { if (e.target.matches('[data-action="cancel-create"]')) { this.switchTab('campaigns'); } if (e.target.matches('[data-action="cancel-edit"]')) { this.switchTab('campaigns'); } }); this.loadCampaigns(); } setupFormInteractions() { // Create campaign button const createBtn = document.querySelector('[data-action="create-campaign"]'); if (createBtn) { createBtn.addEventListener('click', () => this.switchTab('create')); } // Cancel buttons const cancelCreateBtn = document.querySelector('[data-action="cancel-create"]'); if (cancelCreateBtn) { cancelCreateBtn.addEventListener('click', () => this.switchTab('campaigns')); } const cancelEditBtn = document.querySelector('[data-action="cancel-edit"]'); if (cancelEditBtn) { cancelEditBtn.addEventListener('click', () => this.switchTab('campaigns')); } // Handle checkbox changes for government levels document.querySelectorAll('input[name="target_government_levels"]').forEach(checkbox => { checkbox.addEventListener('change', () => { this.updateGovernmentLevelsPreview(); }); }); // Handle settings toggles document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => { checkbox.addEventListener('change', () => { this.handleSettingsChange(checkbox); }); }); } switchTab(tabName) { // Hide all tabs document.querySelectorAll('.tab-content').forEach(tab => { tab.classList.remove('active'); }); // Remove active class from nav buttons document.querySelectorAll('.nav-btn').forEach(btn => { btn.classList.remove('active'); }); // Show selected tab const targetTab = document.getElementById(`${tabName}-tab`); if (targetTab) { targetTab.classList.add('active'); } // Update nav button const targetNavBtn = document.querySelector(`[data-tab="${tabName}"]`); if (targetNavBtn) { targetNavBtn.classList.add('active'); } // Special handling for different tabs if (tabName === 'campaigns') { this.loadCampaigns(); } else if (tabName === 'edit' && this.currentCampaign) { this.populateEditForm(); } } async loadCampaigns() { const loadingDiv = document.getElementById('campaigns-loading'); const listDiv = document.getElementById('campaigns-list'); loadingDiv.classList.remove('hidden'); listDiv.innerHTML = ''; try { const response = await window.apiClient.get('/admin/campaigns'); if (response.success) { this.campaigns = response.campaigns; this.renderCampaignList(); } else { throw new Error(response.error || 'Failed to load campaigns'); } } catch (error) { console.error('Load campaigns error:', error); this.showMessage('Failed to load campaigns: ' + error.message, 'error'); } finally { loadingDiv.classList.add('hidden'); } } renderCampaignList() { const listDiv = document.getElementById('campaigns-list'); if (this.campaigns.length === 0) { listDiv.innerHTML = `

No campaigns yet

Create your first campaign to get started.

`; return; } listDiv.innerHTML = this.campaigns.map(campaign => `

${this.escapeHtml(campaign.title)}

${campaign.status}

Slug: /campaign/${campaign.slug}

Email Count: ${campaign.emailCount || 0}

Created: ${this.formatDate(campaign.created_at)}

View Public Page
`).join(''); // Attach event listeners to campaign actions this.attachCampaignActionListeners(); } attachCampaignActionListeners() { // Edit campaign buttons document.querySelectorAll('[data-action="edit-campaign"]').forEach(btn => { btn.addEventListener('click', (e) => { const campaignId = parseInt(e.target.dataset.campaignId); this.editCampaign(campaignId); }); }); // Delete campaign buttons document.querySelectorAll('[data-action="delete-campaign"]').forEach(btn => { btn.addEventListener('click', (e) => { const campaignId = parseInt(e.target.dataset.campaignId); this.deleteCampaign(campaignId); }); }); // Analytics buttons document.querySelectorAll('[data-action="view-analytics"]').forEach(btn => { btn.addEventListener('click', (e) => { const campaignId = parseInt(e.target.dataset.campaignId); this.viewAnalytics(campaignId); }); }); } async handleCreateCampaign(e) { e.preventDefault(); const formData = new FormData(e.target); const campaignData = { title: formData.get('title'), description: formData.get('description'), email_subject: formData.get('email_subject'), email_body: formData.get('email_body'), call_to_action: formData.get('call_to_action'), status: formData.get('status'), allow_smtp_email: formData.get('allow_smtp_email') === 'on', allow_mailto_link: formData.get('allow_mailto_link') === 'on', collect_user_info: formData.get('collect_user_info') === 'on', show_email_count: formData.get('show_email_count') === 'on', target_government_levels: Array.from(formData.getAll('target_government_levels')) }; try { const response = await window.apiClient.post('/admin/campaigns', campaignData); if (response.success) { this.showMessage('Campaign created successfully!', 'success'); e.target.reset(); this.switchTab('campaigns'); } else { throw new Error(response.error || 'Failed to create campaign'); } } catch (error) { console.error('Create campaign error:', error); this.showMessage('Failed to create campaign: ' + error.message, 'error'); } } editCampaign(campaignId) { this.currentCampaign = this.campaigns.find(c => c.id === campaignId); if (this.currentCampaign) { this.switchTab('edit'); } } populateEditForm() { if (!this.currentCampaign) return; const form = document.getElementById('edit-campaign-form'); const campaign = this.currentCampaign; // Populate form fields form.querySelector('[name="title"]').value = campaign.title || ''; form.querySelector('[name="description"]').value = campaign.description || ''; form.querySelector('[name="email_subject"]').value = campaign.email_subject || ''; form.querySelector('[name="email_body"]').value = campaign.email_body || ''; form.querySelector('[name="call_to_action"]').value = campaign.call_to_action || ''; // Status select form.querySelector('[name="status"]').value = campaign.status || 'draft'; // Checkboxes form.querySelector('[name="allow_smtp_email"]').checked = campaign.allow_smtp_email; form.querySelector('[name="allow_mailto_link"]').checked = campaign.allow_mailto_link; form.querySelector('[name="collect_user_info"]').checked = campaign.collect_user_info; form.querySelector('[name="show_email_count"]').checked = campaign.show_email_count; form.querySelector('[name="allow_email_editing"]').checked = campaign.allow_email_editing; // Government levels const targetLevels = campaign.target_government_levels ? campaign.target_government_levels.split(',').map(l => l.trim()) : []; form.querySelectorAll('[name="target_government_levels"]').forEach(checkbox => { checkbox.checked = targetLevels.includes(checkbox.value); }); } async handleUpdateCampaign(e) { e.preventDefault(); if (!this.currentCampaign) return; const formData = new FormData(e.target); const updates = { title: formData.get('title'), description: formData.get('description'), email_subject: formData.get('email_subject'), email_body: formData.get('email_body'), call_to_action: formData.get('call_to_action'), status: formData.get('status'), allow_smtp_email: formData.get('allow_smtp_email') === 'on', allow_mailto_link: formData.get('allow_mailto_link') === 'on', collect_user_info: formData.get('collect_user_info') === 'on', show_email_count: formData.get('show_email_count') === 'on', target_government_levels: Array.from(formData.getAll('target_government_levels')) }; try { const response = await window.apiClient.makeRequest(`/admin/campaigns/${this.currentCampaign.id}`, { method: 'PUT', body: JSON.stringify(updates) }); if (response.success) { this.showMessage('Campaign updated successfully!', 'success'); this.switchTab('campaigns'); } else { throw new Error(response.error || 'Failed to update campaign'); } } catch (error) { console.error('Update campaign error:', error); this.showMessage('Failed to update campaign: ' + error.message, 'error'); } } async deleteCampaign(campaignId) { const campaign = this.campaigns.find(c => c.id === campaignId); if (!campaign) return; if (!confirm(`Are you sure you want to delete the campaign "${campaign.title}"? This action cannot be undone.`)) { return; } try { const response = await window.apiClient.makeRequest(`/admin/campaigns/${campaignId}`, { method: 'DELETE' }); if (response.success) { this.showMessage('Campaign deleted successfully!', 'success'); this.loadCampaigns(); } else { throw new Error(response.error || 'Failed to delete campaign'); } } catch (error) { console.error('Delete campaign error:', error); this.showMessage('Failed to delete campaign: ' + error.message, 'error'); } } async viewAnalytics(campaignId) { try { const response = await window.apiClient.get(`/admin/campaigns/${campaignId}/analytics`); if (response.success) { this.showAnalyticsModal(response.analytics); } else { throw new Error(response.error || 'Failed to load analytics'); } } catch (error) { console.error('Analytics error:', error); this.showMessage('Failed to load analytics: ' + error.message, 'error'); } } showAnalyticsModal(analytics) { // Create a simple analytics modal const modal = document.createElement('div'); modal.className = 'modal-overlay'; modal.innerHTML = ` `; document.body.appendChild(modal); // Close modal handlers modal.querySelector('.modal-close').addEventListener('click', () => { document.body.removeChild(modal); }); modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); } }); } updateGovernmentLevelsPreview() { const checkboxes = document.querySelectorAll('input[name="target_government_levels"]:checked'); const levels = Array.from(checkboxes).map(cb => cb.value); // Could update a preview somewhere if needed console.log('Selected government levels:', levels); } handleSettingsChange(checkbox) { // Handle real-time settings changes if needed console.log(`Setting ${checkbox.name} changed to:`, checkbox.checked); } showMessage(message, type = 'info') { const container = document.getElementById('message-container'); container.className = `message-${type}`; container.textContent = message; container.classList.remove('hidden'); setTimeout(() => { container.classList.add('hidden'); }, 5000); } escapeHtml(text) { const div = document.createElement('div'); div.textContent = text; return div.innerHTML; } formatDate(dateString) { if (!dateString) return 'N/A'; try { return new Date(dateString).toLocaleDateString('en-CA', { year: 'numeric', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (error) { return dateString; } } } // Initialize admin panel when DOM is loaded document.addEventListener('DOMContentLoaded', async () => { window.adminPanel = new AdminPanel(); await window.adminPanel.init(); });