Files
weval-consulting/api/XTQQ

652 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 v2 — 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:#06080f;--bg2:#0a0e1a;--surface:#0f1525;--card:#111b2e;--hover:#152038;
--border:rgba(255,255,255,.06);--border2:rgba(255,255,255,.1);
--text:#b8c5d8;--muted:#6b7a90;--dim:#3d4b5e;--white:#eef2f7;
--accent:#c9a84c;--accent2:#e8c65a;--accentBg:rgba(201,168,76,.06);--accentBorder:rgba(201,168,76,.2);
--cyan:#22d3ee;--cyanBg:rgba(34,211,238,.06);
--green:#34d399;--greenBg:rgba(52,211,153,.06);
--red:#f87171;--redBg:rgba(248,113,113,.06);
--orange:#fb923c;--orangeBg:rgba(251,146,60,.06);
--purple:#a78bfa;--purpleBg:rgba(167,139,250,.06);
--blue:#60a5fa;--blueBg:rgba(96,165,250,.06);
--email:#60a5fa;--sms:#34d399;--whatsapp:#25d366;--telegram:#229ED9;
--r:12px;--r2:16px;--r3:8px;
--font:'Outfit',system-ui,sans-serif;--mono:'JetBrains Mono',monospace;
--glow:0 0 40px rgba(201,168,76,.08);
}
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:var(--font);background:var(--bg);color:var(--text);display:flex;height:100vh;overflow:hidden;font-size:14px;-webkit-font-smoothing:antialiased}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:var(--border2);border-radius:2px}::-webkit-scrollbar-track{background:transparent}
a{color:var(--accent);text-decoration:none}
button{font-family:var(--font);cursor:pointer;border:none;outline:none}
input,select,textarea{font-family:var(--font);outline:none;background:var(--bg);border:1px solid var(--border2);color:var(--white);border-radius:var(--r3);padding:10px 14px;font-size:13px;transition:border .2s,box-shadow .2s}
input:focus,select:focus,textarea:focus{border-color:var(--accent);box-shadow:0 0 0 3px rgba(201,168,76,.08)}
table{width:100%;border-collapse:collapse}
th{text-align:left;font-size:10px;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;padding:10px 14px;border-bottom:1px solid var(--border)}
td{padding:11px 14px;border-bottom:1px solid var(--border);font-size:13px}
tr:hover td{background:rgba(255,255,255,.02)}
@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
@keyframes slideIn{from{opacity:0;transform:translateX(-8px)}to{opacity:1;transform:translateX(0)}}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.4}}
@keyframes glow{0%,100%{box-shadow:0 0 20px rgba(201,168,76,.1)}50%{box-shadow:0 0 40px rgba(201,168,76,.2)}}
@keyframes spin{to{transform:rotate(360deg)}}
@keyframes gradientShift{0%{background-position:0% 50%}50%{background-position:100% 50%}100%{background-position:0% 50%}}
/* ===== LOGIN ===== */
.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(201,168,76,.04),transparent);pointer-events:none}
.login-box{background:var(--card);border:1px solid var(--border2);border-radius:24px;padding:48px;width:440px;max-width:92vw;animation:fadeIn .5s ease;position:relative;overflow:hidden}
.login-box::before{content:'';position:absolute;top:0;left:0;right:0;height:2px;background:linear-gradient(90deg,transparent,var(--accent),transparent)}
.login-logo{width:52px;height:52px;background:linear-gradient(135deg,var(--accent),var(--accent2));border-radius:14px;display:flex;align-items:center;justify-content:center;margin:0 auto 20px;font-weight:900;color:#0a0d13;font-size:22px;animation:glow 3s infinite}
.login-box h1{font-size:24px;font-weight:800;color:var(--white);text-align:center;letter-spacing:-.03em}
.login-box .sub{font-size:13px;color:var(--muted);text-align:center;margin:4px 0 6px}
.login-box .ver{font-size:10px;color:var(--dim);text-align:center;margin-bottom:24px;font-family:var(--mono)}
.login-box .channels{display:flex;justify-content:center;gap:12px;margin-bottom:24px}
.login-box .ch{width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:14px;border:1px solid var(--border2)}
.ch-email{background:rgba(96,165,250,.1);color:var(--email)}.ch-sms{background:rgba(52,211,153,.1);color:var(--sms)}.ch-wa{background:rgba(37,211,102,.1);color:var(--whatsapp)}.ch-tg{background:rgba(34,158,217,.1);color:var(--telegram)}
.field{margin-bottom:14px}
.field label{display:block;font-size:11px;font-weight:600;color:var(--muted);margin-bottom:5px;text-transform:uppercase;letter-spacing:.06em}
.field input{width:100%}
.btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:11px 22px;border-radius:10px;font-size:13px;font-weight:600;transition:all .2s}
.btn-full{width:100%}
.btn-accent{background:linear-gradient(135deg,var(--accent),var(--accent2));color:#0a0d13}.btn-accent:hover{filter:brightness(1.1);transform:translateY(-1px)}
.btn-ghost{background:transparent;color:var(--text);border:1px solid var(--border2)}.btn-ghost:hover{border-color:var(--muted);background:rgba(255,255,255,.02)}
.btn-sm{padding:7px 14px;font-size:12px;border-radius:var(--r3)}
.btn-email{background:var(--blueBg);color:var(--email);border:1px solid rgba(96,165,250,.15)}.btn-sms{background:var(--greenBg);color:var(--sms);border:1px solid rgba(52,211,153,.15)}.btn-wa{background:rgba(37,211,102,.06);color:var(--whatsapp);border:1px solid rgba(37,211,102,.15)}.btn-tg{background:rgba(34,158,217,.06);color:var(--telegram);border:1px solid rgba(34,158,217,.15)}
.btn-red{background:var(--redBg);color:var(--red);border:1px solid rgba(248,113,113,.15)}
.divider{display:flex;align-items:center;gap:12px;margin:20px 0;color:var(--dim);font-size:12px}.divider::before,.divider::after{content:'';flex:1;height:1px;background:var(--border)}
/* ===== SIDEBAR ===== */
.sidebar{width:260px;background:var(--bg2);border-right:1px solid var(--border);display:flex;flex-direction:column;flex-shrink:0}
.sb-brand{padding:20px;display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--border)}
.sb-logo{width:36px;height:36px;background:linear-gradient(135deg,var(--accent),var(--accent2));border-radius:11px;display:flex;align-items:center;justify-content:center;font-weight:900;color:#0a0d13;font-size:16px}
.sb-brand-text{display:flex;flex-direction:column}
.sb-brand-name{font-size:16px;font-weight:800;color:var(--white);letter-spacing:-.02em}
.sb-brand-sub{font-size:9px;color:var(--dim);font-family:var(--mono);letter-spacing:.05em}
.sb-channels{display:flex;gap:6px;padding:16px 20px;border-bottom:1px solid var(--border)}
.sb-ch{flex:1;height:28px;border-radius:6px;display:flex;align-items:center;justify-content:center;font-size:10px;font-weight:600;cursor:pointer;transition:all .15s;position:relative}
.sb-ch.on{opacity:1}.sb-ch.off{opacity:.35}
.sb-ch-email{background:rgba(96,165,250,.1);color:var(--email);border:1px solid rgba(96,165,250,.15)}
.sb-ch-sms{background:rgba(52,211,153,.1);color:var(--sms);border:1px solid rgba(52,211,153,.15)}
.sb-ch-wa{background:rgba(37,211,102,.1);color:var(--whatsapp);border:1px solid rgba(37,211,102,.15)}
.sb-ch-tg{background:rgba(34,158,217,.1);color:var(--telegram);border:1px solid rgba(34,158,217,.15)}
.sb-ch .dot{position:absolute;top:3px;right:3px;width:5px;height:5px;border-radius:50%}
.dot-on{background:var(--green)}.dot-off{background:var(--red)}
.sb-nav{flex:1;overflow-y:auto;padding:8px 10px}
.sb-sec{padding:14px 10px 6px;font-size:9px;font-weight:700;color:var(--dim);text-transform:uppercase;letter-spacing:.1em}
.sb-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:10px;cursor:pointer;font-size:13px;color:var(--muted);font-weight:500;transition:all .15s;margin-bottom:2px}
.sb-item:hover{background:rgba(255,255,255,.03);color:var(--text)}
.sb-item.active{background:var(--accentBg);color:var(--accent);border:1px solid var(--accentBorder)}
.sb-item svg{width:17px;height:17px;flex-shrink:0;opacity:.6}.sb-item.active svg{opacity:1}
.sb-item .badge-count{margin-left:auto;font-size:10px;font-family:var(--mono);background:var(--accentBg);color:var(--accent);padding:2px 7px;border-radius:10px;font-weight:600}
.sb-user{padding:14px 16px;border-top:1px solid var(--border);display:flex;align-items:center;gap:10px}
.sb-avatar{width:32px;height:32px;border-radius:10px;background:linear-gradient(135deg,var(--accent),var(--accent2));display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:800;color:#0a0d13;flex-shrink:0}
.sb-uinfo{flex:1;min-width:0}
.sb-uname{font-size:12px;font-weight:600;color:var(--white);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sb-utier{font-size:10px;color:var(--dim);font-family:var(--mono)}
.sb-logout{padding:6px;border-radius:8px;background:transparent;color:var(--dim);cursor:pointer;transition:all .15s}
.sb-logout:hover{background:var(--redBg);color:var(--red)}
/* ===== MAIN ===== */
.main{flex:1;display:flex;flex-direction:column;overflow:hidden}
.topbar{height:54px;border-bottom:1px solid var(--border);display:flex;align-items:center;padding:0 28px;gap:14px;background:var(--bg2);flex-shrink:0}
.topbar h2{font-size:17px;font-weight:700;color:var(--white);flex:1;letter-spacing:-.02em}
.topbar-live{font-family:var(--mono);font-size:10px;padding:5px 12px;border-radius:20px;display:flex;align-items:center;gap:6px}
.topbar-live.ok{color:var(--green);background:var(--greenBg);border:1px solid rgba(52,211,153,.15)}
.topbar-live.ok::before{content:'';width:6px;height:6px;border-radius:50%;background:var(--green);animation:pulse 2s infinite}
.content{flex:1;overflow-y:auto;padding:28px;animation:fadeIn .3s ease}
/* ===== STATS ===== */
.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:14px;margin-bottom:28px}
.stat{background:var(--card);border:1px solid var(--border);border-radius:var(--r2);padding:20px;position:relative;overflow:hidden;transition:border-color .2s}
.stat:hover{border-color:var(--border2)}
.stat::before{content:'';position:absolute;top:0;left:0;right:0;height:2px}
.stat.s-accent::before{background:linear-gradient(90deg,var(--accent),var(--accent2))}.stat.s-email::before{background:var(--email)}.stat.s-sms::before{background:var(--sms)}.stat.s-wa::before{background:var(--whatsapp)}.stat.s-tg::before{background:var(--telegram)}.stat.s-green::before{background:var(--green)}.stat.s-red::before{background:var(--red)}.stat.s-purple::before{background:var(--purple)}
.stat-icon{width:36px;height:36px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:16px;margin-bottom:12px}
.stat-label{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px}
.stat-value{font-size:28px;font-weight:800;color:var(--white);font-family:var(--mono);letter-spacing:-.04em;line-height:1}
.stat-sub{font-size:11px;margin-top:6px;font-weight:500;color:var(--muted)}
.stat-sub .up{color:var(--green)}.stat-sub .down{color:var(--red)}
/* ===== CARDS ===== */
.card{background:var(--card);border:1px solid var(--border);border-radius:var(--r2);padding:22px;margin-bottom:18px}
.card-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:18px}
.card-title{font-size:15px;font-weight:700;color:var(--white);letter-spacing:-.01em}
.card-actions{display:flex;gap:8px}
/* ===== BADGES ===== */
.badge{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:20px;font-size:10px;font-weight:600;letter-spacing:.02em}
.badge-draft{background:var(--blueBg);color:var(--blue)}.badge-sent{background:var(--greenBg);color:var(--green)}.badge-scheduled{background:var(--orangeBg);color:var(--orange)}.badge-failed{background:var(--redBg);color:var(--red)}.badge-active{background:var(--greenBg);color:var(--green)}.badge-paused{background:var(--orangeBg);color:var(--orange)}
.badge-email{background:var(--blueBg);color:var(--email)}.badge-sms{background:var(--greenBg);color:var(--sms)}.badge-whatsapp{background:rgba(37,211,102,.08);color:var(--whatsapp)}.badge-telegram{background:rgba(34,158,217,.08);color:var(--telegram)}
/* ===== MODAL ===== */
.modal-bg{position:fixed;inset:0;background:rgba(0,0,0,.65);backdrop-filter:blur(4px);z-index:100;display:flex;align-items:center;justify-content:center;animation:fadeIn .15s}
.modal{background:var(--card);border:1px solid var(--border2);border-radius:var(--r2);padding:32px;width:580px;max-width:92vw;max-height:85vh;overflow-y:auto}
.modal h3{font-size:18px;font-weight:700;color:var(--white);margin-bottom:22px;letter-spacing:-.02em}
.modal .btn-row{display:flex;gap:10px;margin-top:22px}
/* ===== CHANNEL PILLS ===== */
.ch-select{display:flex;gap:8px;margin:14px 0}
.ch-pill{padding:8px 16px;border-radius:20px;font-size:12px;font-weight:600;cursor:pointer;transition:all .2s;border:1px solid var(--border2);background:transparent;color:var(--muted)}
.ch-pill.selected{border-color:currentColor}
.ch-pill[data-ch=email].selected{background:var(--blueBg);color:var(--email);border-color:rgba(96,165,250,.3)}
.ch-pill[data-ch=sms].selected{background:var(--greenBg);color:var(--sms);border-color:rgba(52,211,153,.3)}
.ch-pill[data-ch=whatsapp].selected{background:rgba(37,211,102,.08);color:var(--whatsapp);border-color:rgba(37,211,102,.3)}
.ch-pill[data-ch=telegram].selected{background:rgba(34,158,217,.08);color:var(--telegram);border-color:rgba(34,158,217,.3)}
/* ===== CHAT (AI) ===== */
.chat-wrap{display:flex;flex-direction:column;height:100%}
.chat-msgs{flex:1;overflow-y:auto;padding:0 4px 16px}
.chat-empty{text-align:center;padding:60px 20px;color:var(--dim)}
.chat-empty h3{color:var(--muted);margin-top:12px;font-size:16px}
.chat-u{background:var(--accentBg);border:1px solid var(--accentBorder);border-radius:var(--r) var(--r) 4px var(--r);padding:13px 18px;margin:8px 0;max-width:75%;margin-left:auto;font-size:13px;color:var(--white);line-height:1.6}
.chat-a{background:var(--surface);border:1px solid var(--border);border-radius:4px var(--r) var(--r) var(--r);padding:15px 20px;margin:8px 0;max-width:85%;font-size:13px;line-height:1.7}
.chat-a pre{background:var(--bg);padding:12px;border-radius:var(--r3);overflow-x:auto;font-family:var(--mono);font-size:12px;margin:8px 0}
.chat-a code{font-family:var(--mono);font-size:12px;background:var(--bg);padding:2px 6px;border-radius:4px}
.chat-bar{display:flex;gap:10px;padding:14px 0 0;border-top:1px solid var(--border)}
.chat-bar textarea{flex:1;resize:none;min-height:44px;max-height:120px;font-size:13px;line-height:1.5;border-radius:12px}
.loading-dot{display:inline-block;width:7px;height:7px;border-radius:50%;background:var(--accent);margin:0 3px;animation:pulse .8s infinite}
.loading-dot:nth-child(2){animation-delay:.15s}.loading-dot:nth-child(3){animation-delay:.3s}
.spinner{width:18px;height:18px;border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;animation:spin .6s linear infinite;display:inline-block}
/* ===== TOOLBAR ===== */
.toolbar{display:flex;align-items:center;gap:10px;margin-bottom:18px;flex-wrap:wrap}
.toolbar input[type=search]{width:280px;background:var(--surface);padding-left:36px;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%236b7a90' 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:12px center}
.toolbar .spacer{flex:1}
.toast{position:fixed;bottom:24px;right:24px;background:var(--card);color:var(--white);padding:13px 22px;border-radius:var(--r);font-size:13px;z-index:9999;display:none;border:1px solid var(--border2);box-shadow:0 12px 40px rgba(0,0,0,.5);animation:fadeIn .2s}
.toast.err{border-color:rgba(248,113,113,.2);color:var(--red)}
.tags{display:flex;flex-wrap:wrap;gap:5px}.tag{display:inline-flex;padding:3px 10px;border-radius:20px;font-size:10px;font-weight:600;background:var(--surface);border:1px solid var(--border);color:var(--muted)}
.editor-area{width:100%;min-height:200px;font-family:var(--mono);font-size:13px;line-height:1.7;background:var(--bg);border-radius:var(--r3);padding:16px;resize:vertical}
@media(max-width:768px){.sidebar{width:60px}.sb-brand-text,.sb-sec,.sb-item span,.sb-uinfo,.sb-channels{display:none}.sb-item{justify-content:center;padding:10px}.content{padding:16px}}
</style>
</head>
<body>
<!-- LOGIN -->
<div class="login-wrap" id="loginWrap">
<div class="login-box">
<div class="login-logo">W</div>
<h1>WEVADS IA v2</h1>
<p class="sub">Omnichannel Intelligence Platform</p>
<p class="ver">v2.2.0 — Sovereign Engine</p>
<div class="channels">
<div class="ch ch-email" title="Email">✉</div>
<div class="ch ch-sms" title="SMS">💬</div>
<div class="ch ch-wa" title="WhatsApp">📱</div>
<div class="ch ch-tg" title="Telegram">✈</div>
</div>
<div id="loginForm">
<div class="field"><label>Email</label><input type="email" id="lEmail" placeholder="you@company.com"></div>
<div class="field"><label>Mot de passe</label><input type="password" id="lPass" placeholder="" onkeydown="if(event.key==='Enter')doLogin()"></div>
<button class="btn btn-accent btn-full" onclick="doLogin()" id="btnLogin">Se connecter</button>
<div class="divider">ou</div>
<button class="btn btn-ghost btn-full" onclick="showReg()">Créer un compte</button>
</div>
<div id="regForm" style="display:none">
<div class="field"><label>Nom complet</label><input id="rName" placeholder="Yacine Mahboub"></div>
<div class="field"><label>Email</label><input type="email" id="rEmail" placeholder="you@company.com"></div>
<div class="field"><label>Entreprise</label><input id="rCompany" placeholder="WEVAL Consulting"></div>
<div class="field"><label>Mot de passe</label><input type="password" id="rPass" placeholder="Min. 6 caractères" onkeydown="if(event.key==='Enter')doReg()"></div>
<button class="btn btn-accent btn-full" onclick="doReg()" id="btnReg">Créer mon compte</button>
<div class="divider">ou</div>
<button class="btn btn-ghost btn-full" onclick="showLog()">J'ai déjà un compte</button>
</div>
</div>
</div>
<!-- APP -->
<div class="sidebar" id="sidebar" style="display:none">
<div class="sb-brand">
<div class="sb-logo">W</div>
<div class="sb-brand-text">
<span class="sb-brand-name">WEVADS IA</span>
<span class="sb-brand-sub">v2.2 OMNICHANNEL</span>
</div>
</div>
<div class="sb-channels">
<div class="sb-ch sb-ch-email on" title="Email: ON"><span class="dot dot-on"></span>✉</div>
<div class="sb-ch sb-ch-sms off" title="SMS: Pending" id="chSms"><span class="dot dot-off"></span>SMS</div>
<div class="sb-ch sb-ch-wa off" title="WhatsApp: Pending" id="chWa"><span class="dot dot-off"></span>WA</div>
<div class="sb-ch sb-ch-tg on" title="Telegram: ON" id="chTg"><span class="dot dot-on"></span>TG</div>
</div>
<div class="sb-nav" id="sbNav"></div>
<div class="sb-user">
<div class="sb-avatar" id="sbAv">W</div>
<div class="sb-uinfo"><div class="sb-uname" id="sbName"></div><div class="sb-utier" id="sbTier">admin</div></div>
<div class="sb-logout" onclick="logout()" title="Déconnexion">
<svg width="16" height="16" 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="mainArea" style="display:none">
<div class="topbar">
<h2 id="pageTitle">Dashboard</h2>
<div class="topbar-live ok" id="topStatus">Backend Live</div>
</div>
<div class="content" id="content"></div>
</div>
<div class="toast" id="toast"></div>
<script>
const API='/api/v2',WEVIA='/api/weval-ia-full',PHPAPI='/api/wevads-v2-api.php',SEND_ENGINE='/api/send-engine-unified.php',SMS_API='/api/ovh-sms-setup.php',WA_API='/api/ethica-whatsapp-api.php',V2E='/api/wevads-v2-engine.php';
let TOKEN='',USER={},PAGE='dashboard';
const $=id=>document.getElementById(id),v=id=>$(id)?$(id).value.trim():'';
const PAGES=[
{id:'dashboard',label:'Dashboard',icon:'grid',section:'Principal'},
{id:'send',label:'Envoyer',icon:'zap',section:'Principal'},
{id:'campaigns',label:'Campagnes',icon:'mail',section:'Principal'},
{id:'contacts',label:'Contacts',icon:'users',section:'Principal'},
{id:'templates',label:'Templates',icon:'layout',section:'Principal'},
{id:'channels',label:'Canaux',icon:'radio',section:'Omnichannel'},
{id:'senders',label:'Senders',icon:'mail',section:'Omnichannel'},
{id:'sendengine',label:'Send Engine',icon:'zap',section:'Omnichannel'},
{id:'brain',label:'Brain IA',icon:'cpu',section:'Intelligence'},
{id:'analytics',label:'Analytics',icon:'bar',section:'Intelligence'},
{id:'ai',label:'AI Assistant',icon:'msg',section:'Intelligence'},
{id:'settings',label:'Paramètres',icon:'gear',section:'Système'},
{id:'billing',label:'Facturation',icon:'card',section:'Système'},
];
const ICONS={grid:'<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/>',mail:'<rect x="2" y="4" width="20" height="16" rx="2"/><polyline points="2,4 12,13 22,4"/>',users:'<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',layout:'<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18"/><path d="M9 21V9"/>',radio:'<circle cx="12" cy="12" r="2"/><path d="M16.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"/>',zap:'<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',cpu:'<rect x="4" y="4" width="16" height="16" rx="2"/><rect x="9" y="9" width="6" height="6"/><path d="M9 1v3M15 1v3M9 20v3M15 20v3M20 9h3M20 14h3M1 9h3M1 14h3"/>',bar:'<path d="M18 20V10"/><path d="M12 20V4"/><path d="M6 20v-6"/>',msg:'<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>',gear:'<circle cx="12" cy="12" r="3"/><path d="M12 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"/>',card:'<rect x="1" y="4" width="22" height="16" rx="2"/><path d="M1 10h22"/>'};
function svgIcon(name){return `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${ICONS[name]||''}</svg>`}
function toast(msg,err){const t=$('toast');t.textContent=msg;t.className=err?'toast err':'toast';t.style.display='block';setTimeout(()=>t.style.display='none',4000)}
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML}
function fmtNum(n){return new Intl.NumberFormat('fr-FR').format(n||0)}
function fmtDate(d){if(!d)return'';return new Date(d).toLocaleDateString('fr-FR',{day:'2-digit',month:'short',year:'numeric'})}
function md2h(md){return md.replace(/```[\w]*\n([\s\S]*?)```/g,'<pre>$1</pre>').replace(/^### (.*$)/gm,'<h3>$1</h3>').replace(/^## (.*$)/gm,'<h2>$1</h2>').replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>').replace(/`(.*?)`/g,'<code>$1</code>').replace(/^- (.*$)/gm,'• $1<br>').replace(/\n\n/g,'<br><br>')}
async function api(path,opts={}){const h={'Content-Type':'application/json'};if(TOKEN)h['Authorization']='Bearer '+TOKEN;const r=await fetch(API+path,{...opts,headers:h});const j=await r.json();if(j.status==='error')throw new Error(j.message);return j}
async function phpApi(action){const r=await fetch(PHPAPI+'?action='+action);return r.json()}
function showReg(){$('loginForm').style.display='none';$('regForm').style.display=''}
function showLog(){$('regForm').style.display='none';$('loginForm').style.display=''}
async function doLogin(){
const email=v('lEmail'),pass=v('lPass');if(!email||!pass)return toast('Email et mot de passe requis',true);
$('btnLogin').textContent='Connexion...';$('btnLogin').disabled=true;
try{const r=await api('/auth/login',{method:'POST',body:JSON.stringify({email,password:pass})});TOKEN=r.token;USER=r.user;enter()}
catch(e){toast(e.message,true)}finally{$('btnLogin').textContent='Se connecter';$('btnLogin').disabled=false}
}
async function doReg(){
const name=v('rName'),email=v('rEmail'),company=v('rCompany'),pass=v('rPass');if(!name||!email||!pass)return toast('Champs requis',true);
$('btnReg').textContent='Création...';$('btnReg').disabled=true;
try{const r=await api('/auth/register',{method:'POST',body:JSON.stringify({name,email,company,password:pass})});TOKEN=r.token;USER=r.user;enter()}
catch(e){toast(e.message,true)}finally{$('btnReg').textContent='Créer mon compte';$('btnReg').disabled=false}
}
function enter(){
try{sessionStorage.setItem('wv2t',TOKEN);sessionStorage.setItem('wv2u',JSON.stringify(USER))}catch(e){}
$('loginWrap').style.display='none';$('sidebar').style.display='flex';$('mainArea').style.display='flex';
$('sbName').textContent=USER.name||USER.email;$('sbAv').textContent=(USER.name||'W')[0].toUpperCase();$('sbTier').textContent=USER.role||'user';
renderNav();nav('dashboard');
}
function logout(){TOKEN='';USER={};try{sessionStorage.clear()}catch(e){}$('loginWrap').style.display='flex';$('sidebar').style.display='none';$('mainArea').style.display='none'}
function renderNav(){
let h='',sec='';
PAGES.forEach(p=>{
if(p.section!==sec){sec=p.section;h+=`<div class="sb-sec">${sec}</div>`}
h+=`<div class="sb-item ${PAGE===p.id?'active':''}" data-page="${p.id}" onclick="nav('${p.id}')">${svgIcon(p.icon)}<span>${p.label}</span></div>`;
});
$('sbNav').innerHTML=h;
}
function nav(page){
PAGE=page;$('pageTitle').textContent=PAGES.find(p=>p.id===page)?.label||page;renderNav();
const c=$('content');c.innerHTML='<div style="text-align:center;padding:50px"><div class="spinner"></div></div>';c.scrollTop=0;
const R={dashboard:renderDashboard,send:renderSend,campaigns:renderCampaigns,contacts:renderContacts,templates:renderTemplates,channels:renderChannels,senders:renderSenders,sendengine:renderSendEngine,brain:renderBrain,analytics:renderAnalytics,ai:renderAI,settings:renderSettings,billing:renderBilling};
(R[page]||renderDashboard)();
}
// ====================== DASHBOARD ======================
async function renderDashboard(){
let cmp=[],php={},eng={},tr={};
try{cmp=(await api('/campaigns/list')).items||[]}catch(e){}
try{eng=await(await fetch(V2E+'?action=dashboard&token=WEVADS2026')).json()}catch(e){}
try{tr=await(await fetch(V2E+'?action=tracking_stats&token=WEVADS2026')).json()}catch(e){}
const k=eng.kpis||{};const ts=tr.stats||{};const rs=eng.recent_sends||[];
$('content').innerHTML=`
<div class="stats">
<div class="stat s-accent"><div class="stat-label">Contacts</div><div class="stat-value">${fmtNum(k.contacts_total||0)}</div><div class="stat-sub">${fmtNum(k.contacts_active||0)} actifs</div></div>
<div class="stat s-email"><div class="stat-label">Emails envoyés</div><div class="stat-value">${fmtNum(k.emails_sent_total||0)}</div><div class="stat-sub">${fmtNum(k.emails_sent_today||0)} aujourd'hui</div></div>
<div class="stat s-green"><div class="stat-label">Senders</div><div class="stat-value">${fmtNum(k.senders_active||0)}</div><div class="stat-sub">${k.senders_warming||0} en warmup</div></div>
<div class="stat s-purple"><div class="stat-label">Opens / Clicks</div><div class="stat-value">${fmtNum(ts.total_opens||0)}</div><div class="stat-sub">${fmtNum(ts.total_clicks||0)} clics</div></div>
<div class="stat s-sms"><div class="stat-label">Ethica HCPs</div><div class="stat-value">${fmtNum(k.ethica_hcps||0)}</div><div class="stat-sub">Pharma B2B</div></div>
<div class="stat s-tg"><div class="stat-label">Canaux</div><div class="stat-value">4</div><div class="stat-sub"><span class="up">Email+TG live</span></div></div>
</div>
<div style="display:grid;grid-template-columns:2fr 1fr;gap:18px">
<div class="card">
<div class="card-head"><div class="card-title">Derniers envois</div><div class="card-actions"><button class="btn btn-sm btn-accent" onclick="nav('send')">+ Envoyer</button></div></div>
<table><thead><tr><th>Sender</th><th>Destinataire</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(--mono);font-size:11px;color:var(--muted)">${esc((s.sender||'').split('@')[0])}</td><td style="font-family:var(--mono);font-size:11px">${esc(s.recipient||'')}</td><td style="color:var(--white);font-size:12px">${esc((s.subject||'').substring(0,35))}</td><td><span class="badge badge-email">${esc(s.isp||'')}</span></td><td style="font-size:11px;color:var(--dim)">${(s.created_at||'').substring(0,16)}</td></tr>`).join('')||'<tr><td colspan="5" style="text-align:center;color:var(--dim);padding:30px">Aucun envoi récent</td></tr>'}</tbody></table>
</div>
<div>
<div class="card"><div class="card-title" style="margin-bottom:12px">Infrastructure</div>
<div style="display:grid;gap:8px">
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Campagnes</span><span style="font-family:var(--mono);color:var(--white);font-weight:600">${k.campaigns_total||0} <span style="color:var(--green);font-size:10px">(${k.campaigns_active||0} actives)</span></span></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Warmup Accounts</span><span style="font-family:var(--mono);color:var(--accent);font-weight:600">${fmtNum(k.warmup_accounts||0)}</span></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Domaines actifs</span><span style="font-family:var(--mono);color:var(--white);font-weight:600">${k.domains_active||0}</span></div>
<div style="display:flex;justify-content:space-between;padding:8px 0;border-bottom:1px solid var(--border)"><span style="font-size:12px;color:var(--muted)">Bounces</span><span style="font-family:var(--mono);color:var(--red);font-weight:600">${k.bounces||0}</span></div>
<div style="display:flex;justify-content:space-between;padding:8px 0"><span style="font-size:12px;color:var(--muted)">Tracking Events</span><span style="font-family:var(--mono);color:var(--cyan);font-weight:600">${fmtNum(k.tracking_events||0)}</span></div>
</div>
</div>
<div class="card"><div class="card-title" style="margin-bottom:10px">Actions</div>
<div style="display:grid;gap:6px">
<button class="btn btn-sm btn-accent btn-full" onclick="nav('send')">⚡ Envoyer email</button>
<button class="btn btn-sm btn-email btn-full" onclick="nav('senders')">👥 Gérer senders</button>
<button class="btn btn-sm btn-tg btn-full" onclick="testTelegram()">✈ Test Telegram</button>
<button class="btn btn-sm btn-ghost btn-full" onclick="nav('ai')">🧠 AI Assistant</button>
</div>
</div>
</div>
</div>`;
}
// ====================== SEND PAGE ======================
async function renderSend(){
let methods=[];try{methods=(await(await fetch(V2E+'?action=send_methods&token=WEVADS2026')).json()).items||[]}catch(e){}
$('content').innerHTML=`
<div class="card" style="border:1px solid var(--accentBorder)">
<div class="card-head"><div class="card-title">⚡ Composer & Envoyer</div></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:18px">
<div>
<div class="field"><label>Canal</label>
<div class="ch-select">
<div class="ch-pill selected" data-ch="email" onclick="toggleCh(this)">✉ Email</div>
<div class="ch-pill" data-ch="sms" onclick="toggleCh(this)">💬 SMS</div>
<div class="ch-pill" data-ch="whatsapp" onclick="toggleCh(this)">📱 WhatsApp</div>
<div class="ch-pill" data-ch="telegram" onclick="toggleCh(this)">✈ Telegram</div>
</div>
</div>
<div class="field"><label>Méthode d'envoi</label>
<select id="sendMethod">${methods.map(m=>`<option value="${m.name}">${m.name} — ${m.type}</option>`).join('')}<option value="postfix">Postfix Relay</option></select>
</div>
<div class="field"><label>Destinataire(s)</label><input id="sendTo" placeholder="email@example.com (ou plusieurs séparés par ,)"></div>
<div class="field"><label>Objet</label><input id="sendSubject" placeholder="Objet du message"></div>
<div style="display:flex;gap:8px;align-items:center;margin:8px 0">
<button class="btn btn-sm btn-ghost" style="border-color:var(--purple);color:var(--purple)" onclick="aiOptimizeSubject()">🧠 Optimiser sujet IA</button>
<span id="aiSubjectHint" style="font-size:11px;color:var(--dim)"></span>
</div>
</div>
<div>
<div class="field"><label>From Name</label><input id="sendFromName" placeholder="WEVAL Consulting" value="WEVAL Consulting"></div>
<div class="field"><label>From Email (optionnel)</label><input id="sendFromEmail" placeholder="Auto-select sender"></div>
<div class="field"><label>Priorité</label>
<select id="sendPriority"><option value="normal">Normal</option><option value="high">Haute</option><option value="low">Basse</option></select>
</div>
<div style="display:flex;gap:8px;margin-top:18px">
<button class="btn btn-accent" onclick="doSendTest()" style="flex:1">🧪 Envoyer Test</button>
<button class="btn btn-ghost" onclick="doSendBulk()" style="flex:1;border-color:var(--green);color:var(--green)">🚀 Envoi Bulk</button>
</div>
</div>
</div>
<div class="field" style="margin-top:14px"><label>Contenu HTML</label>
<div style="display:flex;gap:8px;margin-bottom:8px">
<button class="btn btn-sm btn-ghost" onclick="aiGenSendContent()">🧠 Générer contenu IA</button>
<button class="btn btn-sm btn-ghost" onclick="loadTemplate()">📋 Charger template</button>
</div>
<textarea class="editor-area" id="sendBody" rows="10" placeholder="<h1>Bonjour {{name}}</h1><p>Votre message ici...</p>"></textarea>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:18px">
<div class="card">
<div class="card-title" style="margin-bottom:12px">📊 File d'attente</div>
<div id="queueStatus" style="color:var(--muted);font-size:13px">Chargement...</div>
</div>
<div class="card">
<div class="card-title" style="margin-bottom:12px">📨 Derniers envois test</div>
<div id="recentTests" style="color:var(--muted);font-size:13px">Chargement...</div>
</div>
</div>`;
// Load queue
try{const q=await(await fetch(V2E+'?action=queue&token=WEVADS2026')).json();$('queueStatus').innerHTML=`<div style="font-size:24px;font-weight:800;color:var(--white);font-family:var(--mono)">${q.pending||0}</div><div style="font-size:11px;color:var(--muted)">messages en attente</div>`}catch(e){$('queueStatus').textContent='Queue offline'}
try{const t=await(await fetch(V2E+'?action=tracking_stats&token=WEVADS2026')).json();const rc=t.recent||[];$('recentTests').innerHTML=rc.slice(0,5).map(r=>`<div style="display:flex;justify-content:space-between;padding:6px 0;border-bottom:1px solid var(--border);font-size:11px"><span style="font-family:var(--mono);color:var(--accent)">${esc(r.tracking_id||'').substring(0,20)}</span><span class="badge badge-${r.event_type==='open'?'active':'email'}">${r.event_type}</span><span style="color:var(--dim)">${(r.created_at||'').substring(11,19)}</span></div>`).join('')||'Aucun'}catch(e){}
}
async function doSendTest(){
const to=v('sendTo'),subj=v('sendSubject'),body=v('sendBody'),method=v('sendMethod');
if(!to||!subj)return toast('Destinataire et objet requis',true);
toast('Envoi test en cours...');
try{
const r=await fetch(V2E+'?token=WEVADS2026',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:'send_test',to,subject:subj,html:body,method,from_name:v('sendFromName')})});
const j=await r.json();
toast(j.ok||j.success?'✅ Email test envoyé !':'❌ '+(j.error||'Erreur'),!(j.ok||j.success));
}catch(e){toast('Erreur: '+e.message,true)}
}
async function doSendBulk(){toast('Envoi bulk — configurer via Campagnes',true)}
async function aiOptimizeSubject(){const s=v('sendSubject');if(!s)return toast('Écrivez un sujet d\'abord',true);$('aiSubjectHint').textContent='IA en cours...';try{const r=await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:'Optimise ce sujet email pour maximiser le taux d\'ouverture. Retourne UNIQUEMENT le sujet optimisé, rien d\'autre: '+s,mode:'fast'})});const j=await r.json();$('sendSubject').value=j.response||s;$('aiSubjectHint').textContent='✨ Optimisé par IA'}catch(e){$('aiSubjectHint').textContent='Erreur IA'}}
async function aiGenSendContent(){const s=v('sendSubject');$('sendBody').value='Génération IA...';try{const r=await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:'Génère un email HTML professionnel B2B sur: '+(s||'produit innovant')+'. Retourne UNIQUEMENT le HTML.',mode:'fast'})});const j=await r.json();$('sendBody').value=j.response||'Erreur'}catch(e){$('sendBody').value='Erreur: '+e.message}}
async function loadTemplate(){try{const tpl=(await api('/templates/list')).items||[];if(tpl.length>0){$('sendBody').value=tpl[0].html||'';toast('Template "'+tpl[0].name+'" chargé')}else toast('Aucun template',true)}catch(e){toast(e.message,true)}}
// ====================== SENDERS ======================
async function renderSenders(){
let data={};try{data=await(await fetch(V2E+'?action=senders&token=WEVADS2026')).json()}catch(e){}
const items=(data.items||[]).slice(0,50);
$('content').innerHTML=`
<div class="stats">
<div class="stat s-green"><div class="stat-label">Senders actifs</div><div class="stat-value">${items.filter(s=>s.status==='Active').length}+</div></div>
<div class="stat s-accent"><div class="stat-label">Total affiché</div><div class="stat-value">${items.length}</div><div class="stat-sub">sur 650+</div></div>
<div class="stat s-purple"><div class="stat-label">Warmup</div><div class="stat-value">2,036</div><div class="stat-sub">comptes warmup</div></div>
</div>
<div class="card" style="padding:0;overflow:hidden">
<table><thead><tr><th>Email</th><th>Nom</th><th>Domaine</th><th>Statut</th></tr></thead>
<tbody>${items.map(s=>`<tr><td style="font-family:var(--mono);font-size:11px;color:var(--white)">${esc(s.email)}</td><td>${esc(s.name||'')}</td><td style="font-family:var(--mono);font-size:11px;color:var(--muted)">${esc(s.domain||'')}</td><td><span class="badge badge-${s.status==='Active'?'active':'paused'}">${s.status}</span></td></tr>`).join('')}</tbody></table>
</div>`
}
}
// ====================== CAMPAIGNS ======================
let CMP=[];
async function renderCampaigns(){try{CMP=(await api('/campaigns/list')).items||[]}catch(e){CMP=[]}
$('content').innerHTML=`<div class="toolbar"><input type="search" id="cmpS" placeholder="Rechercher..." oninput="filterCmp()"><div class="spacer"></div><button class="btn btn-sm btn-accent" onclick="openCmpModal()">+ Nouvelle campagne</button></div><div class="card" style="padding:0;overflow:hidden"><table><thead><tr><th>Nom</th><th>Sujet</th><th>Canal</th><th>Statut</th><th>Envoyés</th><th>Ouverts</th><th>Actions</th></tr></thead><tbody id="cmpT"></tbody></table></div>`;
renderCmpTable();
}
function renderCmpTable(f=''){const items=f?CMP.filter(c=>[c.name,c.subject].join(' ').toLowerCase().includes(f)):CMP;$('cmpT').innerHTML=items.map(c=>`<tr><td style="color:var(--white);font-weight:600">${esc(c.name)}</td><td>${esc(c.subject)}</td><td><span class="badge badge-email">Email</span></td><td><span class="badge badge-${c.status}">${c.status}</span></td><td style="font-family:var(--mono)">${fmtNum(c.metrics?.sent)}</td><td style="font-family:var(--mono)">${fmtNum(c.metrics?.opened)}</td><td><button class="btn btn-sm btn-ghost" onclick="simSend('${c.id}')">Simuler</button></td></tr>`).join('')||'<tr><td colspan="7" style="text-align:center;color:var(--dim);padding:30px">Aucune campagne</td></tr>'}
function filterCmp(){renderCmpTable(v('cmpS').toLowerCase())}
async function simSend(id){try{await api('/campaigns/'+id+'/send-simulate',{method:'POST'});toast('Envoi simulé');renderCampaigns()}catch(e){toast(e.message,true)}}
function openCmpModal(){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 omnichannel</h3>
<div class="field"><label>Nom</label><input id="ncN" placeholder="Newsletter Mars 2026"></div>
<div class="field"><label>Objet / Message</label><input id="ncS" placeholder="Découvrez nos nouveautés IA"></div>
<div class="field"><label>Canaux</label><div class="ch-select"><div class="ch-pill selected" data-ch="email" onclick="toggleCh(this)">✉ Email</div><div class="ch-pill" data-ch="sms" onclick="toggleCh(this)">💬 SMS</div><div class="ch-pill" data-ch="whatsapp" onclick="toggleCh(this)">📱 WhatsApp</div><div class="ch-pill" data-ch="telegram" onclick="toggleCh(this)">✈ Telegram</div></div></div>
<div class="field"><label>Audience</label><input type="number" id="ncA" value="500"></div>
<div class="field"><label>Contenu HTML</label><textarea class="editor-area" id="ncH" rows="5" placeholder="<h1>Bonjour {{name}}</h1>..."></textarea></div>
<div style="display:flex;align-items:center;gap:8px;margin:12px 0;padding:12px;background:var(--purpleBg);border:1px solid rgba(167,139,250,.15);border-radius:10px"><span style="font-size:16px">🧠</span><div style="flex:1;font-size:12px;color:var(--purple)">IA peut générer le contenu</div><button class="btn btn-sm btn-ghost" onclick="aiGenContent()" style="border-color:rgba(167,139,250,.2);color:var(--purple)">Générer avec IA</button></div>
<div class="btn-row"><button class="btn btn-ghost" onclick="this.closest('.modal-bg').remove()">Annuler</button><button class="btn btn-accent" onclick="createCmp()">Créer</button></div></div>`;
document.body.appendChild(m);
}
function toggleCh(el){el.classList.toggle('selected')}
async function createCmp(){const n=v('ncN'),s=v('ncS');if(!n||!s)return toast('Nom et sujet requis',true);try{await api('/campaigns',{method:'POST',body:JSON.stringify({name:n,subject:s,audience_size:Number(v('ncA'))||0,content_html:v('ncH')})});document.querySelector('.modal-bg')?.remove();toast('Campagne créée');renderCampaigns()}catch(e){toast(e.message,true)}}
async function aiGenContent(){const s=$('ncS');if(!s||!s.value)return toast('Remplissez le sujet d\'abord',true);const ta=$('ncH');ta.value='Génération IA en cours...';try{const r=await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:'Génère un email HTML professionnel B2B pharma sur: '+s.value+'. Retourne UNIQUEMENT le HTML.',mode:'fast'})});const j=await r.json();ta.value=j.response||'Erreur IA'}catch(e){ta.value='Erreur: '+e.message}}
// ====================== CONTACTS ======================
let CTC=[];
async function renderContacts(){try{CTC=(await api('/contacts/list')).items||[]}catch(e){CTC=[]}
$('content').innerHTML=`<div class="toolbar"><input type="search" id="ctcS" placeholder="Rechercher..." oninput="filterCtc()"><div class="spacer"></div><button class="btn btn-sm btn-accent" onclick="openCtcModal()">+ 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>Actions</th></tr></thead><tbody id="ctcT"></tbody></table></div>`;
renderCtcTable();
}
function renderCtcTable(f=''){const items=f?CTC.filter(c=>[c.email,c.first_name,c.last_name].join(' ').toLowerCase().includes(f)):CTC;$('ctcT').innerHTML=items.map(c=>`<tr><td style="color:var(--white);font-weight:600">${esc(c.first_name||'')} ${esc(c.last_name||'')}</td><td style="font-family:var(--mono);font-size:12px">${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-red" onclick="delCtc('${c.id}')">×</button></td></tr>`).join('')||'<tr><td colspan="6" style="text-align:center;color:var(--dim);padding:30px">Aucun contact</td></tr>'}
function filterCtc(){renderCtcTable(v('ctcS').toLowerCase())}
async function delCtc(id){if(!confirm('Supprimer ?'))return;try{await api('/contacts/'+id,{method:'DELETE'});toast('Supprimé');renderContacts()}catch(e){toast(e.message,true)}}
function openCtcModal(){const m=document.createElement('div');m.className='modal-bg';m.onclick=e=>{if(e.target===m)m.remove()};m.innerHTML=`<div class="modal"><h3>Ajouter un contact</h3><div style="display:grid;grid-template-columns:1fr 1fr;gap:14px"><div class="field"><label>Prénom</label><input id="ncF" placeholder="Jean"></div><div class="field"><label>Nom</label><input id="ncL" placeholder="Dupont"></div></div><div class="field"><label>Email</label><input type="email" id="ncE" placeholder="jean@company.com"></div><div class="field"><label>Téléphone</label><input id="ncP" placeholder="+212 6XX XX XX XX"></div><div style="display:grid;grid-template-columns:1fr 1fr;gap:14px"><div class="field"><label>Entreprise</label><input id="ncC" placeholder="ACME"></div><div class="field"><label>Pays</label><input id="ncCo" placeholder="Maroc"></div></div><div class="field"><label>Tags</label><input id="ncTg" placeholder="vip, prospect"></div><div class="btn-row"><button class="btn btn-ghost" onclick="this.closest('.modal-bg').remove()">Annuler</button><button class="btn btn-accent" onclick="addCtc()">Ajouter</button></div></div>`;document.body.appendChild(m)}
async function addCtc(){const e=v('ncE');if(!e)return toast('Email requis',true);try{await api('/contacts',{method:'POST',body:JSON.stringify({email:e,first_name:v('ncF'),last_name:v('ncL'),company:v('ncC'),phone:v('ncP'),country:v('ncCo'),tags:v('ncTg').split(',').map(t=>t.trim()).filter(Boolean)})});document.querySelector('.modal-bg')?.remove();toast('Contact ajouté');renderContacts()}catch(e){toast(e.message,true)}}
// ====================== TEMPLATES ======================
let TPL=[];
async function renderTemplates(){try{TPL=(await api('/templates/list')).items||[]}catch(e){TPL=[]}
$('content').innerHTML=`<div class="toolbar"><div class="spacer"></div><button class="btn btn-sm btn-accent" onclick="openTplModal()">+ Nouveau</button></div><div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:16px" id="tplG"></div>`;
$('tplG').innerHTML=TPL.map(t=>`<div class="card" style="cursor:pointer"><div style="display:flex;justify-content:space-between;margin-bottom:10px"><span style="font-weight:700;color:var(--white)">${esc(t.name)}</span><span class="badge badge-active">${esc(t.category)}</span></div><div style="background:var(--bg);border:1px solid var(--border);border-radius:var(--r3);padding:12px;min-height:60px;font-size:11px;color:var(--muted);overflow:hidden;max-height:80px">${t.html?t.html.substring(0,150)+'...':'<em>Vide</em>'}</div><div style="margin-top:8px;font-size:10px;color:var(--dim)">${fmtDate(t.created_at)}</div></div>`).join('')||'<div style="text-align:center;color:var(--dim);padding:40px;grid-column:1/-1">Aucun template</div>';
}
function openTplModal(){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 template</h3><div class="field"><label>Nom</label><input id="ntN" placeholder="Welcome Email"></div><div class="field"><label>Catégorie</label><select id="ntC"><option value="marketing">Marketing</option><option value="transactional">Transactionnel</option><option value="newsletter">Newsletter</option><option value="general">Général</option></select></div><div class="field"><label>HTML</label><textarea class="editor-area" id="ntH" rows="8" placeholder="<h1>Hello</h1>"></textarea></div><div class="btn-row"><button class="btn btn-ghost" onclick="this.closest('.modal-bg').remove()">Annuler</button><button class="btn btn-accent" onclick="saveTpl()">Enregistrer</button></div></div>`;document.body.appendChild(m)}
async function saveTpl(){const n=v('ntN');if(!n)return toast('Nom requis',true);try{await api('/templates',{method:'POST',body:JSON.stringify({name:n,category:v('ntC'),html:v('ntH')})});document.querySelector('.modal-bg')?.remove();toast('Template créé');renderTemplates()}catch(e){toast(e.message,true)}}
// ====================== CHANNELS ======================
async function renderChannels(){
let se={},sms={},wa={};
try{const r=await fetch(SEND_ENGINE+'?action=status&token=ETHICA_API_2026_SECURE');se=await r.json()}catch(e){}
const infra=se.infrastructure||{};const o365=infra.o365_senders||{};const pmta=infra.pmta||{};
$('content').innerHTML=`
<div class="stats">
<div class="stat s-email"><div class="stat-label">Email</div><div class="stat-value" style="font-size:18px;color:var(--email)">LIVE</div><div class="stat-sub">PMTA + ${o365.active||0} O365 senders</div></div>
<div class="stat s-tg"><div class="stat-label">Telegram</div><div class="stat-value" style="font-size:18px;color:var(--telegram)">LIVE</div><div class="stat-sub">@wevads_alerts_bot</div></div>
<div class="stat s-sms"><div class="stat-label">SMS OVH</div><div class="stat-value" style="font-size:18px;color:var(--orange)">PENDING</div><div class="stat-sub">Credentials requis</div></div>
<div class="stat s-wa"><div class="stat-label">WhatsApp</div><div class="stat-value" style="font-size:18px;color:var(--orange)">PENDING</div><div class="stat-sub">Meta Business API</div></div>
</div>
<div class="card">
<div class="card-head"><div class="card-title">✉ Email — Infrastructure</div><span class="badge badge-active">Opérationnel</span></div>
<table><thead><tr><th>Composant</th><th>Statut</th><th>Détails</th></tr></thead><tbody>
<tr><td style="color:var(--white)">PMTA v5.0r3</td><td><span class="badge badge-active">Running</span></td><td style="font-family:var(--mono);font-size:11px">S95:25 — ${pmta.servers_alive||0}/${pmta.servers_configured||0} IPs</td></tr>
<tr><td style="color:var(--white)">O365 Senders</td><td><span class="badge badge-active">${o365.active||0} actifs</span></td><td style="font-family:var(--mono);font-size:11px">${o365.daily_capacity||0}/jour capacité</td></tr>
<tr><td style="color:var(--white)">Tracking</td><td><span class="badge badge-active">E2E Prouvé</span></td><td style="font-family:var(--mono);font-size:11px">S151→S204→S95→PG (ID 1831)</td></tr>
<tr><td style="color:var(--white)">Méthode active</td><td><span class="badge badge-email">${infra.method||'—'}</span></td><td></td></tr>
</tbody></table>
</div>
<div class="card">
<div class="card-head"><div class="card-title">✈ Telegram — Alertes</div><span class="badge badge-active">Connecté</span></div>
<p style="font-size:13px;color:var(--muted);margin-bottom:14px">Bot @wevads_alerts_bot connecté au chat de Yacine. Utilisé pour les alertes infra, les notifications de campagne, et le monitoring.</p>
<button class="btn btn-sm btn-tg" onclick="testTelegram()">Envoyer test Telegram</button>
</div>
<div class="card">
<div class="card-head"><div class="card-title">💬 SMS OVH — Configuration</div><span class="badge badge-scheduled">Credentials requis</span></div>
<p style="font-size:13px;color:var(--muted);margin-bottom:14px">16,006 contacts avec téléphone. <a href="https://eu.api.ovh.com/createToken/?GET=/sms&GET=/sms/*&POST=/sms/*&PUT=/sms/*&DELETE=/sms/*" target="_blank">Créer un token OVH →</a></p>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px">
<div class="field"><label>App Key</label><input id="smsAK" placeholder="OVH App Key"></div>
<div class="field"><label>App Secret</label><input id="smsAS" placeholder="OVH App Secret"></div>
<div class="field"><label>Consumer Key</label><input id="smsCK" placeholder="OVH Consumer Key"></div>
</div>
<button class="btn btn-sm btn-sms" onclick="configureSMS()">Configurer OVH SMS</button>
</div>
<div class="card">
<div class="card-head"><div class="card-title">📱 WhatsApp — Meta Business</div><span class="badge badge-scheduled">Credentials requis</span></div>
<p style="font-size:13px;color:var(--muted);margin-bottom:14px">54,180 contacts avec téléphone. <a href="https://developers.facebook.com" target="_blank">Meta for Developers →</a></p>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px">
<div class="field"><label>Phone Number ID</label><input id="waPhone" placeholder="Meta Phone Number ID"></div>
<div class="field"><label>Access Token</label><input id="waToken" placeholder="Permanent Access Token"></div>
</div>
<button class="btn btn-sm btn-wa" onclick="configureWA()">Configurer WhatsApp</button>
</div>`;
}
async function testTelegram(){toast('Envoi Telegram...');try{const r=await fetch('https://api.telegram.org/bot8544624912:AAEm9ttXK6JeFqAL-gcvB5sreCBhXzzQwrs/sendMessage',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({chat_id:'7605775322',text:'🔔 Test WEVADS v2.2\nCanal Telegram opérationnel\n'+new Date().toLocaleString('fr-FR'),parse_mode:'Markdown'})});const j=await r.json();toast(j.ok?'Telegram envoyé ✓':'Erreur Telegram',!j.ok)}catch(e){toast('Erreur: '+e.message,true)}}
async function configureSMS(){const ak=v('smsAK'),as=v('smsAS'),ck=v('smsCK');if(!ak||!as||!ck)return toast('3 clés OVH requises',true);toast('Configuration OVH...');try{const r=await fetch(SMS_API+'?token=ETHICA_API_2026_SECURE',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=configure&application_key='+ak+'&application_secret='+as+'&consumer_key='+ck});const j=await r.json();toast(j.ok?'SMS OVH configuré ✓':'Erreur: '+(j.error||''),!j.ok);if(j.ok)renderChannels()}catch(e){toast('Erreur: '+e.message,true)}}
async function configureWA(){const p=v('waPhone'),t=v('waToken');if(!p||!t)return toast('Phone ID et Token requis',true);toast('Configuration WhatsApp...');try{const r=await fetch(WA_API+'?token=ETHICA_API_2026_SECURE',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:'action=add_provider&phone_number_id='+p+'&access_token='+t+'&name=Ethica+WA'});const j=await r.json();toast(j.ok?'WhatsApp configuré ✓':'Erreur: '+(j.error||''),!j.ok);if(j.ok)renderChannels()}catch(e){toast('Erreur: '+e.message,true)}}
// ====================== SEND ENGINE ======================
async function renderSendEngine(){
let se={},eng={},wm={};
try{se=await(await fetch(SEND_ENGINE+'?action=status&token=ETHICA_API_2026_SECURE')).json()}catch(e){}
try{eng=await(await fetch(V2E+'?action=dashboard&token=WEVADS2026')).json()}catch(e){}
try{wm=await(await fetch(V2E+'?action=warmup_status&token=WEVADS2026')).json()}catch(e){}
const infra=se.infrastructure||{};const o365=infra.o365_senders||{};const pmta=infra.pmta||{};const cli=se.clients||{};const k=eng.kpis||{};
$('content').innerHTML=`
<div class="stats">
<div class="stat s-accent"><div class="stat-label">Méthode active</div><div class="stat-value" style="font-size:16px">${(infra.method||'—').toUpperCase()}</div></div>
<div class="stat s-email"><div class="stat-label">O365 Senders</div><div class="stat-value">${o365.active||0}/${o365.total||0}</div><div class="stat-sub">${o365.daily_capacity||0}/jour</div></div>
<div class="stat s-green"><div class="stat-label">PMTA IPs</div><div class="stat-value">${pmta.servers_alive||0}/${pmta.servers_configured||0}</div><div class="stat-sub">${pmta.status||'unknown'}</div></div>
<div class="stat s-purple"><div class="stat-label">Warmup</div><div class="stat-value">${fmtNum(wm.total_accounts||k.warmup_accounts||0)}</div><div class="stat-sub">${wm.active||0} actifs</div></div>
<div class="stat s-tg"><div class="stat-label">Queue</div><div class="stat-value">${k.queue_pending||0}</div></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:18px">
<div class="card"><div class="card-title" style="margin-bottom:14px">Clients</div>
<div style="display:grid;gap:12px">
<div style="padding:14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r)"><div style="font-size:12px;font-weight:600;color:var(--accent);margin-bottom:4px">Ethica (Pharma B2B)</div><div style="font-family:var(--mono);font-size:20px;font-weight:800;color:var(--white)">${fmtNum(cli.ethica?.contacts||0)}</div><div style="font-size:11px;color:var(--muted)">${fmtNum(cli.ethica?.emails||0)} emails</div></div>
<div style="padding:14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r)"><div style="font-size:12px;font-weight:600;color:var(--cyan);margin-bottom:4px">WEVADS (Multi-client)</div><div style="font-family:var(--mono);font-size:20px;font-weight:800;color:var(--white)">${fmtNum(cli.wevads?.contacts||0)}</div><div style="font-size:11px;color:var(--muted)">${fmtNum(k.senders_active||0)} senders • ${k.domains_active||0} domaines</div></div>
</div>
</div>
<div class="card"><div class="card-title" style="margin-bottom:14px">PMTA Servers</div>
<table><thead><tr><th>IP</th><th>Port</th><th>Statut</th></tr></thead>
<tbody>${(pmta.ips||[]).map(ip=>`<tr><td style="font-family:var(--mono);font-size:12px;color:var(--white)">${ip}</td><td style="font-family:var(--mono);font-size:11px">25</td><td><span class="badge badge-${pmta.servers_alive>0?'active':'failed'}">—</span></td></tr>`).join('')}</tbody></table>
<div style="margin-top:12px;font-size:11px;color:var(--dim)">PMTA v5.0r3 sur S95:25 (local) — 4 IPs externes anciennes (Hetzner)</div>
</div>
</div>
<div class="card"><div class="card-title" style="margin-bottom:10px">Métriques envoi</div>
<div style="display:grid;grid-template-columns:repeat(5,1fr);gap:10px">${[['Total envoyés',k.emails_sent_total,'var(--white)'],['Aujourd\'hui',k.emails_sent_today,'var(--green)'],['Opens',k.tracking_events,'var(--accent)'],['Bounces',k.bounces,'var(--red)'],['Send Methods',k.send_methods,'var(--cyan)']].map(([l,v2,c])=>`<div style="text-align:center;padding:14px;background:var(--surface);border:1px solid var(--border);border-radius:var(--r)"><div style="font-size:20px;font-weight:800;color:${c};font-family:var(--mono)">${fmtNum(v2||0)}</div><div style="font-size:10px;color:var(--muted);margin-top:4px">${l}</div></div>`).join('')}</div>
</div>`
}
}
// ====================== BRAIN IA ======================
function renderBrain(){$('content').innerHTML=`
<div class="stats">
<div class="stat s-accent"><div class="stat-label">Modèle IA</div><div class="stat-value" style="font-size:16px">Sovereign</div><div class="stat-sub">qwen2.5-14B GPU</div></div>
<div class="stat s-green"><div class="stat-label">Status</div><div class="stat-value" style="color:var(--green)">LIVE</div><div class="stat-sub">Zero cloud</div></div>
<div class="stat s-email"><div class="stat-label">Fallback</div><div class="stat-value" style="font-size:14px">Groq→Cerebras</div></div>
<div class="stat s-purple"><div class="stat-label">RAG</div><div class="stat-value">95</div><div class="stat-sub">memcells</div></div>
</div>
<div class="card"><div class="card-title" style="margin-bottom:14px">Infra IA</div>
<table><thead><tr><th>Composant</th><th>Adresse</th><th>Statut</th></tr></thead><tbody>
<tr><td style="color:var(--white)">vLLM GPU</td><td style="font-family:var(--mono);font-size:11px">S95:8000</td><td><span class="badge badge-active">Live</span></td></tr>
<tr><td style="color:var(--white)">Groq API</td><td style="font-family:var(--mono);font-size:11px">llama-3.3-70b</td><td><span class="badge badge-active">Fallback #1</span></td></tr>
<tr><td style="color:var(--white)">Cerebras</td><td style="font-family:var(--mono);font-size:11px">qwen-3-235b</td><td><span class="badge badge-active">Fallback #2</span></td></tr>
<tr><td style="color:var(--white)">Ollama S204</td><td style="font-family:var(--mono);font-size:11px">9 modèles</td><td><span class="badge badge-active">Local CPU</span></td></tr>
<tr><td style="color:var(--white)">Qdrant RAG</td><td style="font-family:var(--mono);font-size:11px">S95:6333</td><td><span class="badge badge-active">95 cells</span></td></tr>
</tbody></table></div>`}
// ====================== ANALYTICS ======================
async function renderAnalytics(){
let isp={},del={},tr={};
try{isp=await(await fetch('/api/ethica-sendengine-api.php?action=isp_distribution&token=ETHICA_API_2026_SECURE')).json()}catch(e){}
try{del=await(await fetch(V2E+'?action=deliverability&token=WEVADS2026')).json()}catch(e){}
try{tr=await(await fetch(V2E+'?action=tracking_stats&token=WEVADS2026')).json()}catch(e){}
const d=isp.distribution||{};const total=(d.gmail||0)+(d.microsoft||0)+(d.yahoo||0)+(d.other||0);
const ts=tr.stats||{};const isps=(del.by_isp||[]).slice(0,10);
$('content').innerHTML=`
<div class="stats">
<div class="stat s-green"><div class="stat-label">Total Opens</div><div class="stat-value">${fmtNum(ts.total_opens||0)}</div><div class="stat-sub">${ts.opens_today||0} aujourd'hui</div></div>
<div class="stat s-email"><div class="stat-label">Total Clicks</div><div class="stat-value">${fmtNum(ts.total_clicks||0)}</div><div class="stat-sub">${ts.clicks_today||0} aujourd'hui</div></div>
<div class="stat s-red"><div class="stat-label">Bounces</div><div class="stat-value">${ts.total_bounces||0}</div></div>
<div class="stat s-purple"><div class="stat-label">Events</div><div class="stat-value">${fmtNum(ts.tracking_events||0)}</div></div>
<div class="stat s-accent"><div class="stat-label">Contacts Email</div><div class="stat-value">${fmtNum(total)}</div></div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:18px">
<div class="card"><div class="card-title" style="margin-bottom:14px">Répartition ISP (Ethica HCPs)</div>
<div style="height:24px;display:flex;border-radius:12px;overflow:hidden;background:var(--surface)">${total>0?`<div style="width:${(d.microsoft/total)*100}%;background:var(--blue)" title="Microsoft"></div><div style="width:${(d.yahoo/total)*100}%;background:var(--purple)" title="Yahoo"></div><div style="width:${(d.gmail/total)*100}%;background:var(--red)" title="Gmail"></div><div style="width:${(d.other/total)*100}%;background:var(--muted)" title="Autres"></div>`:''}</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-top:14px">${[['Microsoft',d.microsoft,'var(--blue)'],['Yahoo',d.yahoo,'var(--purple)'],['Gmail',d.gmail,'var(--red)'],['Autres',d.other,'var(--muted)']].map(([n,c,col])=>`<div style="display:flex;align-items:center;gap:8px"><div style="width:10px;height:10px;border-radius:3px;background:${col}"></div><span style="font-size:12px;color:var(--muted)">${n}</span><span style="font-family:var(--mono);font-size:12px;color:var(--white);margin-left:auto">${fmtNum(c||0)}</span></div>`).join('')}</div>
</div>
<div class="card"><div class="card-title" style="margin-bottom:14px">Délivrabilité par ISP (WEVADS)</div>
<table><thead><tr><th>ISP</th><th>Envoyés</th><th>Délivrés</th><th>Taux</th></tr></thead>
<tbody>${isps.map(i=>{const rate=i.total>0?((i.delivered/i.total)*100).toFixed(1):'0';return `<tr><td style="color:var(--white);font-weight:600;text-transform:uppercase">${esc(i.isp)}</td><td style="font-family:var(--mono)">${fmtNum(i.total)}</td><td style="font-family:var(--mono);color:var(--green)">${fmtNum(i.delivered)}</td><td><span style="font-family:var(--mono);font-weight:700;color:${parseFloat(rate)>95?'var(--green)':parseFloat(rate)>90?'var(--accent)':'var(--orange)'}">${rate}%</span></td></tr>`}).join('')||'<tr><td colspan="4" style="text-align:center;color:var(--dim);padding:20px">Pas de données</td></tr>'}</tbody></table>
</div>
</div>
<div class="card"><div class="card-title" style="margin-bottom:14px">Événements récents</div>
<table><thead><tr><th>ID</th><th>Type</th><th>IP</th><th>Date</th></tr></thead>
<tbody>${(tr.recent||[]).slice(0,10).map(e=>`<tr><td style="font-family:var(--mono);font-size:11px;color:var(--accent)">${esc((e.tracking_id||'').substring(0,25))}</td><td><span class="badge badge-${e.event_type==='open'?'active':e.event_type==='click'?'email':'failed'}">${e.event_type}</span></td><td style="font-family:var(--mono);font-size:11px;color:var(--muted)">${esc(e.ip_address||'')}</td><td style="font-size:11px;color:var(--dim)">${(e.created_at||'').substring(0,19)}</td></tr>`).join('')}</tbody></table>
</div>`
}
// ====================== AI ASSISTANT ======================
let chatH=[];
function renderAI(){$('content').innerHTML=`<div class="chat-wrap"><div class="chat-msgs" id="aiM"><div class="chat-empty"><div style="font-size:40px;margin-bottom:8px">🧠</div><h3>WEVIA AI Assistant</h3><p style="font-size:13px;margin-top:8px;line-height:1.6;color:var(--muted)">Optimisez vos sujets, générez du contenu,<br>analysez la délivrabilité de vos campagnes.</p><div style="display:flex;gap:8px;justify-content:center;margin-top:16px;flex-wrap:wrap"><button class="btn btn-sm btn-ghost" onclick="aiQ('Optimise ce sujet: Découvrez nos nouveautés')">Optimiser sujet</button><button class="btn btn-sm btn-ghost" onclick="aiQ('Génère un email B2B pharma professionnel')">Email B2B</button><button class="btn btn-sm btn-ghost" onclick="aiQ('Analyse la délivrabilité et suggère des améliorations')">Audit</button></div></div></div><div class="chat-bar"><textarea id="aiI" rows="1" placeholder="Posez une question à l'IA..." onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();aiS()}" oninput="this.style.height='44px';this.style.height=Math.min(this.scrollHeight,120)+'px'"></textarea><button class="btn btn-sm btn-accent" onclick="aiS()">Envoyer</button></div></div>`}
function aiQ(q){$('aiI').value=q;aiS()}
async function aiS(){const msg=v('aiI');if(!msg)return;const b=$('aiM');const e=b.querySelector('.chat-empty');if(e)e.remove();b.innerHTML+=`<div class="chat-u">${esc(msg)}</div>`;$('aiI').value='';$('aiI').style.height='44px';b.scrollTop=b.scrollHeight;const lid='l'+Date.now();b.innerHTML+=`<div class="chat-a" id="${lid}"><span class="loading-dot"></span><span class="loading-dot"></span><span class="loading-dot"></span></div>`;b.scrollTop=b.scrollHeight;try{chatH.push({role:'user',content:msg});const r=await fetch(WEVIA,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,mode:'full',history:chatH.slice(-6)})});const j=await r.json();const reply=j.response||j.error||'Erreur';chatH.push({role:'assistant',content:reply});const el=$(lid);if(el)el.outerHTML=`<div class="chat-a">${md2h(reply)}</div>`}catch(er){const el=$(lid);if(el)el.outerHTML=`<div class="chat-a" style="border-color:rgba(248,113,113,.2);color:var(--red)">Erreur: ${er.message}</div>`}b.scrollTop=b.scrollHeight}
// ====================== SETTINGS ======================
function renderSettings(){$('content').innerHTML=`<div class="card"><div class="card-title" style="margin-bottom:18px">Profil</div><div style="display:grid;grid-template-columns:1fr 1fr;gap:14px"><div class="field"><label>Nom</label><input value="${esc(USER.name||'')}"></div><div class="field"><label>Email</label><input value="${esc(USER.email||'')}" readonly style="opacity:.6"></div><div class="field"><label>Entreprise</label><input value="${esc(USER.company||'')}"></div><div class="field"><label>Rôle</label><input value="${esc(USER.role||'user')}" readonly style="opacity:.6"></div></div><button class="btn btn-sm btn-accent" style="margin-top:14px" onclick="toast('Sauvegardé')">Sauvegarder</button></div><div class="card"><div class="card-title" style="margin-bottom:14px">Domaine & Auth</div><table><tbody><tr><td style="color:var(--white)">SPF</td><td style="font-family:var(--mono);font-size:11px;color:var(--muted)">v=spf1 include:spf.pmta.weval ~all</td></tr><tr><td style="color:var(--white)">DKIM</td><td style="font-family:var(--mono);font-size:11px;color:var(--muted)">weval2026._domainkey</td></tr><tr><td style="color:var(--white)">DMARC</td><td style="font-family:var(--mono);font-size:11px;color:var(--muted)">p=quarantine</td></tr><tr><td style="color:var(--white)">TLS</td><td><span class="badge badge-active">1.3 Enforced</span></td></tr></tbody></table></div>`}
// ====================== BILLING ======================
function renderBilling(){$('content').innerHTML=`<div class="stats"><div class="stat s-accent"><div class="stat-label">Plan</div><div class="stat-value" style="font-size:18px">Sovereign</div><div class="stat-sub">GPU local inclus</div></div><div class="stat s-green"><div class="stat-label">Emails / mois</div><div class="stat-value">∞</div><div class="stat-sub">Illimité</div></div><div class="stat s-email"><div class="stat-label">Canaux</div><div class="stat-value">4</div><div class="stat-sub">Email+TG+SMS+WA</div></div></div><div class="card"><div class="card-title" style="margin-bottom:14px">Infrastructure souveraine</div><p style="font-size:13px;color:var(--muted);line-height:1.7">Plan Sovereign : GPU dédié, IA locale qwen2.5-14B, routage SMTP PMTA, 4 canaux (Email, SMS, WhatsApp, Telegram), stockage illimité, zéro dépendance cloud pour l'IA.</p></div>`}
// ====================== INIT ======================
try{const st=sessionStorage.getItem('wv2t'),su=sessionStorage.getItem('wv2u');if(st&&su){TOKEN=st;USER=JSON.parse(su);enter()}}catch(e){}
fetch(API+'/health').then(r=>r.json()).then(j=>{if(j.status==='ok')$('topStatus').innerHTML='● Backend Live — '+j.version}).catch(()=>{$('topStatus').innerHTML='● Offline';$('topStatus').className='topbar-live'});
</script>
</body></html>