From 115f1675ad1dc2c8645c5ab4bf6ab490ab572964 Mon Sep 17 00:00:00 2001 From: Lizandro Guarnizo <77708265+lizandrogd@users.noreply.github.com> Date: Fri, 14 Aug 2026 21:49:32 -0500 Subject: [PATCH] =?UTF-8?q?fix(studio):=20los=20campos=20Cliente=20y=20Pla?= =?UTF-8?q?n=20no=20aparec=C3=ADan=20al=20crear=20un=20tenant?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /app/api/clientes/select devuelve el array pelado y /app/umind-planes/list lo envuelve en {items}. El código leía solo .registros/.items, así que con el array quedaba vacío — y el bloque estaba detrás de v-if="clientes.length", o sea que Cliente y Plan simplemente no se veían y no había forma de asignarlos. - lista() acepta las dos formas (verificado contra ambas y contra vacío/null/undefined). - Se cargan con allSettled: que falle uno ya no vacía el otro. - El bloque se muestra siempre para el staff, y cada select dice a dónde ir si su lista está vacía. Antes un fallo de red se veía exactamente igual que "no hay nada configurado", que es lo que enmascaró este bug. Co-Authored-By: Claude Sonnet 5 --- orchestrator/src/components/Sidebar.vue | 48 +++++++++++++------- public/orchestrator/assets/index-D9xKBSe-.js | 26 +++++++++++ public/orchestrator/assets/index-DP70_OF3.js | 26 ----------- public/orchestrator/index.html | 2 +- 4 files changed, 58 insertions(+), 44 deletions(-) create mode 100644 public/orchestrator/assets/index-D9xKBSe-.js delete mode 100644 public/orchestrator/assets/index-DP70_OF3.js diff --git a/orchestrator/src/components/Sidebar.vue b/orchestrator/src/components/Sidebar.vue index 6c4a86d..99ca33d 100644 --- a/orchestrator/src/components/Sidebar.vue +++ b/orchestrator/src/components/Sidebar.vue @@ -37,21 +37,31 @@ async function cargar() { } } -// Clientes y planes solo los necesita el staff para asignarlos; si el endpoint -// no está disponible (portal del cliente) el formulario sigue funcionando. +// Los endpoints del panel no comparten una forma de respuesta: el de clientes +// devuelve el array pelado y el de planes lo envuelve en {items}. Aceptar las +// dos evita que un select quede vacío sin que nadie se entere. +function lista(r) { + if (Array.isArray(r)) return r + return r?.items || r?.registros || [] +} + +// Clientes y planes solo los necesita el staff para asignarlos; en el portal +// del cliente esos endpoints no se alcanzan y el formulario no los muestra. +// Se cargan por separado a propósito: que falle uno no debe vaciar el otro. async function cargarAsignables() { - if (contexto.esPortal) return // endpoints de staff: el cliente no los alcanza - try { - const [c, p] = await Promise.all([ - api.get('/app/api/clientes/select'), - api.get('/app/umind-planes/list'), - ]) - clientes.value = c.registros || c.items || [] - planes.value = p.items || [] - } catch { - clientes.value = [] - planes.value = [] - } + if (contexto.esPortal) return + const [c, p] = await Promise.allSettled([ + api.get('/app/api/clientes/select'), + api.get('/app/umind-planes/list'), + ]) + clientes.value = c.status === 'fulfilled' ? lista(c.value) : [] + planes.value = p.status === 'fulfilled' ? lista(p.value) : [] + // Sin esto un fallo se veía igual que "no hay nada configurado", que es + // justo lo que hizo que los campos Cliente y Plan no aparecieran. + const fallos = [] + if (c.status === 'rejected') fallos.push('clientes') + if (p.status === 'rejected') fallos.push('planes') + if (fallos.length) error.value = `No se pudo cargar la lista de ${fallos.join(' ni ')}.` } function nuevoTenant() { @@ -202,7 +212,7 @@ onMounted(() => { class="input" /> -
+
-

Define quién ve este tenant desde el portal.

+

+ {{ clientes.length ? 'Define quién ve este tenant desde el portal.' : 'No hay clientes activos — creá uno en Clientes.' }} +

@@ -225,7 +237,9 @@ onMounted(() => { {{ p.nombre }} ({{ p.max_agentes === 0 ? '∞' : p.max_agentes }} agentes) -

Límite de agentes y precios de consumo.

+

+ {{ planes.length ? 'Límite de agentes y precios de consumo.' : 'No hay planes — creá uno en uMind Planes.' }} +