diff --git a/nuxt4-app/app/components/app/AppSidebar.vue b/nuxt4-app/app/components/app/AppSidebar.vue index 48e68a9..996010a 100644 --- a/nuxt4-app/app/components/app/AppSidebar.vue +++ b/nuxt4-app/app/components/app/AppSidebar.vue @@ -229,8 +229,19 @@ import type { NavigationMenuItem } from '@nuxt/ui' const route = useRoute() -const open = defineModel('open', { default: true }) -const collapsed = defineModel('collapsed', { default: false }) +// Usar el composable unificado para el estado de la sidebar +const sidebarState = useSidebarState() + +// Exponer como models para compatibilidad con UDashboardSidebar +const open = computed({ + get: () => sidebarState.open.value, + set: (value: boolean) => sidebarState.setOpen(value) +}) + +const collapsed = computed({ + get: () => sidebarState.collapsed.value, + set: (value: boolean) => sidebarState.setCollapsed(value) +}) // Manejo del fallback de iconos para el botón Inicio const perfilIconFallbackIndex = ref(0) diff --git a/nuxt4-app/app/composables/useSidebarState.ts b/nuxt4-app/app/composables/useSidebarState.ts new file mode 100644 index 0000000..d0a272f --- /dev/null +++ b/nuxt4-app/app/composables/useSidebarState.ts @@ -0,0 +1,129 @@ +/** + * Composable unificado para manejar el estado de la sidebar + * + * Centraliza todo el estado relacionado con la sidebar para evitar + * inconsistencias entre múltiples refs y watchers en cascada. + * + * Características: + * - Estado persistente en cookies + * - Sincronización automática entre open/collapsed + * - Manejo de responsive (mobile vs desktop) + * - Cierre automático en navegación (solo mobile) + */ + +import { ref, computed, watch } from 'vue' +import { useRoute } from 'vue-router' + +// Storage key para las cookies +const STORAGE_KEY = 'analytics-dashboard-sidebar' + +// Tipos +interface SidebarState { + open: boolean + collapsed: boolean + size: number +} + +// Estado global singleton +const sidebarState = ref(null) + +export function useSidebarState() { + const route = useRoute() + + // Inicializar estado solo una vez (singleton) + if (!sidebarState.value) { + // Leer de cookie si existe + const savedState = useCookie(STORAGE_KEY, { + default: () => ({ + open: true, + collapsed: false, + size: 28 // defaultSize + }) + }) + + sidebarState.value = savedState.value + } + + // Referencias reactivas al estado + const open = computed({ + get: () => sidebarState.value?.open ?? true, + set: (value: boolean) => { + if (sidebarState.value) { + sidebarState.value.open = value + // Persistir en cookie + const cookie = useCookie(STORAGE_KEY) + cookie.value = sidebarState.value + } + } + }) + + const collapsed = computed({ + get: () => sidebarState.value?.collapsed ?? false, + set: (value: boolean) => { + if (sidebarState.value) { + sidebarState.value.collapsed = value + // Persistir en cookie + const cookie = useCookie(STORAGE_KEY) + cookie.value = sidebarState.value + } + } + }) + + const size = computed({ + get: () => sidebarState.value?.size ?? 28, + set: (value: number) => { + if (sidebarState.value) { + sidebarState.value.size = value + // Persistir en cookie + const cookie = useCookie(STORAGE_KEY) + cookie.value = sidebarState.value + } + } + }) + + // Funciones de control + function toggle() { + open.value = !open.value + } + + function toggleCollapse() { + collapsed.value = !collapsed.value + } + + function setOpen(value: boolean) { + open.value = value + } + + function setCollapsed(value: boolean) { + collapsed.value = value + } + + // Detectar si estamos en mobile + const isMobile = computed(() => { + if (import.meta.server) return false + return window.innerWidth < 1024 // lg breakpoint + }) + + // Auto-cerrar en navegación solo en mobile + if (import.meta.client) { + watch(() => route.fullPath, () => { + if (isMobile.value) { + open.value = false + } + }) + } + + return { + // Estado + open, + collapsed, + size, + isMobile, + + // Acciones + toggle, + toggleCollapse, + setOpen, + setCollapsed + } +} diff --git a/nuxt4-app/app/layouts/dashboard.vue b/nuxt4-app/app/layouts/dashboard.vue index 6733bc5..b84ad65 100644 --- a/nuxt4-app/app/layouts/dashboard.vue +++ b/nuxt4-app/app/layouts/dashboard.vue @@ -1,7 +1,7 @@