{"id":3737,"date":"2025-07-29T12:51:34","date_gmt":"2025-07-29T11:51:34","guid":{"rendered":"https:\/\/strattonapps.com\/?page_id=3737"},"modified":"2025-07-29T16:53:40","modified_gmt":"2025-07-29T15:53:40","slug":"racks-academy","status":"publish","type":"page","link":"https:\/\/strattonapps.com\/es\/racks-academy\/","title":{"rendered":"Racks Academy"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"3737\" class=\"elementor elementor-3737\">\n\t\t\t\t\t\t<div class=\"elementor-inner\">\n\t\t\t\t<div class=\"elementor-section-wrap\">\n\t\t\t\t\t\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-b702c9a elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"b702c9a\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t\t\t<div class=\"elementor-row\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-fa9a25c\" data-id=\"fa9a25c\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-column-wrap elementor-element-populated\">\n\t\t\t\t\t\t\t<div class=\"elementor-widget-wrap\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-8774606 elementor-widget elementor-widget-html\" data-id=\"8774606\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<script>\n(function() {\n  \/\/ <<< INICIO: Bloque para evitar reinicializaci\u00f3n >>>\n  if (window.floatingChatInitialized) {\n    console.warn('Floating Chat: Intento de reinicializaci\u00f3n, omitiendo.');\n    return;\n  }\n  window.floatingChatInitialized = true;\n  \/\/ <<< FIN: Bloque para evitar reinicializaci\u00f3n >>>\n\n  const WEBHOOK_URL = 'https:\/\/primary-production-dbbe.up.railway.app\/webhook\/racks-academy';\n  const IDs = {\n    button: 'floating-chat-button',\n    window: 'floating-chat-window',\n    body: 'floating-chat-body',\n    input: 'floating-chat-input',\n    sendBtn: 'floating-chat-send',\n    typingIndicator: 'floating-chat-typing-indicator'\n  };\n  const CHAT_HISTORY_KEY = 'racksChatHistory';\n\n  let chatBodyEl;\n\n  \/\/ --- Funciones de Ayuda (sin cambios) ---\n  function addMessageToUI(type, content, isHtml = false) {\n    if (!chatBodyEl) return;\n    const bubble = document.createElement('div');\n    bubble.className = type === 'user' ? 'user-bubble' : 'server-bubble';\n    if (isHtml && type === 'server') {\n      bubble.innerHTML = content;\n    } else {\n      bubble.textContent = content;\n    }\n    chatBodyEl.appendChild(bubble);\n    chatBodyEl.scrollTop = chatBodyEl.scrollHeight;\n  }\n\n  function saveMessageToHistory(type, content, isHtml = false) {\n    try {\n      const history = JSON.parse(localStorage.getItem(CHAT_HISTORY_KEY)) || [];\n      history.push({ type, content, isHtml, timestamp: new Date().toISOString() });\n      localStorage.setItem(CHAT_HISTORY_KEY, JSON.stringify(history));\n    } catch (e) {\n      console.error(\"Error guardando historial en localStorage:\", e);\n    }\n  }\n\n  function loadChatHistory() {\n    if (!chatBodyEl) return;\n    try {\n      const history = JSON.parse(localStorage.getItem(CHAT_HISTORY_KEY));\n      if (history && Array.isArray(history)) {\n        history.forEach(msg => addMessageToUI(msg.type, msg.content, msg.isHtml));\n      }\n    } catch (e) {\n      console.error(\"Error cargando historial desde localStorage:\", e);\n    }\n  }\n\n  function showTypingIndicator() {\n    if (!chatBodyEl || document.getElementById(IDs.typingIndicator)) return;\n    const indicatorBubble = document.createElement('div');\n    indicatorBubble.id = IDs.typingIndicator;\n    indicatorBubble.className = 'server-bubble typing-indicator';\n    indicatorBubble.innerHTML = `<span><\/span><span><\/span><span><\/span>`;\n    chatBodyEl.appendChild(indicatorBubble);\n    chatBodyEl.scrollTop = chatBodyEl.scrollHeight;\n  }\n\n  function hideTypingIndicator() {\n    const indicator = document.getElementById(IDs.typingIndicator);\n    if (indicator) indicator.remove();\n  }\n  \/\/ --- Fin Funciones de Ayuda ---\n\n\n  function init() {\n    if (document.getElementById(IDs.button)) return;\n\n    \/\/ --- INICIO: Importaci\u00f3n de fuentes de Google ---\n    const fontLink1 = document.createElement('link');\n    fontLink1.rel = 'preconnect';\n    fontLink1.href = 'https:\/\/fonts.googleapis.com';\n    \n    const fontLink2 = document.createElement('link');\n    fontLink2.rel = 'preconnect';\n    fontLink2.href = 'https:\/\/fonts.gstatic.com';\n    fontLink2.crossOrigin = 'true';\n\n    const fontLink3 = document.createElement('link');\n    fontLink3.rel = 'stylesheet';\n    fontLink3.href = 'https:\/\/fonts.googleapis.com\/css2?family=Bai+Jamjuree:wght@700&family=Inter:wght@400;600&display=swap';\n    \n    document.head.appendChild(fontLink1);\n    document.head.appendChild(fontLink2);\n    document.head.appendChild(fontLink3);\n    \/\/ --- FIN: Importaci\u00f3n de fuentes ---\n\n    const style = document.createElement('style');\n    \/\/ --- INICIO DE ESTILOS CORREGIDOS CON VALORES EXACTOS ---\n    style.textContent = `\n      :root {\n        --chat-primary-color: #92400e;\n        --chat-primary-hover: #c97a2a; \/* Tono m\u00e1s oscuro para hover *\/\n        --chat-font-body: \"Inter\", sans-serif;\n        --chat-font-header: \"Bai Jamjuree\", sans-serif;\n        --chat-bg-dark: #1a1a1c;\n        --chat-border-dark: #2f2f31;\n        --chat-text-light: #fff;\n        --chat-text-dark: #1a1a1c;\n        --chat-server-bubble: #2a2a2e;\n      }\n      #${IDs.button}, #${IDs.window}, #${IDs.input}, #${IDs.sendBtn} {\n        font-family: var(--chat-font-body);\n      }\n      #${IDs.button} {\n        position: fixed; bottom: 20px; right: 20px;\n        width: 60px; height: 60px;\n        border-radius: 50%;\n        background: var(--chat-primary-color);\n        display: flex; align-items: center; justify-content: center;\n        border: none; cursor: pointer;\n        box-shadow: 0 5px 20px rgba(232, 140, 48, 0.25);\n        transition: transform .3s, background-color .3s;\n        z-index: 10000;\n      }\n      #${IDs.button}:hover {\n        transform: scale(1.05);\n        background-color: var(--chat-primary-hover);\n      }\n      #${IDs.button} svg {\n        width: 32px; height: 32px; fill: var(--chat-text-light);\n      }\n\n      #${IDs.window} {\n        position: fixed; bottom: 90px; right: 20px;\n        width: 380px; max-height: 65vh;\n        background: var(--chat-bg-dark);\n        border: 1px solid var(--chat-border-dark);\n        border-radius: 16px;\n        box-shadow: 0 10px 40px rgba(0,0,0,0.3);\n        display: none; flex-direction: column;\n        overflow: hidden; z-index: 9999;\n        color: var(--chat-text-light);\n      }\n      #${IDs.window} header {\n        font-family: var(--chat-font-header);\n        padding: 18px 20px;\n        font-size: 1em; \/* Un poco m\u00e1s grande para el estilo de la fuente *\/\n        font-weight: 300;\n        \/*text-transform: uppercase;*\/\n        color: var(--chat-text-light);\n        border-bottom: 1px solid var(--chat-border-dark);\n        background: var(--chat-bg-dark);\n        text-align: lft;\n      }\n      #${IDs.body} {\n        flex: 1; padding: 16px; overflow-y: auto; background: var(--chat-bg-dark);\n      }\n      #${IDs.body}::-webkit-scrollbar { width: 6px; }\n      #${IDs.body}::-webkit-scrollbar-thumb { background: #444; border-radius: 3px; }\n      #${IDs.body}::-webkit-scrollbar-track { background: var(--chat-bg-dark); }\n      \n      #${IDs.body} .user-bubble, #${IDs.body} .server-bubble {\n      font-family: var(--chat-font-body);\n        padding: 12px 16px;\n        border-radius: 18px;\n        max-width: 85%; margin: 10px 0;\n        font-size: 15px; line-height: 1.5; word-wrap: break-word;\n        box-shadow: 0 2px 4px rgba(0,0,0,0.2);\n        clear: both;\n      }\n      #${IDs.body} .user-bubble {\n          font-family: var(--chat-font-body);\n        background: var(--chat-primary-color);\n        color: #fff; \/*var(--chat-text-dark);*\/\n        \/*font-weight: 600;*\/\n        border-bottom-right-radius: 4px;\n        float: right;\n        font-size: 13px;\n      }\n      #${IDs.body} .server-bubble {\n        background: var(--chat-server-bubble);\n        color: var(--chat-text-light);\n        border-bottom-left-radius: 4px;\n        float: left;\n        font-size: 13px;\n      }\n      #${IDs.body} .server-bubble a {\n        color: var(--chat-primary-color);\n        text-decoration: underline;\n        font-weight: 600;\n      }\n\n      .typing-indicator span { background-color: #777; }\n      .typing-indicator span { height: 8px; width: 8px; border-radius: 50%; display: inline-block; margin: 0 2px; animation: bounce 1.4s infinite ease-in-out both; }\n      .typing-indicator span:nth-child(1) { animation-delay: -0.32s; }\n      .typing-indicator span:nth-child(2) { animation-delay: -0.16s; }\n      @keyframes bounce { 0%, 80%, 100% { transform: scale(0); } 40% { transform: scale(1.0); } }\n\n      .chat-input-container { display: flex; padding: 12px; border-top: 1px solid var(--chat-border-dark); background: var(--chat-bg-dark); }\n      #${IDs.input} { flex: 1; padding: 12px 15px; font-size: 15px; border: 1px solid var(--chat-border-dark); background-color: var(--chat-server-bubble); color: var(--chat-text-light); border-radius: 10px; outline: none; margin-right: 10px; transition: border-color .2s; }\n      #${IDs.input}::placeholder { color: #888; }\n      #${IDs.input}:focus { border-color: var(--chat-primary-color); }\n\n      #${IDs.sendBtn} { padding: 12px 20px; background: var(--chat-primary-color); color: var(--chat-text-light); border: none; border-radius: 10px; cursor: pointer; font-weight: 600; font-size: 15px; transition: background-color .2s; text-transform: uppercase;}\n      #${IDs.sendBtn}:hover { background: var(--chat-primary-hover); }\n\n      @media (max-width: 600px) {\n        #${IDs.button} { width: 55px; height: 55px; bottom: 16px; right: 16px; }\n        #${IDs.button} svg { width: 28px; height: 28px; }\n        #${IDs.window} { width: calc(100% - 32px); height: 75vh; max-height: none; right: 16px; bottom: 80px; }\n      }\n    `;\n    \/\/ --- FIN DE ESTILOS ---\n    document.head.appendChild(style);\n\n    const btn = document.createElement('button');\n    btn.id = IDs.button;\n    btn.type = 'button';\n    btn.setAttribute('aria-label', 'Abrir chat');\n    btn.innerHTML = '<svg viewBox=\"0 0 24 24\"><path d=\"M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2z\"\/><\/svg>';\n    document.body.appendChild(btn);\n\n    const win = document.createElement('div');\n    win.id = IDs.window;\n    win.setAttribute('role', 'log');\n    win.setAttribute('aria-live', 'polite');\n    win.innerHTML = `\n      <header>Racks Academy<\/header>\n      <div id=\"${IDs.body}\"><\/div>\n      <div class=\"chat-input-container\">\n        <input id=\"${IDs.input}\" type=\"text\" placeholder=\"Escribe tu duda...\" autocomplete=\"off\" aria-label=\"Mensaje a enviar\" \/>\n        <button id=\"${IDs.sendBtn}\" type=\"button\" aria-label=\"Enviar mensaje\">Enviar<\/button>\n      <\/div>\n    `;\n    document.body.appendChild(win);\n\n    chatBodyEl = document.getElementById(IDs.body);\n    const inputEl = document.getElementById(IDs.input);\n    const sendBtn = document.getElementById(IDs.sendBtn);\n\n    if (!chatBodyEl || !inputEl || !sendBtn) {\n        console.error('Chat init: Error al obtener elementos del DOM.');\n        return;\n    }\n\n    loadChatHistory();\n\n    btn.addEventListener('click', () => {\n      const isOpen = win.style.display === 'flex';\n      win.style.display = isOpen ? 'none' : 'flex';\n      btn.setAttribute('aria-label', isOpen ? 'Abrir chat' : 'Cerrar chat');\n      if (win.style.display === 'flex') {\n          if(inputEl) inputEl.focus();\n          if(chatBodyEl) chatBodyEl.scrollTop = chatBodyEl.scrollHeight;\n      }\n    });\n\n    async function sendMessage() {\n      const msg = inputEl.value.trim();\n      if (!msg) return;\n\n      addMessageToUI('user', msg);\n      saveMessageToHistory('user', msg);\n      inputEl.value = '';\n      inputEl.focus();\n\n      showTypingIndicator();\n\n      try {\n        const payload = { message: msg, timestamp: new Date().toISOString(), url: window.location.href };\n        const response = await fetch(WEBHOOK_URL, {\n          method: 'POST',\n          headers: { 'Content-Type': 'application\/json' },\n          body: JSON.stringify(payload)\n        });\n        \n        hideTypingIndicator();\n\n        if (!response.ok) {\n          const errorText = await response.text();\n          throw new Error(`Error del servidor: ${response.status}. Respuesta: ${errorText}`);\n        }\n\n        const contentType = response.headers.get(\"content-type\");\n        if (contentType && contentType.includes(\"application\/json\")) {\n            const data = await response.json();\n            if (data.responseMessage) {\n              addMessageToUI('server', data.responseMessage, true);\n              saveMessageToHistory('server', data.responseMessage, true);\n            } else {\n              addMessageToUI('server', \"Respuesta inesperada del servidor.\");\n              saveMessageToHistory('server', \"Respuesta inesperada del servidor.\");\n            }\n        } else {\n            const textData = await response.text();\n            addMessageToUI('server', textData);\n            saveMessageToHistory('server', textData);\n        }\n      } catch (err) {\n        console.error('Chat sendMessage: Error:', err);\n        hideTypingIndicator();\n        let errorMsg = \"Error al enviar mensaje. Intenta de nuevo.\";\n        if (err instanceof TypeError && err.message.toLowerCase().includes(\"failed to fetch\")) {\n            errorMsg = \"Error de red. No se pudo conectar con el servidor.\";\n        }\n        addMessageToUI('server', errorMsg);\n        saveMessageToHistory('server', errorMsg);\n      }\n    }\n\n    sendBtn.addEventListener('click', sendMessage);\n    inputEl.addEventListener('keydown', e => {\n      if (e.key === 'Enter' && !e.shiftKey) {\n        e.preventDefault();\n        sendMessage();\n      }\n    });\n  }\n\n  if (document.readyState === 'loading') {\n    document.addEventListener('DOMContentLoaded', init);\n  } else {\n    init();\n  }\n})();\n<\/script>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t<\/div>\n\t\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":[],"_links":{"self":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages\/3737"}],"collection":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/comments?post=3737"}],"version-history":[{"count":82,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages\/3737\/revisions"}],"predecessor-version":[{"id":3826,"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/pages\/3737\/revisions\/3826"}],"wp:attachment":[{"href":"https:\/\/strattonapps.com\/es\/wp-json\/wp\/v2\/media?parent=3737"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}