feat(export): botón Exportar (Excel + PDF) en Usuarios y Registro de Transacciones
- Usuarios y permisos: exporta los rows visibles (respeta filtros de rol, activos y búsqueda). Columnas: Email, Nombre, Rol, Activo, Último login - Registro de transacciones: exporta las entradas cargadas en pantalla (respeta filtros de fecha/usuario/módulo/acción/texto). Excel incluye Email e IP; el PDF los omite para que quepa - Mismo módulo compartido DonnaExport — UI idéntica a los otros módulos
This commit is contained in:
@@ -170,6 +170,7 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
|
|||||||
border-radius:6px; color:var(--gray-700); transition:all .15s; }
|
border-radius:6px; color:var(--gray-700); transition:all .15s; }
|
||||||
.tb-quick-log:hover { background:var(--navy); color:#fff; border-color:var(--navy); }
|
.tb-quick-log:hover { background:var(--navy); color:#fff; border-color:var(--navy); }
|
||||||
</style>
|
</style>
|
||||||
|
<link rel="stylesheet" href="/assets/donna-export.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
@@ -223,6 +224,7 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
|
|||||||
<option value="0">Solo inactivos</option>
|
<option value="0">Solo inactivos</option>
|
||||||
</select>
|
</select>
|
||||||
<button class="btn-primary" onclick="openCreate()">+ Nuevo usuario</button>
|
<button class="btn-primary" onclick="openCreate()">+ Nuevo usuario</button>
|
||||||
|
<div id="export-mount-users"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tbl-card">
|
<div class="tbl-card">
|
||||||
@@ -285,6 +287,7 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
|
|||||||
<button class="tb-quick-log" onclick="setLogQuick('month')">Mes</button>
|
<button class="tb-quick-log" onclick="setLogQuick('month')">Mes</button>
|
||||||
<button class="tb-quick-log" onclick="setLogQuick('all')">Todo</button>
|
<button class="tb-quick-log" onclick="setLogQuick('all')">Todo</button>
|
||||||
<button class="btn-primary" onclick="loadLog()" style="padding:7px 14px;">Aplicar</button>
|
<button class="btn-primary" onclick="loadLog()" style="padding:7px 14px;">Aplicar</button>
|
||||||
|
<div id="export-mount-log"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -354,6 +357,8 @@ tr.is-me td { background:#FFFBEB; font-weight:500; }
|
|||||||
|
|
||||||
<div class="toast" id="toast"></div>
|
<div class="toast" id="toast"></div>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
|
||||||
|
<script src="/assets/donna-export.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const ROLES = ['admin','socio','abogado','usuario'];
|
const ROLES = ['admin','socio','abogado','usuario'];
|
||||||
const ROLE_LABEL = { admin:'Admin', socio:'Socio', abogado:'Abogado', usuario:'Usuario' };
|
const ROLE_LABEL = { admin:'Admin', socio:'Socio', abogado:'Abogado', usuario:'Usuario' };
|
||||||
@@ -644,7 +649,9 @@ function populateLogDropdowns(d){
|
|||||||
(d.acciones||[]).map(a => `<option value="${esc(a)}">${esc(ACCION_LABEL[a] || a)}</option>`).join('');
|
(d.acciones||[]).map(a => `<option value="${esc(a)}">${esc(ACCION_LABEL[a] || a)}</option>`).join('');
|
||||||
aSel.value = aPrev;
|
aSel.value = aPrev;
|
||||||
}
|
}
|
||||||
|
let lastLogEntries = [];
|
||||||
function renderLog(entries){
|
function renderLog(entries){
|
||||||
|
lastLogEntries = entries || [];
|
||||||
const tbody = $('log-tbody');
|
const tbody = $('log-tbody');
|
||||||
const empty = $('log-empty');
|
const empty = $('log-empty');
|
||||||
const info = $('log-rowinfo');
|
const info = $('log-rowinfo');
|
||||||
@@ -677,6 +684,71 @@ $('log-search').addEventListener('input', () => {
|
|||||||
window._logSearchTimer = setTimeout(loadLog, 350);
|
window._logSearchTimer = setTimeout(loadLog, 350);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── EXPORT (Usuarios) ────────────────────────────────────────────────────
|
||||||
|
const USERS_HEADERS = ['Email','Nombre completo','Rol','Activo','Último login'];
|
||||||
|
function usersExportRows(){
|
||||||
|
return visibleUsers().map(u => [
|
||||||
|
u.email,
|
||||||
|
u.name || '',
|
||||||
|
ROLE_LABEL[u.role] || u.role,
|
||||||
|
u.activo ? 'Sí' : 'No',
|
||||||
|
u.last_login_at ? fmtDate(u.last_login_at) : 'Nunca',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
function usersExportExcel(){
|
||||||
|
DonnaExport.toExcel({
|
||||||
|
filename: 'Usuarios_Donna_AALN', sheetName: 'Usuarios',
|
||||||
|
headers: USERS_HEADERS, rows: usersExportRows(),
|
||||||
|
columnWidths: [32,28,12,10,20],
|
||||||
|
onEmpty: () => toast('Sin usuarios para exportar','error'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function usersExportPDF(){
|
||||||
|
DonnaExport.toPDF({
|
||||||
|
filename: 'Usuarios_Donna_AALN',
|
||||||
|
title: 'Usuarios y permisos',
|
||||||
|
orientation: 'portrait',
|
||||||
|
headers: USERS_HEADERS, rows: usersExportRows(),
|
||||||
|
onEmpty: () => toast('Sin usuarios para exportar','error'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.getElementById('export-mount-users').innerHTML = DonnaExport.buttonHtml('export-wrap-users');
|
||||||
|
DonnaExport.attach('export-wrap-users', { onExcel: usersExportExcel, onPDF: usersExportPDF });
|
||||||
|
|
||||||
|
// ── EXPORT (Registro de transacciones) ───────────────────────────────────
|
||||||
|
const LOG_HEADERS = ['Fecha · Hora','Usuario','Email','Módulo','Acción','Entidad','Detalles','IP'];
|
||||||
|
function logExportRows(){
|
||||||
|
return (lastLogEntries||[]).map(e => [
|
||||||
|
fmtLogDate(e.fecha),
|
||||||
|
e.user_name || '',
|
||||||
|
e.user_email || '',
|
||||||
|
MOD_LABEL[e.modulo] || e.modulo,
|
||||||
|
ACCION_LABEL[e.accion] || e.accion,
|
||||||
|
(e.entity_label || '') + (e.entity_type ? ' ('+e.entity_type+')' : ''),
|
||||||
|
e.detalles || '',
|
||||||
|
e.ip_addr || '',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
function logExportExcel(){
|
||||||
|
DonnaExport.toExcel({
|
||||||
|
filename: 'Registro_Transacciones_AALN', sheetName: 'Auditoría',
|
||||||
|
headers: LOG_HEADERS, rows: logExportRows(),
|
||||||
|
columnWidths: [18,22,28,14,18,28,50,14],
|
||||||
|
onEmpty: () => toast('No hay transacciones para exportar — aplica filtros primero','error'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function logExportPDF(){
|
||||||
|
DonnaExport.toPDF({
|
||||||
|
filename: 'Registro_Transacciones_AALN',
|
||||||
|
title: 'Registro de Transacciones',
|
||||||
|
headers: LOG_HEADERS, rows: logExportRows(),
|
||||||
|
pdfCols: [0,1,3,4,5,6], // omite Email e IP para que quepa
|
||||||
|
onEmpty: () => toast('No hay transacciones para exportar — aplica filtros primero','error'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
document.getElementById('export-mount-log').innerHTML = DonnaExport.buttonHtml('export-wrap-log');
|
||||||
|
DonnaExport.attach('export-wrap-log', { onExcel: logExportExcel, onPDF: logExportPDF });
|
||||||
|
|
||||||
loadUsers();
|
loadUsers();
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
Reference in New Issue
Block a user