P

Pure Email Consolecompose · outbox · providers · suppression — PURE-owned, provider-abstracted

provider —

Compose — queue-then-send

Marketing sends get the unsubscribe footer automatically (CAN-SPAM). Staff allowlist + 30/10-min rate limit enforced server-side. Dispatch cron runs every 5 min; “Send now” dispatches immediately via the first live provider.
'; } $("#c_html").addEventListener("input",preview); preview(); function submit(sendNow, toOverride){ var by=$("#c_by").value.trim(); try{ localStorage.setItem("pure.email.by",by); }catch(e){} var sched=$("#c_sched").value?new Date($("#c_sched").value).toISOString():null; var toRaw=(toOverride||$("#c_to").value).trim(); $("#c_msg").textContent=sendNow?"Sending…":"Queueing…"; $("#c_msg").style.color="var(--p-t3)"; if(!toOverride && toRaw.indexOf(",")>0){ var list=toRaw.split(",").map(function(s){return s.trim();}).filter(Boolean); rpc("mcp_outbox_enqueue_bulk",{p_to:list,p_subject:$("#c_subj").value.trim(),p_html:shellWrap($("#c_html").value),p_category:CAT,p_by:by,p_from:$("#c_from").value,p_scheduled:sched}) .then(function(res){ if(res&&res.ok===false){ $("#c_msg").textContent=res.reason; $("#c_msg").style.color="var(--bad)"; return; } $("#c_msg").textContent="Bulk: "+res.queued+" queued · "+res.suppressed+" suppressed · "+res.rejected+" rejected."; $("#c_msg").style.color="var(--p-navy)"; loadAll(); }) .catch(function(e){ $("#c_msg").textContent="Error: "+e.message; $("#c_msg").style.color="var(--bad)"; }); return; } rpc("mcp_outbox_enqueue",{p_to:toRaw,p_cc:toOverride?"":$("#c_cc").value.trim(),p_bcc:toOverride?"":$("#c_bcc").value.trim(),p_subject:(toOverride?"[test] ":"")+$("#c_subj").value.trim(),p_html:shellWrap($("#c_html").value),p_template:$("#c_preset").value||null,p_category:CAT,p_by:by,p_from:$("#c_from").value,p_send_now:!!sendNow,p_reply_to:$("#c_replyto").value.trim()||null,p_scheduled:toOverride?null:sched}) .then(function(res){ if(res&&res.ok===false){ $("#c_msg").textContent=res.reason||"Rejected."; $("#c_msg").style.color="var(--bad)"; return; } var st=(res&&res.status)||"queued"; $("#c_msg").textContent= st==="sent"?"Sent ✓ (row "+String(res.id).slice(0,8)+"…)": st==="suppressed"?"Suppressed — recipient is opted out (row kept for audit).": (res&&res.scheduled)?"Scheduled ✓ — dispatches at the set time.":"Queued ✓ — next dispatch within 5 min."; $("#c_msg").style.color= st==="sent"?"var(--ok)": st==="suppressed"?"var(--warn)":"var(--p-navy)"; loadAll(); }).catch(function(e){ $("#c_msg").textContent="Error: "+e.message; $("#c_msg").style.color="var(--bad)"; }); } $("#sendnow").addEventListener("click",function(){submit(true);}); var _bt=document.getElementById("sendtest"); if(_bt){ _bt.addEventListener("click",function(){ var me=""; try{ me=localStorage.getItem("pure.account")||localStorage.getItem("pure.email.by")||""; }catch(e){} if(!me){ PureUI.alert("Sign in first so I know your inbox."); return; } submit(true, me); }); } $("#queue").addEventListener("click",function(){submit(false);}); $("#testself").addEventListener("click",function(){ submit(true, $("#c_by").value); }); /* inbox */ document.getElementById("inbrows").addEventListener("click",function(e){ var b=e.target.closest(".mini"); if(!b)return; var id=b.getAttribute("data-id"), a=b.getAttribute("data-a"); if(a==="draft"){ rpc("mcp_inbox_reply_draft",{p_id:id,p_by:$("#c_by").value.trim(),p_html:null}).then(function(r){ PureUI.alert((r&&r.note)||"Draft created."); loadAll(); }); return; } if(a==="poppy"){ b.textContent="Poppy…"; var row=(window.__inb||[]).filter(function(x){return x.id===id;})[0]||{}; fetch("https://mbnakfrkfwxinhxlisyq.supabase.co/functions/v1/chat",{method:"POST",headers:{"Content-Type":"application/json",apikey:K,Authorization:"Bearer "+K},body:JSON.stringify({prompt:"Write a short, warm, professional reply email (HTML paragraphs only, no subject) from Acquisto Real Estate to this inbound message. Sender: "+row["from"]+". Subject: "+row.subject+". Body: "+(row.body||"")+". Sign as The PURE Team."})}) .then(function(r){return r.ok?r.json():Promise.reject(new Error("chat "+r.status));}) .then(function(j){ var txt=j.reply||j.content||j.text||j.completion||j.message||(typeof j==="string"?j:null); if(!txt)throw new Error("empty"); return rpc("mcp_inbox_reply_draft",{p_id:id,p_by:$("#c_by").value.trim(),p_html:String(txt)}); }) .then(function(r){ PureUI.alert("Poppy drafted the reply — "+((r&&r.note)||"held in outbox.")); loadAll(); }) .catch(function(){ rpc("mcp_inbox_reply_draft",{p_id:id,p_by:$("#c_by").value.trim(),p_html:null}).then(function(r){ PureUI.alert("Poppy engine unavailable — standard draft held instead."); loadAll(); }); }); return; } if(a==="spam"){ PureUI.confirm("Mark as spam? Sender joins the deny list.",{title:"Mark as spam",okLabel:"Mark spam",cancelLabel:"Cancel",size:"sm"}).then(function(go){ if(!go)return; rpc("mcp_inbox_action",{p_id:id,p_action:a,p_by:$("#c_by").value.trim()}).then(loadAll); }); return; } rpc("mcp_inbox_action",{p_id:id,p_action:a,p_by:$("#c_by").value.trim()}).then(loadAll); }); $("#r_add").addEventListener("click",function(){ if(!$("#r_label").value.trim()||!$("#r_pattern").value.trim())return; rpc("mcp_inbox_rule_set",{p_id:null,p_label:$("#r_label").value.trim(),p_field:"any",p_pattern:$("#r_pattern").value.trim(),p_action:$("#r_action").value,p_target:$("#r_target").value.trim()||null,p_prio:50,p_active:true,p_by:$("#c_by").value.trim()}).then(function(){ $("#r_label").value="";$("#r_pattern").value="";$("#r_target").value=""; loadAll(); }); }); document.getElementById("rulerows").addEventListener("click",function(e){ var b=e.target.closest(".mini"); if(!b)return; rpc("mcp_inbox_rule_set",{p_id:b.getAttribute("data-id"),p_label:null,p_field:null,p_pattern:null,p_action:null,p_target:null,p_prio:null,p_active:b.getAttribute("data-on")!=="1",p_by:$("#c_by").value.trim()}).then(loadAll); }); /* outbox */ function renderOB(){ var rows=OB.slice().sort(function(a,b){ var av=a[sortK],bv=b[sortK]; if(sortK==="attempts"){av=+av||0;bv=+bv||0;} if(avbv)return 1*sortDir; return 0; }); $("#obrows").innerHTML=rows.map(function(x){ var acts=''; if(x.status==="failed"||x.status==="suppressed"||x.status==="canceled")acts+=''; if(x.status==="queued")acts+=''; if(x.status==="held")acts+=''; return ''+esc(x.ts)+''+esc(x.to)+''+esc(x.subject)+''+esc(x.category==="marketing"?"mktg":"txn")+''+esc(x.status)+''+esc(x.provider||"—")+''+esc(x.attempts)+''+esc((x.by||"").split("@")[0])+''+acts+''; }).join("")||'Outbox is empty — every send writes a row here first.'; $("#c-outbox").textContent=OB.filter(function(x){return x.status==="queued"||x.status==="held";}).length||""; document.querySelectorAll("#obt thead th").forEach(function(th){ th.classList.toggle("sorted",th.getAttribute("data-k")===sortK); }); } document.getElementById("obt").addEventListener("click",function(e){ var th=e.target.closest("th[data-k]"); if(th){ var k=th.getAttribute("data-k"); if(sortK===k)sortDir*=-1; else{sortK=k;sortDir=1;} renderOB(); return; } var b=e.target.closest(".mini"); if(!b)return; rpc("mcp_outbox_action",{p_id:b.getAttribute("data-id"),p_action:b.getAttribute("data-a")}).then(loadAll); }); /* providers */ function renderProv(list){ var first=(list||[]).filter(function(p){return p.role==="first";})[0]; $("#provtxt").textContent="first: "+(first?first.id:"none"); $("#provlist").innerHTML=(list||[]).map(function(p){ return '
'+esc(p.label)+''+(p.from?' · '+esc(p.from)+'':'')+'
'+esc(p.notes||"")+(p.live?"":" — not live")+'
' +''+["first","backup","off"].map(function(r){ return ''; }).join("")+'
'; }).join(""); } $("#provlist").addEventListener("click",function(e){ var b=e.target.closest("button[data-p]"); if(!b)return; rpc("mcp_email_provider_set",{p_id:b.getAttribute("data-p"),p_role:b.getAttribute("data-r"),p_by:$("#c_by").value.trim()}).then(function(res){ if(res&&res.ok===false)PureUI.alert(res.reason||"Not allowed."); loadAll(); }); }); /* suppression */ $("#s_add").addEventListener("click",function(){ var v=$("#s_val").value.trim(); if(!v)return; rpc("mcp_suppression_set",{p_value:v,p_action:"add",p_by:$("#c_by").value.trim()}).then(function(){ $("#s_val").value=""; loadAll(); }); }); $("#denyrows").addEventListener("click",function(e){ var b=e.target.closest(".mini"); if(!b)return; var __dv=b.getAttribute("data-v"); PureUI.confirm("Remove "+__dv+" from the deny list?",{title:"Remove from deny list",okLabel:"Remove",cancelLabel:"Cancel",size:"sm"}).then(function(go){ if(!go)return; rpc("mcp_suppression_set",{p_value:__dv,p_action:"remove",p_by:$("#c_by").value.trim()}).then(loadAll); }); }); function loadAll(){ rpc("mcp_outbox_list",{p_status:null,p_limit:80}).then(function(d){ OB=Array.isArray(d)?d:[]; renderOB(); var sent=OB.filter(function(x){return x.status==="sent";}).length, q=OB.filter(function(x){return x.status==="queued"||x.status==="held";}).length, sup=OB.filter(function(x){return x.status==="suppressed";}).length, fail=OB.filter(function(x){return x.status==="failed";}).length; $("#kpis").innerHTML='
'+OB.length+'outbox rows
'+sent+'sent
'+q+'queued / held
'+sup+'suppressed
'+fail+'failed
'; }); rpc("mcp_email_providers",{}).then(renderProv); rpc("mcp_email_sendlog",{p_limit:60}).then(function(d){ $("#logrows").innerHTML=(d||[]).map(function(x){ return ''+esc(x.ts)+''+esc(x.to)+''+esc(x.subject)+''+esc(x.status)+''+esc(x.by)+''; }).join("")||'No email in the log yet.'; }); rpc("mcp_suppression_list",{}).then(function(d){ $("#denyrows").innerHTML=((d&&d.deny)||[]).map(function(x){ return ''+esc(x.value)+''+esc(x.type)+''+esc(x.reason||"")+''+esc(x.by||"")+''; }).join("")||'Deny list is empty.'; $("#prefrows").innerHTML=((d&&d.prefs)||[]).map(function(x){ return ''+esc(x.contact)+''+(x.unsub_all?"✕ all":"—")+''+(x.marketing===false?"✕ mktg":"ok")+''; }).join("")||'No opt-outs.'; }); rpc("mcp_inbox_list",{p_status:null,p_limit:60}).then(function(d){ var rows=Array.isArray(d)?d:[]; window.__inb=rows; $("#c-inbox").textContent=rows.filter(function(x){return x.status==="new"||x.status==="triaged";}).length||""; $("#inbrows").innerHTML=rows.map(function(x){ var acts=''; if(x.status==="new"||x.status==="triaged")acts+=''; if(x.status!=="done")acts+=''; acts+=''; return ''+esc(x.ts)+''+esc(x["from"])+''+esc(x.subject)+''+esc(x.status)+''+esc(x.routed_to||"—")+''+esc(x.ticket||"—")+''+acts+''; }).join("")||'No inbound yet — flows in once MX points at the webhook. Pipeline is live and rule-tested.'; }).catch(function(){}); rpc("mcp_inbox_rules_list",{}).then(function(d){ $("#rulerows").innerHTML=(d||[]).map(function(r){ return ''+esc(r.prio)+''+esc(r.label)+''+esc(r.field)+' ~ '+esc(r.pattern)+''+esc(r.action)+''+esc(r.target||"—")+''; }).join(""); }).catch(function(){}); rpc("mcp_email_stats",{p_days:30}).then(function(s){ if(!s)return; $("#trkpis").innerHTML=[[s.sent,"sent (30d)"],[s.opened,"opened"],[s.clicked,"clicked"],[s.open_rate+"%","open rate"],[s.click_rate+"%","click rate"],[s.suppressed,"suppressed"],[s.failed,"failed"]].map(function(x){return '
'+x[0]+''+x[1]+'
';}).join(""); $("#evrows").innerHTML=(s.recent_events||[]).map(function(e){ return ''+esc(e.ts)+''+esc(e.event)+''+esc(e.to||"")+''+esc(e.url||"—")+''; }).join("")||'No events yet — they appear as tracked emails get opened and clicked.'; $("#topclicks").innerHTML=(s.top_clicked||[]).map(function(t){ return '
'+esc(t.url)+''+esc(t.n)+'
'; }).join("")||'
No clicks yet.
'; }).catch(function(){}); rpc("mcp_alias_list",{}).then(function(d){ $("#alrows").innerHTML=(d||[]).map(function(a){ var mem=a.kind==="forward"||a.kind==="external"?(a.forward_to||"—"):((a.members||[]).join(", ")||"—"); return ''+esc(a.alias)+''+esc(a.kind)+''+esc(mem)+''+(a.active?"✓":"—")+''; }).join(""); }).catch(function(){}); rpc("mcp_email_templates_list",{}).then(function(d){ var rows=Array.isArray(d)?d:(d&&d.rows)||[]; $("#c_preset").innerHTML=''+rows.map(function(t){ return ''; }).join(""); window.__tpls=rows; }).catch(function(){}); $("#foot").innerHTML="Live: pure_outbox (queue-then-send) · pure_email_providers (adapter config) · pure_comms_log · pure_comms_suppression + pure_comm_prefs · dispatch cron */5 min · all via mcp_* RPCs. Lane A scope 7/3 §2 — P1. Refreshed "+new Date().toLocaleString('en-US',{timeZone:'America/Chicago',month:'short',day:'numeric',hour:'numeric',minute:'2-digit'})+" CT."; } $("#c_preset").addEventListener("change",function(){ var key=this.value, t=(window.__tpls||[]).filter(function(x){return (x.template_key||x.id)===key;})[0]; if(t){ if(t.subject)$("#c_subj").value=t.subject; if(t.body_html){ $("#c_html").value=t.body_html; preview(); } } }); $("#a_save").addEventListener("click",function(){ var alias=$("#a_alias").value.trim(), kind=$("#a_kind").value, mm=$("#a_members").value.trim(); if(!alias){ $("#a_msg").textContent="Alias required."; return; } var members=(kind==="user"||kind==="group")?mm.split(",").map(function(s){return s.trim();}).filter(Boolean):[]; var fwd=(kind==="forward"||kind==="external")?mm:null; rpc("mcp_alias_set",{p_id:null,p_alias:alias,p_kind:kind,p_members:members,p_forward:fwd,p_active:true,p_notes:"Added from console",p_by:$("#c_by").value.trim()}).then(function(){ $("#a_alias").value="";$("#a_members").value=""; $("#a_msg").textContent="Saved."; loadAll(); }); }); $("#v_go").addEventListener("click",function(){ rpc("mcp_email_verify",{p_email:$("#v_email").value.trim()}).then(function(r){ if(!r)return; var col=r.verdict==="ok"?"var(--ok)":r.verdict==="risky"?"var(--warn)":"var(--bad)"; $("#v_out").innerHTML=''+esc(r.verdict)+' '+(r.issues||[]).map(esc).join(" · ")+''; }); }); $("#p_go").addEventListener("click",function(){ var st=$("#p_state").value; rpc("mcp_prefs_set",{p_contact:$("#p_email").value.trim(),p_unsub_all:st==="unsub",p_marketing:st==="ok",p_by:$("#c_by").value.trim()}).then(function(){ $("#p_msg").textContent="Preferences updated + event logged."; loadAll(); }); }); document.getElementById("refresh").addEventListener("click",loadAll); loadAll(); })();