feat: expandir tab Por Categoría en bot-config
- Toggle "Pedir finca al inicio" por categoría → maneja greeting/greeting_flow - Campo editable de greeting_flow (pre-llena ask_finca al activar el toggle) - Selector de menú inicial (greeting_menu, visible cuando NO pide finca) - Selector de flujo de respaldo (fallback_flow) por categoría - Datalist con flows y menús disponibles para autocompletar - save-per-type actualiza greeting, greeting_flow, greeting_menu y fallback_flow preservando menus/flows/commands del seed en cada categoría - Cat 1: sin finca, configura greeting_menu desde admin - Cat 2/3: pide finca activado, greeting_flow = ask_finca Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
0ddab6adcf
commit
909c6be32f
+103
-18
@@ -3193,28 +3193,87 @@ HTML;
|
||||
// Per-category config HTML
|
||||
$perTypeConfig = $config['per_type'] ?? [];
|
||||
$menuKeysList = array_keys($menus);
|
||||
$catLabels = ['1' => 'Categoría 1 — Solo reporta', '2' => 'Categoría 2 — Solo recibe informes', '3' => 'Categoría 3 — Reporta y recibe'];
|
||||
$flowKeysList = array_keys($config['flows'] ?? []);
|
||||
// Include per_type menus/flows in the option lists
|
||||
foreach ($config['per_type'] ?? [] as $pt) {
|
||||
foreach (array_keys($pt['menus'] ?? []) as $k) if (!in_array($k, $menuKeysList)) $menuKeysList[] = $k;
|
||||
foreach (array_keys($pt['flows'] ?? []) as $k) if (!in_array($k, $flowKeysList)) $flowKeysList[] = $k;
|
||||
}
|
||||
$catMeta = [
|
||||
'1' => ['label' => 'Categoría 1 — Solo reporta', 'icon' => '📤', 'bg' => '#f0fdf4', 'bd' => '#86efac', 'clr' => '#166534'],
|
||||
'2' => ['label' => 'Categoría 2 — Solo recibe informes', 'icon' => '📥', 'bg' => '#eff6ff', 'bd' => '#93c5fd', 'clr' => '#1e40af'],
|
||||
'3' => ['label' => 'Categoría 3 — Reporta y recibe', 'icon' => '🔄', 'bg' => '#fdf4ff', 'bd' => '#e879f9', 'clr' => '#701a75'],
|
||||
];
|
||||
$categoryTabHtml = '';
|
||||
// Build shared option lists
|
||||
$menuOptsBase = '<option value="">— Sin menú —</option>';
|
||||
foreach ($menuKeysList as $mk) $menuOptsBase .= '<option value="' . self::h($mk) . '">' . self::h($mk) . '</option>';
|
||||
$flowOptsBase = '<option value="">— Ninguno —</option>';
|
||||
foreach ($menuKeysList as $mk) $flowOptsBase .= '<option value="' . self::h($mk) . '">📑 ' . self::h($mk) . '</option>';
|
||||
foreach ($flowKeysList as $fk) $flowOptsBase .= '<option value="' . self::h($fk) . '">🔀 ' . self::h($fk) . '</option>';
|
||||
|
||||
foreach ([1, 2, 3] as $cat) {
|
||||
$catCfg = $perTypeConfig[(string)$cat] ?? [];
|
||||
$m = $catMeta[(string)$cat];
|
||||
$greetingVal = array_key_exists('greeting', $catCfg) ? $catCfg['greeting'] : null;
|
||||
$askFinca = $greetingVal !== null;
|
||||
$chk = $askFinca ? ' checked' : '';
|
||||
$greetFlow = self::h($catCfg['greeting_flow'] ?? ($askFinca ? 'ask_finca' : ''));
|
||||
$fallback = $catCfg['fallback_flow'] ?? '';
|
||||
$selMenu = $catCfg['greeting_menu'] ?? '';
|
||||
$opts = '<option value="">— Usa configuración global —</option>';
|
||||
foreach ($menuKeysList as $mk) {
|
||||
$s = $selMenu === $mk ? ' selected' : '';
|
||||
$opts .= '<option value="' . self::h($mk) . '"' . $s . '>' . self::h($mk) . '</option>';
|
||||
}
|
||||
$label = $catLabels[(string)$cat];
|
||||
|
||||
// greeting_menu selector (insert selected attr)
|
||||
$menuOpts = str_replace(
|
||||
'value="' . self::h($selMenu) . '"',
|
||||
'value="' . self::h($selMenu) . '" selected',
|
||||
$menuOptsBase
|
||||
);
|
||||
// fallback selector (insert selected attr)
|
||||
$fbOpts = str_replace(
|
||||
'value="' . self::h($fallback) . '"',
|
||||
'value="' . self::h($fallback) . '" selected',
|
||||
$flowOptsBase
|
||||
);
|
||||
|
||||
$greetFlowStyle = $askFinca ? '' : 'display:none';
|
||||
$greetMenuStyle = $askFinca ? 'display:none' : '';
|
||||
|
||||
$label = $m['label']; $icon = $m['icon']; $bg = $m['bg']; $bd = $m['bd']; $clr = $m['clr'];
|
||||
$categoryTabHtml .= <<<CATHTML
|
||||
<div style="background:#f8f9fd;border:1px solid #eef1f5;border-radius:10px;padding:18px;margin-bottom:16px">
|
||||
<div style="font-weight:700;font-size:13px;color:#0b3d91;margin-bottom:12px">{$label}</div>
|
||||
<div class="form-group" style="margin-bottom:0">
|
||||
<label>Menú de bienvenida (greeting menu)</label>
|
||||
<select name="per_type_menu[{$cat}]" style="width:100%;padding:9px 12px;border:1px solid #e2e5ea;border-radius:8px;font-size:13px">
|
||||
{$opts}
|
||||
</select>
|
||||
<div style="font-size:11px;color:#7a8291;margin-top:4px">Menú que se muestra cuando el usuario escribe por primera vez o sin contexto activo</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="background:{$bg};border:1px solid {$bd};border-radius:10px;padding:18px;margin-bottom:16px">
|
||||
<div style="display:flex;align-items:center;gap:8px;margin-bottom:14px">
|
||||
<span style="font-size:20px">{$icon}</span>
|
||||
<span style="font-weight:700;font-size:13px;color:{$clr}">{$label}</span>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom:12px">
|
||||
<label style="display:flex;align-items:center;gap:8px;font-weight:500">
|
||||
<input type="checkbox" name="per_type_ask_finca[{$cat}]" value="1"{$chk} onchange="toggleAskFinca(this,{$cat})">
|
||||
Pedir selección de finca al inicio
|
||||
</label>
|
||||
<div style="font-size:11px;color:#7a8291;margin-top:3px">Al activarlo, el bot solicita la finca antes de mostrar el menú principal</div>
|
||||
</div>
|
||||
<div id="cat-greet-flow-{$cat}" style="{$greetFlowStyle}" class="form-group" style="margin-bottom:12px">
|
||||
<label>Flujo de bienvenida</label>
|
||||
<input type="text" name="per_type_greeting_flow[{$cat}]" value="{$greetFlow}"
|
||||
list="ptFlowsDatalist" placeholder="ej: ask_finca"
|
||||
style="width:100%;padding:8px 12px;border:1px solid #e2e5ea;border-radius:8px;font-size:13px">
|
||||
<div style="font-size:11px;color:#7a8291;margin-top:3px">Flow que se ejecuta en el primer mensaje del usuario</div>
|
||||
</div>
|
||||
<div id="cat-greet-menu-{$cat}" style="{$greetMenuStyle}" class="form-group" style="margin-bottom:12px">
|
||||
<label>Menú inicial</label>
|
||||
<select name="per_type_menu[{$cat}]" style="width:100%;padding:9px 12px;border:1px solid #e2e5ea;border-radius:8px;font-size:13px">
|
||||
{$menuOpts}
|
||||
</select>
|
||||
<div style="font-size:11px;color:#7a8291;margin-top:3px">Menú que se muestra cuando el usuario no tiene contexto activo</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-bottom:0">
|
||||
<label>Flujo de respaldo (fallback)</label>
|
||||
<select name="per_type_fallback[{$cat}]" style="width:100%;padding:9px 12px;border:1px solid #e2e5ea;border-radius:8px;font-size:13px">
|
||||
{$fbOpts}
|
||||
</select>
|
||||
<div style="font-size:11px;color:#7a8291;margin-top:3px">Se muestra cuando el usuario envía algo que no corresponde a ninguna opción</div>
|
||||
</div>
|
||||
</div>
|
||||
CATHTML;
|
||||
}
|
||||
$aiModelMini = $aiModelVal === 'gpt-4o-mini' ? ' selected' : '';
|
||||
@@ -3643,7 +3702,13 @@ HTML;
|
||||
<!-- ─── POR CATEGORÍA ────────────────────────────────────────────────── -->
|
||||
<div class="tab-content" id="tab-categories">
|
||||
<div class="card-h">👥 Configuración por Categoría</div>
|
||||
<p style="font-size:12px;color:#7a8291;margin-bottom:16px">Define qué menú de bienvenida ve cada categoría de usuario cuando escribe por primera vez o sin contexto activo.</p>
|
||||
<p style="font-size:12px;color:#7a8291;margin-bottom:16px">Define el comportamiento inicial de cada categoría de usuario: si pide finca, qué menú muestra y qué hacer ante texto no reconocido.</p>
|
||||
<datalist id="ptFlowsDatalist">
|
||||
HTML;
|
||||
foreach ($menuKeysList as $mk) echo '<option value="' . self::h($mk) . '">📑 Menú</option>' . "\n";
|
||||
foreach ($flowKeysList as $fk) echo '<option value="' . self::h($fk) . '">🔀 Flow</option>' . "\n";
|
||||
echo <<<HTML
|
||||
</datalist>
|
||||
{$categoryTabHtml}
|
||||
<button type="button" id="savePerTypeBtn" onclick="savePerType()" class="btn-primary" style="margin-top:8px">💾 Guardar categorías</button>
|
||||
</div>
|
||||
@@ -3846,13 +3911,33 @@ HTML;
|
||||
const CONFIG = {$botConfigJson};
|
||||
const ENDPOINTS = {$botEndpointsJson};
|
||||
|
||||
function toggleAskFinca(chk, cat) {
|
||||
var flowDiv = document.getElementById('cat-greet-flow-' + cat);
|
||||
var menuDiv = document.getElementById('cat-greet-menu-' + cat);
|
||||
if (chk.checked) {
|
||||
if (flowDiv) flowDiv.style.display = '';
|
||||
if (menuDiv) menuDiv.style.display = 'none';
|
||||
var gf = document.querySelector('[name="per_type_greeting_flow[' + cat + ']"]');
|
||||
if (gf && gf.value === '') gf.value = 'ask_finca';
|
||||
} else {
|
||||
if (flowDiv) flowDiv.style.display = 'none';
|
||||
if (menuDiv) menuDiv.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function savePerType() {
|
||||
const cid = document.querySelector('input[name="company_id"]').value;
|
||||
const fd = new FormData();
|
||||
fd.set('company_id', cid);
|
||||
[1, 2, 3].forEach(function(cat) {
|
||||
var chk = document.querySelector('[name="per_type_ask_finca[' + cat + ']"]');
|
||||
fd.append('per_type_ask_finca[' + cat + ']', chk && chk.checked ? '1' : '0');
|
||||
var gf = document.querySelector('[name="per_type_greeting_flow[' + cat + ']"]');
|
||||
if (gf) fd.append('per_type_greeting_flow[' + cat + ']', gf.value);
|
||||
var sel = document.querySelector('[name="per_type_menu[' + cat + ']"]');
|
||||
if (sel) fd.append('per_type_menu[' + cat + ']', sel.value);
|
||||
var fb = document.querySelector('[name="per_type_fallback[' + cat + ']"]');
|
||||
if (fb) fd.append('per_type_fallback[' + cat + ']', fb.value);
|
||||
});
|
||||
var btn = document.getElementById('savePerTypeBtn');
|
||||
if (btn) { btn.textContent = 'Guardando...'; btn.disabled = true; }
|
||||
|
||||
Reference in New Issue
Block a user