freealberta/influence/app/public/js/email-testing.js

365 lines
14 KiB
JavaScript

/**
* Email Testing Interface
* Handles email preview, testing, and logging functionality
*/
class EmailTesting {
constructor() {
this.apiClient = new APIClient();
this.currentFilter = 'all'; // 'all', 'test', 'live'
this.logLimit = 50;
this.logOffset = 0;
}
/**
* Initialize the email testing interface
*/
async init() {
this.bindEvents();
await this.loadConfiguration();
await this.loadEmailLogs();
}
/**
* Bind event listeners to UI elements
*/
bindEvents() {
// Quick test buttons
document.getElementById('quick-test-btn').addEventListener('click', () => this.sendQuickTest());
document.getElementById('smtp-test-btn').addEventListener('click', () => this.testSMTPConnection());
// Email form buttons
document.getElementById('preview-btn').addEventListener('click', () => this.previewEmail());
document.getElementById('send-test-btn').addEventListener('click', () => this.sendTestEmail());
// Log control buttons
document.getElementById('refresh-logs-btn').addEventListener('click', () => this.loadEmailLogs());
document.getElementById('filter-test-btn').addEventListener('click', () => this.filterLogs('test'));
document.getElementById('filter-all-btn').addEventListener('click', () => this.filterLogs('all'));
// Form validation
document.getElementById('email-test-form').addEventListener('input', () => this.validateForm());
}
/**
* Send a quick test email with default content
*/
async sendQuickTest() {
const button = document.getElementById('quick-test-btn');
button.disabled = true;
button.textContent = 'Sending...';
try {
const response = await this.apiClient.post('/api/emails/test', {
subject: 'Quick Test Email',
message: 'This is a quick test email sent from the BNKops Influence Campaign Tool email testing interface.'
});
if (response.success) {
this.showMessage(`Test email sent successfully to ${response.sentTo}`, 'success');
await this.loadEmailLogs(); // Refresh logs
} else {
this.showMessage(`Failed to send test email: ${response.message}`, 'error');
}
} catch (error) {
this.showMessage(`Error sending test email: ${error.message}`, 'error');
} finally {
button.disabled = false;
button.textContent = 'Send Quick Test Email';
}
}
/**
* Test SMTP connection
*/
async testSMTPConnection() {
const button = document.getElementById('smtp-test-btn');
button.disabled = true;
button.textContent = 'Testing...';
try {
// Note: This endpoint would need to be added to the API
const response = await this.apiClient.get('/api/test-smtp');
if (response.success) {
this.showMessage('SMTP connection test successful', 'success');
} else {
this.showMessage(`SMTP connection failed: ${response.message}`, 'error');
}
} catch (error) {
this.showMessage(`SMTP test error: ${error.message}`, 'error');
} finally {
button.disabled = false;
button.textContent = 'Test SMTP Connection';
}
}
/**
* Preview email content
*/
async previewEmail() {
const formData = this.getFormData();
if (!formData) return;
const button = document.getElementById('preview-btn');
button.disabled = true;
try {
const recipientEmail = formData.recipientEmail || 'recipient@example.com';
const response = await this.apiClient.post('/api/emails/preview', {
recipientEmail: recipientEmail,
subject: formData.subject,
message: formData.message
});
if (response.success) {
this.displayEmailPreview(response.preview, response.html);
} else {
this.showMessage(`Preview failed: ${response.message}`, 'error');
}
} catch (error) {
this.showMessage(`Preview error: ${error.message}`, 'error');
} finally {
button.disabled = false;
}
}
/**
* Send test email
*/
async sendTestEmail() {
const formData = this.getFormData();
if (!formData) return;
const button = document.getElementById('send-test-btn');
button.disabled = true;
button.textContent = 'Sending...';
try {
const response = await this.apiClient.post('/api/emails/test', {
subject: formData.subject,
message: formData.message
});
if (response.success) {
this.showMessage(`Test email sent successfully to ${response.sentTo}`, 'success');
await this.loadEmailLogs(); // Refresh logs
} else {
this.showMessage(`Failed to send test email: ${response.message}`, 'error');
}
} catch (error) {
this.showMessage(`Error sending test email: ${error.message}`, 'error');
} finally {
button.disabled = false;
button.textContent = 'Send Test Email';
}
}
/**
* Get form data and validate
*/
getFormData() {
const subject = document.getElementById('test-subject').value.trim();
const message = document.getElementById('test-message').value.trim();
const recipientEmail = document.getElementById('test-recipient').value.trim();
if (!subject || !message) {
this.showMessage('Please fill in subject and message', 'error');
return null;
}
return {
subject,
message,
recipientEmail: recipientEmail || null
};
}
/**
* Validate form and update button states
*/
validateForm() {
const subject = document.getElementById('test-subject').value.trim();
const message = document.getElementById('test-message').value.trim();
const isValid = subject && message;
document.getElementById('preview-btn').disabled = !isValid;
document.getElementById('send-test-btn').disabled = !isValid;
}
/**
* Display email preview
*/
displayEmailPreview(preview, html) {
const previewDiv = document.getElementById('email-preview');
previewDiv.classList.remove('empty');
const testModeWarning = preview.testMode ?
`<div style="background: #fff3cd; color: #856404; padding: 10px; border-radius: 4px; margin-bottom: 15px;">
<strong>TEST MODE:</strong> Email will be redirected to ${preview.redirectTo}
</div>` : '';
previewDiv.innerHTML = `
${testModeWarning}
<div style="margin-bottom: 15px;">
<strong>From:</strong> ${preview.from}<br>
<strong>To:</strong> ${preview.to}<br>
<strong>Subject:</strong> ${preview.subject}<br>
<strong>Timestamp:</strong> ${new Date(preview.timestamp).toLocaleString()}
</div>
<div style="border-top: 1px solid #ccc; padding-top: 15px;">
<strong>Message Content:</strong>
<div style="margin-top: 10px; padding: 10px; background: #f8f9fa; border-radius: 4px;">
${html}
</div>
</div>
`;
}
/**
* Load and display email logs
*/
async loadEmailLogs() {
const logsDiv = document.getElementById('email-logs');
logsDiv.innerHTML = '<div class="loading">Loading email logs...</div>';
try {
const params = new URLSearchParams();
params.append('limit', this.logLimit);
params.append('offset', this.logOffset);
if (this.currentFilter === 'test') {
params.append('testMode', 'true');
} else if (this.currentFilter === 'live') {
params.append('testMode', 'false');
}
const response = await this.apiClient.get(`/api/emails/logs?${params.toString()}`);
if (response.success) {
this.displayEmailLogs(response.logs);
} else {
logsDiv.innerHTML = `<div class="error-message">Failed to load logs: ${response.message}</div>`;
}
} catch (error) {
logsDiv.innerHTML = `<div class="error-message">Error loading logs: ${error.message}</div>`;
}
}
/**
* Display email logs
*/
displayEmailLogs(logs) {
const logsDiv = document.getElementById('email-logs');
if (!logs || logs.length === 0) {
logsDiv.innerHTML = '<div style="text-align: center; color: #6c757d; padding: 20px;">No email logs found</div>';
return;
}
logsDiv.innerHTML = logs.map(log => {
const statusClass = log.Status === 'sent' ? 'sent' : 'failed';
const testModeClass = log['Test Mode'] ? 'test-mode' : '';
const statusIndicator = log['Test Mode'] ? 'test' : statusClass;
return `
<div class="log-entry ${statusClass} ${testModeClass}">
<div style="display: flex; justify-content: between; align-items: flex-start;">
<div style="flex: 1;">
<strong>${log.Subject}</strong>
<div>To: ${log.Recipient}</div>
${log['Actual Recipient'] && log['Actual Recipient'] !== log.Recipient ?
`<div style="color: #856404;">Actually sent to: ${log['Actual Recipient']}</div>` : ''}
${log.Error ? `<div style="color: #dc3545;">Error: ${log.Error}</div>` : ''}
</div>
<div style="text-align: right;">
<span class="status-indicator status-${statusIndicator}">${log.Status}</span>
${log['Test Mode'] ? '<span class="status-indicator status-test" style="margin-left: 5px;">TEST</span>' : ''}
</div>
</div>
<div class="log-meta">
${log['Sent At'] ? new Date(log['Sent At']).toLocaleString() :
(log.CreatedAt ? new Date(log.CreatedAt).toLocaleString() : 'Unknown time')}
${log['Message ID'] ? ` • ID: ${log['Message ID']}` : ''}
</div>
</div>
`;
}).join('');
}
/**
* Filter logs by type
*/
async filterLogs(filter) {
this.currentFilter = filter;
this.logOffset = 0; // Reset offset when filtering
// Update button states
document.getElementById('filter-all-btn').classList.toggle('btn-success', filter === 'all');
document.getElementById('filter-test-btn').classList.toggle('btn-warning', filter === 'test');
await this.loadEmailLogs();
}
/**
* Load and display current configuration
*/
async loadConfiguration() {
const configDiv = document.getElementById('config-status');
// Since we don't have a dedicated config endpoint, we'll show env-based info
const isTestMode = true; // Assuming test mode based on .env
const testRecipient = 'admin@example.com'; // From .env
configDiv.innerHTML = `
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div>
<h4>Email Test Mode</h4>
<div style="color: ${isTestMode ? '#856404' : '#155724'};">
${isTestMode ? 'ENABLED' : 'DISABLED'}
</div>
<small style="color: #6c757d;">
${isTestMode ? 'All emails will be redirected to test recipient' : 'Emails will be sent to actual recipients'}
</small>
</div>
<div>
<h4>Test Email Recipient</h4>
<div>${testRecipient}</div>
<small style="color: #6c757d;">Emails will be sent here in test mode</small>
</div>
</div>
`;
}
/**
* Show success or error message
*/
showMessage(message, type) {
const messageContainer = document.getElementById('message-container');
const messageClass = type === 'success' ? 'success-message' : 'error-message';
const messageDiv = document.createElement('div');
messageDiv.className = messageClass;
messageDiv.textContent = message;
messageDiv.style.position = 'fixed';
messageDiv.style.top = '20px';
messageDiv.style.left = '50%';
messageDiv.style.transform = 'translateX(-50%)';
messageDiv.style.zIndex = '1000';
messageDiv.style.maxWidth = '500px';
messageContainer.appendChild(messageDiv);
// Auto-remove after 5 seconds
setTimeout(() => {
if (messageDiv.parentNode) {
messageDiv.parentNode.removeChild(messageDiv);
}
}, 5000);
}
}
// Export for use in other modules
if (typeof module !== 'undefined' && module.exports) {
module.exports = EmailTesting;
}