Commit Graph
6 Commits
Author SHA1 Message Date
Soporte ZH Consultores 510304872c feat(auth): nuevo rol 'admin' (programador, modo Dios oculto)
JERARQUÍA DE ROLES
  admin   = programador. Modo Dios. Oculto de dropdowns y del Panel
            de Control para non-admins. Acceso total.
  socio   = modo Dios visible. Acceso al Panel, ve y edita todo.
  abogado = crea casos, ve los suyos + donde es involucrado.
  usuario = solo ve/edita donde es involucrado.

* Migración 008: agrega 'admin' al ENUM users.role. Promueve a
  ozerpa@aal-law.com como admin.
* Auth.php: isAdmin() nuevo. isSocio() y isAbogado() ahora incluyen
  admin (admin hereda todos los poderes de socio). canCreate/canDelete
  también heredan.
* Lookups.php:
  - abogadoNames() ya filtraba a role IN (socio, abogado) → admins no
    aparecen en el dropdown "Abogado Solicitante".
  - involucrablesUsers() agrega filtro role != 'admin' → admins no
    aparecen en el picker de Involucrados.
  - allUsers($includeAdmins) parametrizada: socios reciben la lista
    sin admins, admins reciben todo.
* Bootstrap.php: window.DONNA.isAdmin disponible al frontend.
* admin/api.php:
  - list_users pasa Auth::isAdmin() para filtrar.
  - create_user / update_user: rechazan role='admin' si quien llama no
    es admin. Solo admins pueden editar a otros admins.
  - delete_user: protege admins (extends al check existente de socio).
  - Self-protection extendida: admin no puede demoverse por debajo de
    socio (queda en god-mode tier).
* admin.html: opción 'admin' en role dropdown solo visible si el
  usuario actual es admin. Role pill navy/blanca para admin.
2026-06-05 00:27:54 +00:00
Soporte ZH Consultores 072cc0b6e3 fix(admin): bug del activo + simplificar a Nombre Completo + gear en landing
BUG FIX
* admin.html: el filtro <select id="f-activo"> y el modal <input id="f-activo">
  tenían el MISMO id. document.getElementById devolvía el select (primer en
  DOM order) y la lectura .checked siempre era undefined → JSON enviaba el
  valor stale del select. Renombré el filtro a `f-activo-filter`.

REFACTOR: drop apellido, usar Nombre Completo
* Migración 007: copia apellido→name para placeholders, migra
  escrituras_tramites.abogado y tramites_legales.abogado a usar nombre
  completo, DROP COLUMN apellido + índice.
* Lookups: abogadoApellidos() → abogadoNames() filtra socios+abogados
  activos con name no vacío.
* Auth.php: drop método apellido(), agregado userName().
* tramites/api.php: visibilidad usa users.name = t.abogado.
* admin/api.php: drop apellido en create/update payloads.
* admin.html: drop columna y campo Apellido del table y form.
* Bootstrap envía window.DONNA.abogados con nombres completos.

LANDING (index.php)
* Gear icon en dash-header visible solo si Auth::isSocio() (chequeo
  server-side, sin flash de JS).

CLEANUP
* Eliminado logging de debug en admin/api.php que se agregó para
  diagnosticar el bug del activo.

PERMISOS (sin cambios — confirmación)
* Trámites Legales: solo socio/abogado pueden crear (canCreate() ya lo
  enforza desde 2db8f23).
* Escrituras: cualquier rol autenticado puede crear (escrituras/api.php
  nunca tuvo restricción).
2026-06-04 23:42:06 +00:00
Soporte ZH Consultores 2db8f23ba5 feat(perms): permisología completa con 3 roles (Socio/Abogado/Usuario) + Panel de Control
ESQUEMA (migraciones 005 + 006)
* users: agrega role ENUM('socio','abogado','usuario'), apellido, activo, orden
* Seed: cabrego@aal-law.com como socio + 15 placeholders por apellido para
  poblar el dropdown "Abogado Solicitante" hasta que los reales se logueen.
* tramites_involucrados: tabla N:M (tramite_id, user_id) con CASCADE delete.

AUTH (src/Auth.php)
* userRow() cachea el row del usuario actual por request.
* role(), isSocio(), isAbogado(), canCreate(), canDelete(), requireSocio().
* completeLogin() retorna bool: false si user.activo=0 → callback muestra
  pantalla "cuenta deshabilitada" en lugar de loguear.

BOOTSTRAP (src/Bootstrap.php + src/Lookups.php)
* Bootstrap::inject(view) reemplaza readfile en los wrappers; inyecta
  <script>window.DONNA={me,name,role,isSocio,isAbogado,canCreate,canDelete,
  abogados,users}</script> antes de </head> para que el frontend tenga
  contexto sin requests extra.

TRAMITES — visibilidad + involucrados
* api.php: list filtra por (socio | creador | abogado solicitante mapped via
  users.apellido | involucrado) excepto socios que ven todo. Todas las
  mutaciones validan canView(). create/delete validan rol. Persiste
  involucrados vía syncInvolucrados().
* tramites.html: nueva columna "Involucrados" después de "Abogado", chip
  picker multi-select en el modal, gear icon (oculto a non-socios), oculta
  botones "+ Nuevo" y ✕ para usuarios sin permisos. Auto-incluye al
  creador como involucrado por defecto.
* Calendario y demás apps consumen el API filtrado → heredan la
  visibilidad sin código nuevo.

PANEL DE CONTROL (/admin/)
* index.php: requireSocio() + Bootstrap injection.
* api.php: list_users, create_user, update_user. Salvaguardas: el socio
  no puede demoverse ni desactivarse a sí mismo.
* admin.html: tabla unificada de usuarios con filtros, modal CRUD,
  resaltado de fila propia, placeholders identificados visualmente.

GEAR ICON
* Agregado en escrituras.html, tramites.html, calendario.html con
  display:none por defecto; JS lo revela si window.DONNA.isSocio.
* Link a /admin/.
2026-06-04 23:14:07 +00:00
Soporte ZH Consultores 1bb63f12f6 feat(tramites): app "Control de Trámites Legales" + backend
Migración del frontend localStorage a backend autenticado bajo Donna.

* SQL 004: tramites_legales (todos los campos por dept en columnas
  nullable + ENUMs Sí/No), tramites_obs (bitácora N:1), tramites_hours
  (registro de horas N:1), tramites_seq (contador por departamento).
  IDs server-side tipo ADM-001 / CRP-001 / MIG-001 / LIT-001 con
  SELECT FOR UPDATE para atomicidad.
* public/tramites/api.php: list/create/update/delete + set_status +
  add_obs + add_hours. Valida departamento y estatus contra los enums
  del frontend. Audita creado_por / actualizado_por con el email
  autenticado.
* views/tramites.html: artifact original adaptado. Quitado backup/restore
  localStorage (mysqldump diario ya hace ese trabajo). Cada mutación
  va vía fetch con manejo de 401 → redirect a login. Mapeo
  id<->codigo para preservar el contrato del artifact.
* Apps.php: la card "Gestión de Casos" placeholder se reemplaza por
  "Control de Trámites Legales" en estado live.
2026-05-11 20:03:28 +00:00
Soporte ZH Consultores 81a909a1ec feat(escrituras): añadir columna "Entrada" (########/####)
* SQL: nueva migración 003 que añade `entrada VARCHAR(13)` después de
  `estatus` en `escrituras_tramites`. Aplicada en producción.
* API: INSERT/UPDATE persisten el campo; auditoría lo registra con
  label "Entrada" en el historial.
* Frontend:
  - Nueva columna en el header entre Estatus y Últ. Actualización
  - Input con `pattern=^\d{1,8}/\d{4}$`, `maxlength=13`, placeholder
    "########/####" y validación nativa del navegador
  - newRow() y duplicateRow() incluyen `entrada:''` por defecto
  - exportHeaders / rowToArray / Excel widths actualizados
  - pdfCols renumerado para incluir Entrada en el PDF
2026-05-11 19:31:22 +00:00
Soporte ZH Consultores 4ee32aec6d Initial commit — Donna portal v1
Setup completo del portal interno donna.aal-law.com:

* Landing público con OAuth login + grid de apps autenticado
* Flujo OAuth2 contra Mailcow (mail.aal-law.com)
* Sesiones SSO compartidas en el dominio (cookie donna_session, 8h)
* Whitelist de dominios aal-law.com / aalnlaw.com
* App "Control de Escrituras" migrada desde ZH-Consultores/control-escrituras
  (mismo schema, mismo frontend, ahora con shared Auth/Db/Config libs)
* Migraciones SQL para users, escrituras_tramites, escrituras_historial
* Caddy on :80 con rewrite extless → .php
* Backup diario MariaDB (systemd timer + script) con retención 14d
* Documentación de arquitectura en README + infra files en docs/infra
2026-05-11 19:10:36 +00:00