Files
html/weval-leadmachine.js
2026-04-12 22:57:03 +02:00

394 lines
17 KiB
JavaScript

/**
* WEVAL Lead Machine — Widget chatbot proactif + GA4 + lead tracking
* S'injecte automatiquement sur toutes les pages landing
* Version: 1.0 — 2026-03-02
*/
(function(){
'use strict';
// ============================================
// CONFIG
// ============================================
var API = '/api/weval-ia';
var PROACTIVE_DELAY = 25000; // 25s avant popup proactif
var SCROLL_TRIGGER = 0.4; // 40% de scroll = trigger
var GA_ID = null; // GA4 ID — à remplir quand Yacine crée le compte
// ============================================
// LEAD TRACKING (envoie au backend)
// ============================================
function trackLead(data) {
try {
fetch(API, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message: '[AUTO-LEAD] ' + JSON.stringify(data),
session_id: 'autolead-' + Date.now()
})
});
} catch(e) {}
// GA4 event
if (typeof gtag === 'function') {
gtag('event', 'lead_capture', {
event_category: data.source || 'chatbot',
event_label: data.page || location.pathname,
value: 1
});
}
}
// ============================================
// PROACTIVE CHATBOT WIDGET
// ============================================
var chatState = {open: false, triggered: false, messages: [], step: 0, lead: {}};
// Messages proactifs contextuels par page
var proactiveMessages = {
'/nearshore-it-maroc': "Vous explorez le nearshore IT ? Je peux estimer vos économies en 2 minutes.",
'/services/cloud-sap': "Besoin d'aide pour votre migration SAP S/4HANA ? Je peux évaluer votre situation.",
'/services/intelligence-artificielle': "Curieux de savoir comment l'IA peut aider votre entreprise ? Posez-moi une question.",
'/services/cybersecurite': "Savez-vous si vos systèmes sont vulnérables ? Je peux vous orienter vers un audit gratuit.",
'/services/e-marketing': "Vous cherchez à générer plus de leads ? Décrivez votre situation, je vous propose un plan.",
'/services/transformation-digitale': "Prêt à digitaliser votre entreprise ? Dites-moi votre secteur, je vous guide.",
'/services/recrutement-it': "Quel profil IT recherchez-vous ? Je peux vérifier notre vivier de 5000+ talents.",
'default': "Bonjour ! Comment puis-je vous aider avec votre projet IT ?"
};
var qualifQuestions = [
{q: "Pour mieux vous orienter, quel est votre nom ?", field: 'name'},
{q: "Et votre email professionnel ?", field: 'email', validate: function(v){return v.includes('@')}},
{q: "Dans quelle entreprise travaillez-vous ?", field: 'company'},
{q: "Merci ! Un expert WEVAL vous recontacte sous 24h. En attendant, avez-vous une question ?", field: null}
];
function getProactiveMsg() {
var path = location.pathname.replace(/\/$/, '');
return proactiveMessages[path] || proactiveMessages['default'];
}
function createWidget() {
// Styles
var style = document.createElement('style');
style.textContent = [
'.wl-bubble{position:fixed;bottom:28px;right:28px;width:64px;height:64px;border-radius:50%;background:linear-gradient(135deg,#0891b2,#22d3ee);border:none;cursor:pointer;box-shadow:0 4px 24px rgba(8,145,178,0.4);z-index:99998;display:flex;align-items:center;justify-content:center;font-size:28px;transition:box-shadow .2s;animation:wl-pulse 2s infinite}',
'.wl-bubble:hover{box-shadow:0 4px 36px rgba(8,145,178,0.8)}',
'@keyframes wl-pulse{0%,100%{box-shadow:0 4px 24px rgba(8,145,178,0.4)}50%{box-shadow:0 4px 32px rgba(8,145,178,0.7)}}',
'.wl-badge{position:absolute;top:-2px;right:-2px;background:#ef4444;color:#fff;width:20px;height:20px;border-radius:50%;font-size:11px;font-weight:800;display:flex;align-items:center;justify-content:center;animation:wl-pop .3s}',
'@keyframes wl-pop{0%{transform:scale(0)}100%{transform:scale(1)}}',
'.wl-toast{position:fixed;bottom:100px;right:28px;background:#fff;border-radius:16px;box-shadow:0 8px 40px rgba(0,0,0,0.15);padding:16px 20px;max-width:280px;z-index:99997;font-family:"DM Sans",system-ui,sans-serif;font-size:14px;color:#334155;line-height:1.5;animation:wl-slideUp .4s;cursor:pointer}',
'@keyframes wl-slideUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}',
'.wl-toast::after{content:"";position:absolute;bottom:-8px;right:40px;width:16px;height:16px;background:#fff;transform:rotate(45deg);box-shadow:4px 4px 8px rgba(0,0,0,0.05)}',
'.wl-toast-close{position:absolute;top:8px;right:12px;font-size:16px;color:#94a3b8;cursor:pointer;background:none;border:none}',
'.wl-chat{position:fixed;bottom:28px;right:28px;width:370px;height:520px;background:#fff;border-radius:20px;box-shadow:0 12px 48px rgba(0,0,0,0.18);z-index:99999;display:none;flex-direction:column;font-family:"DM Sans",system-ui,sans-serif;overflow:hidden;animation:wl-slideUp .3s}',
'.wl-chat.open{display:flex}',
'.wl-chat-head{background:linear-gradient(135deg,#0c4a6e,#0891b2);color:#fff;padding:16px 20px;display:flex;justify-content:space-between;align-items:center}',
'.wl-chat-head h4{margin:0;font-size:15px;font-weight:700}',
'.wl-chat-head small{opacity:.7;font-size:12px}',
'.wl-chat-close{background:none;border:none;color:#fff;font-size:22px;cursor:pointer;opacity:.8}',
'.wl-chat-close:hover{opacity:1}',
'.wl-msgs{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px}',
'.wl-msg{max-width:82%;padding:10px 14px;border-radius:14px;font-size:13.5px;line-height:1.5;word-wrap:break-word}',
'.wl-msg.bot{background:#f1f5f9;color:#334155;align-self:flex-start;border-bottom-left-radius:4px}',
'.wl-msg.user{background:linear-gradient(135deg,#0891b2,#22d3ee);color:#fff;align-self:flex-end;border-bottom-right-radius:4px}',
'.wl-msg.typing{background:#f1f5f9;align-self:flex-start;border-bottom-left-radius:4px}',
'.wl-dots{display:flex;gap:4px}.wl-dots span{width:6px;height:6px;background:#94a3b8;border-radius:50%;animation:wl-dot .6s infinite alternate}.wl-dots span:nth-child(2){animation-delay:.2s}.wl-dots span:nth-child(3){animation-delay:.4s}',
'@keyframes wl-dot{to{opacity:.3;transform:translateY(-3px)}}',
'.wl-input-row{padding:12px 16px;border-top:1px solid #e2e8f0;display:flex;gap:8px}',
'.wl-input-row input{flex:1;border:1.5px solid #e2e8f0;border-radius:10px;padding:10px 14px;font-size:14px;font-family:inherit;outline:none}',
'.wl-input-row input:focus{border-color:#0891b2}',
'.wl-input-row button{background:#0891b2;color:#fff;border:none;border-radius:10px;padding:10px 16px;cursor:pointer;font-weight:700;font-size:14px}',
'.wl-input-row button:hover{background:#0e7490}',
'@media(max-width:768px){.wl-bubble{width:56px !important;height:56px !important;bottom:calc(140px + env(safe-area-inset-bottom,0px)) !important;right:16px !important;font-size:22px;transform:none !important;}}',
'@media(max-width:480px){.wl-chat{width:calc(100vw - 16px);height:calc(100vh - 80px);right:8px;bottom:8px;border-radius:16px}.wl-toast{max-width:calc(100vw - 80px);right:16px}.wl-bubble{width:56px !important;height:56px !important;bottom:calc(140px + env(safe-area-inset-bottom,0px)) !important;right:16px !important;font-size:22px;transform:none !important;}}'
].join('\n');
document.head.appendChild(style);
// Bubble button
var bubble = document.createElement('button');
bubble.className = 'wl-bubble';
bubble.innerHTML = '💬';
bubble.setAttribute('aria-label', 'Ouvrir le chat');
document.body.appendChild(bubble);
// Toast (proactive message)
var toast = document.createElement('div');
toast.className = 'wl-toast';
toast.style.display = 'none';
toast.innerHTML = '<button class="wl-toast-close">&times;</button><div class="wl-toast-text"></div>';
document.body.appendChild(toast);
// Chat window
var chat = document.createElement('div');
chat.className = 'wl-chat';
chat.innerHTML = [
'<div class="wl-chat-head">',
' <div><h4>WEVIA — Assistant WEVAL</h4><small>En ligne — répond en &lt;30s</small></div>',
' <button class="wl-chat-close">&times;</button>',
'</div>',
'<div class="wl-msgs" id="wlMsgs"></div>',
'<div class="wl-input-row">',
' <input type="text" id="wlInput" placeholder="Écrivez votre message...">',
' <button id="wlSend">↑</button>',
'</div>'
].join('');
document.body.appendChild(chat);
var msgsEl = document.getElementById('wlMsgs');
var inputEl = document.getElementById('wlInput');
var sendBtn = document.getElementById('wlSend');
function addMsg(text, role) {
var div = document.createElement('div');
div.className = 'wl-msg ' + role;
div.textContent = text;
msgsEl.appendChild(div);
msgsEl.scrollTop = msgsEl.scrollHeight;
chatState.messages.push({role: role, content: text});
}
function showTyping() {
var div = document.createElement('div');
div.className = 'wl-msg typing';
div.id = 'wlTyping';
div.innerHTML = '<div class="wl-dots"><span></span><span></span><span></span></div>';
msgsEl.appendChild(div);
msgsEl.scrollTop = msgsEl.scrollHeight;
}
function hideTyping() {
var t = document.getElementById('wlTyping');
if (t) t.remove();
}
function openChat() {
chatState.open = true;
chat.classList.add('open');
bubble.style.display = 'none';
toast.style.display = 'none';
inputEl.focus();
// First message if empty
if (chatState.messages.length === 0) {
addMsg(getProactiveMsg(), 'bot');
}
// GA4 event
if (typeof gtag === 'function') gtag('event', 'chat_open', {event_category: 'engagement'});
}
function closeChat() {
chatState.open = false;
chat.classList.remove('open');
bubble.style.display = 'flex';
}
async function sendMessage(text) {
if (!text.trim()) return;
addMsg(text, 'user');
inputEl.value = '';
// Lead qualification flow
var step = chatState.step;
if (step < qualifQuestions.length) {
var q = qualifQuestions[step];
if (q.field) {
if (q.validate && !q.validate(text)) {
addMsg("Hmm, ça ne semble pas être un email valide. Pouvez-vous réessayer ?", 'bot');
return;
}
chatState.lead[q.field] = text;
}
chatState.step++;
// Next question or send to API
if (chatState.step < qualifQuestions.length) {
setTimeout(function(){
addMsg(qualifQuestions[chatState.step].q, 'bot');
}, 500);
// If we just got email, track the lead
if (q.field === 'email') {
chatState.lead.page = location.pathname;
chatState.lead.source = 'chatbot_widget';
trackLead(chatState.lead);
}
return;
} else {
// Qualification complete, switch to free chat with API
chatState.lead.page = location.pathname;
chatState.lead.source = 'chatbot_widget';
trackLead(chatState.lead);
setTimeout(function(){
addMsg(qualifQuestions[chatState.step - 1].q, 'bot');
}, 500);
return;
}
}
// Free chat with API
showTyping();
try {
var res = await fetch(API, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
message: text,
session_id: 'widget-' + (chatState.lead.email || Date.now()),
language: 'fr'
})
});
var data = await res.json();
hideTyping();
addMsg(data.response || "Je suis désolé, je n'ai pas pu traiter votre demande. Contactez-nous à contact@weval-consulting.com", 'bot');
} catch(e) {
hideTyping();
addMsg("Connexion interrompue. Envoyez-nous un email à contact@weval-consulting.com", 'bot');
}
}
// Event listeners
bubble.addEventListener('click', openChat);
toast.addEventListener('click', function(e) {
if (e.target.classList.contains('wl-toast-close')) {
toast.style.display = 'none';
return;
}
openChat();
});
chat.querySelector('.wl-chat-close').addEventListener('click', closeChat);
sendBtn.addEventListener('click', function(){ sendMessage(inputEl.value); });
inputEl.addEventListener('keydown', function(e){ if(e.key==='Enter') sendMessage(inputEl.value); });
// Proactive triggers
var proactiveShown = false;
function showProactive() {
if (proactiveShown || chatState.open) return;
proactiveShown = true;
toast.querySelector('.wl-toast-text').textContent = getProactiveMsg();
toast.style.display = 'block';
// Add notification badge
var badge = document.createElement('span');
badge.className = 'wl-badge';
badge.textContent = '1';
bubble.appendChild(badge);
// GA4
if (typeof gtag === 'function') gtag('event', 'chat_proactive_shown', {event_category: 'engagement'});
// Auto-hide after 15s
setTimeout(function(){ if(!chatState.open) toast.style.display = 'none'; }, 15000);
}
// Timer trigger
setTimeout(showProactive, PROACTIVE_DELAY);
// Scroll trigger
var scrollTriggered = false;
window.addEventListener('scroll', function() {
if (scrollTriggered) return;
var scrollPct = window.scrollY / (document.body.scrollHeight - window.innerHeight);
if (scrollPct > SCROLL_TRIGGER) {
scrollTriggered = true;
setTimeout(showProactive, 2000);
}
});
// Exit intent trigger (desktop only)
if (window.innerWidth > 768) {
document.addEventListener('mouseout', function(e) {
if (e.clientY < 5 && !proactiveShown) {
showProactive();
}
});
}
}
// ============================================
// FORM TRACKING — Auto-capture all forms
// ============================================
function trackForms() {
document.querySelectorAll('form').forEach(function(form) {
form.addEventListener('submit', function() {
var data = {};
form.querySelectorAll('input, textarea').forEach(function(el) {
if (el.name && el.value) data[el.name] = el.value;
});
data.page = location.pathname;
data.source = 'form';
trackLead(data);
});
});
}
// ============================================
// SCROLL DEPTH TRACKING
// ============================================
function trackScroll() {
var milestones = [25, 50, 75, 100];
var reached = {};
window.addEventListener('scroll', function() {
var pct = Math.round(100 * window.scrollY / (document.body.scrollHeight - window.innerHeight));
milestones.forEach(function(m) {
if (pct >= m && !reached[m]) {
reached[m] = true;
if (typeof gtag === 'function') {
gtag('event', 'scroll_depth', {event_category: 'engagement', event_label: m + '%', value: m});
}
}
});
});
}
// ============================================
// TIME ON PAGE TRACKING
// ============================================
function trackTime() {
var start = Date.now();
window.addEventListener('beforeunload', function() {
var seconds = Math.round((Date.now() - start) / 1000);
if (typeof gtag === 'function' && seconds > 5) {
gtag('event', 'time_on_page', {event_category: 'engagement', value: seconds});
}
});
}
// ============================================
// CTA CLICK TRACKING
// ============================================
function trackCTAs() {
document.querySelectorAll('a[href*="#audit"], a[href*="#contact"], button[type="submit"]').forEach(function(el) {
el.addEventListener('click', function() {
if (typeof gtag === 'function') {
gtag('event', 'cta_click', {
event_category: 'conversion',
event_label: el.textContent.trim().substring(0, 50),
page: location.pathname
});
}
});
});
}
// ============================================
// INIT
// ============================================
function init() {
// Don't load on admin pages
if (location.pathname.includes('admin') || location.pathname.includes('moulChi')) return;
// Check if React ChatWidget already present (main SPA)
var existingWidget = document.querySelector('.chat-toggle-btn');
if (existingWidget) return; // Don't double up
createWidget();
trackForms();
trackScroll();
trackTime();
// Wait for DOM to settle then track CTAs
setTimeout(trackCTAs, 2000);
}
// Run on DOM ready
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();