394 lines
17 KiB
JavaScript
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">×</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 <30s</small></div>',
|
|
' <button class="wl-chat-close">×</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();
|
|
}
|
|
})();
|