Files
weval-consulting/api/Y]

348 lines
69 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="fr"><head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>WEVADS IA — Omnichannel Intelligence Platform</title>
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
<style>
:root{--bg:#060a11;--bg2:#0b1120;--sf:#101828;--card:#131d30;--hov:#182540;--bd:rgba(255,255,255,.05);--bd2:rgba(255,255,255,.09);--tx:#a8b8cc;--mu:#5c6f85;--dm:#3a4a5c;--wh:#ecf0f5;--ac:#d4a843;--ac2:#e8c55a;--acB:rgba(212,168,67,.07);--acBd:rgba(212,168,67,.2);--cy:#22d3ee;--gn:#34d399;--gnB:rgba(52,211,153,.07);--rd:#f87171;--rdB:rgba(248,113,113,.07);--or:#fb923c;--pu:#a78bfa;--puB:rgba(167,139,250,.07);--bl:#60a5fa;--blB:rgba(96,165,250,.07);--em:#60a5fa;--sm:#34d399;--wa:#25d366;--tg:#229ED9;--r:12px;--r2:16px;--r3:8px;--f:'Outfit',system-ui,sans-serif;--m:'JetBrains Mono',monospace}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:var(--f);background:var(--bg);color:var(--tx);display:flex;height:100vh;overflow:hidden;font-size:14px;-webkit-font-smoothing:antialiased}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:var(--bd2);border-radius:2px}::-webkit-scrollbar-track{background:transparent}
a{color:var(--ac);text-decoration:none}button{font-family:var(--f);cursor:pointer;border:none;outline:none}
input,select,textarea{font-family:var(--f);outline:none;background:var(--bg);border:1px solid var(--bd2);color:var(--wh);border-radius:var(--r3);padding:10px 14px;font-size:13px;transition:.2s}
input:focus,select:focus,textarea:focus{border-color:var(--ac);box-shadow:0 0 0 3px rgba(212,168,67,.07)}
table{width:100%;border-collapse:collapse}th{text-align:left;font-size:10px;font-weight:700;color:var(--mu);text-transform:uppercase;letter-spacing:.08em;padding:10px 14px;border-bottom:1px solid var(--bd)}
td{padding:11px 14px;border-bottom:1px solid var(--bd);font-size:13px}tr:hover td{background:rgba(255,255,255,.015)}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
@keyframes glow{0%,100%{box-shadow:0 0 20px rgba(212,168,67,.1)}50%{box-shadow:0 0 40px rgba(212,168,67,.2)}}
@keyframes spin{to{transform:rotate(360deg)}}
.login-wrap{position:fixed;inset:0;background:var(--bg);display:flex;align-items:center;justify-content:center;z-index:200}
.login-wrap::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse 600px 400px at 50% 40%,rgba(212,168,67,.03),transparent);pointer-events:none}
.login-box{background:var(--card);border:1px solid var(--bd2);border-radius:24px;padding:44px;width:420px;max-width:92vw;animation:fadeIn .5s;position:relative;overflow:hidden}
.login-box::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--ac),transparent)}
.login-logo{width:50px;height:50px;background:linear-gradient(135deg,var(--ac),var(--ac2));border-radius:14px;display:flex;align-items:center;justify-content:center;margin:0 auto 18px;font-weight:900;color:#0a0d13;font-size:20px;animation:glow 3s infinite}
.login-box h1{font-size:22px;font-weight:800;color:var(--wh);text-align:center;letter-spacing:-.03em}
.login-box .sub{font-size:12px;color:var(--mu);text-align:center;margin:4px 0 4px}.login-box .ver{font-size:10px;color:var(--dm);text-align:center;margin-bottom:20px;font-family:var(--m)}
.field{margin-bottom:12px}.field label{display:block;font-size:10px;font-weight:600;color:var(--mu);margin-bottom:4px;text-transform:uppercase;letter-spacing:.06em}.field input{width:100%}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:10px 20px;border-radius:10px;font-size:13px;font-weight:600;transition:.2s}
.btn-full{width:100%}.btn-ac{background:linear-gradient(135deg,var(--ac),var(--ac2));color:#0a0d13}.btn-ac:hover{filter:brightness(1.1);transform:translateY(-1px)}
.btn-gh{background:transparent;color:var(--tx);border:1px solid var(--bd2)}.btn-gh:hover{border-color:var(--mu);background:rgba(255,255,255,.02)}
.btn-sm{padding:7px 14px;font-size:12px;border-radius:var(--r3)}.btn-rd{background:var(--rdB);color:var(--rd);border:1px solid rgba(248,113,113,.12)}
.divider{display:flex;align-items:center;gap:12px;margin:18px 0;color:var(--dm);font-size:11px}.divider::before,.divider::after{content:'';flex:1;height:1px;background:var(--bd)}
.sidebar{width:250px;background:var(--bg2);border-right:1px solid var(--bd);display:flex;flex-direction:column;flex-shrink:0}
.sb-brand{padding:18px;display:flex;align-items:center;gap:11px;border-bottom:1px solid var(--bd)}
.sb-logo{width:34px;height:34px;background:linear-gradient(135deg,var(--ac),var(--ac2));border-radius:10px;display:flex;align-items:center;justify-content:center;font-weight:900;color:#0a0d13;font-size:15px}
.sb-brand-name{font-size:15px;font-weight:800;color:var(--wh);letter-spacing:-.02em}.sb-brand-sub{font-size:8px;color:var(--dm);font-family:var(--m);letter-spacing:.05em}
.sb-ch{display:flex;gap:5px;padding:12px 16px;border-bottom:1px solid var(--bd)}
.sb-chip{flex:1;height:26px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:9px;font-weight:700;position:relative}
.sb-chip.on{opacity:1}.sb-chip.off{opacity:.3}
.sb-chip-em{background:rgba(96,165,250,.08);color:var(--em);border:1px solid rgba(96,165,250,.12)}.sb-chip-sm{background:rgba(52,211,153,.08);color:var(--sm);border:1px solid rgba(52,211,153,.12)}
.sb-chip-wa{background:rgba(37,211,102,.08);color:var(--wa);border:1px solid rgba(37,211,102,.12)}.sb-chip-tg{background:rgba(34,158,217,.08);color:var(--tg);border:1px solid rgba(34,158,217,.12)}
.sb-chip .dot{position:absolute;top:2px;right:2px;width:5px;height:5px;border-radius:50%}.dot-on{background:var(--gn)}.dot-off{background:var(--rd)}
.sb-nav{flex:1;overflow-y:auto;padding:6px 8px}
.sb-sec{padding:12px 10px 5px;font-size:8px;font-weight:700;color:var(--dm);text-transform:uppercase;letter-spacing:.12em}
.sb-it{display:flex;align-items:center;gap:9px;padding:9px 12px;border-radius:9px;cursor:pointer;font-size:12.5px;color:var(--mu);font-weight:500;transition:.15s;margin-bottom:1px}
.sb-it:hover{background:rgba(255,255,255,.025);color:var(--tx)}.sb-it.on{background:var(--acB);color:var(--ac);border:1px solid var(--acBd)}
.sb-it svg{width:16px;height:16px;flex-shrink:0;opacity:.5}.sb-it.on svg{opacity:1}
.sb-it .cnt{margin-left:auto;font-size:9px;font-family:var(--m);background:var(--acB);color:var(--ac);padding:2px 6px;border-radius:8px;font-weight:600}
.sb-user{padding:12px 14px;border-top:1px solid var(--bd);display:flex;align-items:center;gap:9px}
.sb-av{width:30px;height:30px;border-radius:9px;background:linear-gradient(135deg,var(--ac),var(--ac2));display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:800;color:#0a0d13;flex-shrink:0}
.sb-un{font-size:11px;font-weight:600;color:var(--wh)}.sb-ut{font-size:9px;color:var(--dm);font-family:var(--m)}
.sb-out{padding:5px;border-radius:7px;background:transparent;color:var(--dm);cursor:pointer;transition:.15s;margin-left:auto}.sb-out:hover{background:var(--rdB);color:var(--rd)}
.main{flex:1;display:flex;flex-direction:column;overflow:hidden}
.top{height:50px;border-bottom:1px solid var(--bd);display:flex;align-items:center;padding:0 24px;gap:12px;background:var(--bg2);flex-shrink:0}
.top h2{font-size:16px;font-weight:700;color:var(--wh);flex:1;letter-spacing:-.02em}
.top-live{font-family:var(--m);font-size:9px;padding:4px 10px;border-radius:16px;display:flex;align-items:center;gap:5px;color:var(--gn);background:var(--gnB);border:1px solid rgba(52,211,153,.12)}
.top-live::before{content:'';width:5px;height:5px;border-radius:50%;background:var(--gn);animation:pulse 2s infinite}
.content{flex:1;overflow-y:auto;padding:24px;animation:fadeIn .25s}
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(165px,1fr));gap:12px;margin-bottom:24px}
.stat{background:var(--card);border:1px solid var(--bd);border-radius:var(--r2);padding:18px;position:relative;overflow:hidden;transition:.2s}.stat:hover{border-color:var(--bd2)}
.stat::before{content:'';position:absolute;top:0;left:0;right:0;height:2px}
.s-ac::before{background:linear-gradient(90deg,var(--ac),var(--ac2))}.s-bl::before{background:var(--bl)}.s-gn::before{background:var(--gn)}.s-rd::before{background:var(--rd)}.s-pu::before{background:var(--pu)}.s-tg::before{background:var(--tg)}.s-or::before{background:var(--or)}
.st-l{font-size:10px;font-weight:600;color:var(--mu);text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px}
.st-v{font-size:26px;font-weight:800;color:var(--wh);font-family:var(--m);letter-spacing:-.04em;line-height:1}
.st-s{font-size:10px;margin-top:5px;font-weight:500;color:var(--mu)}.st-s .up{color:var(--gn)}.st-s .dn{color:var(--rd)}
.card{background:var(--card);border:1px solid var(--bd);border-radius:var(--r2);padding:20px;margin-bottom:16px}
.card-h{display:flex;align-items:center;justify-content:space-between;margin-bottom:16px}
.card-t{font-size:14px;font-weight:700;color:var(--wh);letter-spacing:-.01em}
.badge{display:inline-flex;padding:3px 9px;border-radius:16px;font-size:9px;font-weight:600;letter-spacing:.02em}
.b-ok{background:var(--gnB);color:var(--gn)}.b-sent{background:var(--gnB);color:var(--gn)}.b-wait{background:rgba(251,146,60,.07);color:var(--or)}.b-err{background:var(--rdB);color:var(--rd)}.b-em{background:var(--blB);color:var(--bl)}.b-ac{background:var(--acB);color:var(--ac)}
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.6);backdrop-filter:blur(3px);z-index:100;display:flex;align-items:center;justify-content:center;animation:fadeIn .12s}
.modal{background:var(--card);border:1px solid var(--bd2);border-radius:var(--r2);padding:28px;width:560px;max-width:92vw;max-height:85vh;overflow-y:auto}
.modal h3{font-size:17px;font-weight:700;color:var(--wh);margin-bottom:18px}.modal .brow{display:flex;gap:8px;margin-top:18px}
.ch-sel{display:flex;gap:6px;margin:10px 0}.ch-p{padding:7px 14px;border-radius:16px;font-size:11px;font-weight:600;cursor:pointer;transition:.2s;border:1px solid var(--bd2);background:transparent;color:var(--mu)}
.ch-p.sel{border-color:currentColor}.ch-p[data-c=email].sel{background:var(--blB);color:var(--em)}.ch-p[data-c=sms].sel{background:var(--gnB);color:var(--sm)}.ch-p[data-c=whatsapp].sel{background:rgba(37,211,102,.07);color:var(--wa)}.ch-p[data-c=telegram].sel{background:rgba(34,158,217,.07);color:var(--tg)}
.chat-w{display:flex;flex-direction:column;height:100%}.chat-m{flex:1;overflow-y:auto;padding:0 4px 14px}
.chat-e{text-align:center;padding:50px 20px;color:var(--dm)}.chat-e h3{color:var(--mu);margin-top:10px;font-size:15px}
.chat-u{background:var(--acB);border:1px solid var(--acBd);border-radius:var(--r) var(--r) 4px var(--r);padding:12px 16px;margin:7px 0;max-width:75%;margin-left:auto;font-size:13px;color:var(--wh);line-height:1.6}
.chat-a{background:var(--sf);border:1px solid var(--bd);border-radius:4px var(--r) var(--r) var(--r);padding:14px 18px;margin:7px 0;max-width:85%;font-size:13px;line-height:1.7}
.chat-a pre{background:var(--bg);padding:10px;border-radius:var(--r3);overflow-x:auto;font-family:var(--m);font-size:12px;margin:7px 0}.chat-a code{font-family:var(--m);font-size:12px;background:var(--bg);padding:2px 5px;border-radius:3px}
.chat-b{display:flex;gap:8px;padding:12px 0 0;border-top:1px solid var(--bd)}.chat-b textarea{flex:1;resize:none;min-height:42px;max-height:110px;font-size:13px;line-height:1.5;border-radius:10px}
.ldot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--ac);margin:0 2px;animation:pulse .8s infinite}.ldot:nth-child(2){animation-delay:.15s}.ldot:nth-child(3){animation-delay:.3s}
.spinner{width:16px;height:16px;border:2px solid var(--bd);border-top-color:var(--ac);border-radius:50%;animation:spin .6s linear infinite;display:inline-block}
.toolbar{display:flex;align-items:center;gap:8px;margin-bottom:16px;flex-wrap:wrap}.toolbar .sp{flex:1}
.toolbar input[type=search]{width:260px;background:var(--sf);padding-left:34px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='13' height='13' viewBox='0 0 24 24' fill='none' stroke='%235c6f85' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.3-4.3'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:11px center}
.toast{position:fixed;bottom:22px;right:22px;background:var(--card);color:var(--wh);padding:12px 20px;border-radius:var(--r);font-size:13px;z-index:9999;display:none;border:1px solid var(--bd2);box-shadow:0 10px 35px rgba(0,0,0,.5);animation:fadeIn .15s}
.toast.err{border-color:rgba(248,113,113,.15);color:var(--rd)}
.tags{display:flex;flex-wrap:wrap;gap:4px}.tag{padding:2px 8px;border-radius:12px;font-size:9px;font-weight:600;background:var(--sf);border:1px solid var(--bd);color:var(--mu)}
.ed{width:100%;min-height:180px;font-family:var(--m);font-size:12px;line-height:1.6;background:var(--bg);border-radius:var(--r3);padding:14px;resize:vertical}
.row2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
@media(max-width:768px){.sidebar{width:55px}.sb-brand-name,.sb-brand-sub,.sb-sec,.sb-it span,.sb-un,.sb-ut,.sb-ch{display:none}.sb-it{justify-content:center;padding:9px}.content{padding:14px}.row2{grid-template-columns:1fr}}
</style></head><body>
<div class="login-wrap" id="LW"><div class="login-box"><div class="login-logo" style="padding:6px;background:none;animation:none"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" style="width:100%;height:100%"><defs><linearGradient id="wg" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#d4a843"/><stop offset="100%" stop-color="#e8c55a"/></linearGradient></defs><rect width="48" height="48" rx="12" fill="url(#wg)"/><path d="M12 16l12 9 12-9" stroke="#0a0d13" stroke-width="2.2" fill="none" stroke-linecap="round"/><rect x="12" y="15" width="24" height="18" rx="3" stroke="#0a0d13" stroke-width="1.5" fill="none" opacity=".5"/><text x="36" y="14" text-anchor="middle" fill="#0a0d13" font-size="10" font-weight="800" font-family="system-ui">IA</text></svg></div><h1>WEVADS IA</h1><p class="sub">Omnichannel Intelligence Platform</p><p class="ver">v3.0 — Sovereign Engine · 344 APIs · 688 Tables</p>
<div id="LF"><div class="field"><label>Email</label><input type="email" id="lE" placeholder="you@company.com"></div><div class="field"><label>Mot de passe</label><input type="password" id="lP" onkeydown="if(event.key==='Enter')doLogin()"></div><button class="btn btn-ac btn-full" onclick="doLogin()" id="bL">Se connecter</button><div class="divider">ou</div><button class="btn btn-gh btn-full" onclick="sR()">Créer un compte</button></div>
<div id="RF" style="display:none"><div class="field"><label>Nom</label><input id="rN"></div><div class="field"><label>Email</label><input type="email" id="rE"></div><div class="field"><label>Entreprise</label><input id="rC"></div><div class="field"><label>Mot de passe</label><input type="password" id="rP" onkeydown="if(event.key==='Enter')doReg()"></div><button class="btn btn-ac btn-full" onclick="doReg()" id="bR">Créer</button><div class="divider">ou</div><button class="btn btn-gh btn-full" onclick="sL()">Connexion</button></div>
</div></div>
<div class="sidebar" id="SB" style="display:none"><div class="sb-brand"><div class="sb-logo" style="padding:4px;background:none"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48" style="width:100%;height:100%"><defs><linearGradient id="wg" x1="0" y1="0" x2="1" y2="1"><stop offset="0%" stop-color="#d4a843"/><stop offset="100%" stop-color="#e8c55a"/></linearGradient></defs><rect width="48" height="48" rx="12" fill="url(#wg)"/><path d="M12 16l12 9 12-9" stroke="#0a0d13" stroke-width="2.2" fill="none" stroke-linecap="round"/><rect x="12" y="15" width="24" height="18" rx="3" stroke="#0a0d13" stroke-width="1.5" fill="none" opacity=".5"/><text x="36" y="14" text-anchor="middle" fill="#0a0d13" font-size="10" font-weight="800" font-family="system-ui">IA</text></svg></div><div><span class="sb-brand-name">WEVADS IA</span><br><span class="sb-brand-sub">v3.0 OMNICHANNEL</span></div></div>
<div class="sb-ch"><div class="sb-chip sb-chip-em on"><span class="dot dot-on"></span>✉</div><div class="sb-chip sb-chip-sm off"><span class="dot dot-off"></span>SMS</div><div class="sb-chip sb-chip-wa off"><span class="dot dot-off"></span>WA</div><div class="sb-chip sb-chip-tg on"><span class="dot dot-on"></span>TG</div></div>
<div class="sb-nav" id="SN"></div>
<div class="sb-user"><div class="sb-av" id="sA">W</div><div style="flex:1;min-width:0"><div class="sb-un" id="sU"></div><div class="sb-ut" id="sT">admin</div></div><div class="sb-out" onclick="logout()" title="Déconnexion"><svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/><line x1="21" y1="12" x2="9" y2="12"/></svg></div></div></div>
<div class="main" id="MA" style="display:none"><div class="top"><h2 id="PT">Dashboard</h2><div class="top-live" id="TL">Backend Live</div></div><div class="content" id="C"></div></div>
<div class="toast" id="T"></div>
<script>
const API='/api/v2',V2E='/api/wevads-v2-engine.php',BRG='/api/adx-bridge.php',WEVIA='/api/weval-ia-full',SE='/api/send-engine-unified.php';
let TK='',U={},PG='dashboard';
const $=id=>document.getElementById(id),v=id=>$(id)?$(id).value.trim():'';
const P=[
['dashboard','Dashboard','M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 14h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7z','Principal'],
['send','⚡ Envoyer','M13 2L3 14h9l-1 8 10-12h-9l1-8z','Principal'],
['campaigns','Campagnes','M2 4h20v16H2V4zm0 4h20M2 8l10 5 10-5','Principal'],
['contacts','Contacts','M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75','Principal'],
['senders','Senders','M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2zM22 6l-10 7L2 6','Email Ops'],
['warmup','Warmup','M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83','Email Ops'],
['domains','Domaines','M12 2a10 10 0 1 0 0 20 10 10 0 0 0 0-20zM2 12h20M12 2a15 15 0 0 1 4 10 15 15 0 0 1-4 10 15 15 0 0 1-4-10 15 15 0 0 1 4-10z','Email Ops'],
['seeds','Seeds','M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z','Email Ops'],
['reputation','Réputation','M9 12l2 2 4-4M20.6 6.7A9.94 9.94 0 0 0 12 2C6.5 2 2 6.5 2 12s4.5 10 10 10 10-4.5 10-10c0-1.5-.3-2.9-.9-4.2','Email Ops'],
['brain','Brain IA','M4 4h16v16H4V4zM9 9h6v6H9V9zM9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3','Intelligence'],
['creative','Creative','M12 20h9M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z','Intelligence'],
['analytics','Analytics','M18 20V10M12 20V4M6 20v-6','Intelligence'],
['ai','AI Assistant','M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z','Intelligence'],
['channels','Canaux','M12 12m-2 0a2 2 0 1 0 4 0 2 2 0 1 0-4 0M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14','Omnichannel'],
['templates','Templates','M3 3h18v18H3V3zM3 9h18M9 21V9','Omnichannel'],
['offers','Offres','M20.59 13.41l-7.17 7.17a2 2 0 0 1-2.83 0L2 12V2h10l8.59 8.59a2 2 0 0 1 0 2.82zM7 7h.01','Business'],
['sponsors','Sponsors','M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2M9 7a4 4 0 1 0 0-8 4 4 0 0 0 0 8M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75','Business'],
['servers','Serveurs','M22 12h-6l-2 3h-4l-2-3H2M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z','Système'],
['infra','Infrastructure','M22 12h-6l-2 3h-4l-2-3H2M5.45 5.11L2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z','Système'],
['settings','Paramètres','M12 12m-3 0a3 3 0 1 0 6 0 3 3 0 1 0-6 0M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42','Système'],
];
function ico(d){return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="${d}"/></svg>`}
function toast(m,e){const t=$('T');t.textContent=m;t.className=e?'toast err':'toast';t.style.display='block';setTimeout(()=>t.style.display='none',3500)}
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML}
function N(n){return new Intl.NumberFormat('fr-FR').format(n||0)}
function D(d){return d?new Date(d).toLocaleDateString('fr-FR',{day:'2-digit',month:'short',year:'numeric'}):''}
function md2h(t){return t.replace(/```[\w]*\n([\s\S]*?)```/g,'<pre>$1</pre>').replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>').replace(/`(.*?)`/g,'<code>$1</code>').replace(/\n\n/g,'<br><br>')}
async function api(p,o={}){const h={'Content-Type':'application/json'};if(TK)h['Authorization']='Bearer '+TK;const r=await fetch(API+p,{...o,headers:h});return r.json()}
async function eng(a){return(await fetch(V2E+'?action='+a+'&token=WEVADS2026')).json()}
async function brg(a){return(await fetch(BRG+'?action='+a+'&token=WEVADS2026')).json()}
function sR(){$('LF').style.display='none';$('RF').style.display=''}
function sL(){$('RF').style.display='none';$('LF').style.display=''}
async function doLogin(){const e=v('lE'),p=v('lP');if(!e||!p)return toast('Requis',1);$('bL').textContent='...';try{const r=await api('/auth/login',{method:'POST',body:JSON.stringify({email:e,password:p})});TK=r.token;U=r.user;enter()}catch(x){toast(x.message,1)}finally{$('bL').textContent='Se connecter'}}
async function doReg(){const n=v('rN'),e=v('rE'),p=v('rP');if(!n||!e||!p)return toast('Requis',1);try{const r=await api('/auth/register',{method:'POST',body:JSON.stringify({name:n,email:e,company:v('rC'),password:p})});TK=r.token;U=r.user;enter()}catch(x){toast(x.message,1)}}
function enter(){try{sessionStorage.setItem('wt',TK);sessionStorage.setItem('wu',JSON.stringify(U))}catch(e){}$('LW').style.display='none';$('SB').style.display='flex';$('MA').style.display='flex';$('sU').textContent=U.name||U.email;$('sA').textContent=(U.name||'W')[0].toUpperCase();$('sT').textContent=U.role||'user';rnav();go('dashboard')}
function logout(){TK='';U={};try{sessionStorage.clear()}catch(e){}$('LW').style.display='flex';$('SB').style.display='none';$('MA').style.display='none'}
function rnav(){let h='',s='';P.forEach(p=>{if(p[3]!==s){s=p[3];h+=`<div class="sb-sec">${s}</div>`}h+=`<div class="sb-it ${PG===p[0]?'on':''}" onclick="go('${p[0]}')">${ico(p[2])}<span>${p[1]}</span></div>`});$('SN').innerHTML=h}
function go(p){PG=p;$('PT').textContent=P.find(x=>x[0]===p)?.[1]||p;rnav();const c=$('C');c.innerHTML='<div style="text-align:center;padding:40px"><div class="spinner"></div></div>';c.scrollTop=0;(RR[p]||RR.dashboard)()}
const RR={};
// =============== DASHBOARD ===============
RR.dashboard=async()=>{let k={},t={},c=[];try{k=(await eng('dashboard')).kpis||{}}catch(e){}try{t=(await eng('tracking_stats')).stats||{}}catch(e){}try{c=(await api('/campaigns/list')).items||[]}catch(e){}const rs=(await eng('dashboard').catch(()=>({}))).recent_sends||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Contacts</div><div class="st-v">${N(k.contacts_total)}</div><div class="st-s">${N(k.contacts_active)} actifs</div></div><div class="stat s-bl"><div class="st-l">Emails envoyés</div><div class="st-v">${N(k.emails_sent_total)}</div><div class="st-s">${k.emails_sent_today||0} today</div></div><div class="stat s-gn"><div class="st-l">Senders</div><div class="st-v">${N(k.senders_active)}</div><div class="st-s">${k.senders_warming||0} warming</div></div><div class="stat s-pu"><div class="st-l">Opens / Clicks</div><div class="st-v">${N(t.total_opens)}</div><div class="st-s">${N(t.total_clicks)} clicks</div></div><div class="stat s-or"><div class="st-l">Warmup</div><div class="st-v">${N(k.warmup_accounts)}</div></div><div class="stat s-tg"><div class="st-l">Canaux</div><div class="st-v">4</div><div class="st-s"><span class="up">Email+TG</span></div></div></div>
<div class="row2"><div class="card"><div class="card-h"><div class="card-t">Derniers envois</div><button class="btn btn-sm btn-ac" onclick="go('send')">+ Envoyer</button></div><table><thead><tr><th>Sender</th><th>To</th><th>Sujet</th><th>ISP</th><th>Date</th></tr></thead><tbody>${rs.slice(0,8).map(s=>`<tr><td style="font-family:var(--m);font-size:10px;color:var(--mu)">${esc((s.sender||'').split('@')[0])}</td><td style="font-family:var(--m);font-size:10px">${esc(s.recipient||'')}</td><td style="color:var(--wh);font-size:11px">${esc((s.subject||'').substring(0,30))}</td><td><span class="badge b-em">${esc(s.isp||'')}</span></td><td style="font-size:10px;color:var(--dm)">${(s.created_at||'').substring(0,16)}</td></tr>`).join('')||'<tr><td colspan="5" style="text-align:center;color:var(--dm);padding:24px">Aucun envoi</td></tr>'}</tbody></table></div>
<div><div class="card"><div class="card-t" style="margin-bottom:10px">Infra</div>${[['Campagnes',k.campaigns_total,'('+k.campaigns_active+' actives)'],['Domaines',k.domains_active,''],['Bounces',k.bounces,''],['Tracking',N(k.tracking_events),'events'],['Ethica HCPs',N(k.ethica_hcps),'pharma']].map(([l,v2,s])=>`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--mu)">${l}</span><span style="font-family:var(--m);font-size:11px;color:var(--wh)">${v2||0} <span style="color:var(--dm);font-size:9px">${s}</span></span></div>`).join('')}</div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Actions</div><div style="display:grid;gap:5px"><button class="btn btn-sm btn-ac btn-full" onclick="go('send')">⚡ Envoyer</button><button class="btn btn-sm btn-gh btn-full" onclick="go('senders')">Senders</button><button class="btn btn-sm btn-gh btn-full" onclick="go('ai')">🧠 AI Assistant</button></div></div></div></div>`};
// =============== SEND ===============
RR.send=async()=>{let m=[];try{m=(await eng('send_methods')).items||[]}catch(e){}
$('C').innerHTML=`<div class="card" style="border-color:var(--acBd)"><div class="card-h"><div class="card-t">⚡ Composer & Envoyer</div></div>
<div class="row2"><div><div class="field"><label>Canal</label><div class="ch-sel"><div class="ch-p sel" data-c="email" onclick="this.classList.toggle('sel')">✉ Email</div><div class="ch-p" data-c="sms" onclick="this.classList.toggle('sel')">💬 SMS</div><div class="ch-p" data-c="whatsapp" onclick="this.classList.toggle('sel')">📱 WhatsApp</div><div class="ch-p" data-c="telegram" onclick="this.classList.toggle('sel')">✈ Telegram</div></div></div>
<div class="field"><label>Méthode</label><select id="sM">${m.map(x=>`<option value="${x.name}">⚙ ${x.name} — ${x.type}</option>`).join('')}</select></div><div class="field"><label>SMTP Config</label><select id="sC"><option value="">— Auto (Brain choisit) —</option></select></div>
<div class="field"><label>Destinataire(s)</label><input id="sTo" placeholder="email@example.com (virgules pour bulk)"></div>
<div class="field"><label>Objet</label><input id="sSu" placeholder="Objet du message"></div>
<div style="display:flex;gap:6px;margin:6px 0"><button class="btn btn-sm btn-gh" style="color:var(--pu);border-color:rgba(167,139,250,.2)" onclick="aiSubj()">🧠 Optimiser IA</button><span id="aiH" style="font-size:10px;color:var(--dm)"></span></div></div>
<div><div class="field"><label>From Name</label><input id="sFN" value="WEVAL Consulting"></div><div class="field"><label>From Email</label><input id="sFE" placeholder="Auto-select"></div>
<div style="display:flex;gap:6px;margin-top:14px"><button class="btn btn-ac" onclick="doSend()" style="flex:1">🧪 Test</button><button class="btn btn-gh" onclick="doBulk()" style="flex:1;color:var(--gn);border-color:rgba(52,211,153,.2)">🚀 Bulk</button></div></div></div>
<div class="field" style="margin-top:12px"><label>HTML</label><div style="display:flex;gap:6px;margin-bottom:6px"><button class="btn btn-sm btn-gh" onclick="aiBody()">🧠 IA</button><button class="btn btn-sm btn-gh" onclick="loadTpl()">📋 Template</button></div><textarea class="ed" id="sBo" rows="8" placeholder="<h1>Bonjour {{name}}</h1>"></textarea></div></div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">File d'attente</div><div id="qS">...</div></div><div class="card"><div class="card-t" style="margin-bottom:10px">Tracking récent</div><div id="rT">...</div></div></div>`;
// Load SMTP configs
try{const sc=await brg('smtp_all');const sel=$('sC');(sc.smtp_configs||[]).forEach(c=>{const o=document.createElement('option');o.value=c.config_name;o.textContent=c.config_name+' — '+c.host+':'+c.port+' ('+Number(c.daily_limit).toLocaleString()+'/day)';sel.appendChild(o)})}catch(e){}
try{const q=await eng('queue');$('qS').innerHTML=`<div style="font-size:22px;font-weight:800;color:var(--wh);font-family:var(--m)">${q.pending||0}</div><div style="font-size:10px;color:var(--mu)">en attente</div>`}catch(e){$('qS').textContent='Offline'}
try{const t=await eng('tracking_stats');$('rT').innerHTML=(t.recent||[]).slice(0,5).map(r=>`<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--bd);font-size:10px"><span style="font-family:var(--m);color:var(--ac)">${esc((r.tracking_id||'').substring(0,18))}</span><span class="badge b-${r.event_type==='open'?'ok':'em'}">${r.event_type}</span></div>`).join('')||'—'}catch(e){}};
async function doSend(){const to=v('sTo'),su=v('sSu'),bo=v('sBo'),me=v('sM');if(!to||!su)return toast('To + Objet requis',1);toast('Envoi '+me+'...');try{const r=await fetch(V2E+'?action=send_test&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'to='+encodeURIComponent(to)+'&subject='+encodeURIComponent(su)+'&html='+encodeURIComponent(bo)+'&method='+me+'&from_name='+encodeURIComponent(v('sFN'))});const j=await r.json();toast(j.sent?'✅ Envoyé via '+me+' ('+j.tracking_id+')':'❌ '+(j.error||''),!j.sent)}catch(e){toast('Err: '+e.message,1)}}
async function doBulk(){const to=v('sTo'),su=v('sSu'),bo=v('sBo');const emails=to.split(',').map(e=>e.trim()).filter(Boolean);if(emails.length<2)return toast('Bulk = 2+ emails',1);toast('Bulk '+emails.length+'...');try{const r=await fetch(V2E+'?action=send_bulk&token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({recipients:emails,subject:su,html:bo,method:v('sM'),from_name:v('sFN')})});const j=await r.json();toast(j.ok?'Bulk: '+emails.length+' emails':'Err',!j.ok)}catch(e){toast('Err',1)}}
async function aiSubj(){const s=v('sSu');if(!s)return toast('Sujet d\'abord',1);$('aiH').textContent='...';try{const r=await(await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:'Optimize this email subject for max open rate. Return ONLY the subject: '+s,mode:'fast'})})).json();$('sSu').value=r.response||s;$('aiH').textContent='✨ Optimisé'}catch(e){$('aiH').textContent='Err'}}
async function aiBody(){$('sBo').value='Génération...';try{const r=await(await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:'Generate a professional B2B HTML email about: '+(v('sSu')||'product launch')+'. Return ONLY HTML.',mode:'fast'})})).json();$('sBo').value=r.response||'Err'}catch(e){$('sBo').value='Err'}}
async function loadTpl(){try{const t=(await api('/templates/list')).items||[];if(t.length){$('sBo').value=t[0].html||'';toast(t[0].name+' chargé')}else toast('0 templates',1)}catch(e){toast('Err',1)}}
// =============== CAMPAIGNS ===============
let CMP=[];
RR.campaigns=async()=>{try{CMP=(await api('/campaigns/list')).items||[]}catch(e){CMP=[]}
$('C').innerHTML=`<div class="toolbar"><input type="search" id="cS" placeholder="Rechercher..." oninput="fCmp()"><div class="sp"></div><button class="btn btn-sm btn-ac" onclick="oCmp()">+ Campagne</button></div><div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Nom</th><th>Sujet</th><th>Statut</th><th>Envoyés</th><th>Ouverts</th><th>Actions</th></tr></thead><tbody id="cT"></tbody></table></div>`;rCmp()};
function rCmp(f=''){const i=f?CMP.filter(c=>[c.name,c.subject].join(' ').toLowerCase().includes(f)):CMP;$('cT').innerHTML=i.map(c=>`<tr><td style="color:var(--wh);font-weight:600">${esc(c.name)}</td><td>${esc(c.subject)}</td><td><span class="badge b-${c.status==='sent'?'sent':c.status==='draft'?'ac':'wait'}">${c.status}</span></td><td style="font-family:var(--m)">${N(c.metrics?.sent)}</td><td style="font-family:var(--m)">${N(c.metrics?.opened)}</td><td><button class="btn btn-sm btn-gh" onclick="simS('${c.id}')">Sim</button></td></tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--dm);padding:24px">—</td></tr>'}
function fCmp(){rCmp(v('cS').toLowerCase())}
async function simS(id){try{await api('/campaigns/'+id+'/send-simulate',{method:'POST'});toast('Simulé');RR.campaigns()}catch(e){toast(e.message,1)}}
function oCmp(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Nouvelle campagne</h3><div class="field"><label>Nom</label><input id="nN"></div><div class="field"><label>Objet</label><input id="nS"></div><div class="field"><label>Audience</label><input type="number" id="nA" value="500"></div><div class="field"><label>HTML</label><textarea class="ed" id="nH" rows="4"></textarea></div><div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">Annuler</button><button class="btn btn-ac" onclick="cCmp()">Créer</button></div></div>`;document.body.appendChild(m)}
async function cCmp(){try{await api('/campaigns',{method:'POST',body:JSON.stringify({name:v('nN'),subject:v('nS'),audience_size:+v('nA'),content_html:v('nH')})});document.querySelector('.modal-bg')?.remove();toast('Créée');RR.campaigns()}catch(e){toast(e.message,1)}}
// =============== CONTACTS ===============
let CTC=[];
RR.contacts=async()=>{try{CTC=(await api('/contacts/list')).items||[]}catch(e){CTC=[]}
$('C').innerHTML=`<div class="toolbar"><input type="search" id="ctS" placeholder="Rechercher..." oninput="fCtc()"><div class="sp"></div><button class="btn btn-sm btn-ac" onclick="oCtc()">+ Ajouter</button></div><div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Nom</th><th>Email</th><th>Entreprise</th><th>Pays</th><th>Tags</th><th></th></tr></thead><tbody id="ctT"></tbody></table></div>`;rCtc()};
function rCtc(f=''){const i=f?CTC.filter(c=>[c.email,c.first_name,c.last_name].join(' ').toLowerCase().includes(f)):CTC;$('ctT').innerHTML=i.map(c=>`<tr><td style="color:var(--wh);font-weight:600">${esc(c.first_name||'')} ${esc(c.last_name||'')}</td><td style="font-family:var(--m);font-size:11px">${esc(c.email)}</td><td>${esc(c.company||'')}</td><td>${esc(c.country||'')}</td><td><div class="tags">${(c.tags||[]).map(t=>`<span class="tag">${esc(t)}</span>`).join('')}</div></td><td><button class="btn btn-sm btn-rd" onclick="dCtc('${c.id}')">×</button></td></tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--dm);padding:24px">—</td></tr>'}
function fCtc(){rCtc(v('ctS').toLowerCase())}
async function dCtc(id){if(!confirm('Supprimer?'))return;try{await api('/contacts/'+id,{method:'DELETE'});toast('OK');RR.contacts()}catch(e){toast(e.message,1)}}
function oCtc(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Contact</h3><div class="row2"><div class="field"><label>Prénom</label><input id="cF"></div><div class="field"><label>Nom</label><input id="cL"></div></div><div class="field"><label>Email</label><input id="cE"></div><div class="field"><label>Tél</label><input id="cP"></div><div class="row2"><div class="field"><label>Entreprise</label><input id="cCo"></div><div class="field"><label>Pays</label><input id="cCy"></div></div><div class="field"><label>Tags</label><input id="cTg" placeholder="vip, prospect"></div><div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">×</button><button class="btn btn-ac" onclick="aCtc()">Ajouter</button></div></div>`;document.body.appendChild(m)}
async function aCtc(){try{await api('/contacts',{method:'POST',body:JSON.stringify({email:v('cE'),first_name:v('cF'),last_name:v('cL'),company:v('cCo'),phone:v('cP'),country:v('cCy'),tags:v('cTg').split(',').map(t=>t.trim()).filter(Boolean)})});document.querySelector('.modal-bg')?.remove();toast('Ajouté');RR.contacts()}catch(e){toast(e.message,1)}}
// =============== SENDERS ===============
RR.senders=async()=>{let d={};try{d=await brg('senders_full')}catch(e){}const pv=d.providers||[];const os=d.o365_status||[];const gs=d.gsuite_workspaces||[];const sm=d.smtp_configs||[];const wt=d.warmup_by_type||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Total Senders</div><div class="st-v">${N(d.total_senders)}</div><div class="st-s">22 providers</div></div><div class="stat s-gn"><div class="st-l">Capacité/jour</div><div class="st-v">${N(d.total_capacity)}</div></div><div class="stat s-bl"><div class="st-l">SMTP Configs</div><div class="st-v">${sm.length}</div></div><div class="stat s-pu"><div class="st-l">GSuite WS</div><div class="st-v">${gs.length}</div></div></div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:12px">Senders par Provider</div><table><thead><tr><th>Provider</th><th>Comptes</th><th>Capacité/jour</th></tr></thead><tbody>${pv.map(p=>`<tr><td style="color:var(--wh);font-weight:600">${esc(p.provider)}</td><td style="font-family:var(--m)">${N(p.cnt)}</td><td style="font-family:var(--m);color:var(--gn)">${N(p.capacity)}</td></tr>`).join('')}</tbody></table></div>
<div><div class="card"><div class="card-t" style="margin-bottom:10px">O365 Status</div>${os.map(s=>`<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">${esc(s.status)}</span><span style="font-family:var(--m);font-size:12px;font-weight:700;color:var(--ac)">${N(s.cnt)}</span></div>`).join('')}</div>
<div class="card"><div class="card-t" style="margin-bottom:10px">GSuite Workspaces</div>${gs.map(g=>`<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-family:var(--m);font-size:10px;color:var(--wh)">${esc(g.domain)}</span><span style="font-size:10px;color:var(--mu)">${g.users_count} users</span><span class="badge b-ok">${esc(g.method)}</span></div>`).join('')||'<div style="color:var(--dm);font-size:11px">—</div>'}</div>
<div class="card"><div class="card-t" style="margin-bottom:10px">SMTP Configs (${sm.length})</div>${sm.slice(0,8).map(s=>`<div style="display:flex;justify-content:space-between;padding:4px 0;border-bottom:1px solid var(--bd)"><span style="font-size:10px;color:var(--wh)">${esc(s.config_name)}</span><span style="font-family:var(--m);font-size:9px;color:var(--mu)">${esc(s.host)}:${s.port}</span></div>`).join('')}</div></div></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Warmup par Type</div><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px">${wt.map(w=>`<div style="padding:10px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:10px;color:var(--mu)">${esc(w.account_type)}</div><div style="font-family:var(--m);font-size:16px;font-weight:800;color:var(--wh)">${N(w.cnt)}</div><div style="font-size:9px;color:var(--gn)">${N(w.cap)}/day</div></div>`).join('')}</div></div>`};
// =============== WARMUP ===============
RR.warmup=async()=>{let d={};try{d=await brg('warmup')}catch(e){}const bt=d.by_type||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Total</div><div class="st-v">${N(d.total)}</div></div><div class="stat s-gn"><div class="st-l">Actifs</div><div class="st-v">${d.active||0}</div></div><div class="stat s-bl"><div class="st-l">Ready</div><div class="st-v">${d.ready||0}</div></div></div>
<div class="card"><div class="card-t" style="margin-bottom:12px">Par type de compte</div><table><thead><tr><th>Type</th><th>Comptes</th><th>Capacité/jour</th><th>Envoyés today</th></tr></thead><tbody>${bt.map(t=>`<tr><td style="color:var(--wh);font-weight:600">${esc(t.account_type||'—')}</td><td style="font-family:var(--m)">${N(t.cnt)}</td><td style="font-family:var(--m);color:var(--gn)">${N(t.capacity)}</td><td style="font-family:var(--m);color:var(--ac)">${N(t.sent)}</td></tr>`).join('')||'<tr><td colspan="4" style="text-align:center;color:var(--dm)">—</td></tr>'}</tbody></table></div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Pipeline Warmup</div><p style="font-size:12px;color:var(--mu);line-height:1.7">Escalade progressive: Phase 1 (10/jour) → Phase 2 (50/jour) → Phase 3 (200/jour) → Phase 4 (500/jour). Validation taux inbox entre chaque palier.</p></div>`};
// =============== DOMAINS ===============
RR.domains=async()=>{let d={};try{d=await brg('domains')}catch(e){}const doms=d.domains||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Domaines</div><div class="st-v">${d.total||0}</div></div><div class="stat s-gn"><div class="st-l">Activés</div><div class="st-v">${doms.filter(x=>x.status==='Activated').length}</div></div></div>
<div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Domaine</th><th>Statut</th><th>Dispo</th><th>Compte</th><th>Brand</th></tr></thead><tbody>${doms.map(d=>`<tr><td style="font-family:var(--m);color:var(--wh);font-weight:600">${esc(d.name)}</td><td><span class="badge b-${d.status==='Activated'?'ok':'wait'}">${d.status}</span></td><td>${esc(d.availability||'')}</td><td style="font-size:10px;color:var(--mu)">${esc(d.account_name||'')}</td><td>${d.has_brand==='Yes'?'✅':'—'}</td></tr>`).join('')}</tbody></table></div>`};
// =============== SEEDS ===============
RR.seeds=async()=>{let d={};try{d=await brg('seeds')}catch(e){}
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Seed Accounts</div><div class="st-v">${N(d.accounts)}</div></div><div class="stat s-bl"><div class="st-l">Inboxes</div><div class="st-v">${N(d.inboxes)}</div></div><div class="stat s-gn"><div class="st-l">Résultats</div><div class="st-v">${N(d.results)}</div></div></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Inbox Testing</div><p style="font-size:12px;color:var(--mu);line-height:1.7">Les seeds testent le placement inbox avant envoi réel. Chaque seed email vérifie inbox vs spam chez Gmail, Outlook, Yahoo, T-Online, GMX.</p><div style="margin-top:12px;display:flex;gap:6px"><button class="btn btn-sm btn-ac" onclick="toast('Seed test lancé')">Lancer test</button><button class="btn btn-sm btn-gh" onclick="toast('Check...')">Vérifier</button></div></div>`};
// =============== REPUTATION ===============
RR.reputation=async()=>{let d={};try{d=await brg('reputation')}catch(e){}
$('C').innerHTML=`<div class="stats"><div class="stat s-rd"><div class="st-l">Bounces</div><div class="st-v">${d.bounces||0}</div></div><div class="stat s-ac"><div class="st-l">Bounce Log</div><div class="st-v">${d.bounce_log||0}</div></div><div class="stat s-bl"><div class="st-l">Blacklisted</div><div class="st-v">${d.blacklisted||0}</div></div></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Blacklist Check</div><div style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px">${['Spamhaus','Barracuda','SORBS','UCEPROTECT'].map(n=>`<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);text-align:center"><div style="font-size:9px;color:var(--mu)">${n}</div><div style="font-size:14px;font-weight:700;color:var(--gn);margin-top:4px">Clean</div></div>`).join('')}</div></div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Monitoring</div><p style="font-size:12px;color:var(--mu);line-height:1.7">Surveillance bounces, blacklists, plaintes. Le Brain Engine ajuste volumes et IPs en fonction du score réputation.</p></div>`};
// =============== BRAIN IA ===============
RR.brain=async()=>{let d={};try{d=await brg('brain')}catch(e){}const mt=d.methods||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Configs</div><div class="st-v">${N(d.configs)}</div></div><div class="stat s-gn"><div class="st-l">Winners</div><div class="st-v">${d.winners||0}</div></div><div class="stat s-pu"><div class="st-l">Learnings</div><div class="st-v">${N(d.learnings)}</div></div><div class="stat s-bl"><div class="st-l">IA Engine</div><div class="st-v" style="font-size:16px">LIVE</div><div class="st-s">Sovereign GPU</div></div></div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Send Methods (Hamid)</div><table><thead><tr><th>Méthode</th><th>Description</th><th>Best ISPs</th></tr></thead><tbody>${mt.map(m=>`<tr><td style="color:var(--wh);font-weight:600">${esc(m.method_name)}</td><td style="font-size:11px;color:var(--mu)">${esc(m.description)}</td><td style="font-family:var(--m);font-size:10px;color:var(--ac)">${esc(m.best_for_isps||'')}</td></tr>`).join('')}</tbody></table></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Stack IA</div>${[['vLLM GPU','S95:8000','qwen2.5-14B'],['Groq','API','llama-3.3-70b'],['Cerebras','API','qwen-3-235b'],['Ollama S204','CPU','9 modèles'],['Qdrant RAG','S95:6333','95 memcells']].map(([n,h,d])=>`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">${n}</span><span style="font-family:var(--m);font-size:9px;color:var(--mu)">${h}</span><span style="font-size:10px;color:var(--ac)">${d}</span></div>`).join('')}</div></div>`};
// =============== CREATIVE ===============
RR.creative=async()=>{let d={};try{d=await brg('creative')}catch(e){}const subs=d.top_subjects||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Performances</div><div class="st-v">${N(d.performance)}</div></div><div class="stat s-bl"><div class="st-l">Traductions</div><div class="st-v">${d.translations||0}</div></div></div>
<div class="card"><div class="card-h"><div class="card-t">Creative Engine IA</div><button class="btn btn-sm btn-ac" onclick="toast('Génération...')">🧠 Générer</button></div><p style="font-size:12px;color:var(--mu);margin-bottom:14px">Analyse performance sujets/from/HTML. Génération variantes IA. Promotion winners automatique.</p>
${subs.length?`<table><thead><tr><th>Sujet</th><th>Open%</th><th>Click%</th></tr></thead><tbody>${subs.map(s=>`<tr><td style="color:var(--wh)">${esc((s.subject||'').substring(0,45))}</td><td style="font-family:var(--m);color:var(--gn)">${s.open_rate||0}%</td><td style="font-family:var(--m);color:var(--ac)">${s.click_rate||0}%</td></tr>`).join('')}</tbody></table>`:''}</div>`};
// =============== ANALYTICS ===============
RR.analytics=async()=>{let t={},d={};try{t=(await eng('tracking_stats')).stats||{}}catch(e){}try{d=await eng('deliverability')}catch(e){}const isps=(d.by_isp||[]).slice(0,10);
$('C').innerHTML=`<div class="stats"><div class="stat s-gn"><div class="st-l">Opens</div><div class="st-v">${N(t.total_opens)}</div></div><div class="stat s-bl"><div class="st-l">Clicks</div><div class="st-v">${N(t.total_clicks)}</div></div><div class="stat s-rd"><div class="st-l">Bounces</div><div class="st-v">${t.total_bounces||0}</div></div><div class="stat s-pu"><div class="st-l">Events</div><div class="st-v">${N(t.tracking_events)}</div></div></div>
<div class="card"><div class="card-t" style="margin-bottom:12px">Délivrabilité par ISP</div><table><thead><tr><th>ISP</th><th>Envoyés</th><th>Délivrés</th><th>Failed</th><th>Taux</th></tr></thead><tbody>${isps.map(i=>{const r=i.total>0?((i.delivered/i.total)*100).toFixed(1):'0';return`<tr><td style="color:var(--wh);font-weight:600;text-transform:uppercase">${esc(i.isp)}</td><td style="font-family:var(--m)">${N(i.total)}</td><td style="font-family:var(--m);color:var(--gn)">${N(i.delivered)}</td><td style="font-family:var(--m);color:var(--rd)">${i.failed||0}</td><td><span style="font-family:var(--m);font-weight:700;color:${+r>98?'var(--gn)':+r>95?'var(--ac)':'var(--or)'}">${r}%</span></td></tr>`}).join('')||'<tr><td colspan="5" style="text-align:center;color:var(--dm)">—</td></tr>'}</tbody></table></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Événements récents</div><table><thead><tr><th>ID</th><th>Type</th><th>IP</th><th>Date</th></tr></thead><tbody>${((await eng('tracking_stats').catch(()=>({}))).recent||[]).slice(0,8).map(e=>`<tr><td style="font-family:var(--m);font-size:10px;color:var(--ac)">${esc((e.tracking_id||'').substring(0,22))}</td><td><span class="badge b-${e.event_type==='open'?'ok':'em'}">${e.event_type}</span></td><td style="font-family:var(--m);font-size:10px;color:var(--mu)">${esc(e.ip_address||'')}</td><td style="font-size:10px;color:var(--dm)">${(e.created_at||'').substring(0,19)}</td></tr>`).join('')}</tbody></table></div>`};
// =============== AI ASSISTANT ===============
let chatH=[];
RR.ai=()=>{$('C').innerHTML=`<div class="chat-w"><div class="chat-m" id="aM"><div class="chat-e"><div style="font-size:36px">🧠</div><h3>WEVIA AI</h3><p style="font-size:12px;margin-top:6px;color:var(--mu)">Optimisez sujets, générez contenu, analysez délivrabilité.</p><div style="display:flex;gap:6px;justify-content:center;margin-top:12px;flex-wrap:wrap"><button class="btn btn-sm btn-gh" onclick="aQ('Optimise: Découvrez nos nouveautés')">Sujet</button><button class="btn btn-sm btn-gh" onclick="aQ('Email B2B pharma')">Email</button><button class="btn btn-sm btn-gh" onclick="aQ('Audit délivrabilité')">Audit</button></div></div></div><div class="chat-b"><textarea id="aI" rows="1" placeholder="Message..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();aS()}" oninput="this.style.height='42px';this.style.height=Math.min(this.scrollHeight,110)+'px'"></textarea><button class="btn btn-sm btn-ac" onclick="aS()">→</button></div></div>`};
function aQ(q){$('aI').value=q;aS()}
async function aS(){const msg=v('aI');if(!msg)return;const b=$('aM');b.querySelector('.chat-e')?.remove();b.innerHTML+=`<div class="chat-u">${esc(msg)}</div>`;$('aI').value='';const lid='l'+Date.now();b.innerHTML+=`<div class="chat-a" id="${lid}"><span class="ldot"></span><span class="ldot"></span><span class="ldot"></span></div>`;b.scrollTop=b.scrollHeight;try{chatH.push({role:'user',content:msg});const r=await(await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,mode:'full',history:chatH.slice(-6)})})).json();const re=r.response||r.error||'Err';chatH.push({role:'assistant',content:re});$(lid).outerHTML=`<div class="chat-a">${md2h(re)}</div>`}catch(e){$(lid).outerHTML=`<div class="chat-a" style="color:var(--rd)">Err: ${e.message}</div>`}b.scrollTop=b.scrollHeight}
// =============== CHANNELS ===============
RR.channels=async()=>{let se={};try{se=await(await fetch(SE+'?action=status&token=ETHICA_API_2026_SECURE')).json()}catch(e){}const inf=se.infrastructure||{};const o=inf.o365_senders||{};
$('C').innerHTML=`<div class="stats"><div class="stat s-bl"><div class="st-l">Email</div><div class="st-v" style="font-size:16px;color:var(--em)">LIVE</div><div class="st-s">PMTA+${o.active||0} O365</div></div><div class="stat s-tg"><div class="st-l">Telegram</div><div class="st-v" style="font-size:16px;color:var(--tg)">LIVE</div></div><div class="stat s-gn"><div class="st-l">SMS</div><div class="st-v" style="font-size:16px;color:var(--or)">PENDING</div></div><div class="stat s-or"><div class="st-l">WhatsApp</div><div class="st-v" style="font-size:16px;color:var(--or)">PENDING</div></div></div>
<div class="card"><div class="card-h"><div class="card-t">✈ Telegram</div><span class="badge b-ok">Connecté</span></div><p style="font-size:12px;color:var(--mu);margin-bottom:10px">@wevads_alerts_bot → Yacine</p><button class="btn btn-sm btn-gh" style="color:var(--tg);border-color:rgba(34,158,217,.2)" onclick="tgTest()">Test Telegram</button></div>
<div class="card"><div class="card-h"><div class="card-t">💬 SMS OVH</div><span class="badge b-wait">Credentials</span></div><div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px"><div class="field"><label>App Key</label><input id="sk1"></div><div class="field"><label>Secret</label><input id="sk2"></div><div class="field"><label>Consumer</label><input id="sk3"></div></div><button class="btn btn-sm btn-gh" style="color:var(--sm)" onclick="cfgSMS()">Configurer</button></div>
<div class="card"><div class="card-h"><div class="card-t">📱 WhatsApp</div><span class="badge b-wait">Credentials</span></div><div class="row2"><div class="field"><label>Phone ID</label><input id="wk1"></div><div class="field"><label>Token</label><input id="wk2"></div></div><button class="btn btn-sm btn-gh" style="color:var(--wa)" onclick="cfgWA()">Configurer</button></div>`};
async function tgTest(){toast('Envoi TG...');try{const r=await(await fetch('https://api.telegram.org/bot8544624912:AAEm9ttXK6JeFqAL-gcvB5sreCBhXzzQwrs/sendMessage',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chat_id:'7605775322',text:'✅ WEVADS IA v3.0 Test\n'+new Date().toLocaleString('fr-FR')})})).json();toast(r.ok?'TG OK':'Err',!r.ok)}catch(e){toast('Err',1)}}
async function cfgSMS(){toast('Config SMS...')}
async function cfgWA(){toast('Config WA...')}
// =============== TEMPLATES ===============
let TPL=[];
RR.templates=async()=>{try{TPL=(await api('/templates/list')).items||[]}catch(e){TPL=[]}
$('C').innerHTML=`<div class="toolbar"><div class="sp"></div><button class="btn btn-sm btn-ac" onclick="oTpl()">+ Nouveau</button></div><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:14px" id="tG"></div>`;
$('tG').innerHTML=TPL.map(t=>`<div class="card" style="cursor:pointer"><div style="display:flex;justify-content:space-between;margin-bottom:8px"><span style="font-weight:700;color:var(--wh)">${esc(t.name)}</span><span class="badge b-ok">${esc(t.category)}</span></div><div style="background:var(--bg);border:1px solid var(--bd);border-radius:var(--r3);padding:10px;min-height:50px;font-size:10px;color:var(--mu);overflow:hidden;max-height:70px">${t.html?t.html.substring(0,120)+'...':'—'}</div></div>`).join('')||'<div style="text-align:center;color:var(--dm);padding:30px;grid-column:1/-1">—</div>'};
function oTpl(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Template</h3><div class="field"><label>Nom</label><input id="tN"></div><div class="field"><label>Cat</label><select id="tC"><option>marketing</option><option>transactional</option><option>newsletter</option></select></div><div class="field"><label>HTML</label><textarea class="ed" id="tH" rows="6"></textarea></div><div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">×</button><button class="btn btn-ac" onclick="sTpl()">Save</button></div></div>`;document.body.appendChild(m)}
async function sTpl(){try{await api('/templates',{method:'POST',body:JSON.stringify({name:v('tN'),category:v('tC'),html:v('tH')})});document.querySelector('.modal-bg')?.remove();toast('Créé');RR.templates()}catch(e){toast(e.message,1)}}
// =============== SEND ENGINE ===============
RR.sendengine=async()=>{let se={},k={},w={};try{se=await(await fetch(SE+'?action=status&token=ETHICA_API_2026_SECURE')).json()}catch(e){}try{k=(await eng('dashboard')).kpis||{}}catch(e){}try{w=await eng('warmup_status')}catch(e){}const inf=se.infrastructure||{};const o=inf.o365_senders||{};const pm=inf.pmta||{};const cl=se.clients||{};
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Méthode</div><div class="st-v" style="font-size:14px">${(inf.method||'—').toUpperCase()}</div></div><div class="stat s-bl"><div class="st-l">O365</div><div class="st-v">${o.active||0}/${o.total||0}</div></div><div class="stat s-gn"><div class="st-l">PMTA</div><div class="st-v">${pm.servers_alive||0}/${pm.servers_configured||0}</div></div><div class="stat s-pu"><div class="st-l">Warmup</div><div class="st-v">${N(w.total_accounts||k.warmup_accounts)}</div></div><div class="stat s-or"><div class="st-l">Queue</div><div class="st-v">${k.queue_pending||0}</div></div></div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Clients</div>${[['Ethica (Pharma)',cl.ethica?.contacts,cl.ethica?.emails,'var(--ac)'],['WEVADS (Multi)',cl.wevads?.contacts,k.senders_active,'var(--cy)']].map(([n,c,e,col])=>`<div style="padding:12px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r);margin-bottom:8px"><div style="font-size:11px;font-weight:600;color:${col}">${n}</div><div style="font-family:var(--m);font-size:18px;font-weight:800;color:var(--wh)">${N(c||0)}</div><div style="font-size:10px;color:var(--mu)">${N(e||0)} active</div></div>`).join('')}</div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Métriques</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">${[['Envoyés',k.emails_sent_total,'var(--wh)'],['Today',k.emails_sent_today,'var(--gn)'],['Bounces',k.bounces,'var(--rd)'],['Methods',k.send_methods,'var(--cy)']].map(([l,v2,c])=>`<div style="text-align:center;padding:10px;background:var(--sf);border:1px solid var(--bd);border-radius:var(--r)"><div style="font-size:16px;font-weight:800;color:${c};font-family:var(--m)">${N(v2||0)}</div><div style="font-size:9px;color:var(--mu);margin-top:3px">${l}</div></div>`).join('')}</div></div></div>`};
// =============== OFFERS ===============
RR.offers=async()=>{let d={};try{d=await brg('offers')}catch(e){}const off=d.offers||[];const sp=d.sponsors||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Offres</div><div class="st-v">${N(d.total_offers)}</div></div><div class="stat s-gn"><div class="st-l">Sponsors</div><div class="st-v">${sp.length}</div></div><div class="stat s-bl"><div class="st-l">Networks</div><div class="st-v">${(d.networks||[]).length}</div></div></div>
<div class="card"><div class="card-h"><div class="card-t">Catalogue Offres (${N(d.total_offers)})</div><button class="btn btn-sm btn-ac" onclick="toast('Import depuis CX3/Double M')">+ Importer</button></div>
<table><thead><tr><th>Offre</th><th>Pays</th><th>Vertical</th><th>Payout</th><th>Statut</th><th>Clicks</th><th>Conv.</th></tr></thead>
<tbody>${off.slice(0,20).map(o=>\`<tr><td style="color:var(--wh);font-weight:500;font-size:11px">\${esc((o.offer_name||'').substring(0,45))}</td><td><span class="badge b-em">\${esc(o.country_code)}</span></td><td style="font-size:11px;color:var(--mu)">\${esc(o.vertical)}</td><td style="font-family:var(--m);color:var(--gn)">\${o.payout>0?'$'+o.payout:'—'}</td><td><span class="badge b-\${o.status==='active'?'ok':o.status==='public'?'ac':'wait'}">\${esc(o.status)}</span></td><td style="font-family:var(--m)">\${N(o.clicks)}</td><td style="font-family:var(--m);color:var(--ac)">\${N(o.conversions)}</td></tr>\`).join('')}</tbody></table></div>`;
};
// =============== SPONSORS ===============
RR.sponsors=async()=>{let d={};try{d=await brg('offers')}catch(e){}const sp=d.sponsors||[];const nw=d.networks||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Sponsors</div><div class="st-v">${sp.length}</div></div><div class="stat s-gn"><div class="st-l">Networks</div><div class="st-v">${nw.length}</div></div><div class="stat s-bl"><div class="st-l">Offres</div><div class="st-v">${N(d.total_offers)}</div></div></div>
<div class="row2"><div class="card"><div class="card-h"><div class="card-t">Sponsors</div><button class="btn btn-sm btn-ac" onclick="oSpon()">+ Ajouter</button></div>
<table><thead><tr><th>Nom</th><th>Type</th><th>Code</th><th>Volume/mois</th><th>Contact</th><th>Actif</th></tr></thead>
<tbody>${sp.map(s=>\`<tr><td style="color:var(--wh);font-weight:600">\${esc(s.name)}</td><td><span class="badge b-\${s.type==='b2b_sponsor'?'ac':'em'}">\${esc(s.type)}</span></td><td style="font-family:var(--m);font-size:10px">\${esc(s.sponsor_code||'—')}</td><td style="font-family:var(--m)">\${N(s.monthly_volume)}</td><td style="font-size:10px;color:var(--mu)">\${esc(s.contact_email||'—')}</td><td>\${s.is_active?'✅':'❌'}</td></tr>\`).join('')}</tbody></table></div>
<div><div class="card"><div class="card-t" style="margin-bottom:10px">Networks</div>${nw.map(n=>\`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="color:var(--wh)">\${esc(n.name)}</span><span class="badge b-\${n.status==='active'?'ok':'wait'}">\${n.status}</span></div>\`).join('')||'<div style="color:var(--dm)">—</div>'}</div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Multi-client</div><p style="font-size:11px;color:var(--mu);line-height:1.6">Chaque sponsor a son propre pool d'offres, budget mensuel, et tracking séparé. Les offres sont attribuées par vertical et pays.</p>
<div style="margin-top:10px;display:grid;gap:5px"><button class="btn btn-sm btn-ac btn-full" onclick="oSpon()">Ajouter Sponsor</button><button class="btn btn-sm btn-gh btn-full" onclick="toast('Import réseau')">Importer Network</button></div></div></div></div>`;
};
function oSpon(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};
m.innerHTML=\`<div class="modal"><h3>Nouveau Sponsor</h3>
<div class="field"><label>Nom</label><input id="spN" placeholder="Nom du sponsor"></div>
<div class="row2"><div class="field"><label>Type</label><select id="spT"><option value="external">External</option><option value="b2b_sponsor">B2B Sponsor</option><option value="affiliate">Affiliate</option></select></div><div class="field"><label>Code</label><input id="spC" placeholder="ethica"></div></div>
<div class="field"><label>Email contact</label><input id="spE" placeholder="contact@sponsor.com"></div>
<div class="row2"><div class="field"><label>Volume/mois</label><input type="number" id="spV" value="20000"></div><div class="field"><label>Budget mensuel</label><input type="number" id="spB" value="0"></div></div>
<div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">Annuler</button><button class="btn btn-ac" onclick="document.querySelector('.modal-bg')?.remove();toast('Sponsor ajouté')">Créer</button></div></div>\`;
document.body.appendChild(m)}
// =============== SERVERS ===============
RR.servers=async()=>{let d={};try{d=await brg('servers')}catch(e){}const mgmt=d.management_servers||[];const prov=d.providers||[];const cloud=d.cloud_accounts||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">MTA Servers</div><div class="st-v">${d.mta_servers||0}</div></div><div class="stat s-gn"><div class="st-l">PMTA Configs</div><div class="st-v">${d.pmta_configs||0}</div></div><div class="stat s-bl"><div class="st-l">VMTAs</div><div class="st-v">${d.vmtas||0}</div></div><div class="stat s-pu"><div class="st-l">SMTP Servers</div><div class="st-v">${d.smtp_servers||0}</div></div></div>
<div class="row2"><div class="card"><div class="card-h"><div class="card-t">Management Servers</div><button class="btn btn-sm btn-ac" onclick="oSrv()">+ Nouveau</button></div>
<table><thead><tr><th>Nom</th><th>IP</th><th>Provider</th><th>Type</th><th>SSH</th><th>Statut</th></tr></thead>
<tbody>${mgmt.map(s=>\`<tr><td style="color:var(--wh);font-weight:600">\${esc(s.name)}</td><td style="font-family:var(--m);font-size:10px">\${esc(s.main_ip)}</td><td>\${esc(s.provider_name)}</td><td><span class="badge b-em">\${esc(s.server_type||'mta')}</span></td><td style="font-family:var(--m);font-size:9px">\${esc(s.ssh_username)}@:\${s.ssh_port}</td><td><span class="badge b-\${s.status==='Activated'?'ok':'wait'}">\${s.status}</span></td></tr>\`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--dm);padding:20px">Aucun serveur</td></tr>'}</tbody></table></div>
<div><div class="card"><div class="card-t" style="margin-bottom:10px">Cloud Providers</div>${prov.map(p=>\`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--bd)"><span style="color:var(--wh);font-size:12px">\${esc(p.name)}</span><span class="badge b-\${p.status==='Activated'?'ok':'wait'}">\${p.status}</span></div>\`).join('')}</div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Comptes Cloud</div>${cloud.map(c=>\`<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">\${esc(c.provider)}</span><span style="font-family:var(--m);font-size:12px;font-weight:700;color:var(--ac)">\${c.count}</span></div>\`).join('')}</div>
<div class="card"><div class="card-t" style="margin-bottom:8px">Créer serveur</div><p style="font-size:11px;color:var(--mu);line-height:1.6;margin-bottom:10px">Provisionner un nouveau VPS avec PMTA pré-installé. Choisir le provider, la région, et le type d'installation.</p>
<div style="display:grid;gap:6px"><button class="btn btn-sm btn-ac btn-full" onclick="oSrv()">Huawei Cloud ECS</button><button class="btn btn-sm btn-gh btn-full" onclick="oSrv()">Hetzner Cloud</button><button class="btn btn-sm btn-gh btn-full" onclick="oSrv()">Digital Ocean</button><button class="btn btn-sm btn-gh btn-full" onclick="oSrv()">Scaleway</button></div></div></div></div>`;
};
function oSrv(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};
m.innerHTML=\`<div class="modal"><h3>Nouveau Serveur MTA</h3>
<div class="field"><label>Provider</label><select id="svP"><option value="huawei">Huawei Cloud ECS</option><option value="hetzner">Hetzner Cloud</option><option value="digitalocean">Digital Ocean</option><option value="scaleway">Scaleway</option><option value="linode">Linode</option><option value="custom">Custom (SSH)</option></select></div>
<div class="field"><label>Nom</label><input id="svN" placeholder="MTA-EU-01"></div>
<div class="row2"><div class="field"><label>IP / Hostname</label><input id="svIP" placeholder="xxx.xxx.xxx.xxx"></div><div class="field"><label>SSH Port</label><input id="svPort" value="22"></div></div>
<div class="row2"><div class="field"><label>SSH User</label><input id="svUser" value="root"></div><div class="field"><label>SSH Password</label><input type="password" id="svPass"></div></div>
<div class="field"><label>Installation</label><select id="svInst"><option value="pmta">PMTA v5.0r3 + Domain</option><option value="pmta6">PMTA v6.0r4</option><option value="kumo">KumoMTA (OSS)</option><option value="postfix">Postfix MTA</option><option value="none">Aucune (SSH seul)</option></select></div>
<div class="field"><label>Domaine(s) associé(s)</label><input id="svDom" placeholder="send.wevads.com, mail.weval.ma"></div>
<div class="brow"><button class="btn btn-gh" onclick="this.closest('.modal-bg').remove()">Annuler</button><button class="btn btn-ac" onclick="createSrv()">Créer & Installer</button></div></div>\`;
document.body.appendChild(m)}
async function createSrv(){toast('Création serveur en cours... (CX relay)');document.querySelector('.modal-bg')?.remove();
// This would trigger via CX relay to S95 for actual server provisioning
toast('Serveur ajouté — installation PMTA en cours via Droid',false)}
// =============== INFRA ===============
RR.infra=async()=>{let d={};try{d=await brg('infra')}catch(e){}const sc=d.schemas||[];
$('C').innerHTML=`<div class="stats"><div class="stat s-ac"><div class="st-l">Tables</div><div class="st-v">${d.tables||0}</div></div><div class="stat s-bl"><div class="st-l">DB Size</div><div class="st-v" style="font-size:16px">${d.db_size||'?'}</div></div><div class="stat s-gn"><div class="st-l">APIs</div><div class="st-v">344</div></div><div class="stat s-pu"><div class="st-l">GOLD</div><div class="st-v">1,411</div></div></div>
<div class="row2"><div class="card"><div class="card-t" style="margin-bottom:10px">Schemas PostgreSQL</div><table><thead><tr><th>Schema</th><th>Tables</th></tr></thead><tbody>${sc.map(s=>`<tr><td style="color:var(--wh);font-weight:600">${esc(s.schemaname)}</td><td style="font-family:var(--m)">${s.cnt}</td></tr>`).join('')}</tbody></table></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Services</div>${[['S95 Apache','ADX 5821+BCG 5823+FMG 5822','ok'],['S95 PMTA v5.0r3','Port 25','ok'],['S95 Postfix','2525/2526','ok'],['S204 Nginx','HTTPS proxy','ok'],['Node.js V2','Port 5850','ok'],['S151 Tracking','HTTP relay','ok'],['Telegram Bot','@wevads_alerts_bot','ok'],['Qdrant RAG','S95:6333','ok'],['n8n Workflows','S95:5678','ok']].map(([n,d,s])=>`<div style="display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--bd)"><span style="font-size:11px;color:var(--wh)">${n}</span><span style="font-family:var(--m);font-size:9px;color:var(--mu)">${d}</span><span class="badge b-${s}">${s}</span></div>`).join('')}</div></div>`};
// =============== SETTINGS ===============
RR.settings=()=>{$('C').innerHTML=`<div class="card"><div class="card-t" style="margin-bottom:14px">Profil</div><div class="row2"><div class="field"><label>Nom</label><input value="${esc(U.name||'')}"></div><div class="field"><label>Email</label><input value="${esc(U.email||'')}" readonly style="opacity:.5"></div></div><button class="btn btn-sm btn-ac" style="margin-top:10px" onclick="toast('OK')">Save</button></div>
<div class="card"><div class="card-t" style="margin-bottom:10px">Auth DNS</div><table><tbody>${[['SPF','v=spf1 include:spf.pmta.weval ~all'],['DKIM','weval2026._domainkey'],['DMARC','p=quarantine'],['TLS','1.3 Enforced']].map(([k,v2])=>`<tr><td style="color:var(--wh)">${k}</td><td style="font-family:var(--m);font-size:11px;color:var(--mu)">${v2}</td></tr>`).join('')}</tbody></table></div>`};
// =============== INIT ===============
try{const st=sessionStorage.getItem('wt'),su=sessionStorage.getItem('wu');if(st&&su){TK=st;U=JSON.parse(su);enter()}}catch(e){}
fetch(API+'/health').then(r=>r.json()).then(j=>{if(j.status==='ok')$('TL').innerHTML='● Live — '+j.version}).catch(()=>{});
</script></body></html>