Files
html/ethica-chatbot.html

348 lines
19 KiB
HTML
Raw 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">
<title>Ethica — AI Chat HCP & Campagnes</title>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;700&family=JetBrains+Mono:wght@400&display=swap" rel="stylesheet">
<style>
*{margin:0;padding:0;box-sizing:border-box}
:root{--bg:#050a12;--bg2:#0c1424;--bg3:#132030;--tx:#d8e4f0;--tx2:#607890;--acc:#00c9a7;--acc2:#0ea5e9;--user:#0f2a40;--bot:#0a1828;--border:#1a3050}
body{background:var(--bg);color:var(--tx);font-family:'DM Sans',sans-serif;height:100vh;display:flex;flex-direction:column}
header{background:linear-gradient(135deg,#0a1830,#081428);padding:14px 20px;display:flex;align-items:center;gap:14px;border-bottom:1px solid var(--border)}
header .logo{font-size:28px}
header h1{font-size:17px;font-weight:700;color:var(--acc)}
header .sub{font-size:11px;color:var(--tx2)}
header .stats{margin-left:auto;display:flex;gap:12px}
header .st{text-align:center;padding:4px 12px;background:var(--bg2);border-radius:6px;border:1px solid var(--border)}
header .st .n{font-size:14px;font-weight:700;color:var(--acc);font-family:'JetBrains Mono',monospace}
header .st .l{font-size:9px;color:var(--tx2);text-transform:uppercase}
.chips{padding:10px 20px;display:flex;gap:8px;overflow-x:auto;background:var(--bg2);border-bottom:1px solid var(--border)}
.chip{padding:6px 14px;border-radius:20px;border:1px solid var(--border);background:var(--bg);font-size:12px;color:var(--tx2);cursor:pointer;white-space:nowrap;transition:.2s}
.chip:hover{border-color:var(--acc);color:var(--acc);background:rgba(0,201,167,.05)}
.chat{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:12px}
.msg{max-width:80%;padding:12px 16px;border-radius:12px;font-size:14px;line-height:1.6;animation:fadeIn .3s}
.msg.bot{background:var(--bot);border:1px solid var(--border);align-self:flex-start;border-bottom-left-radius:2px}
.msg.user{background:var(--user);align-self:flex-end;border-bottom-right-radius:2px}
.msg pre{background:var(--bg);padding:8px 12px;border-radius:6px;margin:8px 0;overflow-x:auto;font-family:'JetBrains Mono',monospace;font-size:12px}
.msg code{font-family:'JetBrains Mono',monospace;font-size:12px;background:rgba(0,201,167,.1);padding:1px 4px;border-radius:3px}
.input-bar{padding:12px 20px;background:var(--bg2);border-top:1px solid var(--border);display:flex;gap:10px}
.input-bar input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:8px;padding:12px 16px;color:var(--tx);font-size:14px;font-family:'DM Sans',sans-serif;outline:none}
.input-bar input:focus{border-color:var(--acc)}
.input-bar button{background:var(--acc);color:#000;border:none;border-radius:8px;padding:12px 20px;font-weight:700;cursor:pointer;font-size:14px}
.input-bar button:hover{opacity:.9}
.typing{color:var(--tx2);font-size:12px;padding:4px 16px;font-style:italic}
.links{padding:8px 20px;background:var(--bg);display:flex;gap:12px;border-top:1px solid var(--border)}
.links a{color:var(--tx2);font-size:11px;text-decoration:none;padding:4px 10px;border:1px solid var(--border);border-radius:4px}
.links a:hover{color:var(--acc);border-color:var(--acc)}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
@media(max-width:768px){header .stats{display:none}.msg{max-width:95%}}
</style><!-- DOCTRINE-60-UX-ENRICH direct-inject-20260424-143214 -->
<style id="doctrine60-ux-direct">
/* DOCTRINE-60-UX-ENRICH injected-direct */
body::before {
content: '';
position: fixed;
top: 0; left: 0; width: 100vw; height: 100vh;
background: radial-gradient(circle at 50% 50%, rgba(100,180,255,0.08), transparent 60%);
pointer-events: none;
z-index: -1;
}
.card, .kpi, .panel, .btn {
transition: all 0.3s cubic-bezier(0.2,0,0.1,1);
}
.card:hover, .kpi:hover, .panel:hover {
box-shadow: 0 4px 20px rgba(100,180,255,0.2);
border-color: rgba(100,180,255,0.5);
}
@keyframes pulseD60 {
0%,100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.05); }
}
.pulse, .live-indicator, .active, .online {
animation: pulseD60 3s ease-in-out infinite;
}
.modal, .chat, .speech, .overlay {
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.enter-stagger {
animation: enterStagD60 0.5s cubic-bezier(0.2,0,0.1,1) forwards;
}
@keyframes enterStagD60 {
from { opacity: 0; transform: translateY(20px); }
to { opacity: 1; transform: translateY(0); }
}
</style>
</head><body>
<header>
<span class="logo">💊</span>
<div><h1>Ethica AI — HCP Intelligence & Campagnes</h1><div class="sub">IA souveraine spécialisée pharma · Emailing · Marketing B2B</div></div>
<div class="stats" id="hdr-stats"></div>
</header>
<div class="chips" id="chips">
<div class="chip" data-q="Combien de HCPs par pays">📊 HCPs par pays</div>
<div class="chip" data-q="Quelles spécialités médicales sont couvertes">🩺 Spécialités</div>
<div class="chip" data-q="État des campagnes email Ethica">📧 Campagnes</div>
<div class="chip" data-q="Taux d'ouverture et de clic email">📈 Taux ouverture</div>
<div class="chip" data-q="Combien de senders actifs">✉️ Senders</div>
<div class="chip" data-q="Stratégie warmup email B2B pharma">🔥 Warmup</div>
<div class="chip" data-q="Réglementation RGPD données santé Maroc">⚖️ RGPD Santé</div>
<div class="chip" data-q="Comment améliorer la délivrabilité email pharma">📬 Délivrabilité</div>
<div class="chip" data-q="Analyse concurrentielle plateforme HCP Maghreb">🔍 Concurrence</div>
<div class="chip" data-q="Pricing campagne email HCP 3 paliers">💰 Pricing</div>
</div>
<div class="chat" id="chat">
<div class="msg bot">Bonjour ! Je suis <strong>Ethica AI</strong>, votre assistant spécialisé en intelligence HCP et campagnes email pharma B2B.<br><br>Je connais vos <strong id="hcp-count">131K+</strong> contacts médecins au Maghreb, les spécialités couvertes, et je peux vous aider sur la stratégie emailing, warmup, délivrabilité, RGPD santé et pricing.<br><br>Posez une question ou cliquez un raccourci ci-dessus.</div>
</div>
<div class="input-bar">
<input type="text" id="input" placeholder="Posez votre question pharma / emailing..." autocomplete="off">
<button onclick="send()">Envoyer</button>
</div>
<div class="links">
<a href="/ethica-hub.html">Hub Ethica</a>
<a href="/arsenal-proxy/ethica-dashboard.html">Dashboard</a>
<a href="/ethica-hcp-manager.html">HCP Manager</a>
<a href="/ethica-drill.html">Drill Analytics</a>
<a href="/ethica-pipeline.html">Pipeline</a>
<a href="/ethica-sms.html">SMS</a>
</div>
<script>
const API = '/api/ethica-brain.php';
const STATS_API = '/api/ethica-stats-api.php';
const ETHICA_PREFIX = '[CONTEXTE: Tu es Ethica AI, expert pharma B2B Maghreb. Spécialisé HCP (médecins, pharmaciens, dentistes), campagnes email marketing santé, warmup, délivrabilité, RGPD santé, pricing. Base: 131K+ HCPs validés (DZ 69%, MA 15%, TN 14%). 14 spécialités. 10 senders. Réponds en expert pharma/emailing concis et actionnable.] ';
const chat = document.getElementById('chat');
const input = document.getElementById('input');
// Load live stats
fetch(STATS_API).then(r=>r.text().then(t=>{/* HTML_GUARD_V2_BATCH */var q=(t||"").trim();if(q.startsWith("<!DOCTYPE")||q.startsWith("<html")){return{error:"[HTTP "+r.status+"]",isHtmlError:true}}try{return JSON.parse(q)}catch(e){return{error:"JSON "+e.message}}})).then(d=>{
const t = d.total || d.count || '131K+';
document.getElementById('hcp-count').textContent = t.toLocaleString ? t.toLocaleString('fr') : t;
document.getElementById('hdr-stats').innerHTML = `
<div class="st"><div class="n">${typeof t==='number'?t.toLocaleString('fr'):t}</div><div class="l">HCPs</div></div>
<div class="st"><div class="n">${d.countries||3}</div><div class="l">Pays</div></div>
<div class="st"><div class="n">${d.specialties||14}</div><div class="l">Spécialités</div></div>
`;
}).catch(()=>{});
// Chips
document.querySelectorAll('.chip').forEach(c=>c.onclick=()=>{input.value=c.dataset.q;send()});
// Enter key
input.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send()}});
function addMsg(text, cls){
const d=document.createElement('div');d.className='msg '+cls;
d.innerHTML=text.replace(/```(\w+)?\n([\s\S]*?)```/g,'<pre>$2</pre>').replace(/\*\*(.*?)\*\*/g,'<strong>$1</strong>').replace(/`(.*?)`/g,'<code>$1</code>').replace(/\n/g,'<br>');
chat.appendChild(d);chat.scrollTop=chat.scrollHeight;return d;
}
async function send(){
const q=input.value.trim();if(!q)return;
addMsg(q,'user');input.value='';
const typing=document.createElement('div');typing.className='typing';typing.textContent='Ethica AI réfléchit...';chat.appendChild(typing);chat.scrollTop=chat.scrollHeight;
try{
const r=await fetch(API,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:ETHICA_PREFIX+q})});
/* HTML_GUARD_V2_BATCH */ const _t_d=await r.text(); let d=null; {var _q=(_t_d||"").trim();if(_q.startsWith("<!DOCTYPE")||_q.startsWith("<html")){d={error:"[HTTP "+(r.status||"?")+"] Backend indisponible",isHtmlError:true};}else{try{d=JSON.parse(_q)}catch(e){d={error:"[JSON] "+e.message}}}}
typing.remove();
addMsg(d.response||d.content||d.error||'Erreur','bot');
}catch(e){typing.remove();addMsg('Erreur de connexion','bot')}
}
</script>
<!-- CARTO_REMOVED -->
<!-- === OPUS UNIVERSAL DRILL-DOWN v1 19avr — append-only, doctrine #14 === -->
<script>
(function(){
if (window.__opusUniversalDrill) return; window.__opusUniversalDrill = true;
var d = document;
var m = d.createElement('div');
m.id = 'opus-udrill';
m.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.82);backdrop-filter:blur(6px);display:none;align-items:center;justify-content:center;z-index:99995;padding:20px;cursor:pointer';
var inner = d.createElement('div');
inner.id = 'opus-udrill-in';
inner.style.cssText = 'max-width:900px;width:100%;max-height:90vh;overflow:auto;background:#0b0d15;border:1px solid rgba(99,102,241,0.35);border-radius:14px;padding:28px;cursor:default;box-shadow:0 20px 60px rgba(0,0,0,0.6);color:#e2e8f0;font:14px/1.55 Inter,system-ui,sans-serif';
inner.addEventListener('click', function(e){ e.stopPropagation(); });
m.appendChild(inner);
m.addEventListener('click', function(){ m.style.display='none'; });
d.addEventListener('keydown', function(e){ if(e.key==='Escape') m.style.display='none'; });
(d.body || d.documentElement).appendChild(m);
function openCard(card) {
// Clone card content + show close btn + increase font-size
var html = '<div style="display:flex;justify-content:flex-end;margin-bottom:14px"><button id="opus-udrill-close" style="padding:6px 14px;background:#171b2a;border:1px solid rgba(99,102,241,0.25);color:#e2e8f0;border-radius:8px;cursor:pointer;font-size:12px">✕ Fermer (Esc)</button></div>';
html += '<div style="transform-origin:top left;font-size:1.05em">' + card.outerHTML + '</div>';
inner.innerHTML = html;
d.getElementById('opus-udrill-close').onclick = function(){ m.style.display='none'; };
m.style.display = 'flex';
}
function wire(root) {
var sels = '.card,[class*="card"],.kpi,[class*="kpi"],.stat,[class*="stat"],.tile,[class*="tile"],.metric,[class*="metric"],.widget,[class*="widget"]';
var cards = root.querySelectorAll(sels);
for (var i = 0; i < cards.length; i++) {
var c = cards[i];
if (c.__opusWired) continue;
if (c.closest('button, a, input, select, textarea, #opus-udrill')) continue;
var r = c.getBoundingClientRect();
if (r.width < 60 || r.height < 40) continue;
c.__opusWired = true;
c.style.cursor = 'pointer';
c.setAttribute('role','button');
c.setAttribute('tabindex','0');
c.addEventListener('click', function(ev){
// If a more-specific drill is already active (e.g. pp-card custom), let it handle
if (ev.target.closest('[data-pp-id]') && window.__opusDrillInit) return;
if (ev.target.closest('a,button,input,select')) return;
ev.preventDefault(); ev.stopPropagation();
openCard(this);
});
c.addEventListener('keydown', function(ev){ if(ev.key==='Enter'||ev.key===' '){ev.preventDefault();openCard(this);} });
}
}
// Initial + mutation observer
var initRun = function(){ wire(d.body || d.documentElement); };
if (d.readyState === 'loading') d.addEventListener('DOMContentLoaded', initRun);
else initRun();
var mo = new MutationObserver(function(muts){
var newCard = false;
for (var i=0;i<muts.length;i++) if (muts[i].addedNodes.length) { newCard = true; break; }
if (newCard) initRun();
});
mo.observe(d.body || d.documentElement, {childList:true, subtree:true});
})();
</script>
<!-- === OPUS UNIVERSAL DRILL-DOWN END === -->
<script src="/api/a11y-auto-enhancer.js" defer></script>
<!-- WTP_UDOCK_V1 (Opus 21-avr t34final) --><script src="/wtp-unified-dock.js" defer></script>
<script src="/opus-antioverlap-doctrine.js?v=1776776094" defer></script>
<!-- Opus v17 · Claude Pattern SSE (auto-injected) -->
<style id="opus-pattern-style">
#opus-pattern-badge{position:fixed;bottom:20px;right:20px;z-index:99990;
background:linear-gradient(135deg,#06b6d4,#8b5cf6);color:#fff;
padding:10px 16px;border-radius:20px;font:700 0.78rem -apple-system,sans-serif;
cursor:pointer;box-shadow:0 4px 16px rgba(0,0,0,0.35);transition:all 0.2s;
display:flex;align-items:center;gap:6px}
#opus-pattern-badge:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(6,182,212,0.4)}
#opus-pattern-modal{display:none;position:fixed;inset:0;background:rgba(0,0,0,0.8);
z-index:99991;align-items:center;justify-content:center;padding:20px}
#opus-pattern-modal.show{display:flex}
#opus-pattern-box{background:#0b0d15;color:#e2e8f0;border:1px solid rgba(6,182,212,0.3);
border-radius:14px;padding:22px;max-width:820px;width:100%;max-height:85vh;overflow:auto;
font:-apple-system,sans-serif}
#opus-pattern-box h3{font:800 1.2rem;margin-bottom:12px;
background:linear-gradient(135deg,#06b6d4,#ec4899);
-webkit-background-clip:text;-webkit-text-fill-color:transparent}
#opus-pattern-input{width:100%;background:#1a1f3a;color:#fff;border:1px solid rgba(100,116,139,0.3);
border-radius:8px;padding:10px;margin-bottom:10px;font:0.9rem -apple-system}
#opus-pattern-run{background:linear-gradient(135deg,#10b981,#06b6d4);color:#fff;border:0;
padding:10px 20px;border-radius:8px;font:700 0.85rem;cursor:pointer;margin-bottom:14px}
.phase-card{background:rgba(15,23,42,0.8);border:1px solid rgba(100,116,139,0.2);
border-left:3px solid #06b6d4;border-radius:8px;padding:10px 14px;margin-bottom:8px;
font-size:0.82rem}
.phase-card.done{border-left-color:#22c55e}
.phase-card.active{border-left-color:#f59e0b;animation:pulse 1.2s ease infinite}
.phase-name{font-weight:800;color:#06b6d4;margin-bottom:4px;font-size:0.78rem;text-transform:uppercase;letter-spacing:1px}
.phase-data{font-size:0.72rem;color:#94a3b8;font-family:ui-monospace,monospace}
@keyframes pulse{0%,100%{opacity:1}50%{opacity:0.6}}
#opus-pattern-close{position:absolute;top:14px;right:20px;background:0;border:0;color:#94a3b8;
font-size:1.6rem;cursor:pointer}
</style>
<div id="opus-pattern-badge" onclick="window.__opusPatternOpen()">
<span>🧠</span><span>Claude Pattern</span>
</div>
<div id="opus-pattern-modal" onclick="if(event.target.id==='opus-pattern-modal')window.__opusPatternClose()">
<div id="opus-pattern-box">
<button id="opus-pattern-close" onclick="window.__opusPatternClose()">×</button>
<h3>🧠 Claude Pattern · 7 phases REAL (SSE live)</h3>
<p style="font-size:0.82rem;color:#94a3b8;margin-bottom:12px">Backend: <b id="opus-pattern-bot">ethica-chatbot</b> · anti-hallucination · langue naturelle</p>
<input id="opus-pattern-input" placeholder="Posez une question (FR ou EN)..." value="bonjour quel est le statut" />
<button id="opus-pattern-run" onclick="window.__opusPatternRun()">▶ Lancer (SSE stream)</button>
<div id="opus-pattern-output"></div>
</div>
</div>
<script>
(function(){
const BOT = 'ethica-chatbot';
window.__opusPatternOpen = () => document.getElementById('opus-pattern-modal').classList.add('show');
window.__opusPatternClose = () => document.getElementById('opus-pattern-modal').classList.remove('show');
window.__opusPatternRun = () => {
const msg = document.getElementById('opus-pattern-input').value.trim();
if (!msg) return;
const out = document.getElementById('opus-pattern-output');
out.innerHTML = '';
const OPUS_SESSION_KEY = 'opus_chatbot_session_' + BOT;
let sess = localStorage.getItem(OPUS_SESSION_KEY);
if (!sess) {
sess = 'opus-' + BOT + '-' + Date.now().toString(36) + '-' + Math.random().toString(36).substr(2, 6);
localStorage.setItem(OPUS_SESSION_KEY, sess);
}
// CF_BYPASS_V23 · direct 127.0.0.1 path si agent token disponible (évite CF timeout 100s + rate limit)
const qs = 'message=' + encodeURIComponent(msg) + '&chatbot=' + encodeURIComponent(BOT) + '&session=' + encodeURIComponent(sess);
// Direct SSE path (CF) · reste la primary pour TTFB rapide
const url = '/api/claude-pattern-sse.php?' + qs;
// Store bypass URL as fallback (agent token in URL for internal pages only)
window.__opusBypassUrl = '/api/cf-bypass-helper.php?target=' + encodeURIComponent('/api/claude-pattern-sse.php?' + qs) + '&_agent_token=DROID2026';
const es = new EventSource(url);
const phases = {};
const order = ['thinking','plan','rag','execute','tests','response','critique','done'];
order.forEach(p => {
const card = document.createElement('div');
card.className = 'phase-card';
card.id = 'phase-' + p;
card.innerHTML = '<div class="phase-name">' + p.toUpperCase() + '</div><div class="phase-data">⏳ waiting...</div>';
out.appendChild(card);
});
order.forEach(evName => {
es.addEventListener(evName, (e) => {
const data = JSON.parse(e.data);
const card = document.getElementById('phase-' + evName);
if (card) {
card.classList.add('done');
card.classList.remove('active');
let txt;
if (evName === 'response' && data.text) {
txt = '<div style="background:rgba(6,182,212,0.1);padding:10px;border-radius:6px;margin-top:6px;color:#e2e8f0;font-size:0.82rem">' + (data.text.substring(0, 600)) + (data.text.length > 600 ? '...' : '') + '</div>';
} else if (evName === 'tests') {
txt = '<div>' + data.passed + '/' + data.total + ' tests ✓</div>';
} else if (evName === 'critique') {
txt = '<div>Quality: <b style="color:' + (data.quality === 'EXCELLENT' ? '#22c55e' : (data.quality === 'OK' ? '#f59e0b' : '#ef4444')) + '">' + data.quality + '</b> (' + (data.quality_score * 5).toFixed(0) + '/5)</div>';
} else {
txt = JSON.stringify(data).substring(0, 300);
}
card.querySelector('.phase-data').innerHTML = txt;
}
if (evName === 'done') es.close();
});
});
es.addEventListener('error', () => es.close());
};
})();
</script>
<!-- DOCTRINE-60-UX-JS --><script id="doctrine60-ux-js-direct">
// DOCTRINE-60-UX-JS staggered entrance
(function(){
if (!('IntersectionObserver' in window)) return;
const obs = new IntersectionObserver((entries) => {
entries.forEach((e, i) => {
if (e.isIntersecting) {
setTimeout(() => e.target.classList.add('enter-stagger'), i * 80);
obs.unobserve(e.target);
}
});
});
document.querySelectorAll('.card, .kpi, .panel').forEach(el => obs.observe(el));
})();
</script>
</body></html>