- Nueva tabla calendar_events (tipo, fecha, hora_inicio/fin, ubicación,
descripción, creador) — migración 011
- API CRUD /calendario/api.php (list/create/update/delete) con AuditLog
- Permisos: cualquiera autenticado crea; solo creador o Socio/Admin
edita y elimina
- UI calendario: chip "Eventos", botón "+ Nuevo evento" en toolbar y
en vista Día, modal con tipo/fecha/horario/ubicación, opción
"Todo el día"
- Detalle de evento manual muestra creador, horario y ubicación;
acciones Editar/Eliminar inline (sin navegación) si tiene permiso
- Colores por tipo: reserva sala, cumpleaños, recordatorio, evento
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/.
Cliente-side: el calendario hace fetch a /escrituras/api.php?action=list
y /tramites/api.php?action=list y agrega los eventos en memoria. Sin
backend nuevo (cero costo de mantenimiento, datos siempre frescos).
Eventos generados automáticamente:
* Escrituras → fecha_solicitud
* Trámites adm/corp/mig/lit → fecha_solicitud
* Trámites mig → fecha_vencimiento (resaltado como deadline)
* Trámites lit → fecha_termino (resaltado como deadline)
Vistas: Año (12 mini-meses con dots por evento + marca roja para
vencimientos), Mes (grid con chips ≤3 por día), Semana (7 columnas),
Día (lista de cards). Hoy resaltado en todas.
Resaltado de vencimientos:
* Vencido (días<0, status abierto) → rojo + animación flash
* ≤7d → naranja
* >7d → color por fuente
Funcionalidad adicional:
* Strip "Próximos vencimientos" con los 8 deadlines más urgentes
* KPIs: total · ≤7d · ≤30d · vencidos abiertos
* Filtros por fuente (chips) + búsqueda por texto
* Atajos: ←/→ mueve período, T hoy, Y/M/W/D cambia vista, Esc cierra
* Auto-refresh cada 60s en segundo plano (pausa si la modal está abierta)
* Misma barra superior estandarizada que Escrituras y Trámites
Popup ViewOnly al hacer click en un evento muestra ficha completa con
todos los campos relevantes según fuente y dept. Dos botones:
"Regresar al calendario" cierra · "Editar evento" navega a
/escrituras/?edit=ID o /tramites/?edit=CODIGO.
Deep-link: escrituras.html y tramites.html detectan ?edit=X al cargar
y auto-abren la modal de edición sobre esa fila. URL se limpia con
history.replaceState para que un refresh no re-abra la modal.
Apps.php: card placeholder "Agenda" reemplazada por "Calendario" live.