fix: avoid PHP interpolating JS template literals in addFlowCard heredoc

Replace all backtick template literals inside addFlowCard (which lives
inside a PHP heredoc) with plain string concatenation. PHP was evaluating
${k}, ${Date.now()}, ${menuOpts}, etc. as PHP variable expressions,
emitting deprecation warnings inside the <script> block and corrupting
the JavaScript — causing all tab buttons on bot-config to stop responding.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Lizandro Guarnizo
2026-06-28 17:28:33 -05:00
co-authored by Claude Sonnet 4.6
parent 1116b8ece9
commit 845dd08a3a
+81 -79
View File
@@ -3123,92 +3123,94 @@ function addFlowCard() {
const menus = CONFIG?.menus || {}; const menus = CONFIG?.menus || {};
let menuOpts = '<option value="">— Selecciona menú —</option>'; let menuOpts = '<option value="">— Selecciona menú —</option>';
for (const k of Object.keys(menus)) menuOpts += `<option value="${k}">📑 ${k}</option>`; for (const k of Object.keys(menus)) menuOpts += '<option value="' + k + '">📑 ' + k + '</option>';
const eps = ENDPOINTS || {}; const eps = ENDPOINTS || {};
let epOpts = '<option value="">— Ninguno —</option>'; let epOpts = '<option value="">— Ninguno —</option>';
for (const [k, ep] of Object.entries(eps)) epOpts += `<option value="${k}">${ep.name || k}</option>`; for (const [k, ep] of Object.entries(eps)) epOpts += '<option value="' + k + '">' + (ep.name || k) + '</option>';
const flows = CONFIG?.flows || {}; const flows = CONFIG?.flows || {};
let nnOpts = '<option value="">— Ninguno —</option>'; let nnOpts = '<option value="">— Ninguno —</option>';
for (const k of Object.keys(flows)) nnOpts += `<option value="${k}">${k}</option>`; for (const k of Object.keys(flows)) nnOpts += '<option value="' + k + '">' + k + '</option>';
container.insertAdjacentHTML('beforeend', ` const cardId = 'flow-card-' + Date.now();
<div class="item-card" id="flow-card-${Date.now()}"> container.insertAdjacentHTML('beforeend',
<div class="item-head"> '<div class="item-card" id="' + cardId + '">' +
<span>ID: <input type="text" name="flow_key[]" value="" style="border:none;background:transparent;font-weight:700;color:#0b3d91;width:180px;font-size:13px" placeholder="nuevo_flujo"></span> ' <div class="item-head">' +
<button type="button" class="del" onclick="this.closest('.item-card').remove()"></button> ' <span>ID: <input type="text" name="flow_key[]" value="" style="border:none;background:transparent;font-weight:700;color:#0b3d91;width:180px;font-size:13px" placeholder="nuevo_flujo"></span>' +
</div> ' <button type="button" class="del" onclick="this.closest(\'.item-card\').remove()">✕</button>' +
<div class="form-row" style="align-items:flex-start;gap:10px"> ' </div>' +
<div class="form-group" style="min-width:200px"> ' <div class="form-row" style="align-items:flex-start;gap:10px">' +
<label>¿Qué hace?</label> ' <div class="form-group" style="min-width:200px">' +
<select name="flow_type[]" onchange="flowTabChange(this)"> ' <label>¿Qué hace?</label>' +
<optgroup label="Respuesta"><option value="text" selected>💬 Texto fijo</option></optgroup> ' <select name="flow_type[]" onchange="flowTabChange(this)">' +
<optgroup label="Navegar"><option value="menu">📑 Mostrar menú</option></optgroup> ' <optgroup label="Respuesta"><option value="text" selected>💬 Texto fijo</option></optgroup>' +
<optgroup label="Acción directa"><option value="function">⚡ Función especial</option></optgroup> ' <optgroup label="Navegar"><option value="menu">📑 Mostrar menú</option></optgroup>' +
<optgroup label="Formulario"> ' <optgroup label="Acción directa"><option value="function">⚡ Función especial</option></optgroup>' +
<option value="collect_input">✏️ Pedir dato</option> ' <optgroup label="Formulario">' +
<option value="dynamic_list">📋 Lista dinámica</option> ' <option value="collect_input">✏️ Pedir dato</option>' +
<option value="submit_form">🚀 Enviar formulario</option> ' <option value="dynamic_list">📋 Lista dinámica</option>' +
</optgroup> ' <option value="submit_form">🚀 Enviar formulario</option>' +
</select> ' </optgroup>' +
</div> ' </select>' +
<div style="flex:1;display:flex;flex-direction:column;gap:10px"> ' </div>' +
<div class="ftg-text"><textarea name="flow_message[]" rows="2" placeholder="Texto que recibirá el usuario"></textarea></div> ' <div style="flex:1;display:flex;flex-direction:column;gap:10px">' +
<div class="ftg-menu" style="display:none"><label>Menú a mostrar</label><select name="flow_menu[]">${menuOpts}</select></div> ' <div class="ftg-text"><textarea name="flow_message[]" rows="2" placeholder="Texto que recibirá el usuario"></textarea></div>' +
<div class="ftg-fn" style="display:none"> ' <div class="ftg-menu" style="display:none"><label>Menú a mostrar</label><select name="flow_menu[]">' + menuOpts + '</select></div>' +
<label>Función</label> ' <div class="ftg-fn" style="display:none">' +
<select name="flow_function[]" onchange="flowFnTabChange(this)"> ' <label>Función</label>' +
<option value="goodbye">↩️ Salir (goodbye)</option> ' <select name="flow_function[]" onchange="flowFnTabChange(this)">' +
<option value="api_report">📄 Descargar informe</option> ' <option value="goodbye">↩️ Salir (goodbye)</option>' +
<option value="forward_to_ai">🤖 Escalar a IA</option> ' <option value="api_report">📄 Descargar informe</option>' +
<option value="forward_to_agent">👤 Escalar a agente</option> ' <option value="forward_to_ai">🤖 Escalar a IA</option>' +
</select> ' <option value="forward_to_agent">👤 Escalar a agente</option>' +
<div class="ftg-ep" style="display:none;flex-direction:column;gap:8px;margin-top:8px"> ' </select>' +
<div><label>Endpoint</label><select name="flow_ep_key[]">${epOpts}</select></div> ' <div class="ftg-ep" style="display:none;flex-direction:column;gap:8px;margin-top:8px">' +
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"> ' <div><label>Endpoint</label><select name="flow_ep_key[]">' + epOpts + '</select></div>' +
<div><label>Periodo</label><select name="flow_date_mode[]"><option value="">Sin fecha</option><option value="today">Hoy</option><option value="last_30">Últimos 30 días</option></select></div> ' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">' +
<div><label>Nombre archivo</label><input type="text" name="flow_filename[]" placeholder="reporte.pdf"></div> ' <div><label>Periodo</label><select name="flow_date_mode[]"><option value="">Sin fecha</option><option value="today">Hoy</option><option value="last_30">Últimos 30 días</option></select></div>' +
</div> ' <div><label>Nombre archivo</label><input type="text" name="flow_filename[]" placeholder="reporte.pdf"></div>' +
<div><label>Mensaje al enviar</label><input type="text" name="flow_caption[]" placeholder="Aquí tienes el informe."></div> ' </div>' +
</div> ' <div><label>Mensaje al enviar</label><input type="text" name="flow_caption[]" placeholder="Aquí tienes el informe."></div>' +
</div> ' </div>' +
<div class="ftg-collect" style="display:none;flex-direction:column;gap:8px"> ' </div>' +
<div><label>Pregunta al usuario</label><input type="text" name="flow_prompt[]" placeholder="¿Desde qué fecha? (YYYY-MM-DD)"></div> ' <div class="ftg-collect" style="display:none;flex-direction:column;gap:8px">' +
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"> ' <div><label>Pregunta al usuario</label><input type="text" name="flow_prompt[]" placeholder="¿Desde qué fecha? (YYYY-MM-DD)"></div>' +
<div><label>Grupo formulario</label><input type="text" name="flow_meta_group[]" placeholder="form_prod"></div> ' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">' +
<div><label>Campo</label><input type="text" name="flow_meta_key[]" placeholder="desde"></div> ' <div><label>Grupo formulario</label><input type="text" name="flow_meta_group[]" placeholder="form_prod"></div>' +
</div> ' <div><label>Campo</label><input type="text" name="flow_meta_key[]" placeholder="desde"></div>' +
<div><label>Flujo siguiente</label><select name="flow_next_node[]">${nnOpts}</select></div> ' </div>' +
</div> ' <div><label>Flujo siguiente</label><select name="flow_next_node[]">' + nnOpts + '</select></div>' +
<div class="ftg-dynlist" style="display:none;flex-direction:column;gap:8px"> ' </div>' +
<div><label>Endpoint (lista)</label><select name="flow_dl_ep[]">${epOpts}</select></div> ' <div class="ftg-dynlist" style="display:none;flex-direction:column;gap:8px">' +
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"> ' <div><label>Endpoint (lista)</label><select name="flow_dl_ep[]">' + epOpts + '</select></div>' +
<div><label>Campo ID</label><input type="text" name="flow_val_field[]" placeholder="id"></div> ' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">' +
<div><label>Campo etiqueta</label><input type="text" name="flow_lbl_field[]" placeholder="nombre"></div> ' <div><label>Campo ID</label><input type="text" name="flow_val_field[]" placeholder="id"></div>' +
</div> ' <div><label>Campo etiqueta</label><input type="text" name="flow_lbl_field[]" placeholder="nombre"></div>' +
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"> ' </div>' +
<div><label>Encabezado</label><input type="text" name="flow_header[]" placeholder="Selecciona..."></div> ' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">' +
<div><label>Título sección</label><input type="text" name="flow_sec_title[]" placeholder="Opciones"></div> ' <div><label>Encabezado</label><input type="text" name="flow_header[]" placeholder="Selecciona..."></div>' +
</div> ' <div><label>Título sección</label><input type="text" name="flow_sec_title[]" placeholder="Opciones"></div>' +
<div><label>Cuerpo</label><input type="text" name="flow_body[]" placeholder="¿Cuál deseas?"></div> ' </div>' +
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"> ' <div><label>Cuerpo</label><input type="text" name="flow_body[]" placeholder="¿Cuál deseas?"></div>' +
<div><label>Grupo formulario</label><input type="text" name="flow_dl_meta_group[]" placeholder="form_prod"></div> ' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">' +
<div><label>Campo</label><input type="text" name="flow_dl_meta_key[]" placeholder="finca_id"></div> ' <div><label>Grupo formulario</label><input type="text" name="flow_dl_meta_group[]" placeholder="form_prod"></div>' +
</div> ' <div><label>Campo</label><input type="text" name="flow_dl_meta_key[]" placeholder="finca_id"></div>' +
<div><label>Flujo siguiente</label><select name="flow_dl_next[]">${nnOpts}</select></div> ' </div>' +
</div> ' <div><label>Flujo siguiente</label><select name="flow_dl_next[]">' + nnOpts + '</select></div>' +
<div class="ftg-submit" style="display:none;flex-direction:column;gap:8px"> ' </div>' +
<div><label>Endpoint destino</label><select name="flow_sf_ep[]">${epOpts}</select></div> ' <div class="ftg-submit" style="display:none;flex-direction:column;gap:8px">' +
<div><label>Grupo formulario</label><input type="text" name="flow_sf_meta_group[]" placeholder="form_prod"></div> ' <div><label>Endpoint destino</label><select name="flow_sf_ep[]">' + epOpts + '</select></div>' +
<div style="display:grid;grid-template-columns:1fr 1fr;gap:8px"> ' <div><label>Grupo formulario</label><input type="text" name="flow_sf_meta_group[]" placeholder="form_prod"></div>' +
<div><label>Nombre archivo</label><input type="text" name="flow_sf_filename[]" placeholder="reporte.pdf"></div> ' <div style="display:grid;grid-template-columns:1fr 1fr;gap:8px">' +
<div><label>Mensaje</label><input type="text" name="flow_sf_caption[]" placeholder="Aquí tienes el informe."></div> ' <div><label>Nombre archivo</label><input type="text" name="flow_sf_filename[]" placeholder="reporte.pdf"></div>' +
</div> ' <div><label>Mensaje</label><input type="text" name="flow_sf_caption[]" placeholder="Aquí tienes el informe."></div>' +
</div> ' </div>' +
</div> ' </div>' +
</div> ' </div>' +
</div>`); ' </div>' +
'</div>'
);
container.lastElementChild.scrollIntoView({behavior:'smooth'}); container.lastElementChild.scrollIntoView({behavior:'smooth'});
} }