264 lines
26 KiB
HTML
264 lines
26 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="fr">
|
|
<head>
|
|
<link rel="icon" href="/favicon.ico" type="image/x-icon">
|
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png">
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Ethica Pharma — Sponsor Dashboard</title>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
<style>
|
|
:root{--bg:#f0f4f8;--bg2:#fff;--bg3:#f8fafc;--border:#e2e8f0;--text:#1e293b;--text2:#475569;--text3:#94a3b8;
|
|
--ethica:#0d9488;--ethica-light:#ccfbf1;--ethica-dark:#065f46;--blue:#3b82f6;--purple:#8b5cf6;--orange:#f59e0b;--red:#ef4444;--green:#22c55e;}
|
|
*{margin:0;padding:0;box-sizing:border-box;}
|
|
body{background:var(--bg);color:var(--text);font-family:'Inter',sans-serif;min-height:100vh;}
|
|
.header{background:linear-gradient(135deg,#0d9488,#065f46);color:#fff;padding:20px 32px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100;box-shadow:0 2px 12px rgba(13,148,136,.3);}
|
|
.header-left{display:flex;align-items:center;gap:14px;}
|
|
.header-logo{width:40px;height:40px;background:#fff;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;}
|
|
.header h1{font-size:20px;font-weight:700;}
|
|
.header-sub{font-size:12px;opacity:.8;}
|
|
.header-right{display:flex;gap:12px;align-items:center;}
|
|
.header-chip{background:rgba(255,255,255,.15);border:1px solid rgba(255,255,255,.25);border-radius:20px;padding:5px 14px;font-size:11px;display:flex;align-items:center;gap:6px;}
|
|
.header-chip .dot{width:7px;height:7px;border-radius:50%;background:#4ade80;animation:pulse 2s infinite;}
|
|
@keyframes pulse{50%{opacity:.4;}}
|
|
.tabs{display:flex;gap:0;background:#fff;border-bottom:1px solid var(--border);padding:0 32px;overflow-x:auto;}
|
|
.tab{padding:12px 18px;font-size:13px;font-weight:600;color:var(--text3);cursor:pointer;border-bottom:2px solid transparent;transition:all .2s;white-space:nowrap;}
|
|
.tab:hover{color:var(--text);background:#f8fafc;}
|
|
.tab.active{color:var(--ethica);border-bottom-color:var(--ethica);}
|
|
.content{max-width:1400px;margin:0 auto;padding:24px 32px;}
|
|
.panel{display:none;}.panel.active{display:block;}
|
|
.stats-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:14px;margin-bottom:24px;}
|
|
.stat-card{background:#fff;border-radius:12px;padding:16px 18px;box-shadow:0 1px 3px rgba(0,0,0,.08);border-left:3px solid var(--ethica);}
|
|
.stat-card.blue{border-left-color:var(--blue);}.stat-card.purple{border-left-color:var(--purple);}.stat-card.orange{border-left-color:var(--orange);}
|
|
.stat-card.red{border-left-color:var(--red);}.stat-card.green{border-left-color:var(--green);}
|
|
.stat-label{font-size:11px;color:var(--text3);text-transform:uppercase;letter-spacing:.5px;font-weight:600;}
|
|
.stat-val{font-size:28px;font-weight:800;margin-top:4px;}
|
|
.stat-sub{font-size:11px;color:var(--text3);margin-top:2px;}
|
|
.card{background:#fff;border-radius:12px;box-shadow:0 1px 3px rgba(0,0,0,.08);margin-bottom:20px;overflow:hidden;}
|
|
.card-head{padding:16px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;}
|
|
.card-head h3{font-size:15px;font-weight:700;display:flex;align-items:center;gap:8px;}
|
|
.card-body{padding:20px;}
|
|
.grid-2{display:grid;grid-template-columns:1fr 1fr;gap:20px;}
|
|
.tbl{width:100%;border-collapse:collapse;font-size:13px;}
|
|
.tbl th{text-align:left;padding:10px 12px;font-weight:600;color:var(--text3);font-size:11px;text-transform:uppercase;letter-spacing:.5px;border-bottom:2px solid var(--border);}
|
|
.tbl td{padding:10px 12px;border-bottom:1px solid #f1f5f9;}
|
|
.tbl tr:hover td{background:#f8fafc;}
|
|
.badge{display:inline-block;padding:3px 10px;border-radius:10px;font-size:10px;font-weight:700;}
|
|
.badge-green{background:#dcfce7;color:#16a34a;}.badge-blue{background:#dbeafe;color:#2563eb;}
|
|
.badge-orange{background:#fef3c7;color:#d97706;}.badge-red{background:#fee2e2;color:#dc2626;}
|
|
.badge-gray{background:#f1f5f9;color:#64748b;}.badge-teal{background:var(--ethica-light);color:var(--ethica-dark);}
|
|
.progress-bar{height:8px;background:#e2e8f0;border-radius:4px;overflow:hidden;}
|
|
.progress-fill{height:100%;border-radius:4px;transition:width .5s;}
|
|
.form-group{margin-bottom:14px;}
|
|
.form-group label{display:block;font-size:12px;font-weight:600;color:var(--text2);margin-bottom:4px;}
|
|
.form-group input,.form-group select,.form-group textarea{width:100%;padding:9px 12px;border:1px solid var(--border);border-radius:8px;font-size:13px;font-family:inherit;}
|
|
.form-group textarea{min-height:120px;resize:vertical;}
|
|
.btn{padding:9px 20px;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;transition:all .2s;}
|
|
.btn-primary{background:var(--ethica);color:#fff;}.btn-primary:hover{background:var(--ethica-dark);}
|
|
.btn-sm{padding:5px 12px;font-size:11px;}
|
|
.btn-outline{background:transparent;border:1px solid var(--border);color:var(--text2);}.btn-outline:hover{border-color:var(--ethica);color:var(--ethica);}
|
|
.btn-row{display:flex;gap:8px;flex-wrap:wrap;margin-top:12px;}
|
|
.filters{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px;align-items:center;}
|
|
.filters select,.filters input{padding:7px 12px;border:1px solid var(--border);border-radius:8px;font-size:12px;}
|
|
.toast{position:fixed;top:20px;right:20px;background:#065f46;color:#fff;padding:12px 20px;border-radius:10px;font-size:13px;font-weight:600;z-index:9999;display:none;box-shadow:0 4px 20px rgba(0,0,0,.2);}
|
|
.toast.show{display:block;animation:slideIn .3s;}
|
|
@keyframes slideIn{from{transform:translateX(100px);opacity:0;}}
|
|
.nav-links{display:flex;gap:8px;}
|
|
.nav-links a{color:#fff;text-decoration:none;font-size:11px;padding:4px 10px;border:1px solid rgba(255,255,255,.3);border-radius:6px;transition:all .15s;}
|
|
.nav-links a:hover{background:rgba(255,255,255,.15);}
|
|
@media(max-width:768px){.content{padding:16px;}.grid-2{grid-template-columns:1fr;}.stats-row{grid-template-columns:repeat(2,1fr);}}
|
|
.in-iframe nav{display:none!important}.in-iframe .hero{padding-top:3rem!important;min-height:auto!important}.in-iframe footer{display:none!important}.in-iframe .cta{display:none!important}.in-iframe .wv-links{display:none!important}
|
|
body{background:#0b0d14!important;color:#e2e8f0!important}
|
|
section{background:transparent!important}
|
|
h1,h2,h3,h4{color:#f1f5f9!important}
|
|
h1 em,h2 em{color:#d4a843!important}
|
|
p,li,span,label,small{color:#94a3b8!important}
|
|
b,strong{color:#e2e8f0!important}
|
|
a{color:#60a5fa!important}
|
|
input,select,textarea{background:#0b0d14!important;color:#e2e8f0!important;border:1px solid #1e293b!important;border-radius:8px!important}
|
|
input::placeholder,textarea::placeholder{color:#475569!important}
|
|
.hero,.cta,[class*="hero"],[class*="cta"]{background:transparent!important}
|
|
[class*="card"],[class*="plan"],[class*="feat"],[class*="block"],[class*="item"],[class*="tier"]{background:linear-gradient(135deg,#111827,#0f172a)!important;border:1px solid #1e293b!important;border-radius:12px!important}
|
|
[class*="stat"]{background:rgba(255,255,255,.03)!important;border:1px solid #1e293b!important;border-radius:12px!important}
|
|
table{border-color:#1e293b!important}
|
|
th{background:#111827!important;color:#94a3b8!important;border-color:#1e293b!important}
|
|
td{border-color:#1e293b!important;color:#e2e8f0!important}
|
|
.badge,[class*="badge"]{background:rgba(212,168,67,.1)!important;color:#d4a843!important;border:1px solid rgba(212,168,67,.2)!important}
|
|
footer,nav{display:none!important}
|
|
hr{border-color:#1e293b!important}
|
|
code,pre{background:#1e293b!important;color:#e2e8f0!important}
|
|
div[style*="background: #f"],div[style*="background:#f"],div[style*="background: white"],div[style*="background:white"],div[style*="background-color: #f"],div[style*="background-color:#f"]{background:#111827!important}
|
|
input,select,textarea{background:#0b0d14!important;color:#e2e8f0!important;border:1px solid #1e293b!important;border-radius:8px!important}input::placeholder{color:#475569!important}</style>
|
|
<link rel="canonical" href="https://weval-consulting.com/products/ethica.html">
|
|
<meta property="og:title" content="Ethica Pharma — Sponsor Dashboard">
|
|
<meta property="og:description" content="ethica - Solutions IA souveraines pour entreprises. France · Maroc · États-Unis · International">
|
|
<meta property="og:url" content="https://weval-consulting.com/products/ethica.html">
|
|
<meta property="og:type" content="website">
|
|
<meta property="og:site_name" content="WEVAL Consulting">
|
|
<meta property="og:image" content="https://weval-consulting.com/assets/logo-weval-png-DChrMGao.png">
|
|
<meta name="twitter:card" content="summary">
|
|
<meta name="twitter:title" content="Ethica Pharma — Sponsor Dashboard">
|
|
<meta name="twitter:description" content="ethica - Solutions IA souveraines pour entreprises. France · Maroc · États-Unis · International">
|
|
<meta name="description" content="ethica - Solutions IA souveraines pour entreprises. France · Maroc · États-Unis · International">
|
|
<link rel="alternate" hreflang="fr" href="https://weval-consulting.com/products/ethica.html">
|
|
<link rel="alternate" hreflang="x-default" href="https://weval-consulting.com/products/ethica.html">
|
|
<script>if(window!==window.top)document.documentElement.classList.add("in-iframe")</script>
|
|
<link rel="stylesheet" href="/assets/dark-iframe.css"></head>
|
|
<body>
|
|
<div class="header">
|
|
<div class="header-left">
|
|
<div class="header-logo">💊</div>
|
|
<div><h1>Ethica Pharma</h1><div class="header-sub">Sponsor B2B — Médecins — Maghreb, MENA & Europe</div></div>
|
|
</div>
|
|
<div class="header-right">
|
|
<div class="header-chip"><span class="dot"></span> <span id="hdr-status">Live</span></div>
|
|
<div class="header-chip" id="hdr-contacts">📊 --</div>
|
|
<div class="header-chip" id="hdr-sent">📧 --</div>
|
|
<div class="nav-links">
|
|
<a href="/ethica-drill.html"><i class="fas fa-search"></i> Drill</a>
|
|
<a href="/ethica-hcp-manager.html"><i class="fas fa-flask"></i> Validator</a> <a href="/ethica-sms.html"><i class="fas fa-mobile-alt"></i> SMS</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tabs">
|
|
<div class="tab active" onclick="showTab('overview')"><i class="fas fa-chart-pie"></i> Vue d'ensemble</div>
|
|
<div class="tab" onclick="showTab('contacts')"><i class="fas fa-user-md"></i> Contacts</div>
|
|
<div class="tab" onclick="showTab('campaigns')"><i class="fas fa-paper-plane"></i> Campagnes</div>
|
|
<div class="tab" onclick="showTab('senders')"><i class="fas fa-envelope"></i> Senders</div>
|
|
<div class="tab" onclick="showTab('import')"><i class="fas fa-file-import"></i> Import CSV</div>
|
|
</div>
|
|
<div class="content">
|
|
<div class="toast" id="toast"></div>
|
|
|
|
<div class="panel active" id="overview">
|
|
<div class="stats-row">
|
|
<div class="stat-card"><div class="stat-label">Contacts Actifs</div><div class="stat-val" id="s-contacts" style="color:var(--ethica)">--</div><div class="stat-sub">MA / TN / DZ</div></div>
|
|
<div class="stat-card blue"><div class="stat-label">Envoyés (mois)</div><div class="stat-val" id="s-sent-month" style="color:var(--blue)">--</div><div class="stat-sub">Objectif: 20,000</div></div>
|
|
<div class="stat-card green"><div class="stat-label">Taux Ouverture</div><div class="stat-val" id="s-open-rate" style="color:var(--green)">--</div></div>
|
|
<div class="stat-card purple"><div class="stat-label">Taux Clic</div><div class="stat-val" id="s-click-rate" style="color:var(--purple)">--</div></div>
|
|
<div class="stat-card orange"><div class="stat-label">Campagnes</div><div class="stat-val" id="s-campaigns" style="color:var(--orange)">--</div></div>
|
|
<div class="stat-card red"><div class="stat-label">Bounces</div><div class="stat-val" id="s-bounces" style="color:var(--red)">--</div></div>
|
|
<div class="stat-card"><div class="stat-label">Senders</div><div class="stat-val" id="s-senders" style="color:var(--ethica)">--</div></div>
|
|
<div class="stat-card green"><div class="stat-label">Envoyés Total</div><div class="stat-val" id="s-sent-total" style="color:var(--green)">--</div></div>
|
|
</div>
|
|
<div class="grid-2">
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-globe-africa"></i> Contacts par Pays</h3></div><div class="card-body" id="chart-pays">Chargement...</div></div>
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-stethoscope"></i> Contacts par Spécialité</h3></div><div class="card-body" id="chart-spec">Chargement...</div></div>
|
|
</div>
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-history"></i> Campagnes Récentes</h3></div><div class="card-body"><table class="tbl"><thead><tr><th>Nom</th><th>Status</th><th>Envoyés</th><th>Opens</th><th>Clicks</th><th>Date</th></tr></thead><tbody id="tbl-campaigns"><tr><td colspan="6" style="color:var(--text3)">Chargement...</td></tr></tbody></table></div></div>
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-chart-bar"></i> Objectif Mensuel</h3><span id="vol-label" style="font-size:12px;color:var(--text3)">-- / 20,000</span></div><div class="card-body"><div class="progress-bar"><div class="progress-fill" id="vol-bar" style="width:0%;background:var(--ethica);"></div></div></div></div>
|
|
</div>
|
|
|
|
<div class="panel" id="contacts">
|
|
<div class="filters">
|
|
<select id="f-spec" onchange="loadContacts()"><option value="">Toutes spécialités</option></select>
|
|
<select id="f-pays" onchange="loadContacts()"><option value="">Tous pays</option><option value="MA">🇲🇦 Maroc</option><option value="TN">🇹🇳 Tunisie</option><option value="DZ">🇩🇿 Algérie</option></select>
|
|
<input type="text" id="f-search" placeholder="Rechercher..." oninput="clearTimeout(window._cs);window._cs=setTimeout(loadContacts,400);" style="width:250px;">
|
|
<span id="contacts-count" style="font-size:12px;color:var(--text3);margin-left:auto;">--</span>
|
|
</div>
|
|
<div class="card"><div class="card-body" style="overflow-x:auto;"><table class="tbl"><thead><tr><th>Email</th><th>Nom</th><th>Spécialité</th><th>Ville</th><th>Pays</th><th>Téléphone</th><th>Adresse</th><th>Valid</th><th>Consent</th><th>Envois</th></tr></thead><tbody id="tbl-contacts"><tr><td colspan="10" style="color:var(--text3)">Chargement...</td></tr></tbody></table></div></div>
|
|
<div class="btn-row" id="contacts-pager"></div>
|
|
</div>
|
|
|
|
<div class="panel" id="campaigns">
|
|
<div class="grid-2">
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-plus-circle"></i> Nouvelle Campagne</h3></div><div class="card-body">
|
|
<div class="form-group"><label>Nom</label><input type="text" id="c-name" placeholder="Newsletter Mars 2026"></div>
|
|
<div class="form-group"><label>Objet</label><input type="text" id="c-subject" placeholder="Dr {{PRENOM}}, nouveautés {{SPECIALITE}}"></div>
|
|
<div class="form-group"><label>Expéditeur</label><input type="text" id="c-from-name" value="Ethica Pharma"></div>
|
|
<div class="form-group"><label>Spécialités ciblées</label><input type="text" id="c-specs" placeholder="generaliste,gastro-enterologue"></div>
|
|
<div class="form-group"><label>Pays</label><input type="text" id="c-pays" placeholder="MA,TN,DZ"></div>
|
|
<div class="form-group"><label>Creative HTML</label><textarea id="c-html" placeholder="{{NOM}} {{PRENOM}} {{SPECIALITE}} {{VILLE}} {{PAYS}}"></textarea></div>
|
|
<button class="btn btn-primary" onclick="createCampaign()"><i class="fas fa-save"></i> Créer</button>
|
|
</div></div>
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-list"></i> Campagnes</h3></div><div class="card-body" id="campaigns-list">Chargement...</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel" id="senders">
|
|
<div class="grid-2">
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-envelope"></i> Senders Ethica</h3></div><div class="card-body" id="senders-list">Chargement...</div></div>
|
|
<div class="card"><div class="card-head"><h3><i class="fas fa-plus"></i> Ajouter</h3></div><div class="card-body">
|
|
<div class="form-group"><label>Email</label><input type="text" id="snd-email" placeholder="sender@accoff04.onmicrosoft.com"></div>
|
|
<div class="form-group"><label>Tenant</label><input type="text" id="snd-tenant" placeholder="accoff04.onmicrosoft.com"></div>
|
|
<div class="form-group"><label>Limite/jour</label><input type="number" id="snd-limit" value="50"></div>
|
|
<button class="btn btn-primary" onclick="addSender()"><i class="fas fa-plus"></i> Ajouter</button>
|
|
<button class="btn btn-outline" onclick="resetDaily()" style="margin-left:8px;"><i class="fas fa-redo"></i> Reset</button>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="panel" id="import">
|
|
<div class="card" style="max-width:700px;"><div class="card-head"><h3><i class="fas fa-file-csv"></i> Import CSV</h3></div><div class="card-body">
|
|
<p style="font-size:13px;color:var(--text2);margin-bottom:12px;">Format: <code>email,nom,prenom,spécialité,ville,pays</code></p>
|
|
<div class="form-group"><textarea id="csv-data" rows="12" placeholder="dr.benali@gmail.com,Benali,Ahmed,generaliste,Casablanca,MA"></textarea></div>
|
|
<button class="btn btn-primary" onclick="importCSV()"><i class="fas fa-upload"></i> Importer</button>
|
|
<div id="import-result" style="margin-top:12px;"></div>
|
|
</div></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
const API='/api/ethica-api.php';
|
|
const paysFlags={MA:'🇲🇦',TN:'🇹🇳',DZ:'🇩🇿'};
|
|
const statusBadge={active:'badge-green',draft:'badge-gray',paused:'badge-orange',completed:'badge-blue',failed:'badge-red'};
|
|
const valBadge={valid:'badge-green',invalid:'badge-red',risky:'badge-orange',catch_all:'badge-blue',unknown:'badge-gray'};
|
|
const conBadge={pending:'badge-gray','opt-in':'badge-green','opt-out':'badge-red',implicit:'badge-blue'};
|
|
|
|
function showTab(id){document.querySelectorAll('.panel').forEach(p=>p.classList.remove('active'));document.querySelectorAll('.tab').forEach(t=>t.classList.remove('active'));document.getElementById(id).classList.add('active');event.target.closest('.tab').classList.add('active');if(id==='contacts')loadContacts();if(id==='campaigns')loadCampaigns();if(id==='senders')loadSenders();}
|
|
function toast(m){const t=document.getElementById('toast');t.textContent=m;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),3000);}
|
|
|
|
async function loadDashboard(){
|
|
try{
|
|
const r=await(await fetch(API+'?action=dashboard')).json();
|
|
document.getElementById('s-contacts').textContent=r.contacts_active.toLocaleString();
|
|
document.getElementById('s-sent-month').textContent=r.sends_month.toLocaleString();
|
|
document.getElementById('s-open-rate').textContent=r.open_rate+'%';
|
|
document.getElementById('s-click-rate').textContent=r.click_rate+'%';
|
|
document.getElementById('s-campaigns').textContent=r.campaigns_total;
|
|
document.getElementById('s-bounces').textContent=r.bounces_total.toLocaleString();
|
|
document.getElementById('s-senders').textContent=r.senders;
|
|
document.getElementById('s-sent-total').textContent=r.sends_total.toLocaleString();
|
|
document.getElementById('hdr-contacts').textContent='📊 '+r.contacts_active.toLocaleString();
|
|
document.getElementById('hdr-sent').textContent='📧 '+r.sends_total.toLocaleString();
|
|
const pct=Math.min(100,r.sends_month/20000*100);
|
|
document.getElementById('vol-bar').style.width=pct+'%';
|
|
document.getElementById('vol-label').textContent=r.sends_month.toLocaleString()+' / 20,000';
|
|
let ph='';(r.contacts_by_pays||[]).forEach(([p,c])=>{const pc=r.contacts_active>0?(c/r.contacts_active*100).toFixed(1):0;ph+=`<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;"><span style="font-size:20px;">${paysFlags[p]||p}</span><div style="flex:1;"><div class="progress-bar"><div class="progress-fill" style="width:${pc}%;background:var(--ethica);"></div></div></div><span style="font-size:13px;font-weight:600;min-width:100px;text-align:right;">${c.toLocaleString()} (${pc}%)</span></div>`;});
|
|
document.getElementById('chart-pays').innerHTML=ph||'<p style="color:var(--text3)">Aucun</p>';
|
|
let sh='';(r.contacts_by_spec||[]).forEach(([s,c])=>{const pc=r.contacts_active>0?(c/r.contacts_active*100).toFixed(1):0;sh+=`<div style="display:flex;align-items:center;gap:10px;margin-bottom:8px;"><span style="font-size:12px;font-weight:600;min-width:140px;">${s}</span><div style="flex:1;"><div class="progress-bar"><div class="progress-fill" style="width:${pc}%;background:var(--purple);"></div></div></div><span style="font-size:12px;font-weight:600;min-width:70px;text-align:right;">${c.toLocaleString()}</span></div>`;});
|
|
document.getElementById('chart-spec').innerHTML=sh||'<p style="color:var(--text3)">Aucun</p>';
|
|
let ch='';(r.recent_campaigns||[]).forEach(c=>{ch+=`<tr><td><strong>${c.name}</strong></td><td><span class="badge ${statusBadge[c.status]||'badge-gray'}">${c.status}</span></td><td>${c.sent_count||0}</td><td>${c.open_count||0}</td><td>${c.click_count||0}</td><td style="font-size:12px;color:var(--text3)">${new Date(c.created_at).toLocaleDateString('fr-FR')}</td></tr>`;});
|
|
document.getElementById('tbl-campaigns').innerHTML=ch||'<tr><td colspan="6" style="color:var(--text3)">Aucune campagne</td></tr>';
|
|
// Populate specs filter
|
|
const specs=await(await fetch(API+'?action=spécialités')).json();
|
|
let so='<option value="">Toutes spécialités</option>';
|
|
(specs.spécialités||[]).forEach(s=>so+=`<option value="${s.code}">${s.label}</option>`);
|
|
document.getElementById('f-spec').innerHTML=so;
|
|
}catch(e){console.error(e);}
|
|
}
|
|
async function loadContacts(page=1){
|
|
const spec=document.getElementById('f-spec').value,pays=document.getElementById('f-pays').value,search=document.getElementById('f-search').value;
|
|
let url=`${API}?action=contacts&page=${page}&limit=50`;
|
|
if(spec)url+=`&spécialité=${spec}`;if(pays)url+=`&pays=${pays}`;if(search)url+=`&search=${encodeURIComponent(search)}`;
|
|
const d=await(await fetch(url)).json();
|
|
document.getElementById('contacts-count').textContent=`${d.total.toLocaleString()} contacts — p${d.page}/${d.pages}`;
|
|
let h='';(d.contacts||[]).forEach(c=>{h+=`<tr><td style="font-weight:500">${c.email}</td><td>${c.nom||''} ${c.prenom||''}</td><td><span class="badge badge-teal">${c.spécialité}</span></td><td>${c.ville||''}</td><td>${paysFlags[c.pays]||c.pays}</td><td style="font-size:11px">${c.telephone||''}</td><td style="font-size:10px;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${c.adresse||''}</td><td style="font-size:11px">${c.telephone||''}</td><td style="font-size:10px;max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${c.adresse||''}</td><td><span class="badge ${valBadge[c.email_valid]||'badge-gray'}">${c.email_valid||'?'}</span></td><td><span class="badge ${conBadge[c.consent_status]||'badge-gray'}">${c.consent_status||'pending'}</span></td><td>${c.sends_count||0}</td></tr>`;});
|
|
document.getElementById('tbl-contacts').innerHTML=h||'<tr><td colspan="10" style="color:var(--text3)">Aucun</td></tr>';
|
|
let pg='';for(let i=1;i<=Math.min(d.pages,10);i++)pg+=`<button class="btn btn-sm ${i===d.page?'btn-primary':'btn-outline'}" onclick="loadContacts(${i})">${i}</button>`;
|
|
document.getElementById('contacts-pager').innerHTML=pg;
|
|
}
|
|
async function loadCampaigns(){const r=await(await fetch(API+'?action=campaigns')).json();let h='';(r.campaigns||[]).forEach(c=>{h+=`<div style="border:1px solid var(--border);border-radius:10px;padding:14px;margin-bottom:10px;"><div style="display:flex;justify-content:space-between;"><strong>${c.name}</strong><span class="badge ${statusBadge[c.status]||'badge-gray'}">${c.status}</span></div><div style="font-size:12px;color:var(--text3);margin:6px 0;">📧 ${c.sent_count||0} | 👁 ${c.open_count||0} | 🖱 ${c.click_count||0}</div><div class="btn-row">${c.status==='draft'?`<button class="btn btn-sm btn-primary" onclick="activateCamp(${c.id})">▶ Activer</button>`:c.status==='active'?`<button class="btn btn-sm btn-outline" onclick="pauseCamp(${c.id})">⏸ Pause</button>`:''}</div></div>`;});document.getElementById('campaigns-list').innerHTML=h||'<p style="color:var(--text3)">Aucune</p>';}
|
|
async function createCampaign(){const fd=new FormData();fd.append('action','campaign_create');['name','subject_line','from_name','target_spécialités','target_pays','creative_html'].forEach((k,i)=>{const ids=['c-name','c-subject','c-from-name','c-specs','c-pays','c-html'];fd.append(k,document.getElementById(ids[i]).value);});const r=await(await fetch(API,{method:'POST',body:fd})).json();if(r.success){toast('✅ Campagne créée');loadCampaigns();}else toast('❌ '+(r.error||'Erreur'));}
|
|
async function activateCamp(id){const fd=new FormData();fd.append('action','campaign_activate');fd.append('id',id);await fetch(API,{method:'POST',body:fd});toast('✅ Activée');loadCampaigns();}
|
|
async function pauseCamp(id){const fd=new FormData();fd.append('action','campaign_pause');fd.append('id',id);await fetch(API,{method:'POST',body:fd});toast('⏸ Pausée');loadCampaigns();}
|
|
async function loadSenders(){const r=await(await fetch(API+'?action=senders')).json();let h='<table class="tbl"><thead><tr><th>Email</th><th>Tenant</th><th>Limite</th><th>Envoyés</th><th>Status</th></tr></thead><tbody>';(r.senders||[]).forEach(s=>{const pct=s.daily_limit>0?(s.sent_today/s.daily_limit*100).toFixed(0):0;h+=`<tr><td style="font-weight:500">${s.email}</td><td style="color:var(--text3)">${s.tenant||'—'}</td><td>${s.daily_limit}</td><td>${s.sent_today} <div class="progress-bar" style="width:80px;display:inline-block;vertical-align:middle;"><div class="progress-fill" style="width:${pct}%;background:${pct>80?'var(--orange)':'var(--green)'}"></div></div></td><td><span class="badge ${s.status==='active'?'badge-green':'badge-red'}">${s.status}</span></td></tr>`;});h+='</tbody></table>';document.getElementById('senders-list').innerHTML=h;}
|
|
async function addSender(){const fd=new FormData();fd.append('action','add_sender');fd.append('email',document.getElementById('snd-email').value);fd.append('tenant',document.getElementById('snd-tenant').value);fd.append('daily_limit',document.getElementById('snd-limit').value);await fetch(API,{method:'POST',body:fd});toast('✅ Ajouté');loadSenders();}
|
|
async function resetDaily(){const fd=new FormData();fd.append('action','reset_daily');await fetch(API,{method:'POST',body:fd});toast('🔄 Reset');loadSenders();}
|
|
async function importCSV(){const csv=document.getElementById('csv-data').value;if(!csv.trim()){toast('❌ Vide');return;}const fd=new FormData();fd.append('action','import_contacts');fd.append('csv',csv);const r=await(await fetch(API,{method:'POST',body:fd})).json();document.getElementById('import-result').innerHTML=r.success?`<span style="color:var(--green);font-weight:600;">✅ ${r.imported} importés, ${r.duplicates} doublons</span>`:`<span style="color:var(--red);">❌ ${r.error||'Erreur'}</span>`;if(r.success)toast(`✅ ${r.imported} importés`);}
|
|
loadDashboard();setInterval(loadDashboard,30000);
|
|
</script>
|
|
</body>
|
|
</html>
|