TradeAlgo Logo

TradeAlgo

Email Marketing Studio • Admin Portal

------

Restricted to Authorized TradeAlgo Personnel Only.

© 2026 TradeAlgo. All rights reserved.

Action completed
` }, { id: 'market_insights', title: 'Weekly Market Digest & Algo Performance', category: 'Performance Insights', subject: '📊 Weekly Quantitative Digest: Top Performing Trading Strategies & Alpha Signals', html: ` TradeAlgo Market Digest
TRADEALGO WEEKLY QUANTITATIVE REPORT
Cumulative Growth

Market Volatility Meets Algorithmic Precision

Here is your weekly recap of top performing quantitative algorithms and breakout strategies on TradeAlgo. Space.

Key Takeaway of the Week
Momentum algorithms generated a +8.4% edge over benchmark SPY, driven by disciplined stop management during intraday pullbacks.
View Full Performance Breakdown →
© 2026 TradeAlgo. All rights reserved.
` }, { id: 'platform_feature', title: 'New Feature: Screener & Backtester Pro', category: 'Feature Update', subject: 'âš¡ New Release: Real-Time Algorithmic Screener & Backtesting Engine', html: ` New Feature Launch
TRADEALGO
Backtesting Engine
Feature Upgrade

Ultra-Fast Backtesting Engine Released

Simulate 10 years of tick-level order book data in under 3 seconds. Test custom slippage, leverage models, and commission structures effortlessly.

Try Backtesting Engine Now →
© 2026 TradeAlgo. All rights reserved.
` } ]; // ---------------------------------------------------- // PRE-DEFINED RECIPIENT LIST GROUPS // ---------------------------------------------------- const DEFAULT_LISTS = [ { id: 'vip_traders', name: 'VIP Algorithmic Traders (Sample)', emails: ['vip.trader@tradealgo.space', 'quant.lead@tradealgo.space', 'alpha.fund@tradealgo.space'] }, { id: 'newsletter_subscribers', name: 'Weekly Newsletter Subscribers (Sample)', emails: ['subscriber1@tradealgo.space', 'investor@tradealgo.space', 'trader99@tradealgo.space'] } ]; // State Variables let allHostedImages = []; let currentCaptcha = ''; let currentDevice = 'desktop'; let savedTemplates = []; let savedLists = []; // ---------------------------------------------------- // AUTHENTICATION & LOGIN GATEWAY // ---------------------------------------------------- function generateCaptcha() { const chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; let res = ''; for (let i = 0; i < 6; i++) { res += chars.charAt(Math.floor(Math.random() * chars.length)); } return res; } function refreshCaptcha() { currentCaptcha = generateCaptcha(); const el = document.getElementById('captchaCode'); if (el) el.innerText = currentCaptcha; } function toggleLoginPassword() { const passInput = document.getElementById('loginPassword'); const icon = document.getElementById('loginEyeIcon'); if (passInput.type === 'password') { passInput.type = 'text'; icon.innerHTML = ''; } else { passInput.type = 'password'; icon.innerHTML = ''; } } function checkAuthState() { const isAuth = sessionStorage.getItem('tradealgo_auth') === 'true' || localStorage.getItem('tradealgo_auth') === 'true'; const loginView = document.getElementById('loginView'); const studioApp = document.getElementById('studioApp'); if (isAuth) { if (loginView) loginView.classList.add('hidden'); if (studioApp) studioApp.classList.remove('hidden'); initStudio(); } else { if (loginView) loginView.classList.remove('hidden'); if (studioApp) studioApp.classList.add('hidden'); refreshCaptcha(); } } function handleLoginSubmit(e) { e.preventDefault(); const rawEmail = document.getElementById('loginEmail').value; const rawPass = document.getElementById('loginPassword').value; const rawCaptcha = document.getElementById('captchaInput').value; const email = (rawEmail || '').trim().toLowerCase(); const password = (rawPass || '').trim(); const captchaInput = (rawCaptcha || '').trim(); const remember = document.getElementById('rememberSession').checked; const errorBox = document.getElementById('loginError'); const errorMsg = document.getElementById('loginErrorMsg'); const submitBtn = document.getElementById('loginSubmitBtn'); const btnText = document.getElementById('loginBtnText'); errorBox.classList.add('hidden'); if (!captchaInput || captchaInput.toLowerCase() !== currentCaptcha.toLowerCase()) { errorMsg.innerText = 'Invalid verification code. Please try again.'; errorBox.classList.remove('hidden'); refreshCaptcha(); document.getElementById('captchaInput').value = ''; return; } submitBtn.disabled = true; btnText.innerText = 'Authenticating...'; setTimeout(() => { const validUsers = [ 'admin@tradealgo.space', 'admin@tradalgo.space', 'tradealgosphere@gmail.com', 'admin' ]; const validPasswords = [ 'admin@1234', 'Admin@1234', 'admin1234', 'Admin1234', 'admin', 'tradealgo123' ]; const isValidEmail = validUsers.includes(email); const isValidPassword = validPasswords.includes(password); if (isValidEmail && isValidPassword) { sessionStorage.setItem('tradealgo_auth', 'true'); if (remember) { localStorage.setItem('tradealgo_auth', 'true'); } const userLabel = document.getElementById('activeUserLabel'); if (userLabel) userLabel.innerText = email.includes('@') ? email : 'admin@tradealgo.space'; document.getElementById('loginPassword').value = ''; document.getElementById('captchaInput').value = ''; checkAuthState(); } else { errorMsg.innerText = 'Invalid credentials. Please verify your administrator username/email and password.'; errorBox.classList.remove('hidden'); refreshCaptcha(); document.getElementById('captchaInput').value = ''; } submitBtn.disabled = false; btnText.innerText = 'Sign In to Dashboard'; }, 300); } function logoutAdmin() { sessionStorage.removeItem('tradealgo_auth'); localStorage.removeItem('tradealgo_auth'); checkAuthState(); } // ---------------------------------------------------- // INITIALIZATION & ASSET LOADING // ---------------------------------------------------- const API_BASE = window.location.origin.includes('tradealgo.space') ? '' : 'https://tradealgo.space'; async function initStudio() { loadTemplatesState(); loadListsState(); await loadServerConfigInfo(); await loadHostedImages(); // Load first template if empty const htmlEl = document.getElementById('htmlContent'); if (!htmlEl.value) { resetSampleTemplate(); } else { updatePreview(); updateRenderedCodeDisplay(); } updateRecipientCount(); } async function loadServerConfigInfo() { try { const res = await fetch(`${API_BASE}/api/v1/email/config-info`); if (res.ok) { const cfg = await res.json(); if (cfg.smtp_host) document.getElementById('smtpHost').value = cfg.smtp_host; if (cfg.smtp_port) document.getElementById('smtpPort').value = cfg.smtp_port; if (cfg.smtp_user) document.getElementById('smtpUser').value = cfg.smtp_user; if (cfg.default_from_email) document.getElementById('fromEmail').value = cfg.default_from_email; if (cfg.default_from_name) document.getElementById('fromName').value = cfg.default_from_name; } } catch (err) { console.warn('Could not load backend email config info:', err); } } async function loadHostedImages(forceRefresh = false) { const countBadge = document.getElementById('hostedImagesCountBadge'); const grid = document.getElementById('hostedImagesGrid'); try { countBadge.innerText = 'Loading images...'; const res = await fetch(`${API_BASE}/api/v1/email/hosted-images`); const data = await res.json(); if (res.ok && data.images) { allHostedImages = data.images; renderHostedImages(allHostedImages); countBadge.innerText = `${data.total} images available`; if (forceRefresh) showToast(`Refreshed ${data.total} hosted images`); } else { grid.innerHTML = '
No images returned from server.
'; countBadge.innerText = '0 images'; } } catch (err) { console.error('Error fetching images:', err); grid.innerHTML = '
Failed to load hosted images.
'; countBadge.innerText = 'Error loading'; } } function renderHostedImages(images) { const grid = document.getElementById('hostedImagesGrid'); if (!images || images.length === 0) { grid.innerHTML = '
No matching images found.
'; return; } grid.innerHTML = images.map(img => `
${img.name} ${img.size}

${img.name}

`).join(''); } function filterHostedImages(query) { const q = (query || '').toLowerCase().trim(); if (!q) { renderHostedImages(allHostedImages); return; } const filtered = allHostedImages.filter(img => img.name.toLowerCase().includes(q)); renderHostedImages(filtered); } function insertImageIntoHtml(url, name) { const textarea = document.getElementById('htmlContent'); const imgTag = `\n${name}\n`; const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = textarea.value; if (start !== undefined && end !== undefined && start !== end) { textarea.value = text.substring(0, start) + imgTag + text.substring(end); } else if (start !== undefined) { textarea.value = text.substring(0, start) + imgTag + text.substring(start); } else { textarea.value += imgTag; } updatePreview(); updateRenderedCodeDisplay(); showToast(`Embedded ${name} into HTML editor`); } function copyImageLink(url) { navigator.clipboard.writeText(url).then(() => { showToast('Image URL copied to clipboard!'); }).catch(() => { showToast('Failed to copy link'); }); } // ---------------------------------------------------- // TEMPLATE MANAGEMENT // ---------------------------------------------------- function loadTemplatesState() { const local = localStorage.getItem('tradealgo_saved_templates'); if (local) { try { savedTemplates = JSON.parse(local); } catch (e) { savedTemplates = [...DEFAULT_TEMPLATES]; } } else { savedTemplates = [...DEFAULT_TEMPLATES]; saveTemplatesToStorage(); } populateTemplateDropdown(); } function saveTemplatesToStorage() { localStorage.setItem('tradealgo_saved_templates', JSON.stringify(savedTemplates)); } function populateTemplateDropdown() { const select = document.getElementById('templateSelect'); select.innerHTML = ''; savedTemplates.forEach(t => { const opt = document.createElement('option'); opt.value = t.id; opt.innerText = `${t.title} [${t.category || 'General'}]`; select.appendChild(opt); }); } function handleTemplateSelect(id) { if (!id) return; const tmpl = savedTemplates.find(t => t.id === id); if (tmpl) { document.getElementById('subject').value = tmpl.subject || ''; document.getElementById('htmlContent').value = tmpl.html || ''; updatePreview(); updateRenderedCodeDisplay(); showToast(`Loaded: ${tmpl.title}`); } document.getElementById('templateSelect').value = ''; } function resetSampleTemplate() { const first = DEFAULT_TEMPLATES[0]; document.getElementById('subject').value = first.subject; document.getElementById('htmlContent').value = first.html; updatePreview(); updateRenderedCodeDisplay(); showToast('Reset to default TradeAlgo strategy template'); } function openSaveTemplateModal() { document.getElementById('newTemplateName').value = ''; document.getElementById('saveTemplateModal').classList.remove('hidden'); document.getElementById('saveTemplateModal').classList.add('flex'); } function closeSaveTemplateModal() { document.getElementById('saveTemplateModal').classList.add('hidden'); document.getElementById('saveTemplateModal').classList.remove('flex'); } function handleSaveTemplateConfirm() { const name = document.getElementById('newTemplateName').value.trim(); const cat = document.getElementById('newTemplateCategory').value; const subject = document.getElementById('subject').value.trim(); const html = document.getElementById('htmlContent').value.trim(); if (!name) { alert('Please enter a template name.'); return; } const id = 'custom_' + Date.now(); savedTemplates.push({ id, title: name, category: cat, subject, html }); saveTemplatesToStorage(); populateTemplateDropdown(); closeSaveTemplateModal(); showToast(`Saved template: ${name}`); } function openManageTemplatesModal() { renderTemplatesList(); document.getElementById('manageTemplatesModal').classList.remove('hidden'); document.getElementById('manageTemplatesModal').classList.add('flex'); } function closeManageTemplatesModal() { document.getElementById('manageTemplatesModal').classList.add('hidden'); document.getElementById('manageTemplatesModal').classList.remove('flex'); } function renderTemplatesList() { const container = document.getElementById('templatesListContainer'); if (!savedTemplates.length) { container.innerHTML = '

No saved templates available.

'; return; } container.innerHTML = savedTemplates.map(t => `

${t.title}

${t.category || 'General'}

${t.subject || 'No Subject'}

`).join(''); } function applyTemplateFromManage(id) { handleTemplateSelect(id); closeManageTemplatesModal(); } function deleteTemplate(id) { if (confirm('Are you sure you want to delete this template?')) { savedTemplates = savedTemplates.filter(t => t.id !== id); saveTemplatesToStorage(); populateTemplateDropdown(); renderTemplatesList(); showToast('Template deleted'); } } function exportTemplatesJson() { const dataStr = 'data:text/json;charset=utf-8,' + encodeURIComponent(JSON.stringify(savedTemplates, null, 2)); const downloadAnchor = document.createElement('a'); downloadAnchor.setAttribute('href', dataStr); downloadAnchor.setAttribute('download', 'tradealgo_email_templates.json'); document.body.appendChild(downloadAnchor); downloadAnchor.click(); downloadAnchor.remove(); showToast('Exported templates as JSON'); } function importTemplatesJson(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = function(e) { try { const imported = JSON.parse(e.target.result); if (Array.isArray(imported)) { savedTemplates = imported; saveTemplatesToStorage(); populateTemplateDropdown(); renderTemplatesList(); showToast(`Imported ${imported.length} templates successfully!`); } else { alert('Invalid JSON structure. Expected an array of templates.'); } } catch (err) { alert('Could not parse JSON file: ' + err.message); } }; reader.readAsText(file); event.target.value = ''; } // ---------------------------------------------------- // RECIPIENT LIST MANAGEMENT // ---------------------------------------------------- function loadListsState() { const local = localStorage.getItem('tradealgo_saved_lists'); if (local) { try { savedLists = JSON.parse(local); } catch (e) { savedLists = [...DEFAULT_LISTS]; } } else { savedLists = [...DEFAULT_LISTS]; saveListsToStorage(); } populateListDropdown(); } function saveListsToStorage() { localStorage.setItem('tradealgo_saved_lists', JSON.stringify(savedLists)); } function populateListDropdown() { const select = document.getElementById('recipientListSelect'); select.innerHTML = ''; savedLists.forEach(l => { const opt = document.createElement('option'); opt.value = l.id; opt.innerText = `${l.name} (${l.emails.length})`; select.appendChild(opt); }); } function handleRecipientListSelect(id) { if (!id) return; const item = savedLists.find(l => l.id === id); if (item) { document.getElementById('toEmail').value = item.emails.join(', '); updateRecipientCount(); showToast(`Loaded list: ${item.name}`); } document.getElementById('recipientListSelect').value = ''; } function openSaveListModal() { const emails = parseEmailList(document.getElementById('toEmail').value); if (!emails.length) { alert('Please enter at least one valid email address to save a list.'); return; } document.getElementById('newListName').value = ''; document.getElementById('saveListModal').classList.remove('hidden'); document.getElementById('saveListModal').classList.add('flex'); } function closeSaveListModal() { document.getElementById('saveListModal').classList.add('hidden'); document.getElementById('saveListModal').classList.remove('flex'); } function handleSaveListConfirm() { const name = document.getElementById('newListName').value.trim(); const emails = parseEmailList(document.getElementById('toEmail').value); if (!name) { alert('Please provide a name for this list.'); return; } const id = 'list_' + Date.now(); savedLists.push({ id, name, emails }); saveListsToStorage(); populateListDropdown(); closeSaveListModal(); showToast(`Saved list: ${name} (${emails.length} emails)`); } function openManageListsModal() { renderListsView(); document.getElementById('manageListsModal').classList.remove('hidden'); document.getElementById('manageListsModal').classList.add('flex'); } function closeManageListsModal() { document.getElementById('manageListsModal').classList.add('hidden'); document.getElementById('manageListsModal').classList.remove('flex'); } function renderListsView() { const container = document.getElementById('recipientListsContainer'); if (!savedLists.length) { container.innerHTML = '

No saved recipient lists found.

'; return; } container.innerHTML = savedLists.map(l => `
${l.name}

${l.emails.length} contact(s): ${l.emails.slice(0, 2).join(', ')}${l.emails.length > 2 ? '...' : ''}

`).join(''); } function applyListFromManage(id) { handleRecipientListSelect(id); closeManageListsModal(); } function deleteList(id) { if (confirm('Delete this recipient list?')) { savedLists = savedLists.filter(l => l.id !== id); saveListsToStorage(); populateListDropdown(); renderListsView(); showToast('List deleted'); } } // ---------------------------------------------------- // PREVIEW & EDITOR TABS // ---------------------------------------------------- function switchTab(tab) { const btnPreview = document.getElementById('tabBtnPreview'); const btnCode = document.getElementById('tabBtnCode'); const btnRendered = document.getElementById('tabBtnRenderedCode'); const contentPreview = document.getElementById('tabContentPreview'); const contentCode = document.getElementById('tabContentCode'); const contentRendered = document.getElementById('tabContentRenderedCode'); const deviceControls = document.getElementById('previewDeviceControls'); const btnCopy = document.getElementById('btnCopyRenderedCode'); // Reset [btnPreview, btnCode, btnRendered].forEach(b => { b.className = 'px-4 py-1.5 text-xs font-bold rounded-lg transition-all text-slate-600 hover:text-slate-900 cursor-pointer flex items-center gap-1.5'; }); [contentPreview, contentCode, contentRendered].forEach(c => c.classList.add('hidden')); deviceControls.classList.add('hidden'); btnCopy.classList.add('hidden'); if (tab === 'preview') { btnPreview.className = 'px-4 py-1.5 text-xs font-bold rounded-lg transition-all shadow-sm bg-white text-blue-600 cursor-pointer flex items-center gap-1.5'; contentPreview.classList.remove('hidden'); deviceControls.classList.remove('hidden'); updatePreview(); } else if (tab === 'code') { btnCode.className = 'px-4 py-1.5 text-xs font-bold rounded-lg transition-all shadow-sm bg-white text-blue-600 cursor-pointer flex items-center gap-1.5'; contentCode.classList.remove('hidden'); } else if (tab === 'rendered_code') { btnRendered.className = 'px-4 py-1.5 text-xs font-bold rounded-lg transition-all shadow-sm bg-white text-blue-600 cursor-pointer flex items-center gap-1.5'; contentRendered.classList.remove('hidden'); btnCopy.classList.remove('hidden'); updateRenderedCodeDisplay(); } } function setPreviewDevice(device) { currentDevice = device; const iframe = document.getElementById('previewIframe'); const btnD = document.getElementById('devBtnDesktop'); const btnM = document.getElementById('devBtnMobile'); if (device === 'mobile') { iframe.style.width = '375px'; iframe.style.borderRadius = '28px'; iframe.style.border = '8px solid #334155'; btnM.className = 'px-2.5 py-1 text-xs font-semibold rounded-lg bg-white text-blue-600 shadow-sm cursor-pointer'; btnD.className = 'px-2.5 py-1 text-xs font-semibold rounded-lg text-slate-500 hover:text-slate-800 cursor-pointer'; } else { iframe.style.width = '100%'; iframe.style.borderRadius = '0px'; iframe.style.border = 'none'; btnD.className = 'px-2.5 py-1 text-xs font-semibold rounded-lg bg-white text-blue-600 shadow-sm cursor-pointer'; btnM.className = 'px-2.5 py-1 text-xs font-semibold rounded-lg text-slate-500 hover:text-slate-800 cursor-pointer'; } } function updatePreview() { const code = document.getElementById('htmlContent').value; const iframe = document.getElementById('previewIframe'); if (iframe) { iframe.srcdoc = code; } } function updateRenderedCodeDisplay() { const code = document.getElementById('htmlContent').value; const display = document.getElementById('renderedCodeDisplay'); if (display) { display.innerText = code; } } function copyRenderedCodeToClipboard() { const code = document.getElementById('htmlContent').value; navigator.clipboard.writeText(code).then(() => { showToast('Rendered HTML code copied to clipboard!'); }).catch(() => { showToast('Failed to copy code'); }); } function parseEmailList(str) { if (!str) return []; const regex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/; const raw = str.split(/[,;\n\r]+/); const valid = []; raw.forEach(e => { const cleaned = e.trim(); if (cleaned && regex.test(cleaned) && !valid.includes(cleaned)) { valid.push(cleaned); } }); return valid; } function updateRecipientCount() { const val = document.getElementById('toEmail').value; const list = parseEmailList(val); const el = document.getElementById('recipientCount'); if (el) el.innerText = list.length; } // ---------------------------------------------------- // SMTP DRAWER & SETTINGS // ---------------------------------------------------- function toggleSmtpDrawer() { const content = document.getElementById('smtpDrawerContent'); const arrow = document.getElementById('smtpArrowIcon'); if (content.classList.contains('hidden')) { content.classList.remove('hidden'); arrow.classList.add('rotate-180'); } else { content.classList.add('hidden'); arrow.classList.remove('rotate-180'); } } function openSmtpDrawer() { const content = document.getElementById('smtpDrawerContent'); const arrow = document.getElementById('smtpArrowIcon'); content.classList.remove('hidden'); arrow.classList.add('rotate-180'); setTimeout(() => { document.getElementById('smtpPassword').focus(); }, 100); } function toggleSmtpPassword() { const p = document.getElementById('smtpPassword'); const icon = document.getElementById('smtpEyeIcon'); if (p.type === 'password') { p.type = 'text'; icon.innerHTML = ''; } else { p.type = 'password'; icon.innerHTML = ''; } } // ---------------------------------------------------- // MODAL DIALOG CONTROLLER // ---------------------------------------------------- function showModal(opts) { const { type, title, subtitle, detailsHtml, primaryBtnText, secondaryBtnText, onPrimary, onSecondary } = opts; const overlay = document.getElementById('modalOverlay'); const iconContainer = document.getElementById('modalIconContainer'); const titleEl = document.getElementById('modalTitle'); const subEl = document.getElementById('modalSubtitle'); const bodyEl = document.getElementById('modalBody'); const pBtn = document.getElementById('modalPrimaryBtn'); const sBtn = document.getElementById('modalSecondaryBtn'); titleEl.innerText = title; subEl.innerText = subtitle || ''; bodyEl.innerHTML = detailsHtml || ''; // Clear icon styles iconContainer.className = 'p-3 rounded-2xl shrink-0 flex items-center justify-center'; pBtn.className = 'px-5 py-2.5 rounded-xl text-xs font-bold text-white transition-all shadow-md cursor-pointer'; if (type === 'loading') { iconContainer.classList.add('bg-blue-50', 'text-blue-600'); iconContainer.innerHTML = ''; pBtn.classList.add('hidden'); } else if (type === 'success') { iconContainer.classList.add('bg-emerald-50', 'text-emerald-600', 'border', 'border-emerald-100'); iconContainer.innerHTML = ''; pBtn.classList.remove('hidden'); pBtn.classList.add('bg-emerald-600', 'hover:bg-emerald-500', 'shadow-emerald-600/30'); } else if (type === 'error') { iconContainer.classList.add('bg-rose-50', 'text-rose-600', 'border', 'border-rose-100'); iconContainer.innerHTML = ''; pBtn.classList.remove('hidden'); pBtn.classList.add('bg-rose-600', 'hover:bg-rose-500', 'shadow-rose-600/30'); } pBtn.innerText = primaryBtnText || 'Close'; pBtn.onclick = onPrimary || closeModal; if (secondaryBtnText) { sBtn.innerText = secondaryBtnText; sBtn.classList.remove('hidden'); sBtn.onclick = onSecondary || closeModal; } else { sBtn.classList.add('hidden'); } overlay.classList.remove('hidden'); overlay.classList.add('flex'); } function closeModal() { const overlay = document.getElementById('modalOverlay'); overlay.classList.add('hidden'); overlay.classList.remove('flex'); } function handleModalOverlayClick(e) { if (e.target.id === 'modalOverlay') { closeModal(); } } function showToast(msg) { const toast = document.getElementById('toastNotification'); const text = document.getElementById('toastMessage'); text.innerText = msg; toast.classList.remove('translate-y-20', 'opacity-0'); setTimeout(() => { toast.classList.add('translate-y-20', 'opacity-0'); }, 3000); } // ---------------------------------------------------- // API & EMAIL DISPATCH HANDLERS // ---------------------------------------------------- async function handleTestSmtp() { const host = document.getElementById('smtpHost').value.trim(); const port = document.getElementById('smtpPort').value.trim(); const protocol = document.getElementById('smtpProtocol').value; const user = document.getElementById('smtpUser').value.trim(); const pass = document.getElementById('smtpPassword').value.trim(); if (!pass) { showModal({ type: 'error', title: 'SMTP Password Missing', subtitle: 'Please enter your Gmail App Password or SMTP password.', detailsHtml: `

Password Required:

Enter the 16-character App Password generated from your Google Account security settings.

`, primaryBtnText: 'Enter Password', onPrimary: () => { closeModal(); openSmtpDrawer(); } }); return; } showModal({ type: 'loading', title: 'Testing SMTP Handshake...', subtitle: `Connecting to ${host}:${port} (${protocol.toUpperCase()})...`, detailsHtml: `

• Target Server: ${host}:${port}

• Authentication: ${user}

• Protocol: ${protocol.toUpperCase()}

Initiating TLS/SSL socket and authenticating credentials...

`, primaryBtnText: '' }); const btn = document.getElementById('btnTestSmtp'); btn.disabled = true; try { const payload = { smtp_host: host, smtp_port: parseInt(port, 10), smtp_user: user, smtp_password: pass, smtp_use_ssl: protocol === 'ssl' }; const res = await fetch(`${API_BASE}/api/v1/email/test-smtp`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await res.json(); if (res.ok && data.success) { showModal({ type: 'success', title: 'SMTP Handshake Successful!', subtitle: 'Credentials and server connection verified successfully.', detailsHtml: `
SMTP Host & Port: ${host}:${port}
Authenticated Account: ${user}
Encryption: ${protocol.toUpperCase()} Active

✓ Your mail server is authenticated and ready to broadcast TradeAlgo marketing campaigns.

`, primaryBtnText: 'Great, Continue!' }); } else { const reason = data.detail || 'Connection rejected or handshake failed.'; showModal({ type: 'error', title: 'SMTP Handshake Failed', subtitle: 'Unable to connect or authenticate with the mail server.', detailsHtml: `

Server Response:

${reason}

Troubleshooting Suggestions:

Gmail App Password: For Gmail/Google Workspace, generate an App Password (Google Account → Security → 2-Step Verification → App Passwords).

Host & Port: Ensure port 587 is used for STARTTLS, or 465 for SSL.

`, primaryBtnText: 'Close', secondaryBtnText: 'Update SMTP Settings', onSecondary: () => { closeModal(); openSmtpDrawer(); } }); } } catch (err) { showModal({ type: 'error', title: 'Network / Connection Error', subtitle: 'Could not reach TradeAlgo backend API endpoint.', detailsHtml: `

Error Details:

${err.message}
`, primaryBtnText: 'Dismiss' }); } finally { btn.disabled = false; } } async function handleSendEmail(e) { e.preventDefault(); const toVal = document.getElementById('toEmail').value.trim(); const fromName = document.getElementById('fromName').value.trim(); const fromEmail = document.getElementById('fromEmail').value.trim(); const subject = document.getElementById('subject').value.trim(); const htmlContent = document.getElementById('htmlContent').value.trim(); const host = document.getElementById('smtpHost').value.trim(); const port = document.getElementById('smtpPort').value.trim(); const protocol = document.getElementById('smtpProtocol').value; const user = document.getElementById('smtpUser').value.trim(); const pass = document.getElementById('smtpPassword').value.trim(); const recipientList = parseEmailList(toVal); if (recipientList.length === 0) { showModal({ type: 'error', title: 'Recipient Required', subtitle: 'Please provide at least one valid recipient email in the "To" box.', detailsHtml: '

Specify target emails separated by commas or lines.

', primaryBtnText: 'OK' }); return; } if (!pass) { showModal({ type: 'error', title: 'SMTP Password Missing', subtitle: 'Please enter your SMTP Password in settings before broadcasting.', detailsHtml: `

You must provide an SMTP Password or App Password in the SMTP configuration drawer below.

`, primaryBtnText: 'Enter Password', onPrimary: () => { closeModal(); openSmtpDrawer(); } }); return; } showModal({ type: 'loading', title: 'Dispatching Marketing Campaign...', subtitle: `Broadcasting to ${recipientList.length} contact(s) via ${host}...`, detailsHtml: `

• Total Targets: ${recipientList.length} recipient(s)

• Sender: ${fromName} <${fromEmail}>

• Subject: ${subject}

Packaging multipart MIME contents and transmitting via SMTP...

`, primaryBtnText: '' }); const btn = document.getElementById('btnSend'); btn.disabled = true; try { const payload = { to: recipientList.join(', '), from_email: fromEmail, from_name: fromName, subject: subject, html_content: htmlContent, smtp_host: host, smtp_port: parseInt(port, 10), smtp_user: user, smtp_password: pass, smtp_use_ssl: protocol === 'ssl' }; const res = await fetch(`${API_BASE}/api/v1/email/send-marketing`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await res.json(); if (res.ok && data.success) { const sentList = data.recipients || recipientList; showModal({ type: 'success', title: 'Campaign Broadcasted Successfully!', subtitle: `Email delivered to ${data.sent_count} of ${data.total_recipients} recipient(s).`, detailsHtml: `
Delivered Count: ${data.sent_count} / ${data.total_recipients}
Subject: ${subject}
Delivered Recipients:
${sentList.join('
')}
`, primaryBtnText: 'Done!' }); } else { const reason = data.detail || 'Failed to dispatch email broadcast.'; showModal({ type: 'error', title: 'Email Broadcast Failed', subtitle: 'The mail server did not accept the dispatch request.', detailsHtml: `

Failure Reason:

${reason}

Next Steps:

• Click Test SMTP Handshake to verify credentials.

• Verify email recipient addresses are formatted accurately.

`, primaryBtnText: 'Close', secondaryBtnText: 'Inspect Settings', onSecondary: () => { closeModal(); openSmtpDrawer(); } }); } } catch (err) { showModal({ type: 'error', title: 'System / Network Error', subtitle: 'Failed to broadcast campaign.', detailsHtml: `

Error Details:

${err.message}
`, primaryBtnText: 'Dismiss' }); } finally { btn.disabled = false; } } // Run on page load document.addEventListener('DOMContentLoaded', checkAuthState); checkAuthState();