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)
|
||||
Reference in New Issue
Block a user