fix(studio): uMind era inusable en móvil
El SPA no tenía ningún tratamiento responsive: un sidebar fijo de 256px siempre visible. En un teléfono de 375px eso deja 119px para el contenido. Ahora en móvil el sidebar sale de flujo y entra deslizándose desde la izquierda, con botón en el header y fondo oscuro para cerrarlo. Se cierra solo al elegir un tenant: dejarlo abierto tapa justo la pantalla a la que se acaba de entrar. En escritorio (md+) no cambia nada — sigue fijo y sin botón. El estado vive en lib/ui.js porque lo tocan dos componentes que no son padre/hijo: el botón del header y el propio sidebar. También se achica el padding del contenido en pantallas chicas y las tabs del agente pasan a scrollear en su propia caja en vez de desbordar. Medido a 492px de ancho: scrollWidth igual a innerWidth y ningún elemento sobresaliendo. Escritorio verificado sin regresión. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e2440b04f1
commit
ce5956878d
@@ -1,13 +1,25 @@
|
||||
<script setup>
|
||||
import Sidebar from './components/Sidebar.vue'
|
||||
import { tema, alternarTema } from './lib/tema.js'
|
||||
import { menuAbierto } from './lib/ui.js'
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="min-h-screen flex">
|
||||
<Sidebar />
|
||||
<main class="flex-1 min-w-0 flex flex-col">
|
||||
<header class="h-14 shrink-0 flex items-center justify-end gap-1 px-6 border-b border-borde">
|
||||
<header class="h-14 shrink-0 flex items-center gap-1 px-4 sm:px-6 border-b border-borde">
|
||||
<!-- Solo en móvil: en escritorio el sidebar está siempre visible. -->
|
||||
<button
|
||||
class="btn-ghost !px-2 !py-1.5 md:hidden"
|
||||
aria-label="Abrir menú"
|
||||
@click="menuAbierto = true"
|
||||
>
|
||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M4 6h16M4 12h16M4 18h16" />
|
||||
</svg>
|
||||
</button>
|
||||
<span class="flex-1"></span>
|
||||
<button
|
||||
class="btn-ghost !px-2.5 !py-1.5"
|
||||
:title="tema === 'oscuro' ? 'Cambiar a claro' : 'Cambiar a oscuro'"
|
||||
@@ -16,7 +28,7 @@ import { tema, alternarTema } from './lib/tema.js'
|
||||
<span class="text-base leading-none">{{ tema === 'oscuro' ? '☀️' : '🌙' }}</span>
|
||||
</button>
|
||||
</header>
|
||||
<div class="flex-1 max-w-5xl w-full mx-auto px-8 py-8 animate-aparecer">
|
||||
<div class="flex-1 max-w-5xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-6 sm:py-8 animate-aparecer">
|
||||
<router-view />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<script setup>
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { api } from '../lib/api.js'
|
||||
import { apiUmind, contexto } from '../lib/contexto.js'
|
||||
import { menuAbierto } from '../lib/ui.js'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
@@ -20,6 +21,10 @@ const form = ref(vacio())
|
||||
// anidada de sus agentes (/tenants/:tenantId/agentes/:agenteId).
|
||||
const tenantActivoId = computed(() => route.params.tenantId || route.params.id)
|
||||
|
||||
// Al elegir un tenant en móvil el menú se cierra solo: dejarlo abierto tapa
|
||||
// justo la pantalla a la que se acaba de entrar.
|
||||
watch(() => route.fullPath, () => { menuAbierto.value = false })
|
||||
|
||||
function vacio() {
|
||||
return { nombre: '', dominios_permitidos: '', activo: true, cliente_id: null, plan_id: null }
|
||||
}
|
||||
@@ -121,7 +126,20 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<aside class="w-64 shrink-0 h-screen sticky top-0 flex flex-col border-r border-borde bg-superficie">
|
||||
<!-- Fondo oscuro en móvil: sin esto el menú abierto se superpone al
|
||||
contenido sin dejar claro que hay que cerrarlo. -->
|
||||
<div
|
||||
v-if="menuAbierto"
|
||||
class="fixed inset-0 bg-black/50 z-30 md:hidden"
|
||||
@click="menuAbierto = false"
|
||||
></div>
|
||||
|
||||
<aside
|
||||
class="w-64 shrink-0 flex flex-col border-r border-borde bg-superficie
|
||||
fixed inset-y-0 left-0 z-40 transition-transform duration-200
|
||||
md:static md:h-screen md:sticky md:top-0 md:translate-x-0"
|
||||
:class="menuAbierto ? 'translate-x-0' : '-translate-x-full'"
|
||||
>
|
||||
<div class="h-14 px-4 flex items-center border-b border-borde">
|
||||
<router-link to="/" class="text-base font-semibold text-texto">
|
||||
uMind <span class="text-brand">Studio</span>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { ref } from 'vue'
|
||||
|
||||
// Estado del menú lateral en móvil. Vive acá y no en App.vue porque lo tocan
|
||||
// dos componentes que no son padre/hijo: el botón del header y el propio
|
||||
// sidebar, que se cierra solo al navegar.
|
||||
export const menuAbierto = ref(false)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -4,8 +4,8 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>uMind Studio</title>
|
||||
<script type="module" crossorigin src="/orchestrator/assets/index-DsDQ67K1.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/orchestrator/assets/index-DyYio0bD.css">
|
||||
<script type="module" crossorigin src="/orchestrator/assets/index-B67EqHas.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/orchestrator/assets/index-D_GQdYSJ.css">
|
||||
</head>
|
||||
<!-- Sin clase de fondo: el color lo pone body en style.css desde los tokens,
|
||||
que son los que cambian con el tema. Una utilidad acá le ganaba a la
|
||||
|
||||
Reference in New Issue
Block a user