← Dashboard
📫 Email Inbox
⚙ Settings
Loading…
📫

Select a message to read it

From:
To:
Date:
✎ New Email
' + bodyHtml + ''; } else { newFrame.srcdoc = ' ' + escHtml(bodyText) + ''; } bodyWrap.appendChild(newFrame); } function clearReadPane() { document.getElementById('readPlaceholder').style.display = 'flex'; document.getElementById('msgHeader').style.display = 'none'; document.getElementById('msgBodyWrap').style.display = 'none'; state.openMsg = null; closeCompose(); } // ── Message actions ────────────────────────────────────────────────────────── function replyToMsg() { if (!state.openMsg) return; var msg = state.openMsg; var fromAddr = msg.from || ''; var subj = msg.subject || ''; if (!/^re:/i.test(subj)) subj = 'Re: ' + subj; var quoted = ''; var bodyText = msg.body_text || msg.text || ''; var bodyHtml = msg.body_html || msg.html || ''; if (bodyText) { quoted = '\n\n--- Original Message ---\nFrom: ' + fromAddr + '\n\n' + bodyText.split('\n').map(function(l) { return '> ' + l; }).join('\n'); } else if (bodyHtml) { quoted = '\n\n--- Original Message ---\nFrom: ' + fromAddr; } document.getElementById('composeTitle').textContent = '\u270E Reply'; document.getElementById('cTo').value = fromAddr; document.getElementById('cSubject').value = subj; setComposeBodyText(quoted); state.composeReplyToUid = state.openMsg && state.openMsg.uid ? state.openMsg.uid : 0; document.getElementById('composePanel').classList.add('visible'); document.getElementById('cBody').focus(); document.getElementById('cBody').scrollTop = 0; } function deleteMsg() { if (!state.selected) return; var uid = state.selected.uid; var folder = state.selected.folder; if (!confirm('Delete this message?')) return; fetch(appUrl('api/imap_action.php'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'delete', uid: uid, folder: folder }) }) .then(function(r) { return r.json(); }) .then(function(d) { if (d && d.success === false) { toast(d.error || 'Delete failed', 'err'); return; } toast('Message deleted.', 'ok'); state.messages = state.messages.filter(function(m) { return m.uid != uid; }); state.filtered = state.filtered.filter(function(m) { return m.uid != uid; }); state.selected = null; clearReadPane(); renderMessageList(state.filtered); }) .catch(function(err) { toast('Error: ' + err.message, 'err'); }); } function markUnread() { if (!state.selected) return; var uid = state.selected.uid; fetch(appUrl('api/imap_action.php'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'unread', uid: uid }) }) .then(function(r) { return r.json(); }) .then(function(d) { if (d && d.success === false) { toast(d.error || 'Action failed', 'err'); return; } toast('Marked as unread.', 'ok'); state.messages.forEach(function(m) { if (m.uid == uid) { m.seen = false; m.unread = true; } }); state.filtered.forEach(function(m) { if (m.uid == uid) { m.seen = false; m.unread = true; } }); document.querySelectorAll('.msg-row[data-uid="' + uid + '"]').forEach(function(r) { r.classList.add('unread'); }); }) .catch(function(err) { toast('Error: ' + err.message, 'err'); }); } // ── Compose ────────────────────────────────────────────────────────────────── function openCompose(preserveValues) { preserveValues = !!preserveValues; document.getElementById('composeTitle').textContent = '\u270E New Email'; if (!preserveValues) { document.getElementById('cTo').value = ''; document.getElementById('cSubject').value = ''; document.getElementById('cBody').innerHTML = ''; state.composeBodyHtml = ''; document.getElementById('sendStatus').textContent = ''; } state.composeReplyToUid = preserveValues ? state.composeReplyToUid : 0; document.getElementById('composePanel').classList.add('visible'); document.getElementById('cTo').focus(); } function closeCompose() { document.getElementById('composePanel').classList.remove('visible'); document.getElementById('sendStatus').textContent = ''; state.composeBodyHtml = ''; state.composeReplyToUid = null; } function sendEmail() { var to = document.getElementById('cTo').value.trim(); var subject = document.getElementById('cSubject').value.trim(); var bodyHtml = getComposeBodyHtml(); var bodyText = getComposeBodyText(); var status = document.getElementById('sendStatus'); if (!to) { status.textContent = 'Please enter a recipient.'; status.style.color='var(--accent)'; return; } if (!subject) { status.textContent = 'Please enter a subject.'; status.style.color='var(--accent)'; return; } status.textContent = 'Sending…'; status.style.color = 'var(--muted)'; fetch(appUrl('api/imap_send.php'), { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ to_email: to, subject: subject, body_html: bodyHtml, body_text: bodyText, folder: state.folder || 'INBOX', reply_to_uid: state.composeReplyToUid || 0 }) }) .then(function(r) { return r.json(); }) .then(function(d) { if (d && d.success === false) { status.textContent = 'Error: ' + (d.error || 'Send failed'); status.style.color = 'var(--accent-deep)'; return; } status.textContent = ''; closeCompose(); toast('Message sent.', 'ok'); }) .catch(function(err) { status.textContent = 'Error: ' + err.message; status.style.color = 'var(--accent-deep)'; }); } window.addEventListener('message', function(event) { if (event.origin !== window.location.origin) return; var data = event.data || {}; if (!data || typeof data !== 'object') return; if (data.type === 'email_compose_apply') { applyComposeDraft(data.compose || {}); return; } if (data.type === 'email_get_context') { loadSelectedContextForParent(); } }); function composeBodyEl() { return document.getElementById('cBody'); } function normalizeComposeHtml(html) { var raw = String(html || '').trim(); if (!raw) return ''; var bodyMatch = raw.match(/]*>([\s\S]*?)<\/body>/i); if (bodyMatch && bodyMatch[1] !== undefined) { raw = bodyMatch[1]; } raw = raw.replace(/]*>/ig, '') .replace(/]*>/ig, '') .replace(/<\/html>/ig, '') .replace(//ig, '') .replace(/]*>/ig, '') .replace(/<\/body>/ig, '') .trim(); return raw; } function setComposeBodyHtml(html) { var el = composeBodyEl(); el.innerHTML = normalizeComposeHtml(html); state.composeBodyHtml = el.innerHTML; } function setComposeBodyText(text) { var el = composeBodyEl(); var safe = escHtml(String(text || '')).replace(/\n/g, '
'); el.innerHTML = safe; state.composeBodyHtml = el.innerHTML; } function getComposeBodyHtml() { var el = composeBodyEl(); var html = (el.innerHTML || '').trim(); if (!html) { return '

'; } return html; } function getComposeBodyText() { var el = composeBodyEl(); return (el.innerText || el.textContent || '').trim(); } function applyComposeDraft(compose) { compose = compose || {}; openCompose(true); document.getElementById('cTo').value = compose.to_email || compose.to || ''; document.getElementById('cSubject').value = compose.subject || ''; state.composeReplyToUid = 0; state.composeBodyHtml = compose.body_html || ''; var plainBody = compose.body_text || compose.body || ''; if (!plainBody && state.composeBodyHtml) { plainBody = state.composeBodyHtml .replace(//gi, '\n') .replace(/<\/p>/gi, '\n\n') .replace(/<[^>]+>/g, '') .replace(/ /gi, ' '); } if (state.composeBodyHtml) { setComposeBodyHtml(state.composeBodyHtml); } else { setComposeBodyText(plainBody); } document.getElementById('sendStatus').textContent = 'Draft loaded into compose.'; } window.applyComposeDraft = applyComposeDraft; function applyStoredComposeDraft(force) { var raw = ''; try { raw = window.localStorage.getItem('customerdb_email_compose_draft') || ''; } catch (storageError) { return false; } if (!raw) return false; try { var parsed = JSON.parse(raw) || {}; var ts = Number(parsed.ts || 0); if (!force && ts && ts <= Number(state.lastAppliedDraftTs || 0)) { return false; } if (!parsed.payload || typeof parsed.payload !== 'object') { return false; } state.lastAppliedDraftTs = ts || Date.now(); applyComposeDraft(parsed.payload); return true; } catch (parseError) { return false; } } composeBodyEl().addEventListener('input', function() { state.composeBodyHtml = composeBodyEl().innerHTML; }); window.addEventListener('storage', function(event) { if (event.key === 'customerdb_email_compose_draft') { applyStoredComposeDraft(false); } }); window.addEventListener('focus', function() { applyStoredComposeDraft(false); }); // ── Not-configured / error states ──────────────────────────────────────────── function showNotConfigured() { var list = document.getElementById('msgList'); list.innerHTML = ''; document.getElementById('pagination').style.display = 'none'; var main = document.getElementById('readPane'); document.getElementById('readPlaceholder').style.display = 'none'; document.getElementById('msgHeader').style.display = 'none'; document.getElementById('msgBodyWrap').style.display = 'none'; document.getElementById('msgBodyWrap').innerHTML = ''; // Show notice in main pane var existing = document.getElementById('configNotice'); if (existing) existing.remove(); var notice = document.createElement('div'); notice.id = 'configNotice'; notice.className = 'config-notice'; notice.innerHTML = '
📫
' + '

IMAP Not Configured

' + '

Connect your email account to view your inbox right inside Ella\'s Alterations.

' + 'Configure IMAP in Settings'; main.insertBefore(notice, main.firstChild); // Also show in sidebar list.innerHTML = ''; } function showError(errMsg) { var list = document.getElementById('msgList'); list.innerHTML = ''; document.getElementById('pagination').style.display = 'none'; var main = document.getElementById('readPane'); document.getElementById('readPlaceholder').style.display = 'none'; document.getElementById('msgHeader').style.display = 'none'; document.getElementById('msgBodyWrap').style.display = 'none'; document.getElementById('msgBodyWrap').innerHTML = ''; var existing = document.getElementById('configNotice'); if (existing) existing.remove(); var notice = document.createElement('div'); notice.id = 'configNotice'; notice.className = 'config-notice'; notice.innerHTML = '
' + '

IMAP Error

' + '

' + escHtml(errMsg) + '

' + 'Configure IMAP Settings'; main.insertBefore(notice, main.firstChild); } // ── Refresh ────────────────────────────────────────────────────────────────── function refreshInbox() { var existing = document.getElementById('configNotice'); if (existing) existing.remove(); document.getElementById('readPlaceholder').style.display = 'flex'; document.getElementById('msgHeader').style.display = 'none'; document.getElementById('msgBodyWrap').style.display = 'none'; state.selected = null; state.openMsg = null; closeCompose(); loadMessageList(); } // ── Keyboard shortcuts ─────────────────────────────────────────────────────── document.addEventListener('keydown', function(e) { // Escape closes compose if (e.key === 'Escape') closeCompose(); // R to reply when a message is open and compose not focused if (e.key === 'r' && state.openMsg && document.activeElement.tagName !== 'INPUT' && document.activeElement.tagName !== 'TEXTAREA') { replyToMsg(); } }); // ── Boot ───────────────────────────────────────────────────────────────────── loadMessageList(); applyStoredComposeDraft(true);