/* Backend-served support chat widget — pro support-agent UI. Embed: */ (function () { "use strict"; var me = document.currentScript; if (!me) { var ss = document.getElementsByTagName("script"); for (var i = ss.length - 1; i >= 0; i--) { if (ss[i].src && ss[i].src.indexOf("/widget.js") !== -1) { me = ss[i]; break; } } } var SRC = me ? new URL(me.src) : null; var BACKEND = SRC ? SRC.origin : ""; var TENANT = (SRC && SRC.searchParams.get("t")) || "default"; var QS = "?t=" + encodeURIComponent(TENANT); var SESSION_KEY = "ssb_session_id_" + TENANT; var cfg = { brand_name: "Asistente", brand_color: "#e8491d", welcome: "¡Hola! ¿En qué puedo ayudarte?", assistant_name: "Asistente", avatar_url: "" }; function getSid() { try { return localStorage.getItem(SESSION_KEY) || null; } catch (e) { return null; } } function setSid(id) { try { if (id) localStorage.setItem(SESSION_KEY, id); } catch (e) {} } function shade(hex, p) { try { var n = parseInt(hex.replace("#", ""), 16); var r = (n >> 16) & 255, g = (n >> 8) & 255, b = n & 255; r = Math.max(0, Math.min(255, r + Math.round(255 * p / 100))); g = Math.max(0, Math.min(255, g + Math.round(255 * p / 100))); b = Math.max(0, Math.min(255, b + Math.round(255 * p / 100))); return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); } catch (e) { return hex; } } // Professional "support agent" (person + headset) icon — clear & not a photo. var ICON_AGENT = ''; var ICON_SEND = ''; function fillAvatar(el) { if (cfg.avatar_url) { el.style.background = shade(cfg.brand_color, 40) + " url('" + cfg.avatar_url + "') center/cover no-repeat"; el.innerHTML = ""; } else { el.classList.add("ssb-av-icon"); el.innerHTML = ICON_AGENT; } } function esc(s) { return s.replace(/&/g, "&").replace(//g, ">"); } function md(text) { var lines = esc(text).split(/\n/), out = [], inList = false; for (var i = 0; i < lines.length; i++) { var ln = lines[i], bullet = ln.match(/^\s*[-•]\s+(.*)$/); if (bullet) { if (!inList) { out.push(""); inList = false; } out.push(ln.trim() === "" ? "
" : "
" + inline(ln) + "
"); } } if (inList) out.push(""); return out.join(""); } function inline(s) { s = s.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, '$1'); s = s.replace(/\*\*([^*]+)\*\*/g, "$1"); s = s.replace(/`([^`]+)`/g, "$1"); return s; } function injectStyles() { var c = cfg.brand_color, c2 = shade(c, -18); var css = ".ssb-av{background:linear-gradient(135deg," + shade(c, 8) + "," + c2 + ");border-radius:50%;flex:none;display:flex;align-items:center;justify-content:center;overflow:hidden}" + ".ssb-av-icon svg{width:64%;height:64%;fill:#fff}" + "#ssb-launch{position:fixed;bottom:20px;right:20px;width:62px;height:62px;border-radius:50%;cursor:pointer;z-index:99998;border:0;background:linear-gradient(135deg," + c + "," + c2 + ");box-shadow:0 8px 24px rgba(0,0,0,.28);display:flex;align-items:center;justify-content:center;transition:transform .2s}" + "#ssb-launch:hover{transform:scale(1.06)}#ssb-launch svg{width:32px;height:32px;fill:#fff}" + "#ssb-launch .ssb-dot{position:absolute;top:5px;right:5px;width:13px;height:13px;border-radius:50%;background:#22c55e;border:2px solid #fff}" + "#ssb-launch .ssb-pulse{position:absolute;inset:0;border-radius:50%;animation:ssbpulse 2.2s infinite}" + "@keyframes ssbpulse{0%{box-shadow:0 0 0 0 " + c + "66}70%{box-shadow:0 0 0 16px " + c + "00}100%{box-shadow:0 0 0 0 " + c + "00}}" + "#ssb-panel{position:fixed;bottom:20px;right:20px;width:380px;max-width:94vw;height:600px;max-height:80vh;background:#fff;border-radius:18px;box-shadow:0 18px 60px rgba(0,0,0,.3);display:none;flex-direction:column;overflow:hidden;z-index:99999;font-family:system-ui,-apple-system,Segoe UI,sans-serif;opacity:0;transform:translateY(16px) scale(.98);transition:opacity .22s,transform .22s}" + "#ssb-panel.open{display:flex;opacity:1;transform:none}" + "#ssb-head{background:linear-gradient(135deg," + c + "," + c2 + ");color:#fff;padding:14px 16px;display:flex;align-items:center;gap:12px}" + "#ssb-head .ssb-av{width:44px;height:44px;border:2px solid rgba(255,255,255,.7)}" + "#ssb-head .ssb-name{font-weight:700;font-size:15px;line-height:1.2}" + "#ssb-head .ssb-status{font-size:12px;opacity:.92;display:flex;align-items:center;gap:5px;margin-top:2px}" + "#ssb-head .ssb-status i{width:8px;height:8px;border-radius:50%;background:#22e07b;display:inline-block;animation:ssblive 2s infinite}" + "@keyframes ssblive{0%{box-shadow:0 0 0 0 #22e07b88}70%{box-shadow:0 0 0 6px #22e07b00}100%{box-shadow:0 0 0 0 #22e07b00}}" + "#ssb-head .ssb-x{margin-left:auto;background:transparent;border:0;color:#fff;font-size:24px;cursor:pointer;opacity:.85;line-height:1}" + "#ssb-msgs{flex:1;overflow-y:auto;padding:16px 14px;background:#f4f5f7;display:flex;flex-direction:column;gap:10px}" + ".ssb-row{display:flex;align-items:flex-end;gap:8px;max-width:88%}" + ".ssb-row.user{align-self:flex-end;flex-direction:row-reverse}.ssb-row.bot{align-self:flex-start}" + ".ssb-row .ssb-av{width:28px;height:28px}" + ".ssb-b{padding:10px 13px;border-radius:16px;font-size:14px;line-height:1.5;word-wrap:break-word}" + ".ssb-row.bot .ssb-b{background:#fff;color:#1a1a1a;border:1px solid #e6e6e9;border-bottom-left-radius:5px;box-shadow:0 1px 2px rgba(0,0,0,.04)}" + ".ssb-row.user .ssb-b{background:" + c + ";color:#fff;border-bottom-right-radius:5px}" + ".ssb-b a{color:inherit;text-decoration:underline}.ssb-row.bot .ssb-b a{color:" + c2 + "}" + ".ssb-b ul{margin:4px 0;padding-left:18px}.ssb-b li{margin:2px 0}.ssb-b code{background:#eee;padding:1px 4px;border-radius:4px;font-size:13px}" + ".ssb-typing{display:flex;gap:4px;padding:12px 14px}.ssb-typing span{width:7px;height:7px;border-radius:50%;background:#bbb;animation:ssbbounce 1.2s infinite}" + ".ssb-typing span:nth-child(2){animation-delay:.2s}.ssb-typing span:nth-child(3){animation-delay:.4s}" + "@keyframes ssbbounce{0%,60%,100%{transform:translateY(0);opacity:.5}30%{transform:translateY(-5px);opacity:1}}" + "#ssb-form{display:flex;align-items:center;gap:8px;padding:10px;border-top:1px solid #ececef;background:#fff}" + "#ssb-input{flex:1;border:1px solid #e0e0e4;border-radius:22px;padding:11px 15px;font-size:14px;outline:none}" + "#ssb-input:focus{border-color:" + c + "}" + "#ssb-send{width:42px;height:42px;border-radius:50%;border:0;background:" + c + ";color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;flex:none}#ssb-send svg{width:19px;height:19px;fill:#fff}" + "#ssb-foot{text-align:center;font-size:11px;color:#9aa;padding:6px;background:#fff}" + ".ssb-cards{align-self:flex-start;display:flex;flex-direction:column;gap:8px;max-width:92%;margin-left:36px}" + ".ssb-card{display:flex;gap:10px;background:#fff;border:1px solid #e6e6e9;border-radius:14px;padding:8px;box-shadow:0 1px 3px rgba(0,0,0,.05)}" + ".ssb-card img{width:60px;height:60px;object-fit:cover;border-radius:10px;flex:none;background:#f0f0f2}" + ".ssb-card .ssb-ci{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px}" + ".ssb-card .ssb-ct{font-size:13px;font-weight:600;color:#1a1a1a;line-height:1.25}" + ".ssb-card .ssb-cp{font-size:14px;font-weight:700;color:" + c2 + "}" + ".ssb-card .ssb-cs{font-size:11px;font-weight:600}.ssb-card .ssb-in{color:#1a7f37}.ssb-card .ssb-out{color:#b00}" + ".ssb-card .ssb-cb{display:flex;gap:6px;margin-top:2px}" + ".ssb-card a.ssb-add{background:" + c + ";color:#fff;font-size:12px;font-weight:600;padding:6px 10px;border-radius:8px;text-decoration:none;text-align:center}" + ".ssb-card a.ssb-view{font-size:12px;color:#666;text-decoration:underline;padding:6px 4px;align-self:center}"; var st = document.createElement("style"); st.textContent = css; document.head.appendChild(st); } function build() { injectStyles(); var name = cfg.assistant_name || cfg.brand_name; var launch = document.createElement("button"); launch.id = "ssb-launch"; launch.setAttribute("aria-label", "Abrir chat de soporte"); launch.innerHTML = '' + ICON_AGENT + ''; var panel = document.createElement("div"); panel.id = "ssb-panel"; panel.innerHTML = '
' + '
' + esc(name) + '
' + '
En línea · Responde en segundos
' + '
' + '
' + '
' + '
' + '
Asistente con IA
'; document.body.appendChild(launch); document.body.appendChild(panel); fillAvatar(panel.querySelector("#ssb-head .ssb-av")); var msgs = panel.querySelector("#ssb-msgs"); var form = panel.querySelector("#ssb-form"); var input = panel.querySelector("#ssb-input"); var greeted = false; function row(who) { var r = document.createElement("div"); r.className = "ssb-row " + who; if (who === "bot") { var a = document.createElement("div"); a.className = "ssb-av"; fillAvatar(a); r.appendChild(a); } var b = document.createElement("div"); b.className = "ssb-b"; r.appendChild(b); msgs.appendChild(r); msgs.scrollTop = msgs.scrollHeight; return b; } function botMsg(t) { var b = row("bot"); b.innerHTML = md(t); msgs.scrollTop = msgs.scrollHeight; return b; } function money(p) { return p == null ? "" : (Math.round(p * 100) / 100).toLocaleString("es-ES", { minimumFractionDigits: 2 }) + " €"; } function renderCards(products) { if (!products || !products.length) return; var wrap = document.createElement("div"); wrap.className = "ssb-cards"; products.slice(0, 6).forEach(function (p) { var card = document.createElement("div"); card.className = "ssb-card"; var img = p.image ? '' : ''; var stock = p.available ? '● En stock' : '● Agotado'; var add = (p.available && p.add_url) ? 'Añadir al carrito' : ""; var view = p.url ? 'Ver' : ""; card.innerHTML = img + '
' + esc(p.title || "") + '
' + '
' + money(p.price) + '
' + stock + '
' + add + view + '
'; wrap.appendChild(card); }); msgs.appendChild(wrap); msgs.scrollTop = msgs.scrollHeight; } function userMsg(t) { row("user").textContent = t; msgs.scrollTop = msgs.scrollHeight; } function typing() { var b = row("bot"); b.classList.add("ssb-typing"); b.innerHTML = ""; msgs.scrollTop = msgs.scrollHeight; return b; } function toggle() { panel.classList.toggle("open"); if (panel.classList.contains("open")) { if (!greeted) { botMsg(cfg.welcome); greeted = true; } input.focus(); } } launch.addEventListener("click", toggle); panel.querySelector(".ssb-x").addEventListener("click", toggle); form.addEventListener("submit", function (e) { e.preventDefault(); var text = input.value.trim(); if (!text) return; userMsg(text); input.value = ""; var t = typing(); fetch(BACKEND + "/chat" + QS, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ message: text, session_id: getSid() }) }).then(function (r) { return r.json(); }).then(function (d) { setSid(d.session_id); t.classList.remove("ssb-typing"); t.innerHTML = md(d.reply || "…"); renderCards(d.products); msgs.scrollTop = msgs.scrollHeight; }).catch(function () { t.classList.remove("ssb-typing"); t.textContent = "Lo siento, ha habido un problema. Inténtalo de nuevo."; }); }); } function start() { fetch(BACKEND + "/widget-config" + QS).then(function (r) { return r.json(); }) .then(function (d) { if (d) cfg = Object.assign(cfg, d); }).catch(function () {}) .then(function () { if (document.body) build(); else document.addEventListener("DOMContentLoaded", build); }); } start(); })();