dmolinari created pull request dmolinari/SIG-CM2.0#62

dmolinari
Tue, 21 Apr 2026 17:07:43 +0000

62#feat(frontend): sidebar colapsable por secciones + fly-out en modo colapsado#

Propósito

Mejora UX sobre PR #61 (categorización inicial). Dos problemas observados:

1. Modo expandido: las 4 secciones agrupadas siempre mostraban todos sus items → lista aún larga.

2. Modo colapsado: planteamiento inicial de mostrar TODOS los icons de todos los items generaba scroll vertical excesivo.

Solución

Modo expandido (240px)

Cada sección es colapsable independiente con chevron toggle.

Auto-expand de la sección activa: si estás en /admin/medios, "Maestros" se expande automáticamente — el header queda disabled sin chevron para evitar esconder items de la ruta actual.

Default primer uso: todas colapsadas excepto la activa — sidebar limpio al login.

Preferencia per-sección persistida en localStorage (key: sidebar-sections-collapsed). Si dejás "Catálogo" abierta, la próxima sesión arranca así.

Override runtime: la sección activa SIEMPRE prevalece, aunque localStorage diga otra cosa.

Modo colapsado (68px) — patrón VS Code / Azure Portal

1 icono por grupo (4 grupos + Dashboard), no 12+ icons de items individuales.

Hover en el icono de grupo → fly-out panel al lado derecho con el nombre del grupo + items clickeables.

Dot indicator en el icono del grupo que contiene la ruta activa.

Icons de grupo: ShieldCheck (Seguridad), Building2 (Maestros), Package (Catálogo), Calculator (Tasación).

z-index management (pedido explícito)

aside wrapper en ProtectedLayout: z-10 → z-30 (sobre contenido de página).

HoverCardContent del fly-out: z-[60] (por encima de cualquier overlay de página; deja los modal dialogs standard en z-50 intactos).

hover-card.tsx de shadcn: envuelto en HoverCardPrimitive.Portal (faltaba — sin portal, el fly-out podía quedar cortado por overflow del aside o el layout).

Archivos

Nuevos

src/web/src/hooks/useSidebarSections.ts — hook con isCollapsed(name) + toggle(name) + persistencia localStorage.

src/web/src/components/ui/hover-card.tsx — shadcn primitive (agregado vía npx shadcn@latest add hover-card, luego ajustado con Portal).

Modificados

src/web/src/components/layout/AppSidebar.tsx — refactor completo: ExpandedSection (collapsible) + CollapsedSectionFlyout (hover panel).

src/web/src/layouts/ProtectedLayout.tsx — z-index del sidebar wrapper.

src/web/src/components/layout/__tests__/AppSidebar.test.tsx — 10 → 16 tests.

src/web/package.json + package-lock.json — @radix-ui/react-hover-card.

Tests nuevos (6 casos agregados)

Default collapsed except la sección con ruta activa

Header de sección activa NO toggleable (disabled, sin chevron)

Click en header no-activo expande/colapsa

aria-expanded refleja estado

En Dashboard (/) ninguna sección contiene active → todas colapsadas

Preferencia persiste en localStorage

Total: 16/16 AppSidebar tests ✓

Verify

vitest: 526/526 green (+16 sidebar) — sin regresiones

tsc --noEmit: 0 errors, 0 warnings

Frontend-only, sin cambios de backend/BD

Screenshots mentales para review

Expandido + ruta en Maestros:

🏠 Dashboard
SEGURIDAD       ▶      ← colapsada (chevron rotado)
MAESTROS (activa)      ← expandida SIEMPRE (no-toggleable)
  📰 Medios ← active
  📑 Secciones
  🏪 Puntos de Venta
CATÁLOGO        ▶
TASACIÓN        ▶

Colapsado + hover en Maestros:

[☰]
[🏠]
────
[🛡️]          ← ShieldCheck (Seguridad)
[🏢] ← hover  ────────────────┐
[📦]          │  MAESTROS     │
[🧮]          │  📰 Medios    │
              │  📑 Secciones │
              │  🏪 PdV       │
              └───────────────┘

No merge automático

Siguiendo el flujo acordado: NO mergeo hasta tu OK después de revisar la UI en el browser. Link: #62