feat(reportes): add functionality for downloading client and user reports (#12)
Some checks failed
Aduanasoft/PANEL_BASES_ANEXO24/pipeline/head There was a failure building this commit
Some checks failed
Aduanasoft/PANEL_BASES_ANEXO24/pipeline/head There was a failure building this commit
- Introduced new functions to download Excel reports for clients and users. - Added UI elements for administrative users to trigger report downloads. - Implemented error handling for report generation failures. - Enhanced the existing portal user listing with node data for better reporting. This update improves the reporting capabilities within the application, allowing for easier access to client and user data. Reviewed-on: #12 Co-authored-by: AlexeerCT <acazares@aduanasoft.com.mx> Co-committed-by: AlexeerCT <acazares@aduanasoft.com.mx>
This commit is contained in:
@@ -190,6 +190,32 @@ export async function listPortalUsers(): Promise<any[]> {
|
||||
return r.rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* Usuarios del portal (cliente/autoridad) con los datos de su nodo, para el
|
||||
* reporte de asesores. NO incluye contraseñas: `portal_users.password_hash`
|
||||
* es un hash bcrypt irreversible y nunca se expone.
|
||||
*/
|
||||
export async function listPortalUsersWithNode(): Promise<any[]> {
|
||||
const sql = `
|
||||
SELECT
|
||||
pu.id AS "ID",
|
||||
pu.is_authority_client AS "ClienteAutoridad",
|
||||
pu.full_name AS "Nombre",
|
||||
pu.username AS "Usuario",
|
||||
pu.bd_shelter AS "BD_Shelter",
|
||||
dn.node_subnode_key AS "NodoSubNodo",
|
||||
dn.legal_name AS "Cliente",
|
||||
dn.rfc AS "RFC",
|
||||
dn.database_name AS "BDName",
|
||||
dn.is_active AS "NodoActivo"
|
||||
FROM ${qUsers()} pu
|
||||
LEFT JOIN ${qNodes()} dn ON pu.database_node_id = dn.id
|
||||
ORDER BY dn.node_subnode_key, pu.is_authority_client, pu.username
|
||||
`;
|
||||
const r = await pgPool.query(sql);
|
||||
return r.rows;
|
||||
}
|
||||
|
||||
export async function lookupNodeByNodoOrBdName(nodoName: string): Promise<any | null> {
|
||||
const sql = `
|
||||
SELECT
|
||||
|
||||
50
src/lib/server/report-excel.ts
Normal file
50
src/lib/server/report-excel.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Utilidades compartidas para los reportes en Excel del panel:
|
||||
* - Guard de sesión admin (los reportes administrativos son confidenciales).
|
||||
* - Estilos de encabezado y filas (cebra) reutilizados por todas las hojas.
|
||||
*/
|
||||
import type { Cookies } from '@sveltejs/kit';
|
||||
import type ExcelJS from 'exceljs';
|
||||
import { verifyToken } from './auth';
|
||||
import { getUserById } from './users';
|
||||
import type { Usuario } from './auth';
|
||||
|
||||
/**
|
||||
* Valida la cookie de sesión y exige rol admin. Devuelve el usuario o `null`
|
||||
* (sin lanzar) para que el endpoint responda 401/403 con JSON, no un redirect.
|
||||
*/
|
||||
export async function getAdminFromCookies(cookies: Cookies): Promise<Usuario | null> {
|
||||
const token = cookies.get('session_token');
|
||||
if (!token) return null;
|
||||
const session = verifyToken(token);
|
||||
if (!session) return null;
|
||||
const user = await getUserById(session.userId);
|
||||
if (!user || !user.activo || !user.es_admin) return null;
|
||||
return user;
|
||||
}
|
||||
|
||||
/** Aplica el estilo de encabezado oscuro a la primera fila de una hoja. */
|
||||
export function styleHeader(row: ExcelJS.Row): void {
|
||||
row.eachCell((cell) => {
|
||||
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF1E293B' } };
|
||||
cell.font = { bold: true, color: { argb: 'FFFFFFFF' }, size: 11 };
|
||||
cell.alignment = { vertical: 'middle', horizontal: 'center' };
|
||||
cell.border = { bottom: { style: 'thin', color: { argb: 'FF94A3B8' } } };
|
||||
});
|
||||
row.height = 20;
|
||||
}
|
||||
|
||||
/** Aplica el cebrado (filas alternas) y borde inferior tenue a una fila de datos. */
|
||||
export function styleDataRow(row: ExcelJS.Row, index: number): void {
|
||||
const fill: ExcelJS.FillPattern = {
|
||||
type: 'pattern',
|
||||
pattern: 'solid',
|
||||
fgColor: { argb: index % 2 === 0 ? 'FFF8FAFC' : 'FFFFFFFF' }
|
||||
};
|
||||
row.eachCell((cell) => {
|
||||
cell.fill = fill;
|
||||
cell.alignment = { vertical: 'middle' };
|
||||
cell.border = { bottom: { style: 'hair', color: { argb: 'FFE2E8F0' } } };
|
||||
});
|
||||
row.height = 16;
|
||||
}
|
||||
Reference in New Issue
Block a user