PageSourceSearch

https://softland.com/whatsapp/cr/widget.js

js softland.com collected 2026-10-10 00:23:59 UTC 15,968 bytes, 464 lines download raw bytes

1/**
2 * Widget de WhatsApp con flujo de preguntas
3 * Uso: <script src="widget.js"></script>
4 */
5
6(function () {
7  "use strict";
8
9  // Configuración
10  // Configuración
11  // Detectar automáticamente la URL de la API basada en la ubicación de este script
12  const scripts = document.getElementsByTagName("script");
13  const currentScript = scripts[scripts.length - 1];
14  const scriptPath = currentScript.src;
15  const baseUrl = scriptPath.substring(0, scriptPath.lastIndexOf("/"));
16  const API_URL = `${baseUrl}/api.php`;
17
18  let config = null;
19  let currentStep = 0;
20  let selectedSolution = null;
21  let selectedUserRange = null;
22  let clickStatId = null;
23
24  // Crear estilos
25  const styles = `
26        .wa-widget-container {
27            position: fixed;
28            bottom: 20px;
29            right: 20px;
30            z-index: 9999;
31            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
32        }
33        
34        .wa-widget-button {
35            width: 60px;
36            height: 60px;
37            border-radius: 50%;
38            background: #25D366;
39            box-shadow: 0 4px 12px rgba(37, 211, 102, 0.4);
40            cursor: pointer;
41            display: flex;
42            align-items: center;
43            justify-content: center;
44            transition: all 0.3s ease;
45            border: none;
46            position: relative;
47        }
48        
49        .wa-widget-button:hover {
50            transform: scale(1.1);
51            box-shadow: 0 6px 20px rgba(37, 211, 102, 0.6);
52        }
53        
54        .wa-widget-button svg {
55            width: 32px;
56            height: 32px;
57            fill: white;
58        }
59        
60        .wa-widget-modal {
61            position: absolute;
62            bottom: 80px;
63            right: 0;
64            width: 360px;
65            max-width: calc(100vw - 40px);
66            background: white;
67            border-radius: 12px;
68            box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
69            overflow: hidden;
70            opacity: 0;
71            transform: scale(0.8) translateY(20px);
72            transition: all 0.3s cubic-bezier(0.68, -0.55, 0.265, 1.55);
73            pointer-events: none;
74        }
75        
76        .wa-widget-modal.active {
77            opacity: 1;
78            transform: scale(1) translateY(0);
79            pointer-events: all;
80        }
81        
82        .wa-widget-header {
83            background: #25D366;
84            color: white;
85            padding: 20px;
86            text-align: center;
87            font-weight: 600;
88            font-size: 16px;
89            position: relative;
90        }
91        
92        .wa-widget-close {
93            position: absolute;
94            top: 50%;
95            right: 15px;
96            transform: translateY(-50%);
97            background: rgba(255, 255, 255, 0.2);
98            border: none;
99            color: white;
100            width: 28px;
101            height: 28px;
102            border-radius: 50%;
103            cursor: pointer;
104            display: flex;
105            align-items: center;
106            justify-content: center;
107            transition: background 0.2s;
108            font-size: 20px;
109            line-height: 1;
110        }
111        
112        .wa-widget-close:hover {
113            background: rgba(255, 255, 255, 0.3);
114        }
115        
116        .wa-widget-body {
117            padding: 24px;
118            max-height: 400px;
119            overflow-y: auto;
120        }
121        
122        .wa-widget-title {
123            font-size: 18px;
124            font-weight: 700;
125            margin-bottom: 12px;
126            color: #1a1a1a;
127        }
128        
129        .wa-widget-message {
130            font-size: 14px;
131            color: #666;
132            margin-bottom: 20px;
133            line-height: 1.5;
134        }
135        
136        .wa-widget-option {
137            background: #25D366;
138            color: white;
139            border: none;
140            padding: 14px 20px;
141            border-radius: 8px;
142            width: 100%;
143            margin-bottom: 10px;
144            cursor: pointer;
145            font-size: 14px;
146            font-weight: 500;
147            transition: all 0.2s;
148            text-align: left;
149        }
150        
151        .wa-widget-option:hover {
152            background: #20BD5A;
153            transform: translateX(4px);
154        }
155        
156        .wa-widget-option:active {
157            transform: translateX(4px) scale(0.98);
158        }
159        
160        .wa-widget-back {
161            background: #f0f0f0;
162            color: #666;
163            border: none;
164            padding: 12px 20px;
165            border-radius: 8px;
166            width: 100%;
167            margin-top: 10px;
168            cursor: pointer;
169            font-size: 14px;
170            font-weight: 500;
171            transition: all 0.2s;
172        }
173        
174        .wa-widget-back:hover {
175            background: #e0e0e0;
176        }
177        
178        @media (max-width: 480px) {
179            .wa-widget-container {
180                bottom: 10px;
181                right: 10px;
182            }
183            
184            .wa-widget-modal {
185                width: calc(100vw - 20px);
186                right: -10px;
187            }
188        }
189    `;
190
191  // Inyectar estilos
192  const styleSheet = document.createElement("style");
193  styleSheet.textContent = styles;
194  document.head.appendChild(styleSheet);
195
196  // Crear HTML del widget
197  const widgetHTML = `
198        <div class="wa-widget-container">
199            <button class="wa-widget-button" id="wa-widget-toggle">
200                <svg viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
201                    <path d="M16 0C7.164 0 0 7.164 0 16c0 2.825.738 5.488 2.025 7.788L.05 31.95l8.363-2.05A15.93 15.93 0 0 0 16 32c8.836 0 16-7.164 16-16S24.836 0 16 0zm0 29.333c-2.488 0-4.863-.688-6.888-1.988l-.488-.3-5.05 1.238 1.3-4.75-.325-.513A13.267 13.267 0 0 1 2.667 16c0-7.363 5.97-13.333 13.333-13.333S29.333 8.637 29.333 16 23.363 29.333 16 29.333zm7.3-9.95c-.4-.2-2.363-1.163-2.725-1.3-.363-.138-.625-.2-.888.2-.263.4
201-1.013 1.3-1.238 1.563-.225.263-.45.3-.85.1-.4-.2-1.688-.625-3.213-2-.188-1.063-.338-1.838-.5-2.238-.163-.4-.013-.613.175-.813.175-.188.4-.488.6-.738.2-.25.263-.425.4-.7.138-.275.063-.525-.038-.738-.1-.213-.888-2.138-1.213-2.925-.325-.763-.65-.65-.888-.663-.225-.013-.488-.013-.75-.013s-.688.1-1.05.5c-.363.4-1.375 1.338-1.375 3.263s1.413 3.788 1.6 4.05c.188.263 2.65 4.05 6.425 5.675.9.388 1.6.625 2.15.8.9.288 1.725.25 2.375.15.725-.113 2.363-.963 2.7-1.9.338-.938.338-1.738.238-1.9-.1-.163-.363-.263-.763-.463z"/>
202                </svg>
203            </button>
204            <div class="wa-widget-modal" id="wa-widget-modal">
205                <div class="wa-widget-header">
206                    <span id="wa-widget-company-name">Cargando...</span>
207                    <button class="wa-widget-close" id="wa-widget-close">×</button>
208                </div>
209                <div class="wa-widget-body" id="wa-widget-content">
210                    <div class="wa-widget-title">Cargando...</div>
211                    <div class="wa-widget-message">Por favor espera...</div>
212                </div>
213            </div>
214        </div>
215    `;
216
217  // Inicializar widget cuando el DOM esté listo
218  function init() {
219    // Crear contenedor
220    const container = document.createElement("div");
221    container.innerHTML = widgetHTML;
222    document.body.appendChild(container.firstElementChild);
223
224    // Obtener elementos
225    const toggleBtn = document.getElementById("wa-widget-toggle");
226    const modal = document.getElementById("wa-widget-modal");
227    const closeBtn = document.getElementById("wa-widget-close");
228
229    // Event listeners
230    toggleBtn.addEventListener("click", toggleModal);
231    closeBtn.addEventListener("click", closeModal);
232
233    // Cargar configuración
234    loadConfig();
235  }
236
237  // Cargar configuración desde la API
238  async function loadConfig() {
239    try {
240      const response = await fetch(`${API_URL}?action=get_config`);
241      const result = await response.json();
242
243      if (result.success) {
244        config = result.data;
245        document.getElementById("wa-widget-company-name").textContent =
246          config.company_name;
247        showStep1();
248      } else {
249        showError("Error al cargar la configuración");
250      }
251    } catch (error) {
252      console.error("Error:", error);
253      showError("Error de conexión");
254    }
255  }
256
257  // Mostrar Paso 1: Soluciones
258  function showStep1() {
259    currentStep = 1;
260    selectedSolution = null;
261    selectedUserRange = null;
262
263    const content = document.getElementById("wa-widget-content");
264    content.innerHTML = `
265            <div class="wa-widget-title">Soluciones</div>
266            <div class="wa-widget-message">Antes de hablar con un representante, necesitamos saber qué solución te interesa conocer</div>
267            ${config.solutions
268              .map(
269                (solution) => `
270                <button class="wa-widget-option" data-solution-id="${solution.id}" data-solution-name="${solution.name}">
271                    ${solution.name}
272                </button>
273            `,
274              )
275              .join("")}
276        `;
277
278    // Event listeners para las opciones
279    content.querySelectorAll(".wa-widget-option").forEach((btn) => {
280      btn.addEventListener("click", function () {
281        const solutionId = this.getAttribute("data-solution-id");
282        const solutionName = this.getAttribute("data-solution-name");
283        handleSolutionSelect(solutionId, solutionName);
284      });
285    });
286  }
287
288  // Manejar selección de solución
289  function handleSolutionSelect(solutionId, solutionName) {
290    selectedSolution = { id: solutionId, name: solutionName };
291
292    // Si seleccionó "Otro", ir directo a WhatsApp
293    if (solutionName.toLowerCase() === "otro") {
294      trackAndRedirect(solutionId, null);
295    } else {
296      showStep2();
297    }
298  }
299
300  // Mostrar Paso 2: Cantidad de usuarios
301  function showStep2() {
302    currentStep = 2;
303
304    const content = document.getElementById("wa-widget-content");
305    content.innerHTML = `
306            <div class="wa-widget-title">Cantidad de usuarios</div>
307            <div class="wa-widget-message">Por último, necesitamos saber la cantidad de usuarios concurrentes requerida</div>
308            ${config.user_ranges
309              .map(
310                (range) => `
311                <button class="wa-widget-option" data-range-id="${range.id}" data-range-text="${range.range_text}">
312                    ${range.range_text}
313                </button>
314            `,
315              )
316              .join("")}
317            <button class="wa-widget-back" id="wa-widget-back-btn">Volver</button>
318        `;
319
320    // Event listeners para las opciones
321    content.querySelectorAll(".wa-widget-option").forEach((btn) => {
322      btn.addEventListener("click", function () {
323        const rangeId = this.getAttribute("data-range-id");
324        const rangeText = this.getAttribute("data-range-text");
325        handleUserRangeSelect(rangeId, rangeText);
326      });
327    });
328
329    // Event listener para volver
330    document
331      .getElementById("wa-widget-back-btn")
332      .addEventListener("click", showStep1);
333  }
334
335  // Manejar selección de rango de usuarios
336  function handleUserRangeSelect(rangeId, rangeText) {
337    selectedUserRange = { id: rangeId, text: rangeText };
338    trackAndRedirect(selectedSolution.id, rangeId);
339  }
340
341  // Registrar estadística y redirigir a WhatsApp
342  async function trackAndRedirect(solutionId, userRangeId) {
343    try {
344      // Registrar click
345      const trackResponse = await fetch(`${API_URL}?action=track_click`, {
346        method: "POST",
347        headers: { "Content-Type": "application/json" },
348        body: JSON.stringify({
349          page_url: window.location.href,
350          solution_id: solutionId,
351          user_range_id: userRangeId,
352        }),
353      });
354
355      const trackResult = await trackResponse.json();
356      console.log('=== Track result ===', trackResult);
357      console.log('Selected Solution:', selectedSolution);
358      console.log('Selected User Range:', selectedUserRange);
359
360      // Enviar evento a Google Tag Manager DataLayer
361      if (selectedSolution) {
362        // Normalizar el nombre de la solución para el evento
363        const solutionName = selectedSolution.name
364          .toUpperCase()
365          .replace(/SOFTLAND\s*/gi, "")
366          .trim();
367
368        let eventName;
369        let eventLabel;
370        
371        if (selectedUserRange) {
372          // Caso: Solución + Rango de usuarios
373          const userRangeNormalized = selectedUserRange.text
374            .replace(/\s*colaboradores?/gi, "")
375            .replace(/\s*usuarios?/gi, "")
376            .replace(/Más\s+de\s+/gi, "MAS_DE_") // Reemplazar "Más de " completo
377            .replace(/Mas\s+de\s+/gi, "MAS_DE_") // Reemplazar "Mas de " completo
378            .replace(/De\s+/gi, "")
379            .replace(/\s+a\s+/gi, "_A_")
380            .replace(/\s+/g, "_")
381            .toUpperCase();
382          
383          eventName = `WHATSAPP_${solutionName}_${userRangeNormalized}`;
384          eventLabel = `${selectedSolution.name} - ${selectedUserRange.text}`;
385        } else {
386          // Caso: Solo solución (ej: "Otro")
387          eventName = `WHATSAPP_${solutionName}`;
388          eventLabel = selectedSolution.name;
389        }
390        
391        console.log('=== Sending DataLayer Event ===');
392        console.log('Event name:', eventName);
393        console.log('Event label:', eventLabel);
394        
395        // Enviar al DataLayer
396        window.dataLayer = window.dataLayer || [];
397        window.dataLayer.push({
398          event: eventName,
399          eventCategory: "WhatsApp Button",
400          eventLabel: eventLabel,
401          solution: selectedSolution.name,
402          userRange: selectedUserRange ? selectedUserRange.text : "N/A",
403        });
404        
405        console.log("✅ DataLayer event pushed:", eventName);
406        console.log("Current dataLayer:", window.dataLayer);
407      } else {
408        console.warn('⚠️ No solution selected, event not sent');
409      }
410
411      // Obtener URL de WhatsApp
412      const params = new URLSearchParams({
413        action: "get_whatsapp_url",
414        solution: selectedSolution.name,
415        user_range: selectedUserRange ? selectedUserRange.text : "",
416      });
417
418      const urlResponse = await fetch(`${API_URL}?${params}`);
419      const urlResult = await urlResponse.json();
420
421      if (urlResult.success) {
422        // Redirigir a WhatsApp
423        window.open(urlResult.url, "_blank");
424        closeModal();
425      }
426    } catch (error) {
427      console.error("Error:", error);
428      showError("Error al procesar la solicitud");
429    }
430  }
431
432  // Toggle modal
433  function toggleModal() {
434    const modal = document.getElementById("wa-widget-modal");
435    modal.classList.toggle("active");
436
437    if (modal.classList.contains("active") && currentStep === 0) {
438      showStep1();
439    }
440  }
441
442  // Cerrar modal
443  function closeModal() {
444    const modal = document.getElementById("wa-widget-modal");
445    modal.classList.remove("active");
446  }
447
448  // Mostrar error
449  function showError(message) {
450    const content = document.getElementById("wa-widget-content");
451    content.innerHTML = `
452            <div class="wa-widget-title">Error</div>
453            <div class="wa-widget-message">${message}</div>
454            <button class="wa-widget-back" onclick="location.reload()">Reintentar</button>
455        `;
456  }
457
458  // Inicializar cuando el DOM esté listo
459  if (document.readyState === "loading") {
460    document.addEventListener("DOMContentLoaded", init);
461  } else {
462    init();
463  }
464})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.