Commit Graph

331 Commits

Author SHA1 Message Date
fa76d0055a feat(web): infra UI completa pre-ADM-001 — DataTable + 8 shadcn + MCP global
shadcn MCP server registrado globalmente (claude mcp add -s user shadcn -- npx -y shadcn@latest mcp). Disponible en cualquier sesion/proyecto.

8 componentes shadcn nuevos via CLI (cd src/web && npx shadcn@latest add ...):
- table, select, popover, pagination, breadcrumb, alert-dialog
- command, dialog (deps de combobox y alert-dialog)
Total instalados ahora: 22

Fix gotcha shadcn CLI: agregado compilerOptions.paths al root tsconfig.json
(sino crea folder literal '@/' en lugar de resolver el alias). Antes solo
estaba en tsconfig.app.json que el CLI no lee.

@tanstack/react-table 8.21 instalado.

Nuevo componente <DataTable> generico (src/web/src/components/ui/data-table.tsx):
- Wrapper sobre TanStack Table
- Priority columns: meta { priority: 'high' | 'medium' | 'low' }
  → hidden md:table-cell / hidden lg:table-cell automatico
- Tap-to-expand row mobile (chevron auto-aparece cuando hay cols hidden,
  click despliega panel con hidden cells como dl/dt/dd)
- Loading state con DataTableSkeleton
- Empty state customizable
- onRowClick callback con stop-propagation correcto en chevron
- 14 tests cubriendo todas las features

Refactor UsersTable a DataTable como dogfood (mismo output visual,
columnas con priority alta/media/baja). 150 tests frontend totales verde.

Doc Obsidian 2.14 v2.4 actualizado con seccion DataTable completa,
componentes ampliados a 22, MCP global, y gotcha del tsconfig.

Engram sig-cm2/design-system actualizado a v2.4.

Skill registry actualizado con compact rules de DataTable y MCP.
2026-04-16 11:54:14 -03:00
5f7d9e6b89 docs(skill-registry): actualizar compact rules design system v2.3
Sincroniza con doc Obsidian 2.14 v2.3 y engram sig-cm2/design-system:
- Agrega violet accent y nueva guidance
- Density bumpeada a 40px (input h-10), radius 10px base
- Utilities CSS (.glass/.gradient-mesh/.grid-bg/.surface/.focus-glow)
- Card variants ampliados
- Tooltip Radix obligatorio (no CSS absolute en sidebars)
- Sidebar colapsable con useSidebar
- TooltipProvider y Toaster ya en App.tsx
- Browser autofill fix mencionado
2026-04-16 11:32:07 -03:00
83d76b95d4 feat(web): tooltips Radix + toggle sidebar al lado del brand
Cambios:
- Instalado @radix-ui/react-tooltip 1.2.8 (componente faltante de
  shadcn/ui que faltaba en el set inicial).
- Nuevo src/web/src/components/ui/tooltip.tsx (shadcn pattern):
  TooltipProvider, Tooltip, TooltipTrigger, TooltipContent con
  animaciones data-state (fade + zoom + slide direccional).
- App.tsx: TooltipProvider envuelve toda la app con delayDuration 150ms.
- AppSidebar refactorizado:
  - Toggle button MOVIDO al header (top), al lado izquierdo del nombre
    'SIG-CM 2.0'. Eliminado boton bottom (era redundante).
  - Cuando collapsed: solo el toggle visible centrado (68px width).
  - Cuando expanded: [Toggle] [SIG-CM 2.0] aligned left.
  - Quitado overflow-hidden del aside (era lo que impedia que los
    tooltips fueran visibles — los clipping containers padres tampoco
    importan ahora porque Radix portalea el tooltip a body).
  - Tooltips en TODOS los items collapsed (incluido el toggle) y en
    items disabled muestra 'Label · Próximamente'.
  - Eliminado el componente CSS-only SidebarTooltip (reemplazado por
    Radix que se renderiza fuera del DOM tree con Portal).

El bug original era que tanto el aside con overflow-hidden como el
ProtectedLayout con overflow-hidden clipean cualquier elemento que
intente escapar via absolute positioning. Radix Portal soluciona
eso renderizando el tooltip en document.body.

Tests 136/136 verde.
2026-04-16 11:26:41 -03:00
7b7ef1c137 feat(web): sidebar colapsable con tooltips + fix scroll horizontal
Cambios:
- Nuevo hook useSidebar() con persistencia en localStorage
  ('sidebar-collapsed' = '1'/'0').
- SidebarNav refactorizado:
  - Width controlled internamente (w-60 expanded, w-[68px] collapsed)
  - Toggle button al pie con PanelLeftClose/Open icon
  - Brand mark con gradient brand+violet (consistencia con login)
  - Active indicator: barra vertical sutil a la izquierda cuando expanded,
    bg accent cuando collapsed
  - SectionLabel se reemplaza por divider sutil cuando collapsed
- Custom SidebarTooltip puro CSS (sin radix dep nuevo) que aparece
  a la derecha del item con animacion fade + slide al hover. Funciona
  con group-hover/item y group-hover/toggle (Tailwind named groups).
- Items disabled muestran badge 'Próx.' chico (era 'Próximamente' largo)
  y en tooltip cuando collapsed: 'Label · Próximamente'.
- Fix scroll horizontal: overflow-x-hidden en nav, truncate en spans,
  shrink-0 en iconos y badges. Layout robusto a labels largos.
- ProtectedLayout deja de hardcodear lg:w-60 — sidebar controla su width.
- AppHeader Sheet (mobile) usa <SidebarNav forceExpanded /> para que
  en mobile siempre se vea expanded sin importar el state desktop.

Tests 136/136 verde.
2026-04-16 11:21:42 -03:00
41b6882b5c feat(web): mas contraste cards/tables sobre bg + utility .surface
Cambios de tokens:
- Light --background: 0.988 -> 0.962 (slate cool, hace pop el card white)
- Dark --background: 0.135 -> 0.130 (mas oscuro)
- Dark --card: 0.180 -> 0.220 (salto +0.090 vs bg, antes solo +0.045)
- Dark --popover: 0.200 -> 0.245 (popovers/dropdowns aun mas elevados)
- Dark --secondary/muted/accent/input: bumpeados al nivel correspondiente
  para que la jerarquia visual mantenga proporciones

Card variants:
- default: shadow-sm -> shadow-md (mas elevacion default)
- nuevo variant 'flat' (sin shadow) para cuando se necesite

Nueva utility .surface:
- Mismo treatment visual que <Card variant='default'> pero como clase
  CSS — para contenedores que no usan el componente Card (ej: tablas,
  listas custom). bg-card + border + radius + shadow-md.

UsersTable refactorizado para usar .surface en lugar de border manual.
Cualquier futura tabla/lista usa .surface por consistencia.

Tests 136/136 verde.
2026-04-16 11:15:36 -03:00
278e1cf378 fix(web): light mode profundidad + grid global + autofill fix
Cambios:
- index.css: fix de browser autofill (Chrome/Safari forzaba bg amarillo +
  texto blanco que rompia contraste). Override -webkit-text-fill-color
  + box-shadow inset para mantener tokens del DS. Esta era la causa real
  de las 'letras blancas en gris' que se veian en login.
- index.css: utility .grid-bg global (7% opacity light, 10% dark) — para
  usar como fondo cuadriculado en todos los layouts.
- PublicLayout: agrego .grid-bg layer + bg-background explicito + glow
  blobs mas intensos (25%/20% en light vs 10% antes). Light ahora
  tiene la misma profundidad visual que dark.
- ProtectedLayout: agrego .grid-bg + glow blobs sutiles en corners para
  dar profundidad al dashboard y todas las secciones internas. Resalta
  futuros componentes glass.

Tests: 136/136 verde.
2026-04-16 11:10:06 -03:00
3bc2625e21 fix(web): agregar ThemeToggle en PublicLayout (login)
El ThemeToggle solo vivia en AppHeader (ProtectedLayout), por lo que
desde /login era imposible cambiar el tema. Movido a esquina superior
derecha con z-index 20 sobre el gradient mesh.

useTheme defaultea a system preference, pero el usuario tiene que poder
override desde cualquier pantalla — incluido el login.
2026-04-16 11:05:39 -03:00
6e6c729bac feat(web): design system v2 — tech sophisticated con glass + gradient mesh
Cambios principales:
- Agregado violet accent (oklch 0.62 0.20 280) para combo tech con brand cyan
- Neutrals con shift sutil hacia hue 250 (slate-violet)
- Dark mode con bg oklch(0.135 0.018 252) — no pure black, feel mas tech
- Inputs con token --input propio (white en light, elevado en dark) y --input-border mas prominente. Fixea problema de input gris feo
- Card soporta variant glass/elevated/default
- Multi-layer shadows reales (shadow-sm/md/lg/xl/glow)
- Gradient mesh utility (.gradient-mesh + token --gradient-mesh)
- Clase .glass para glassmorphism (backdrop-blur 20px + saturate 180%)
- Border radius default 10px (era 8px) — mas moderno
- Headings con tracking-tight -0.015em

LoginPage redesigned:
- PublicLayout con gradient mesh + 2 glow blobs (brand+violet) + grid sutil
- Card variant glass para el form
- Logo mark con bg-gradient-to-br from-brand-500 to-violet-500
- Inputs con bg propio + ring brand glow al focus

Tests: 136/136 verde.
Doc Obsidian 2.14 actualizado v2.0. Engram sig-cm2/design-system actualizado.
2026-04-16 11:02:59 -03:00
c488e2430d feat(web): bootstrap design system con paleta brand El Dia
- Reemplazo de tokens HSL por OKLCH (Tailwind 4 native)
- Brand color #008fbe escalado a brand-50..950
- Neutral cool slate (complementa brand)
- Semantic: success/warning/destructive como tokens
- Background tinted off-white (no pure white) para warmth
- Dark mode usa neutral-950 (no pure black)
- Brand utilities expuestos via @theme inline (bg-brand-500, etc)
- Focus rings con ring brand color
- Selection con brand-200/800

Skill registry actualizado con compact rules de design system para auto-inyeccion en sub-agents.

Source of truth: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.14 Design System.md
Engram: sig-cm2/design-system
2026-04-16 10:46:07 -03:00
492705c076 Merge pull request 'UDT-009: Overrides de PermisosJson por usuario — cierre módulo Auth' (#12) from feature/UDT-009 into main 2026-04-16 13:12:23 +00:00
6822d56e11 fix(web): montar Toaster + feedback toast en PermisosEditor [UDT-009]
Sonner estaba como dependencia pero el componente Toaster nunca se monto
en el arbol de la app. ChangeMyPasswordPage ya usaba toast() pero no
mostraba nada visualmente. Agregado <Toaster richColors closeButton /> en
App.tsx (top-right) y toasts de exito/error en PermisosEditor.handleSave
para confirmar al usuario que el cambio se persistio.
2026-04-16 10:11:04 -03:00
a30b10ebff fix(web): UsuarioPermisos shape nested para matchear backend [UDT-009]
El backend devuelve { rolPermisos, overrides: {grant, deny}, effective }
(nested) segun spec, pero el frontend lo lee como {grant, deny} planos.
Causaba TypeError: permisoData.grant is not iterable al abrir tab Permisos.
Tests del frontend actualizados con el shape correcto.
2026-04-16 10:06:43 -03:00
b7882613a4 feat(web): UserEditPage con tabs Perfil+Permisos [UDT-009] 2026-04-15 21:50:55 -03:00
9dbf3e895d feat(web): tabs.tsx + PermisosEditor tri-state [UDT-009] 2026-04-15 21:49:48 -03:00
c1426b2257 feat(web): api + hooks permisos overrides [UDT-009] 2026-04-15 21:48:06 -03:00
7d4dc4d2bb feat(api): PUT /api/v1/users/{id}/permisos/overrides + excepciones domain + ExceptionFilter [UDT-009] 2026-04-15 21:43:38 -03:00
47323302cc feat(api): GET /api/v1/users/{id}/permisos con CQRS handler [UDT-009] 2026-04-15 21:43:08 -03:00
5fd88b5a9d feat(infra): IUsuarioRepository.UpdatePermisosJsonAsync + impl Dapper [UDT-009] 2026-04-15 21:33:39 -03:00
bf64ffb35e feat(api): PermissionAuthorizationHandler resuelve overrides desde DB por request [UDT-009] 2026-04-15 21:32:35 -03:00
fb07a1139a feat(application): LoginCommandHandler usa PermisoResolver para permisos efectivos [UDT-009] 2026-04-15 21:29:33 -03:00
86310de286 feat(security): remover claim permisos del JWT post-UDT-009 [UDT-009] 2026-04-15 21:28:26 -03:00
54955231bf feat(infra): V009 migration + Usuario.WithPermisosJson + SqlTestFixture V009 schema [UDT-009] 2026-04-15 21:27:29 -03:00
da1eb83ac1 feat(application): PermisosOverride record + PermisoResolver static helper [UDT-009] 2026-04-15 21:25:09 -03:00
be86c2fac9 chore(repo): bootstrap feature/UDT-009 [UDT-009] 2026-04-15 21:23:43 -03:00
68897f446b Merge pull request 'UDT-008: Gestión completa de usuarios' (#11) from feature/UDT-008 into main 2026-04-16 00:01:36 +00:00
06908263f6 fix(web): cablear ResetPasswordModal en UserEditPage [UDT-008]
El row click de UsersListPage navega directo a /usuarios/:id/editar,
por lo que el modal montado solo en UserDetailPage no era alcanzable
desde el flujo real. Ahora tambien esta en el header del EditPage,
al lado del boton Volver, oculto cuando el target es el user logueado.
2026-04-15 21:00:08 -03:00
9e93c70d8b refactor(web): mover Cambiar contraseña de sidebar a menu perfil [UDT-008]
La seccion Mi cuenta en el sidebar quedaba desprolija con un unico item.
Se movio Cambiar contraseña al dropdown del avatar en AppHeader donde
pertenece semanticamente.
2026-04-15 20:55:26 -03:00
851fed8692 fix(web): cablear ResetPasswordModal en UserDetailPage [UDT-008]
El componente ResetPasswordModal estaba implementado pero nunca montado en una pagina.
Ahora se renderiza en UserDetailPage, oculto cuando el target es el usuario logueado
(evita hit de cannot-self-reset en backend).
2026-04-15 20:54:25 -03:00
2e2d4543ad feat(web): router wiring completo + nav link usuarios + MustChangePasswordGate integration [UDT-008]
- Agrega ProtectedPage helper que combina ProtectedRoute + MustChangePasswordGate + ProtectedLayout
- Rutas nuevas: /usuarios, /usuarios/:id, /usuarios/:id/editar con permisos RBAC
- /perfil/contrasena sin MustChangePasswordGate (evita redirect loop)
- Sidebar: sección "Mi cuenta" con cambio de contraseña; link Usuarios en sección admin
2026-04-15 18:12:54 -03:00
25ed0f6452 feat(web): ChangeMyPasswordPage + ResetPasswordModal — hooks, pages, modal [UDT-008] 2026-04-15 18:09:59 -03:00
64e0a8b5fb feat(web): UserDetailPage + UserEditPage — get/update/deactivate/reactivate hooks y pages [UDT-008] 2026-04-15 18:06:54 -03:00
9512f4125d feat(web): UsersListPage — api client, hook, filters, table, pagination [UDT-008] 2026-04-15 18:05:07 -03:00
d998d215e0 feat(web): authStore username+mustChangePassword + MustChangePasswordGate [UDT-008] 2026-04-15 18:02:20 -03:00
7d96d5ff18 feat(api): ResetPassword admin — TempPasswordGenerator, handler, endpoint POST /{id}/password/reset [UDT-008]
Batch 7: POST /api/v1/users/{id}/password/reset (admin only).
- TempPasswordGenerator: RandomNumberGenerator.Fill, 12-char min, full charset diversity, never logs result
- ResetUsuarioPasswordCommandHandler: self-reset guard, 404, hash, mustChangePassword=true, revoke all tokens
- ExceptionFilter: CannotSelfResetException → 400 {error: cannot-self-reset}
- Unit tests: TempPasswordGeneratorTests (8), ResetUsuarioPasswordCommandHandlerTests (5)
- Integration tests: ResetPasswordEndpointTests (6) — 200/length/self-reset/404/401/403
2026-04-15 17:55:45 -03:00
a3bd066f7b feat(api): ChangeMyPassword — validator, handler, endpoint PUT /me/password [UDT-008] 2026-04-15 17:52:15 -03:00
473566f255 feat(api): Deactivate + Reactivate usuarios — idempotentes, anti-lockout, revoke tokens [UDT-008] 2026-04-15 17:50:54 -03:00
14c385fdb1 feat(api): UpdateUsuario — handler, validator, anti-lockout guard, revoke tokens [UDT-008] 2026-04-15 17:49:19 -03:00
2925336783 feat(api): List + GetById usuarios — handlers, repo, endpoints [UDT-008] 2026-04-15 17:46:23 -03:00
9dcd63543e feat(auth): extend LoginResponse with username + mustChangePassword + ultimoLogin [UDT-008] 2026-04-15 17:39:48 -03:00
d1f7b3805b feat(domain): V008 migration + Usuario with-methods + DomainException hierarchy [UDT-008] 2026-04-15 17:36:46 -03:00
5ddc5ddf02 chore(udt-008): bootstrap rama feature/UDT-008 [UDT-008] 2026-04-15 17:34:12 -03:00
c0d1ea4ac2 Merge pull request 'UDT-006: Middleware de Autorización (RBAC enforcement)' (#10) from feature/UDT-006 into main 2026-04-15 20:15:18 +00:00
8513e99554 test(api): assert count 21 permisos admin post-V007 [UDT-006] 2026-04-15 16:49:54 -03:00
96e7290fb7 refactor(web): eliminar guards inline rol admin en páginas de roles/permisos [UDT-006] 2026-04-15 16:49:21 -03:00
f6cdd7650b feat(web): ProtectedRoute extraído + router migrado + CreateUserPage cleanup [UDT-006] 2026-04-15 16:41:39 -03:00
8935115da9 feat(web): usePermission + CanPerform [UDT-006] 2026-04-15 16:40:23 -03:00
2efd5e2fdb feat(web): authStore + useLogin persisten permisos [UDT-006] 2026-04-15 16:39:18 -03:00
0218d8d371 feat(api): migrar controllers admin a RequirePermission [UDT-006] 2026-04-15 16:34:32 -03:00
4866c4f21f feat(api): ForbiddenProblemDetailsHandler 403 shape [UDT-006] 2026-04-15 16:27:36 -03:00
58d0df601f feat(api): RequirePermissionAttribute + PermissionAuthorizationHandler [UDT-006] 2026-04-15 16:26:30 -03:00