diff --git a/admin/DashboardController.php b/admin/DashboardController.php index 2271d92..7c2c1df 100644 --- a/admin/DashboardController.php +++ b/admin/DashboardController.php @@ -2353,24 +2353,51 @@ HTML; HTML; $menuIdx = 0; foreach ($menus as $mk => $menu) { - $mt = $menu['type'] ?? 'list'; - $mh = self::h($menu['header'] ?? ''); - $mb = self::h($menu['body'] ?? ''); - $mf = self::h($menu['footer'] ?? ''); + $mt = $menu['type'] ?? 'button'; + $mb = self::h($menu['body'] ?? ''); + $mh = self::h($menu['header'] ?? ''); + $mf = self::h($menu['footer'] ?? ''); $mbtn = self::h($menu['button'] ?? ''); + $isBtn = $mt === 'button'; + + $listStyle = $isBtn ? 'display:none' : ''; + $btnStyle = $isBtn ? '' : 'display:none'; + echo "
- Menú: - + Menú: +
-
-
-
-
-
+
+ +
+
+ +
+
+
+
-
+
+
Botones (máx 3):
+
"; + foreach ($menu['buttons'] ?? [] as $bi => $btn) { + $bid = self::h($btn['id'] ?? ''); + $btt = self::h($btn['title'] ?? ''); + echo "
+ + + +
"; + } + echo "
+ +
+
Secciones:
"; $si = 0; @@ -2381,26 +2408,23 @@ HTML;
-
Filas (cada fila es una opción):
"; +
Filas:
"; $ri = 0; foreach ($section['rows'] ?? [] as $row) { - $rid = self::h($row['id'] ?? ''); - $rt = self::h($row['title'] ?? ''); - $rd = self::h($row['description'] ?? ''); + $rid = self::h($row['id'] ?? ''); $rt = self::h($row['title'] ?? ''); $rd = self::h($row['description'] ?? ''); echo "
- +
"; $ri++; } - echo " -
"; + echo "
"; $si++; } echo "
- + "; $menuIdx++; @@ -2572,21 +2596,56 @@ function addCmd() { } // Menus +function menuTypeChange(sel, mi) { + const isBtn = sel.value === 'button'; + document.getElementById('btn-area-' + mi).style.display = isBtn ? '' : 'none'; + document.getElementById('sections-area-' + mi).style.display = isBtn ? 'none' : ''; + document.querySelectorAll('.list-fields-' + mi).forEach(el => el.style.display = isBtn ? 'none' : ''); +} + +function addBtnRow(mi) { + const container = document.getElementById('btn-rows-' + mi); + const div = document.createElement('div'); + div.className = 'inline-group'; + div.style.cssText = 'display:flex;gap:8px;align-items:center;margin-bottom:4px'; + div.innerHTML = + '' + + '' + + ''; + container.appendChild(div); +} + function addMenu() { const container = document.getElementById('menuContainer'); const idx = container.children.length; const div = document.createElement('div'); div.className = 'item-card'; div.id = 'menu-card-' + idx; - div.innerHTML = '
Menú:
' + + div.innerHTML = + '
Menú: ' + + '
' + '
' + - '
' + - '
' + - '
' + - '
' + - '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '' + '
' + - '
Secciones:
'; + '
' + + '
Botones (máx 3):
' + + '
' + + '' + + '
' + + ''; container.appendChild(div); } @@ -2921,13 +2980,22 @@ function deleteCommand(key) { function editMenu(key) { const menu = CONFIG.menus[key]; if (!menu) return; + const isBtn = (menu.type || 'button') === 'button'; + + const buttonsHtml = (menu.buttons || []).map((b, bi) => \` +
+ + + +
\`).join(''); + const sectionsHtml = (menu.sections || []).map((s, si) => \`
-
Filas (botones):
+
Filas:
\${(s.rows || []).map((r, ri) => \`
@@ -2936,16 +3004,37 @@ function editMenu(key) {
\`).join('')} -
\`).join('')} + \`).join(''); + showModal(\`

✏️ Editar Menú: \${key}

-
-
-
-
-
-
\${sectionsHtml}
- +
+
+ +
+
+
+
+
+
+
+
+
+
+
+
+
Botones (máx 3):
+
\${buttonsHtml}
+ +
+
+
Secciones:
+
\${sectionsHtml}
+ +
@@ -2953,6 +3042,26 @@ function editMenu(key) {
\`); } +function editMenuTypeToggle() { + const isBtn = document.getElementById('editMenuType').value === 'button'; + document.getElementById('editBtnArea').style.display = isBtn ? '' : 'none'; + document.getElementById('editSectionsArea').style.display = isBtn ? 'none' : ''; + document.getElementById('editListFields').style.display = isBtn ? 'none' : ''; +} + +function addEditBtnRow() { + const cont = document.getElementById('editBtnRows'); + const bi = cont.querySelectorAll('.edit-btn-row').length; + const div = document.createElement('div'); + div.className = 'edit-btn-row'; + div.style.cssText = 'display:flex;gap:6px;margin-bottom:4px'; + div.innerHTML = \` + + + \`; + cont.appendChild(div); +} + function addSectionInline() { const cont = document.getElementById('sectionsEditContainer'); const si = cont.children.length; @@ -3000,39 +3109,52 @@ function createRowInput(si, ri) { function saveMenu(oldKey) { const newKey = document.getElementById('editMenuKey').value.trim(); if (!newKey) { alert('El ID del menú es requerido'); return; } - const menu = { - type: 'list', - header: document.getElementById('editMenuHeader').value, - body: document.getElementById('editMenuBody').value, - footer: document.getElementById('editMenuFooter').value, - button: document.getElementById('editMenuButton').value || 'Menú', - sections: [] - }; - const cont = document.getElementById('sectionsEditContainer'); - const secDivs = cont.children; - for (let si = 0; si < secDivs.length; si++) { - const secDiv = secDivs[si]; - const titleEl = secDiv.querySelector('.sec-title-' + si) || secDiv.querySelector('input[placeholder="Título sección"]'); - const title = titleEl?.value?.trim(); - if (!title) continue; - const rows = []; - const rowEls = secDiv.querySelectorAll('[class^="row-id-"]'); - const rowSet = new Set(); - rowEls.forEach(el => { - const match = el.className.match(/row-id-(\\d+)-(\\d+)/); - if (!match) return; - const rsi = parseInt(match[1]); - const rri = parseInt(match[2]); - if (rsi !== si || rowSet.has(rri)) return; - rowSet.add(rri); - const titleInput = secDiv.querySelector('.row-title-' + rsi + '-' + rri); - const descInput = secDiv.querySelector('.row-desc-' + rsi + '-' + rri); - const id = el.value.trim(); - const t = titleInput?.value?.trim(); - if (id && t) rows.push({ id, title: t, description: descInput?.value?.trim() || '' }); + const menuType = document.getElementById('editMenuType').value; + const body = document.getElementById('editMenuBody').value; + + let menu; + if (menuType === 'button') { + const buttons = []; + document.getElementById('editBtnRows').querySelectorAll('.edit-btn-row').forEach((row, bi) => { + const idEl = row.querySelector('.ebtn-id-' + bi) || row.querySelector('input[placeholder*="ID"]'); + const titleEl = row.querySelector('.ebtn-title-' + bi) || row.querySelector('input[placeholder*="Título"]'); + const id = idEl?.value?.trim(); + const title = titleEl?.value?.trim(); + if (id && title) buttons.push({ id, title: title.substring(0, 20) }); }); - if (rows.length > 0) menu.sections.push({ title, rows }); - } + menu = { type: 'button', body, buttons }; + } else { + const header = document.getElementById('editMenuHeader')?.value || ''; + const footer = document.getElementById('editMenuFooter')?.value || ''; + const button = document.getElementById('editMenuButton')?.value || 'Ver opciones'; + menu = { type: 'list', header, body, footer, button, sections: [] }; + const cont = document.getElementById('sectionsEditContainer'); + const secDivs = cont.children; + for (let si = 0; si < secDivs.length; si++) { + const secDiv = secDivs[si]; + const titleEl = secDiv.querySelector('.sec-title-' + si) || secDiv.querySelector('input[placeholder="Título sección"]'); + const title = titleEl?.value?.trim(); + if (!title) continue; + const rows = []; + const rowEls = secDiv.querySelectorAll('[class^="row-id-"]'); + const rowSet = new Set(); + rowEls.forEach(el => { + const match = el.className.match(/row-id-(\\d+)-(\\d+)/); + if (!match) return; + const rsi = parseInt(match[1]); + const rri = parseInt(match[2]); + if (rsi !== si || rowSet.has(rri)) return; + rowSet.add(rri); + const titleInput = secDiv.querySelector('.row-title-' + rsi + '-' + rri); + const descInput = secDiv.querySelector('.row-desc-' + rsi + '-' + rri); + const id = el.value.trim(); + const t = titleInput?.value?.trim(); + if (id && t) rows.push({ id, title: t, description: descInput?.value?.trim() || '' }); + }); + if (rows.length > 0) menu.sections.push({ title, rows }); + } + } // end else list + delete CONFIG.menus[oldKey]; CONFIG.menus[newKey] = menu; // Update commands that reference this menu @@ -3466,46 +3588,52 @@ HTML; private static function renderMenuNode(string $menuKey, array $menu, array $flows, array $allMenus, int $companyId): void { - $typeLabel = ($menu['type'] ?? 'list') === 'list' ? 'Lista' : 'Botones'; - $typeBadge = ($menu['type'] ?? 'list') === 'list' ? 'badge-list' : 'badge-button'; - $hasRows = false; - foreach ($menu['sections'] ?? [] as $section) { - if (!empty($section['rows'])) { $hasRows = true; break; } + $isButton = ($menu['type'] ?? 'list') === 'button'; + $typeLabel = $isButton ? 'Botones' : 'Lista'; + $typeBadge = $isButton ? 'badge-button' : 'badge-list'; + + // Unifica items: botones directos o filas de secciones + $items = []; + if ($isButton) { + foreach ($menu['buttons'] ?? [] as $btn) { + $items[] = ['id' => $btn['id'], 'title' => $btn['title'], 'description' => '']; + } + } else { + foreach ($menu['sections'] ?? [] as $section) { + foreach ($section['rows'] ?? [] as $row) { + $items[] = ['id' => $row['id'], 'title' => $row['title'], 'description' => $row['description'] ?? '']; + } + } } echo '
'; echo ''; // node-card - // ── Show first sub-menu connection inline ── - foreach ($menu['sections'] ?? [] as $section) { - foreach ($section['rows'] ?? [] as $row) { - $flow = $flows[$row['id']] ?? null; - if ($flow && $flow['type'] === 'menu' && isset($flow['menu'])) { - echo '
'; - echo '
'; - echo '
🔘 ' . self::h($row['title']) . ' →
'; - self::renderFlowNode($row['id'], $flow, $allMenus, $companyId, true); - echo '
'; - break 2; - } + // Mostrar primer submenú en árbol + foreach ($items as $item) { + $flow = $flows[$item['id']] ?? null; + if ($flow && $flow['type'] === 'menu' && isset($flow['menu'])) { + echo '
'; + echo '
'; + echo '
🔲 ' . self::h($item['title']) . ' →
'; + self::renderFlowNode($item['id'], $flow, $allMenus, $companyId, true); + echo '
'; + break; } } @@ -3583,21 +3709,19 @@ HTML; echo '
📑 Sub-menú: ' . self::h($subKey) . '
'; if ($subMenu['body']) echo '
' . self::h($subMenu['body']) . '
'; - // Show sub-menu's rows - $hasRows = false; - foreach ($subMenu['sections'] ?? [] as $section) { - if (!empty($section['rows'])) { $hasRows = true; break; } - } - if ($hasRows) { + // Mostrar items del sub-menú (buttons o sections[].rows) + $subIsBtn = ($subMenu['type'] ?? 'list') === 'button'; + $subItems = $subIsBtn + ? ($subMenu['buttons'] ?? []) + : array_merge(...array_map(fn($s) => $s['rows'] ?? [], $subMenu['sections'] ?? [])); + if (!empty($subItems)) { echo '
'; - foreach ($subMenu['sections'] ?? [] as $section) { - foreach ($section['rows'] ?? [] as $row) { - echo '
'; - echo '🔘'; - echo '' . self::h($row['title']) . ''; - echo '' . self::h($row['id']) . ''; - echo '
'; - } + foreach ($subItems as $item) { + echo '
'; + echo '' . ($subIsBtn ? '🔲' : '🔘') . ''; + echo '' . self::h($item['title']) . ''; + echo '' . self::h($item['id']) . ''; + echo '
'; } echo '
'; } else { diff --git a/public/index.php b/public/index.php index 1e17a4c..4906756 100644 --- a/public/index.php +++ b/public/index.php @@ -324,40 +324,57 @@ $routes = [ if (!empty($commands)) $config['commands'] = $commands; // Menus - $menuKeys = $_POST['menu_key'] ?? []; - $menuTypes = $_POST['menu_type'] ?? []; - $menuHeaders = $_POST['menu_header'] ?? []; - $menuBodies = $_POST['menu_body'] ?? []; - $menuFooters = $_POST['menu_footer'] ?? []; - $menuButtons = $_POST['menu_button'] ?? []; + $menuKeys = $_POST['menu_key'] ?? []; + $menuTypes = $_POST['menu_type'] ?? []; + $menuBodies = $_POST['menu_body'] ?? []; + $menuHeaders = $_POST['menu_header'] ?? []; + $menuFooters = $_POST['menu_footer'] ?? []; + $menuBtnTrigger = $_POST['menu_button'] ?? []; $menuSections = $_POST['menu_sections'] ?? []; + $menuBtnIds = $_POST['menu_btn_id'] ?? []; + $menuBtnTitles = $_POST['menu_btn_title'] ?? []; $menus = []; foreach ($menuKeys as $i => $mk) { - $mk = trim($mk); + $mk = trim($mk); + $type = $menuTypes[$i] ?? 'button'; if ($mk === '') continue; - $sections = []; - if (!empty($menuSections[$i])) { - foreach ($menuSections[$i] as $si => $section) { + if ($type === 'button') { + $buttons = []; + foreach ($menuBtnIds[$i] ?? [] as $j => $btnId) { + $btnId = trim($btnId); + $btnTitle = mb_substr(trim($menuBtnTitles[$i][$j] ?? ''), 0, 20); + if ($btnId !== '' && $btnTitle !== '') { + $buttons[] = ['id' => $btnId, 'title' => $btnTitle]; + } + } + $menus[$mk] = [ + 'type' => 'button', + 'body' => trim($menuBodies[$i] ?? ''), + 'buttons' => $buttons, + ]; + } else { + $sections = []; + foreach ($menuSections[$i] ?? [] as $si => $section) { $title = trim($section['title'] ?? ''); if ($title === '') continue; $rows = []; foreach ($section['rows'] ?? [] as $row) { - $rid = trim($row['id'] ?? ''); + $rid = trim($row['id'] ?? ''); $rtitle = trim($row['title'] ?? ''); if ($rid === '' || $rtitle === '') continue; $rows[] = ['id' => $rid, 'title' => $rtitle, 'description' => trim($row['description'] ?? '')]; } $sections[] = ['title' => $title, 'rows' => $rows]; } + $menus[$mk] = [ + 'type' => 'list', + 'header' => trim($menuHeaders[$i] ?? ''), + 'body' => trim($menuBodies[$i] ?? ''), + 'footer' => trim($menuFooters[$i] ?? ''), + 'button' => trim($menuBtnTrigger[$i] ?? ''), + 'sections' => $sections, + ]; } - $menus[$mk] = [ - 'type' => $menuTypes[$i] ?? 'list', - 'header' => trim($menuHeaders[$i] ?? ''), - 'body' => trim($menuBodies[$i] ?? ''), - 'footer' => trim($menuFooters[$i] ?? ''), - 'button' => trim($menuButtons[$i] ?? ''), - 'sections' => $sections, - ]; } if (!empty($menus)) $config['menus'] = $menus; diff --git a/services/NormalBot.php b/services/NormalBot.php index 9208b73..5feb9a3 100644 --- a/services/NormalBot.php +++ b/services/NormalBot.php @@ -262,7 +262,7 @@ class NormalBot return self::enqueueInteractive($to, $interactive, $company); } - if ($menuType === 'buttons') { + if ($menuType === 'button') { $buttons = []; foreach ($menu['buttons'] ?? [] as $btn) { $buttons[] = [