- Cambiar botones de confirmación (Guardar/Importar) en settings para usar --brand-success
- Actualizar color de tab activa en sidebar de --brand-primary a --brand-success
- Reemplazar colores hardcoded por variables CSS en badges de tipos de café:
* VistaTablaIngresos: Usar var(--coffee-uva/verde/mojado/oreado)
* informe-ingresos: Aplicar variables --coffee-* y --status-*
- Cambiar text-green-400 por text-[var(--brand-success)] en panorama
- Quitar badge de mantenimiento de Comparativa Cosechas
Todos los cambios siguen las reglas de THEME_SYSTEM.md para garantizar
que los colores respondan correctamente a los cambios de tema del usuario.
Cambios realizados:
- Agregada variable success (#00dc82 Nuxt green) para botones de confirmación y elementos de éxito
- Agregados inputs de color para todas las variables de café (Uva, Oreado, Mojado, Verde)
- Agregados inputs de color para todas las variables de estados (Pendiente, Pagado, Anulado)
- Actualizada interfaz ThemeColors con nueva propiedad success
- Actualizados todos los temas preestablecidos (café, azul, verde, carbón) con variable success
- Actualizada validación de importación de temas con nueva propiedad
- Agregado CSS --brand-success en main.css
Los usuarios ahora pueden personalizar completamente todos los colores del sistema:
- 9 colores base (incluye success)
- 4 colores de tipos de café
- 3 colores de estados de pago
Total: 16 variables personalizables
La variable success se aplica a:
- Botones de confirmación
- Página seleccionada en sidebar
- Indicadores de éxito
- Crear composable useNotifications con gestión por localStorage
- Almacenamiento separado por usuario usando UID de Authentik
- Auto-limpieza de notificaciones mayores a 30 días
- Sincronización automática entre pestañas
- Filtrado por tipo, búsqueda y gestión completa
- Crear wrapper useToast para guardar toasts automáticamente
- Intercepta todos los toasts de la aplicación
- Guarda historial sin afectar funcionalidad existente
- Implementar endpoints de API para notificaciones del backend
- POST /api/notifications/send para enviar notificaciones
- GET /api/notifications/list para obtener pendientes
- Actualizar página de notificaciones con funcionalidad real
- Búsqueda y filtros por tipo (info, warning, success, error)
- Eliminar individual o todas las notificaciones
- Marcar como leídas individual o todas
- Badges de origen (toast, backend, manual)
- Estados vacíos con mensajes informativos
- Actualizar badge del sidebar con contador dinámico
- Muestra número real de notificaciones no leídas
- Se oculta cuando no hay notificaciones
- Inicializar sistema en app.vue
- Auto-inicialización al montar la app
- Limpieza automática de notificaciones antiguas
Cambios:
- Agregar sección de Variables de Tipos de Café (--coffee-*)
- Agregar sección de Variables de Estados (--status-*)
- Agregar Ejemplo 7: Usar Colores de Tipos de Café
- Agregar Ejemplo 8: Usar Colores de Estados
- Actualizar Ejemplo 6 con todas las propiedades del tema
- Actualizar Resumen de Comandos Rápidos con nuevas variables
- Documentación completa para las 15 variables CSS del sistema
La documentación ahora refleja el sistema extendido de temas.
Cambios:
- app.vue: Reemplazar #fef9f0 con var(--brand-text)
- layouts/dashboard.vue: Usar var(--brand-text)
- layouts/informe.vue: Usar var(--brand-text)
Todos los componentes base ahora responden al sistema de temas.
Cambios:
- Agregar documentación completa del sistema de temas en /settings
- Extender ThemeColors con 7 nuevas variables:
* coffeeUva, coffeeOreado, coffeeMojado, coffeeVerde
* statusPendiente, statusPagado, statusAnulado
- Actualizar useTheme.ts para aplicar nuevas variables CSS
- Actualizar main.css con definiciones de nuevas variables
- Actualizar temas predefinidos (azul, verde, carbón)
- Mantener colores de café y estados consistentes en todos los temas
Las nuevas variables permiten personalizar:
- Colores de identificación de tipos de café en gráficas
- Colores de estados de pago en tablas y badges
Remueve las páginas en modo mantenimiento que no se van a utilizar:
Páginas eliminadas:
- /metadatos - Visualización de estructura de tablas
- /rawExplorer - Exploración de datos en formato raw
Cambios en sidebar:
- Removidos enlaces de "Metadatos" y "Explorador de datos raw"
- Sidebar ahora solo muestra páginas activas y funcionales
Páginas que permanecen:
- Panorama Facturador (funcional)
- Informe Ingresos (funcional)
- Explorador de datos (funcional)
- Comparativa Cosechas (en mantenimiento, se restaurará próximamente)
- Metabase Debug (herramienta de desarrollo)
Simplifica la navegación mostrando solo funcionalidades relevantes.
Remueve el badge de "Mantenimiento" del enlace de Informe Ingresos
en el sidebar, ya que la página está completamente funcional con:
- Todos los totales funcionando correctamente
- Lista de Ingresos con tabla completa
- Top 10 Clientes ranking
- Serie Temporal Acumulada
- Botones de copia (Texto y JSON) en todos los componentes
- Footer con resumen en todos los exports
- Filtros avanzados funcionando
- Sistema de actualizacion con cambios pendientes
La página está lista para producción.
- Crear THEME_SYSTEM.md como documento único y completo
- Eliminar DEVELOPER_GUIDE.md y THEME_CUSTOMIZATION.md (desactualizados)
- Actualizar README.md con referencia al nuevo documento
- Incluye arquitectura, variables, reglas, ejemplos y troubleshooting
- Documentación clara para usuarios y desarrolladores
Implementa botones "Copiar Texto" y "Copiar JSON" en todos los
componentes de datos del Panorama Facturador:
Componentes actualizados:
- SecosVendidos: Exporta inventario y proyecciones de café seco
- RechazosSubproductos: Exporta tabla de rechazos con totales
- Totales Financieros: Exporta vista general financiera (directo en página)
Además:
- Los componentes de Totales (IngresoCompra, Monetarios, Verde)
ahora reciben contadores y metadata para incluir footer
- Todos los textos incluyen footer con:
- Rango de fechas
- Ingresos y rechazos filtrados vs totales
- Fecha de generación
Nota: Panorama no tiene datos de clientes, solo ingresos y rechazos.
El problema era que Nuxt UI usa la escala neutral de forma invertida:
- neutral-50 se usa para texto claro
- neutral-900/950 se usa para fondos oscuros
Esto causaba que los labels tuvieran el mismo color que el fondo de las cards.
Cambios:
- Invertir el mapeo de --ui-color-neutral-*
- neutral-50/100 ahora mapean a --brand-text/--brand-text-muted
- neutral-900/950 ahora mapean a --brand-bg
PROBLEMA: Los componentes de Nuxt UI (UCard, UButton, etc.) usaban
colores fijos de --color-coffee-* que no respondían a cambios de tema.
SOLUCIÓN: Mapear --ui-color-neutral-* a variables --brand-* dinámicas:
- neutral-50/100 → --brand-bg (fondos más oscuros)
- neutral-200 → --brand-surface (fondos elevados)
- neutral-300/400 → --brand-border (bordes)
- neutral-500/600/700 → --brand-primary/primary-strong (interactivos)
- neutral-800/900/950 → --brand-text/text-muted (textos)
RESULTADO: Ahora los UCard y otros componentes de Nuxt UI cambian
correctamente cuando se selecciona un tema predefinido.
Archivos modificados:
- app/assets/css/main.css: Remapeo de variables --ui-color-neutral-*
- Configurar prop :ui en todos los UCard para usar variables CSS del tema
- Reemplazar colores hardcoded (amber, green) por variables de tema
- Los UCard ahora responden correctamente a cambios de tema
- Corrige problema donde los cards no cambiaban de color al seleccionar presets
Archivos modificados:
- app/pages/settings.vue: Agregar :ui prop con bg-[var(--brand-surface)] y border
Implementa un footer consistente en todas las funciones de copiar texto
que incluye información de contexto del informe:
Footer incluye:
- 📅 Rango de fechas aplicado
- 📦 Cantidad de ingresos filtrados vs total
- 👥 Cantidad de clientes con ingresos vs total
- 🕐 Fecha y hora de generación
Cambios en archivos:
- informe-ingresos.vue: Footer en Lista, Top 10 y Serie Temporal
- TotalesIngresoCompra.vue: Recibe contadores y metadata
- TotalesMonetarios.vue: Recibe contadores y metadata
- TotalesVerde.vue: Recibe contadores y metadata
El footer usa datos de la query "Informe Ingresos - Contadores de Filtros"
que proporciona totales con y sin filtros aplicados.
Cambia todos los botones de copia en informe-ingresos.vue para
usar el mismo estilo que los componentes de Totales:
- color: "gray" (antes "neutral")
- variant: "soft" (antes "outline")
- icon JSON: "i-lucide-braces" (antes "i-lucide-code")
Ahora todos los botones de copia en la página tienen apariencia
consistente.
Implementa botones "Copiar Texto" y "Copiar JSON" en los tres
componentes de totales del informe de ingresos:
- TotalesIngresoCompra: Exporta datos de Uva, Mojado, Oreado y totales
- TotalesMonetarios: Exporta inversiones, precios promedio y restantes
- TotalesVerde: Exporta métricas de café verde
Cada componente incluye:
- Formato de texto con emojis para compartir en WhatsApp
- Formato JSON para integración con sistemas
- Alertas de confirmación al copiar
Todos los componentes de datos del informe ahora tienen
funcionalidad de exportación consistente.
Implementa funcionalidad de copia en tres secciones del Informe:
📋 Funcionalidades agregadas:
1. Lista de Ingresos
- Copiar Texto: Formato WhatsApp con emojis y legible
- Copiar JSON: Formato estructurado para sistemas
2. Top 10 Clientes
- Copiar Texto: Ranking formateado con métricas
- Copiar JSON: Array de objetos con datos completos
3. Serie Temporal Acumulada
- Copiar Texto: Evolución temporal con emojis
- Copiar JSON: Datos completos para análisis
✨ Características:
- Botones con iconos (i-lucide-copy y i-lucide-code)
- Disabled cuando no hay datos disponibles
- Alertas de confirmación al copiar
- Formato texto optimizado para WhatsApp
- Incluye metadata: rango de fechas y timestamp
Uso:
- Copiar Texto → Compartir en WhatsApp/Telegram
- Copiar JSON → Integración con otros sistemas
- Altura máxima de 600px con scroll vertical
- Header sticky que se mantiene visible al hacer scroll
- Mejora la usabilidad cuando hay muchos registros
Agrega tres secciones principales con datos reales de Metabase:
1. Lista de Ingresos (Query ID 50)
- Tabla completa con detalles de cada ingreso
- Campos: ID, fecha, cliente, tipo, peso, precio, total, estado
- Badges de colores para tipos y estados
- Soporte para 1000 registros
2. Top 10 Clientes (Query ID 51)
- Ranking por monto total pagado
- Muestra: nombre, cédula, ubicación
- Métricas: total pagado, número de ingresos, quintales
- Diseño en cards con numeración
3. Serie Temporal Acumulada (Query ID 52)
- Datos desglosados por fecha, tipo y estado
- Columnas del período: ingresos, peso seco, inversión
- Columnas acumuladas: qq acumulados, inversión acumulada
- Tabla scrollable con sticky header
Correcciones técnicas:
- Usar campos correctos de las queries de Metabase
- created_at en lugar de fecha
- cliente_nombre en lugar de nombre_cliente
- cliente_id, cliente_cedula, cliente_ubicacion
- fecha_grupo, peso_seco_periodo, inversion_acumulada
- Crear THEME_CUSTOMIZATION.md con guía completa de uso
- Documentar las 8 variables CSS y sus propósitos
- Incluir guías de buenas prácticas y accesibilidad
- Agregar ejemplos de temas predefinidos
- Explicar persistencia en localStorage
- Documentar arquitectura técnica del sistema
- Incluir troubleshooting y roadmap futuro
- Crear formulario completo de configuración de colores
- Agregar 8 campos editables: bg, surface, border, primary, primaryStrong, accent, text, textMuted
- Implementar color pickers + inputs de texto hexadecimal
- Agregar vista previa en tiempo real (toggle opcional)
- Guardar configuración en localStorage
- Aplicar cambios dinámicamente con CSS variables
- Incluir botón para restaurar colores por defecto
- Colores default: tema café actual de la aplicación
Problema: Las queries de Metabase en el endpoint /api/metabase/informe
retornaban 0 filas porque se enviaban par\u00e1metros con arrays vac\u00edos,
lo cual activaba filtros condicionales sin valores.
Soluci\u00f3n: Modificar buildParameters() para solo incluir par\u00e1metros
opcionales (cliente_ids, tipos, estados, ubicaciones, calidades) cuando
tengan valores (arrays no vac\u00edos). Los filtros condicionales [[...]]
de Metabase ahora se omiten correctamente cuando no hay valores.
- Corregir prop :ui de 'wrapper' a 'root' en UDashboardSidebar
- Agregar mapeo explícito de variables --ui-color-neutral-* a coffee
- Esto fuerza a Nuxt UI a usar la paleta café en lugar de slate/azul
- Soluciona el problema de fondo azul en todos los navegadores
- Agregar fondo café oscuro al sidebar (var(--brand-surface))
- Personalizar estilos del input de búsqueda con colores del tema
- Mejorar indicador visual de tab seleccionada con borde izquierdo
- Aplicar estilos consistentes a los badges de teclado (⌘ K)
Los shades estaban configurados para light mode (50=claro, 950=oscuro).
En dark mode, Nuxt UI espera la escala invertida (50=oscuro, 950=claro).
Cambios:
- 50: #1b1209 (más oscuro)
- 500: #c08040 (medio - café principal)
- 950: #fef9f0 (más claro)
Esto asegura que en dark mode se vean los colores café/dorado correctamente.
Nuevo componente creado:
- SimpleMultiSelector.vue: Componente genérico reutilizable
- Normaliza strings a objetos {label, value}
- Capitaliza primera letra automáticamente
- Props configurables: icon, placeholder, labels singular/plural
- Mismo estilo espectacular que otros selectores
Cambios en informe-ingresos.vue:
1. Layout mejorado:
- Grid de 3 columnas (antes 2 columnas + 1 separado)
- Tipos, Estados y Calidades en mismo nivel
- Distribución más equilibrada y consistente
2. Reemplazar UCheckboxGroup por SimpleMultiSelector:
- Tipos de Café: icon coffee, "tipo/tipos"
- Estados: icon check-circle, "estado/estados"
- Calidades: icon star, "calidad/calidades"
3. Labels consistentes:
- Todas las secciones usan mismo formato
- text-sm font-medium mb-2
Beneficios:
- Interfaz más limpia y moderna
- Búsqueda integrada en cada filtro
- Tags visuales de selección
- Consistencia total en todos los filtros
- Mismo tema café/dorado en toda la app
- Mejor uso del espacio (3 columnas iguales)
Problema identificado:
- El array de ubicaciones viene como array de strings simples,
no como objetos con {label, value}
- Ejemplo: ["breñales, la union, lempira", "buenos aires, ..."]
Solución implementada:
1. Normalización de datos:
- Crear computed normalizedUbicaciones que transforma el array
- Si el item es string: usa el string como label y value
- Si el item es objeto: usa sus propiedades label y value
- Filtrar null/undefined durante la transformación
2. Agregar prop loading:
- Agregada prop opcional loading?: boolean
- Pasar loading al UInputMenu para mostrar spinner
3. Simplificar lógica de filtrado:
- Usar normalizedUbicaciones como base
- Filtrar por query sin errores de toLowerCase
4. Mejorar selectedUbicacionesObjects:
- Usar normalizedUbicaciones para encontrar seleccionados
- Comparar con value normalizado
Ahora el componente:
- Muestra todas las ubicaciones correctamente
- Funciona con array de strings o array de objetos
- Muestra animación de loading cuando carga datos
- Búsqueda funciona sin errores
Implementa la configuración correcta de colores según Nuxt UI 4:
- Define color 'coffee' con shades 50-950 usando @theme en CSS
- Configura aliases primary y neutral a 'coffee' en app.config.ts
- Esto permite que todos los componentes de Nuxt UI usen el tema café/dorado
- Reemplaza los colores azules y verdes por defecto globalmente
Problemas corregidos:
1. Error "Cannot read properties of undefined (reading 'toLowerCase')":
- Agregar validación para filtrar items undefined/null
- Verificar que cada item tenga label y value antes de usar toLowerCase
- Prevenir errores cuando el array de ubicaciones tiene items inválidos
2. Selección múltiple no funcionaba:
- Mejorar onSelectionChange para manejar diferentes tipos de valores
- Agregar validación de array antes de procesar
- Filtrar items null/undefined antes de extraer valores
- Manejar tanto objetos como strings en el array de valores
3. Tags no aparecían en el input:
- El problema estaba en el procesamiento de valores seleccionados
- Ahora maneja correctamente item.value y strings directos
El componente ahora funciona igual que ClienteMultiSelector:
- Sin errores en consola
- Tags visuales aparecen correctamente
- Selección múltiple funciona perfectamente
Nuevo componente creado:
- UbicacionMultiSelector.vue: InputMenu con tema personalizado
- Mismo estilo y funcionalidad que ClienteMultiSelector
- Búsqueda en tiempo real de ubicaciones
- Tags visuales para selección múltiple
- Contador y botón "Limpiar todo"
Cambios en informe-ingresos.vue:
- Reemplazar UCheckboxGroup de ubicaciones por UbicacionMultiSelector
- Mover selector de ubicaciones a su propia sección (fuera del grid)
- Grid ahora tiene 2 columnas (Tipos y Estados) en lugar de 3
- Mantener layout consistente con selector de clientes
Estilos aplicados (igual que ClienteMultiSelector):
- Fondo: --brand-surface
- Bordes: --brand-border con focus dorado
- Item highlighted: tono dorado suave
- Tags: color --brand-primary
Ahora ubicaciones tiene la misma UX moderna que clientes.
Reemplaza todos los colores por defecto de Nuxt UI (azules y verdes)
en la sidebar con la paleta de colores café/dorado personalizada.
- Toggle button: color neutral con hover café/dorado
- Tarjeta de usuario: fondo brand-surface con borde brand-border
- Avatar: ring brand-primary, indicador brand-accent
- Avatar generado: background café (c08040) en lugar de azul
- Botones de acción: hover brand-primary/10, iconos en colores del tema
- Badge notificaciones: brand-accent con texto brand-bg
- Botón cerrar sesión: colores del tema en lugar de rojo
- Vista colapsada: todos los colores actualizados
Se aplica el mismo tema café/dorado a todos los InputMenu de filtros:
- Tipo de café
- Estado
- Ubicación
- Calidad
Estilos aplicados (igual que ClienteMultiSelector):
- Fondo: --brand-surface (#1f180f)
- Borde: --brand-border (#3a2a16)
- Focus ring: ring-1 con --brand-primary (#e0c080)
- Item highlighted: rgba(224,192,128,0.12)
- Tags: color --brand-primary con bordes dorados
- Transición suave en el borde de focus
Todos los filtros ahora mantienen la coherencia visual
con el resto de la aplicación.
Cambios aplicados:
- Reemplazar ring-2 (2px) por ring-1 (1px) para borde más delgado
- Cambiar color de focus de verde por defecto a --brand-primary (#e0c080)
- Aplicar focus:ring-1 y focus:border-[--brand-primary] en el input base
- Agregar focus-within:ring-1 en el root para consistencia
- Agregar transition-shadow para animación suave del borde
Ahora el borde de focus es más delgado y usa el color dorado
de la app en lugar del verde por defecto de Nuxt UI.
- Añadir hover con fondo color brand (#c08040) al 5% de opacidad
- Transición suave de 200ms en cambio de colores
- Bordes redondeados para mejor estética
- Sin cambios de tamaño ni desplazamiento
- Aplicado a todos los filtros: ubicaciones, tipos, estados y calidades
Colores aplicados según el sistema de diseño de la app:
- Fondo del input: --brand-surface (#1f180f)
- Borde: --brand-border (#3a2a16)
- Texto: --brand-text (#fef9f0)
- Placeholder y iconos: --brand-text-muted (#d8c7a6)
- Dropdown: mismo fondo y borde que el input
- Item highlighted: rgba(224,192,128,0.12) (tono dorado suave)
- Tags seleccionados: rgba(224,192,128,0.14) con borde más fuerte
- Color de tags: --brand-primary (#e0c080)
- Hover en botón delete: tono dorado más intenso
El componente ahora tiene la misma paleta de colores que las cards
de Metabase Debug y el resto de la aplicación, eliminando los
colores azules/verdes por defecto de Nuxt UI.
- Reemplazar checkboxes individuales por UCheckboxGroup en filtros de:
* Ubicaciones (con scroll vertical para listas largas)
* Tipos de Café (Uva, Mojado, Oreado, Verde)
* Estados (Pagado, Pendiente)
* Calidades (orientación horizontal)
- Simplificar modelo de datos:
* Eliminar filterTipos y filterEstados (objetos con booleanos)
* Usar directamente selectedTipos y selectedEstados (arrays)
* Eliminar computed tiposArray y estadosArray (innecesarios)
- Beneficios:
* Código más limpio y mantenible
* Mejor consistencia con Nuxt UI
* UI más compacta y profesional
* Menos lógica de conversión de datos
Correcciones de funcionalidad:
- Cambiar v-model por :model-value y @update:model-value para control manual
- Crear selectedClientesObjects computed para sincronizar con props
- Implementar onSelectionChange para extraer IDs correctamente
- Arreglar binding bidireccional con selectedIds
Correcciones de estilo:
- Agregar size="sm" para consistencia con otros inputs
- Usar variables CSS de la app (--brand-text, --brand-text-muted)
- Mantener coherencia visual con el resto de componentes
- Corregir contador para usar props.selectedIds directamente
Ahora el componente:
- Permite seleccionar y deseleccionar usuarios correctamente
- Muestra tags visuales de los seleccionados
- Mantiene el estilo consistente con la app
- Respeta el mínimo de 4 caracteres para búsqueda
Correcciones aplicadas:
- Usar ignore-filter para control manual del filtrado
- Implementar label-key y value-key correctamente
- Usar slot #item-label en lugar de #item para personalización
- Usar slot #empty para mensajes cuando no hay resultados
- Mapear items a formato InputMenuItem con label y value
- Usar clases de Nuxt UI (text-muted) en lugar de variables CSS
- Simplificar lógica eliminando control manual del estado open
Funcionalidad:
- Filtrado solo se activa con mínimo 4 caracteres
- Búsqueda por nombre y cédula
- Selección múltiple con tags visuales
- Formato de cédula mantenido
Cambios implementados:
- Reemplazar input con checkboxes por UInputMenu con selección múltiple
- Filtrado por nombre y cédula únicamente (sin ubicación)
- El menú de sugerencias solo se abre después de 4 caracteres
- Mantener contador de seleccionados y botón "Limpiar todo"
- Actualizar tipo de cedula de number a string para manejar formato correcto
- Simplificar lógica eliminando filtros por ubicación
Mejoras de UX:
- Interfaz más limpia y moderna con InputMenu
- Búsqueda más eficiente con mínimo de caracteres
- Tags visuales para items seleccionados
- Formato de cédula mantenido (XXXX-XXXX-XXXXX)
Se actualiza la referencia de "Informe Ingresos - Lista Simple de Clientes"
al nuevo nombre "Clientes - Lista Simple" (card ID: 55).
Esta query ahora también corrige el formato de cédula usando LPAD para
agregar ceros iniciales cuando sea necesario (formato 13 dígitos).
Problema identificado:
- Páginas con layout 'informe' mostraban icono inconsistente
- Páginas con layout 'dashboard' funcionaban correctamente
- Layout 'informe' seguía usando refs locales obsoletas
Causa raíz:
El layout 'informe' no fue actualizado en el refactor inicial:
- Línea 4: <AppSidebar v-model:open="sidebarOpen" v-model:collapsed="sidebarCollapsed" />
- Líneas 190-191: Refs locales que sobrescriben el composable
- Las props v-model forzaban estado local en lugar de usar singleton
Análisis por layout:
✅ Funcionaban (layout: dashboard):
- index, explorer, rawExplorer, metadatos, notifications, settings
❌ No funcionaban (layout: informe):
- panorama, informe-ingresos, comparativa-cosechas, metabase-debug
Solución aplicada:
1. Eliminar v-model:open y v-model:collapsed de <AppSidebar />
2. Remover refs locales sidebarOpen y sidebarCollapsed
3. Remover función isMobile() duplicada
4. Usar useSidebarState() como única fuente de verdad
Cambios:
- app/layouts/informe.vue:4 - Remover v-models de AppSidebar
- app/layouts/informe.vue:183 - Usar useSidebarState() composable
- app/layouts/informe.vue:190-191 - Eliminar refs locales
Resultado:
✓ Icono consistente en TODAS las páginas
✓ Ambos layouts usan la misma arquitectura
✓ Estado completamente unificado
✓ Sin refs locales que sobrescriban el singleton
Referencias:
- app/layouts/informe.vue:4
- app/layouts/informe.vue:183
Cambiado de 'icon' a 'leadingIcon' (propiedad correcta de Nuxt UI)
y agregados estados de color explícitos:
- Tab activa: text-[#1b1209] (negro oscuro)
- Tab inactiva: text-[var(--brand-text-muted)] (gris apagado)
- Transición suave de colores con transition-colors duration-200
Esto asegura que el icono sea visible y cambie de color
correctamente al seleccionar diferentes tabs.
Problema:
- El icono del toggle mostraba comportamiento inconsistente entre páginas
- Algunas páginas cargaban con icono correcto (hamburguesa) y otras con "X"
- Race condition entre múltiples watchers compitiendo por el estado
Causa raíz detectada:
1. DashboardSidebar.vue:58 tiene watcher con {immediate:true} que sincroniza
nuestro composable → contexto de DashboardGroup
2. DashboardSidebar.vue:60 tiene watcher que cierra sidebar en navegación
3. Nuestro composable TAMBIÉN tenía un watcher de navegación (duplicado)
4. Múltiples watchers registrándose cada vez que se llama useSidebarState()
5. Race condition: dependiendo del orden de montaje de componentes,
el toggle podía leer el estado antes o después de la sincronización
Solución:
- Eliminar completamente el watcher de navegación de nuestro composable
- Dejar que DashboardSidebar de Nuxt UI maneje el cierre en navegación
- Nuestro composable solo mantiene el estado, no lógica de ciclo de vida
- Evita múltiples registros de watchers
- Elimina la race condition
Cambios:
- Removido watcher de route.fullPath (líneas 110-116)
- Removidas importaciones innecesarias (watch, useRoute)
- Actualizado comentario del header
- Simplificado: composable ahora es puramente state management
Resultado:
✓ Icono consistente en todas las páginas
✓ No más race conditions
✓ Menos watchers = mejor performance
✓ DashboardSidebar maneja toda la lógica de navegación
Referencias:
- app/composables/useSidebarState.ts:14
- app/composables/useSidebarState.ts:29
Agregado @ts-expect-error para suprimir la advertencia sobre
la propiedad 'icon' que no está en los tipos formales pero es
una customización válida en Nuxt UI.
Agregado z-20 al icono para que herede correctamente el color
del trigger activo. Ahora el icono cambia a negro (#1b1209)
cuando la tab está seleccionada, igual que el label.
El icono del toggle mostraba "X" (cerrar) en lugar del icono hamburguesa
al cargar la página, aunque se corregía tras la primera interacción.
Causa raíz:
- UDashboardGroup inicializa sidebarOpen en false
- Nuestro composable inicializaba open en true por defecto
- El toggle lee el estado del contexto de DashboardGroup
- Desincronización causaba que el icono mostrara el estado incorrecto
Solución:
- Cambiar el default de open a false en el composable
- En desktop, open=false es correcto (no hay overlay, sidebar siempre visible)
- En mobile, open=false significa overlay cerrado (comportamiento esperado)
- Alineación total con el comportamiento de Nuxt UI
Archivos modificados:
- app/composables/useSidebarState.ts:43-46 (default open: false)
- app/composables/useSidebarState.ts:57 (fallback a false)
Referencias:
- app/composables/useSidebarState.ts:46
- app/composables/useSidebarState.ts:57
Cambios realizados:
- Tabs inactivos ahora tienen fondo transparente en lugar de #1c140c
- Indicador tiene z-10 para estar por encima del fondo
- Labels tienen z-20 para estar por encima del indicador
- Hover de tabs inactivos usa bg semi-transparente (#2a1f14/50)
- Agregada transición suave de colores (transition-colors duration-200)
Esto soluciona el problema donde el badge color cobre se ocultaba
durante la animación de deslizamiento entre tabs.
Soluciona todos los problemas identificados en la arquitectura anterior:
Cambios principales:
- Nuevo composable useSidebarState() que centraliza todo el estado
- Elimina múltiples fuentes de verdad que causaban desincronización
- Remueve watchers en cascada y hooks indirectos
- Elimina workarounds manuales de DOM y aria-hidden
- Implementa persistencia consistente en cookies
- Manejo responsive automático (mobile vs desktop)
Archivos modificados:
- app/composables/useSidebarState.ts (nuevo): Composable singleton
- app/components/app/AppSidebar.vue: Usa el nuevo composable
- app/layouts/dashboard.vue: Simplificado, sin refs locales ni workarounds
- docs/SIDEBAR_ARCHITECTURE.md (nuevo): Documentación completa
Beneficios:
✓ Estado consistente en toda la aplicación
✓ No más flickering o comportamientos anómalos
✓ Código más simple y mantenible
✓ Mejor performance (menos re-renders)
✓ Auto-close en mobile al navegar
Referencias:
- app/composables/useSidebarState.ts:1
- app/components/app/AppSidebar.vue:232
- app/layouts/dashboard.vue:40