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).
This commit is contained in:
Soporte ZH Consultores
2026-06-04 23:42:06 +00:00
parent 2db8f23ba5
commit 072cc0b6e3
8 changed files with 83 additions and 58 deletions
+10 -19
View File
@@ -157,18 +157,18 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
<div class="intro">
<h1>Usuarios y permisos</h1>
<p>Administra la lista unificada de usuarios del sistema. El campo <b>Apellido</b> es lo que aparece en el dropdown "Abogado Solicitante" (solo si el rol es <b>Socio</b> o <b>Abogado</b>). Los emails que empiezan con <code>placeholder-</code> son los apellidos sembrados al inicio para que los dropdowns no salieran vacíos; reemplázalos con los emails reales conforme los abogados se logueen.</p>
<p>Administra la lista unificada de usuarios del sistema. El campo <b>Nombre completo</b> es lo que aparece en el dropdown "Abogado Solicitante" (solo si el rol es <b>Socio</b> o <b>Abogado</b>). Los emails que empiezan con <code>placeholder-</code> son los nombres sembrados al inicio para que los dropdowns no salieran vacíos; reemplázalos con los emails reales conforme los abogados se logueen.</p>
</div>
<div class="toolbar">
<input type="search" id="search" placeholder="Buscar por email, nombre o apellido…">
<input type="search" id="search" placeholder="Buscar por email o nombre…">
<select id="f-role">
<option value="">— Todos los roles —</option>
<option value="socio">Socio</option>
<option value="abogado">Abogado</option>
<option value="usuario">Usuario</option>
</select>
<select id="f-activo">
<select id="f-activo-filter">
<option value="">— Activos e inactivos —</option>
<option value="1">Solo activos</option>
<option value="0">Solo inactivos</option>
@@ -182,8 +182,7 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
<thead>
<tr>
<th>Email</th>
<th>Nombre</th>
<th>Apellido</th>
<th>Nombre completo</th>
<th>Rol</th>
<th>Activo</th>
<th>Orden</th>
@@ -209,15 +208,11 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
<input type="email" id="f-email" placeholder="usuario@aal-law.com">
<div class="fg-hint" id="hint-email">El email debe coincidir exactamente con la cuenta de Mailcow que el usuario usa para entrar.</div>
</div>
<div class="fg">
<label>Nombre completo</label>
<input type="text" id="f-name" placeholder="Ej: Carlos Abrego">
</div>
<div class="fg-row">
<div class="fg">
<label>Apellido (dropdown)</label>
<input type="text" id="f-apellido" placeholder="Ej: Abrego">
<div class="fg-hint">Solo aparece en dropdowns si el rol es Socio o Abogado.</div>
<label>Nombre completo</label>
<input type="text" id="f-name" placeholder="Ej: Carlos Abrego">
<div class="fg-hint">Aparece en el dropdown "Abogado Solicitante" si el rol es Socio o Abogado.</div>
</div>
<div class="fg">
<label>Rol</label>
@@ -310,12 +305,12 @@ async function loadUsers(){
function visibleUsers(){
const q = $('search').value.toLowerCase().trim();
const fR = $('f-role').value;
const fA = $('f-activo').value;
const fA = $('f-activo-filter').value;
return users.filter(u => {
if (fR && u.role !== fR) return false;
if (fA !== '' && String(u.activo) !== fA) return false;
if (q) {
const blob = ((u.email||'')+' '+(u.name||'')+' '+(u.apellido||'')).toLowerCase();
const blob = ((u.email||'')+' '+(u.name||'')).toLowerCase();
if (!blob.includes(q)) return false;
}
return true;
@@ -338,7 +333,6 @@ function render(){
return `<tr class="${rowCls}">
<td><b>${esc(u.email)}</b>${isPlaceholder?' <span class="placeholder">(placeholder)</span>':''}${isMe?' <span style="color:var(--gold-d);font-size:10px;">(tú)</span>':''}</td>
<td>${esc(u.name) || '<span class="placeholder">—</span>'}</td>
<td>${esc(u.apellido) || '<span class="placeholder">—</span>'}</td>
<td><span class="role-pill role-${u.role}">${ROLE_LABEL[u.role] || u.role}</span></td>
<td><span class="activo-pill activo-${u.activo}">${u.activo ? 'Activo' : 'Inactivo'}</span></td>
<td>${u.orden}</td>
@@ -353,7 +347,6 @@ function openCreate(){
$('modal-title').textContent = 'Nuevo usuario';
$('f-email').value = ''; $('f-email').readOnly = false;
$('f-name').value = '';
$('f-apellido').value = '';
$('f-role-edit').value = 'usuario';
$('f-orden').value = '0';
$('f-activo').checked = true;
@@ -370,7 +363,6 @@ function openEdit(id){
$('f-email').value = u.email;
$('f-email').readOnly = false;
$('f-name').value = u.name || '';
$('f-apellido').value = u.apellido || '';
$('f-role-edit').value = u.role;
$('f-orden').value = u.orden;
$('f-activo').checked = u.activo === 1;
@@ -390,7 +382,6 @@ async function saveUser(){
const payload = {
email: $('f-email').value.trim().toLowerCase(),
name: $('f-name').value.trim(),
apellido: $('f-apellido').value.trim(),
role: $('f-role-edit').value,
orden: parseInt($('f-orden').value, 10) || 0,
activo: $('f-activo').checked ? 1 : 0,
@@ -423,7 +414,7 @@ async function saveUser(){
document.addEventListener('keydown', e => {
if (e.key === 'Escape') closeModal();
});
['search','f-role','f-activo'].forEach(id => {
['search','f-role','f-activo-filter'].forEach(id => {
$(id).addEventListener('input', render);
$(id).addEventListener('change', render);
});