nis2-agile/public/mobile-conversion.js
DevEnv nis2-agile 1d934e4e63 [FEAT] UI: guida online, landing EN, mobile-conversion, ai-assistant, bug-reporter + help/i18n
- public/guida.html, index-en.html, service-continuity.html
- public/js/ai-assistant.js, bug-reporter.js (FAB supporto)
- public/mobile-conversion.css/js
- index.html, common.js, help.js, risks.html: aggiornamenti UI/help

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 15:42:00 +02:00

193 lines
7.4 KiB
JavaScript

/* ════════════════════════════════════════════════════════════════
* Mobile Conversion Layer — Agile Technology suite
* Versione 1.0 — 2026-05-18
*
* Si attiva solo su mobile (≤768px).
* Configurabile via <script data-*>:
*
* <script src="mobile-conversion.js"
* data-primary-text="Prenota demo"
* data-primary-action="agilehub-open"
* data-primary-href="#contact"
* data-phone="+390000000000"
* data-whatsapp="390000000000"
* data-wa-message="Buongiorno, vorrei info su…"
* data-show-wa-fab="false"
* data-trust-items='["12 moduli","4 anni","AI"]'
* data-trust-after="section.hero"
* data-trust-theme="dark"
* data-accent="#E31E24"
* data-accent2="#B71518"
* data-scroll-trigger="400"
* defer></script>
*
* data-primary-action:
* - "agilehub-open" → apre la modale AgileHubApplet (se presente)
* - "anchor" → smooth scroll a data-primary-href
* - "external" → location.assign(data-primary-href)
* ════════════════════════════════════════════════════════════════ */
(function () {
'use strict';
/* ── Config dal <script data-*> ── */
var script = document.currentScript || (function () {
var s = document.getElementsByTagName('script');
for (var i = s.length - 1; i >= 0; i--) {
if (s[i].src && s[i].src.indexOf('mobile-conversion') > -1) return s[i];
}
return s[s.length - 1];
})();
var ds = script.dataset || {};
var CFG = {
primaryText: ds.primaryText || 'Contattaci',
primaryAction: ds.primaryAction || 'anchor',
primaryHref: ds.primaryHref || '#contact',
primaryIcon: ds.primaryIcon || '→',
phone: ds.phone || '',
whatsapp: ds.whatsapp || '',
waMessage: ds.waMessage || '',
showWaFab: ds.showWaFab === 'true',
trustItems: ds.trustItems || null, // JSON array
trustAfter: ds.trustAfter || null, // CSS selector
trustTheme: ds.trustTheme || 'light', // 'light' | 'dark'
accent: ds.accent || '#E31E24',
accent2: ds.accent2 || '#B71518',
scrollTrigger: parseInt(ds.scrollTrigger || '400', 10)
};
/* ── Bail-out se desktop o se siamo in un iframe ── */
if (window.innerWidth > 768) return;
/* ── Inject CSS variables override (se accent custom) ── */
if (CFG.accent !== '#E31E24' || CFG.accent2 !== '#B71518') {
var st = document.createElement('style');
st.textContent = ':root{--mobconv-accent:'+CFG.accent+';--mobconv-accent2:'+CFG.accent2+'}';
document.head.appendChild(st);
}
/* ── Parse trust items ── */
var trustList = null;
if (CFG.trustItems) {
try { trustList = JSON.parse(CFG.trustItems); } catch (e) { trustList = null; }
}
/* ── Costruisci WhatsApp URL ── */
function waUrl() {
if (!CFG.whatsapp) return null;
var msg = CFG.waMessage ? '?text=' + encodeURIComponent(CFG.waMessage) : '';
return 'https://wa.me/' + CFG.whatsapp + msg;
}
/* ── Render sticky bar ── */
function buildSticky() {
var html = ['<div class="mobconv-sticky" id="mobconv-sticky"><div class="mobconv-sticky-row">'];
if (CFG.phone) {
html.push('<a class="mobconv-pill" href="tel:' + CFG.phone.replace(/\s/g,'') + '" aria-label="Telefono">📞</a>');
}
var wa = waUrl();
if (wa) {
html.push('<a class="mobconv-pill wa" href="' + wa + '" target="_blank" rel="noopener" aria-label="WhatsApp">💬</a>');
}
// Primary CTA
var ctaAttrs = 'class="mobconv-cta" id="mobconv-primary"';
if (CFG.primaryAction === 'agilehub-open') {
ctaAttrs += ' href="#" data-agilehub-open';
} else if (CFG.primaryAction === 'external') {
ctaAttrs += ' href="' + CFG.primaryHref + '" target="_blank" rel="noopener"';
} else {
ctaAttrs += ' href="' + CFG.primaryHref + '"';
}
html.push('<a ' + ctaAttrs + '>' + CFG.primaryText + ' <span style="opacity:.85">' + CFG.primaryIcon + '</span></a>');
html.push('</div></div>');
return html.join('');
}
/* ── Render WhatsApp FAB ── */
function buildFab() {
if (!CFG.showWaFab || !CFG.whatsapp) return '';
return '<a class="mobconv-wa-fab" data-enabled="true" href="' + waUrl() + '" target="_blank" rel="noopener" aria-label="Scrivici su WhatsApp">' +
'<i class="fab fa-whatsapp"></i>' +
'</a>';
}
/* ── Render trust ribbon ── */
function buildTrust() {
if (!trustList || !trustList.length) return null;
var dark = CFG.trustTheme === 'dark' ? ' dark' : '';
var items = trustList.map(function (t) {
return '<span class="mobconv-trust-item">' + t + '</span>';
}).join('');
return '<div class="mobconv-trust' + dark + '"><div class="mobconv-trust-row">' + items + '</div></div>';
}
/* ── Mount on DOM ready ── */
function mount() {
// Inject sticky bar at end of body
var stickyHtml = buildSticky();
var fabHtml = buildFab();
var wrap = document.createElement('div');
wrap.innerHTML = stickyHtml + fabHtml;
while (wrap.firstChild) document.body.appendChild(wrap.firstChild);
// Inject trust ribbon after hero (or specified selector)
var trustHtml = buildTrust();
if (trustHtml && CFG.trustAfter) {
var anchor = document.querySelector(CFG.trustAfter);
if (anchor) {
var t = document.createElement('div');
t.innerHTML = trustHtml;
anchor.parentNode.insertBefore(t.firstChild, anchor.nextSibling);
}
}
// Padding body bottom per non coprire CTA esistenti in fondo pagina
document.body.classList.add('mobconv-padded');
// Smooth scroll per anchor primary
if (CFG.primaryAction === 'anchor') {
var btn = document.getElementById('mobconv-primary');
if (btn) {
btn.addEventListener('click', function (e) {
var href = btn.getAttribute('href');
if (href && href.charAt(0) === '#') {
var tgt = document.querySelector(href);
if (tgt) {
e.preventDefault();
tgt.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}
});
}
}
// agilehub-open: il modulo agilehub-applet.js auto-binda [data-agilehub-open]
// quindi nessun bind manuale serve qui.
// Scroll listener: show sticky bar dopo X px
var stickyEl = document.getElementById('mobconv-sticky');
var triggered = false;
function onScroll() {
var y = window.scrollY || document.documentElement.scrollTop;
if (!triggered && y >= CFG.scrollTrigger) {
stickyEl.classList.add('show');
triggered = true;
} else if (triggered && y < CFG.scrollTrigger - 60) {
// Reset solo se scrolla MOLTO in alto (-60 hysteresis)
stickyEl.classList.remove('show');
triggered = false;
}
}
window.addEventListener('scroll', onScroll, { passive: true });
onScroll(); // check iniziale
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', mount);
} else {
mount();
}
})();