utn-cli 2.1.1 → 2.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +7 -0
- package/commands/commit.js +14 -1
- package/package.json +1 -1
- package/templates/backend/package.json +3 -3
- package/templates/backend/rutas/misc.js +51 -0
- package/templates/backend/servicios/Nucleo/Miscelaneas.js +176 -20
- package/templates/frontend/package.json +9 -8
- package/templates/frontend/public/Manual.md +1 -0
- package/templates/frontend/src/app/Componentes/Nucleo/gestion-actividad/gestion-actividad.component.ts +11 -3
- package/templates/frontend/src/app/Componentes/Nucleo/graficos/graficos.component.ts +2 -4
- package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.css +318 -0
- package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.html +43 -0
- package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.ts +74 -0
- package/templates/frontend/src/app/Componentes/Nucleo/mensajes/mensajes.component.ts +5 -3
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component.ts +12 -4
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.ts +12 -3
- package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.html +13 -10
- package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.ts +18 -6
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.html +2 -2
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.ts +9 -13
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.ts +7 -9
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-reporte/tarjeta-reporte.component.ts +1 -6
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.html +0 -2
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.ts +34 -57
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.html +3 -3
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.ts +46 -68
- package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.html +14 -3
- package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.ts +52 -14
- package/templates/frontend/src/app/app.routes.ts +4 -0
package/commands/commit.js
CHANGED
|
@@ -20,6 +20,8 @@ export async function runCommit() {
|
|
|
20
20
|
'¿Ya realizó el update de los módulos de UTN?',
|
|
21
21
|
'¿Ya revisó la visualización en móvil?',
|
|
22
22
|
'¿Ya hizo el stage de todos los cambios?',
|
|
23
|
+
'¿Ya hizo la actualización del manual?',
|
|
24
|
+
'¿Ya actualizó la función de monitoreo?',
|
|
23
25
|
'¿El archivo app.routes.ts hace uso de la estrategia de "Lazy Loading"?'
|
|
24
26
|
];
|
|
25
27
|
|
|
@@ -29,8 +31,19 @@ export async function runCommit() {
|
|
|
29
31
|
[preguntas[i], preguntas[j]] = [preguntas[j], preguntas[i]];
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
const PREGUNTA_MANUAL = '¿Ya hizo la actualización del manual?';
|
|
35
|
+
const PROMPT_MANUAL = `Necesito actualizar el manual Manual.md que se encuentra en la carpeta public, en donde se aborden todas las secciones nuevas que se contemplan en el frontend, el nivel de detalle del manual debe de ser alto, ya que no va a contener imágenes para guiar al usuario en la forma de ejecutar las diferentes acciones, debe ser compatible con las reglas de accesibilidad y para que sea leío programas lectores de pantalla, en el archivo app.routes.ts están las rutas disponibles que se deben incluir en el manual omitiendo las que estén comentadas.`;
|
|
36
|
+
|
|
32
37
|
for (const pregunta of preguntas) {
|
|
33
|
-
if (!await confirmarPaso(pregunta))
|
|
38
|
+
if (!await confirmarPaso(pregunta)) {
|
|
39
|
+
if (pregunta === PREGUNTA_MANUAL) {
|
|
40
|
+
console.log('\nPuede usar el siguiente prompt con Claude para actualizar el manual:\n');
|
|
41
|
+
console.log('─'.repeat(60));
|
|
42
|
+
console.log(PROMPT_MANUAL);
|
|
43
|
+
console.log('─'.repeat(60));
|
|
44
|
+
}
|
|
45
|
+
return closeReadLine();
|
|
46
|
+
}
|
|
34
47
|
}
|
|
35
48
|
|
|
36
49
|
try {
|
package/package.json
CHANGED
|
@@ -21,11 +21,11 @@
|
|
|
21
21
|
"bcryptjs": "^2.4.3",
|
|
22
22
|
"cors": "^2.8.6",
|
|
23
23
|
"express": "^4.22.1",
|
|
24
|
-
"helmet": "^7.2.0",
|
|
25
24
|
"google-auth-library": "^9.15.1",
|
|
25
|
+
"helmet": "^7.2.0",
|
|
26
26
|
"jsonwebtoken": "^9.0.3",
|
|
27
|
-
"mysql2": "^3.
|
|
28
|
-
"nodemailer": "^8.0.
|
|
27
|
+
"mysql2": "^3.22.3",
|
|
28
|
+
"nodemailer": "^8.0.7",
|
|
29
29
|
"pdf-lib": "^1.17.1",
|
|
30
30
|
"pdfkit": "^0.15.2"
|
|
31
31
|
}
|
|
@@ -486,6 +486,23 @@ Router.get('/validarToken', async (solicitud, respuesta, next) => {
|
|
|
486
486
|
return respuesta.json({ body: await Miscelaneo.validarTokenV2(solicitud.headers.authorization), error: undefined });
|
|
487
487
|
});
|
|
488
488
|
|
|
489
|
+
Router.get('/inicializar', async (solicitud, respuesta, next) => {
|
|
490
|
+
try {
|
|
491
|
+
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
492
|
+
try {
|
|
493
|
+
return respuesta.json({ body: await Miscelaneo.inicializar(solicitud), error: undefined });
|
|
494
|
+
} catch (error) {
|
|
495
|
+
const MensajeDeError = 'No fue posible inicializar el módulo';
|
|
496
|
+
console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
|
|
497
|
+
return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
|
|
501
|
+
} catch (error) {
|
|
502
|
+
next(error);
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
489
506
|
Router.get('/descargarArchivo/:ArchivoId', async (solicitud, respuesta, next) => {
|
|
490
507
|
try {
|
|
491
508
|
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
@@ -777,4 +794,38 @@ Router.get('/obtenerIdentificacion/:Identificador', async (solicitud, respuesta,
|
|
|
777
794
|
}
|
|
778
795
|
});
|
|
779
796
|
|
|
797
|
+
Router.get('/obtenerTarjetasDelContenedor', async (solicitud, respuesta, next) => {
|
|
798
|
+
try {
|
|
799
|
+
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
800
|
+
try {
|
|
801
|
+
return respuesta.json({ body: await Miscelaneo.obtenerTarjetasDelContenedor(solicitud.headers.authorization), error: undefined });
|
|
802
|
+
} catch (error) {
|
|
803
|
+
const MensajeDeError = 'No fue posible obtener las tarjetas del contenedor';
|
|
804
|
+
console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
|
|
805
|
+
return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
|
|
809
|
+
} catch (error) {
|
|
810
|
+
next(error);
|
|
811
|
+
}
|
|
812
|
+
});
|
|
813
|
+
|
|
814
|
+
Router.get('/obtenerTarjetas', async (solicitud, respuesta, next) => {
|
|
815
|
+
try {
|
|
816
|
+
if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
|
|
817
|
+
try {
|
|
818
|
+
return respuesta.json({ body: await Miscelaneo.obtenerTarjetas(), error: undefined });
|
|
819
|
+
} catch (error) {
|
|
820
|
+
const MensajeDeError = 'No fue posible obtener las tarjetas';
|
|
821
|
+
console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
|
|
822
|
+
return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
|
|
826
|
+
} catch (error) {
|
|
827
|
+
next(error);
|
|
828
|
+
}
|
|
829
|
+
});
|
|
830
|
+
|
|
780
831
|
module.exports = Router;
|
|
@@ -250,7 +250,8 @@ class Miscelaneo {
|
|
|
250
250
|
}
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
-
async RegistrarElServicio(Servicio) {
|
|
253
|
+
async RegistrarElServicio(Servicio, Tarjetas = null) {
|
|
254
|
+
const archivo = this._archivoFuenteActual;
|
|
254
255
|
try {
|
|
255
256
|
await ejecutarConsultaSIGU("REPLACE INTO `SIGU`.`SIGU_ModulosV2Secciones` (`Modulo`, `Descripcion`) VALUES (?, ?)"
|
|
256
257
|
, [this.NombreCanonicoDelModulo, Servicio]);
|
|
@@ -258,6 +259,70 @@ class Miscelaneo {
|
|
|
258
259
|
} catch (error) {
|
|
259
260
|
console.error(error.message);
|
|
260
261
|
}
|
|
262
|
+
|
|
263
|
+
if (!Tarjetas) return;
|
|
264
|
+
|
|
265
|
+
const listaTarjetas = Array.isArray(Tarjetas) ? Tarjetas : [Tarjetas];
|
|
266
|
+
|
|
267
|
+
for (const tarjeta of listaTarjetas) {
|
|
268
|
+
try {
|
|
269
|
+
const titulo = tarjeta['Título'];
|
|
270
|
+
const existentes = await ejecutarConsultaSIGU(
|
|
271
|
+
`SELECT CAST(JSON_VALUE(\`Datos\`, '$."Posición"') AS UNSIGNED) AS Posicion, JSON_VALUE(\`Datos\`, '$."Título"') AS Titulo, JSON_VALUE(\`Datos\`, '$."Descripción"') AS Descripcion FROM \`SIGU\`.\`ModulosV2Tarjetas\` WHERE \`Modulo\` = ? AND JSON_VALUE(\`Datos\`, '$."Título"') = ?`,
|
|
272
|
+
[this.NombreCanonicoDelModulo, titulo]
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
let datosFinales;
|
|
276
|
+
if (existentes.length > 0) {
|
|
277
|
+
datosFinales = { ...tarjeta, 'Posición': existentes[0].Posicion, 'Título': existentes[0].Titulo, 'Descripción': existentes[0].Descripcion, 'Archivo': archivo };
|
|
278
|
+
await ejecutarConsultaSIGU(
|
|
279
|
+
`UPDATE \`SIGU\`.\`ModulosV2Tarjetas\` SET \`Datos\` = ?, \`LastUser\` = 'Sistema' WHERE \`Modulo\` = ? AND JSON_VALUE(\`Datos\`, '$."Título"') = ?`,
|
|
280
|
+
[JSON.stringify(datosFinales), this.NombreCanonicoDelModulo, titulo]
|
|
281
|
+
);
|
|
282
|
+
} else {
|
|
283
|
+
let posicion = tarjeta['Posición'];
|
|
284
|
+
if (posicion === undefined) {
|
|
285
|
+
const [{ NuevaPosicion }] = await ejecutarConsultaSIGU(
|
|
286
|
+
`SELECT COALESCE(MAX(CAST(JSON_VALUE(\`Datos\`, '$."Posición"') AS UNSIGNED)), 0) + 10 AS NuevaPosicion FROM \`SIGU\`.\`ModulosV2Tarjetas\` WHERE \`Modulo\` = ?`,
|
|
287
|
+
[this.NombreCanonicoDelModulo]
|
|
288
|
+
);
|
|
289
|
+
posicion = NuevaPosicion;
|
|
290
|
+
}
|
|
291
|
+
datosFinales = { ...tarjeta, 'Posición': posicion, 'Archivo': archivo };
|
|
292
|
+
await ejecutarConsultaSIGU(
|
|
293
|
+
`INSERT INTO \`SIGU\`.\`ModulosV2Tarjetas\` (\`Modulo\`, \`Datos\`, \`LastUser\`) VALUES (?, ?, 'Sistema')`,
|
|
294
|
+
[this.NombreCanonicoDelModulo, JSON.stringify(datosFinales)]
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
console.log(`Tarjeta "${titulo}" registrada correctamente`);
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.error(`Error al registrar tarjeta "${tarjeta['Título']}":`, error.message);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
_archivoFuenteActual = null;
|
|
305
|
+
_archivoCache = new Map();
|
|
306
|
+
|
|
307
|
+
_buscarArchivoRecursivo(dir, nombre) {
|
|
308
|
+
if (this._archivoCache.has(nombre)) return this._archivoCache.get(nombre);
|
|
309
|
+
const fs = require('fs');
|
|
310
|
+
const path = require('path');
|
|
311
|
+
const buscar = (directorio) => {
|
|
312
|
+
for (const entrada of fs.readdirSync(directorio, { withFileTypes: true })) {
|
|
313
|
+
const ruta = path.join(directorio, entrada.name);
|
|
314
|
+
if (entrada.isDirectory()) {
|
|
315
|
+
const resultado = buscar(ruta);
|
|
316
|
+
if (resultado) return resultado;
|
|
317
|
+
} else if (entrada.name === nombre) {
|
|
318
|
+
return ruta;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return null;
|
|
322
|
+
};
|
|
323
|
+
const resultado = buscar(dir);
|
|
324
|
+
this._archivoCache.set(nombre, resultado);
|
|
325
|
+
return resultado;
|
|
261
326
|
}
|
|
262
327
|
|
|
263
328
|
async DatosParaReporteCSV() {
|
|
@@ -381,7 +446,7 @@ class Miscelaneo {
|
|
|
381
446
|
// }
|
|
382
447
|
let LastUser = '';
|
|
383
448
|
if (Resultado) {
|
|
384
|
-
LastUser = Resultado.
|
|
449
|
+
LastUser = Resultado.Identificador;
|
|
385
450
|
}
|
|
386
451
|
LastUser = LastUser + '@' + Solicitud.headers.host.trim()
|
|
387
452
|
+ '@' + Solicitud.headers["user-agent"].trim()
|
|
@@ -867,7 +932,7 @@ class Miscelaneo {
|
|
|
867
932
|
return;
|
|
868
933
|
}
|
|
869
934
|
const Permisos = await ejecutarConsultaSIGU("SELECT `PermisoId` FROM `SIGU`.`SIGU_PermisosPersonasV2` WHERE `Identificador` = ?"
|
|
870
|
-
, [Resultado.
|
|
935
|
+
, [Resultado.Identificador]);
|
|
871
936
|
const Modulos = await ejecutarConsultaSIGU("SELECT `a`.`Nombre`, `a`.`Padre`, `a`.`Descripcion`, `a`.`Detalle`,\
|
|
872
937
|
`a`.`Tipo`, IF(`a`.`Icono` <> '', CONCAT('https://storage.sigu.utn.ac.cr/images/cards/', `a`.`Icono`), '') AS `Icono`, `a`.`Color`, `a`.`Correo`,\
|
|
873
938
|
`a`.`Version`, `a`.`FechaDePublicacion`, `a`.`AcuerdoDeNivelDeServicio`, `a`.`DiccionarioDeDatos`, `a`.`Repositorios`,\
|
|
@@ -902,7 +967,7 @@ class Miscelaneo {
|
|
|
902
967
|
return;
|
|
903
968
|
}
|
|
904
969
|
return await ejecutarConsultaSIGU("UPDATE `SIGU`.`SIGU_NotificacionesV2` SET `Estado` = 'Leída' WHERE `Identificador` = ? AND `FechaYHoraDeCreacion` = ?"
|
|
905
|
-
, [Resultado.
|
|
970
|
+
, [Resultado.Identificador, FechaYHoraDeCreacion]);
|
|
906
971
|
}
|
|
907
972
|
|
|
908
973
|
async obtenerNotificaciones(Token) {
|
|
@@ -916,7 +981,7 @@ class Miscelaneo {
|
|
|
916
981
|
console.log(error);
|
|
917
982
|
return;
|
|
918
983
|
}
|
|
919
|
-
return await ejecutarConsultaSIGU("SELECT `Notificacion` AS `valor`, CONCAT(`FechaYHoraDeCreacion`) AS `llave`, false AS `tachado` FROM `SIGU`.`SIGU_NotificacionesV2` WHERE `Identificador` = ? AND `Estado` = 'Sin leer' ORDER BY `FechaYHoraDeCreacion` DESC LIMIT 10", [Resultado.
|
|
984
|
+
return await ejecutarConsultaSIGU("SELECT `Notificacion` AS `valor`, CONCAT(`FechaYHoraDeCreacion`) AS `llave`, false AS `tachado` FROM `SIGU`.`SIGU_NotificacionesV2` WHERE `Identificador` = ? AND `Estado` = 'Sin leer' ORDER BY `FechaYHoraDeCreacion` DESC LIMIT 10", [Resultado.Identificador]);
|
|
920
985
|
}
|
|
921
986
|
|
|
922
987
|
async reporteDeIncidencia(Solicitud, Datos) {
|
|
@@ -1373,7 +1438,7 @@ class Miscelaneo {
|
|
|
1373
1438
|
, `a`.`Nombre`, `a`.`PrimerApellido`, `a`.`SegundoApellido`\
|
|
1374
1439
|
, (SELECT GROUP_CONCAT(`b`.`CuentaIBAN`) FROM `SIGU`.`SIGU_CuentasBancariasPersonas` `b` WHERE `b`.`Estado` = TRUE AND `a`.`Identificador` = `b`.`Identificador`) AS `CuentasIBAN`\
|
|
1375
1440
|
FROM `SIGU`.`SIGU_Personas` `a` WHERE `a`.`Identificador` = ?"
|
|
1376
|
-
, [Resultado.
|
|
1441
|
+
, [Resultado.Identificador]);
|
|
1377
1442
|
}
|
|
1378
1443
|
|
|
1379
1444
|
async obtenerDatosDeLaPersonaPorIdentificacion(Identificacion) {
|
|
@@ -1465,7 +1530,7 @@ class Miscelaneo {
|
|
|
1465
1530
|
Resultado.token = token;
|
|
1466
1531
|
const DatosDeLaPersona = await ejecutarConsultaSIGU("SELECT `Identificacion`, `Nombre`, `PrimerApellido`,\
|
|
1467
1532
|
`SegundoApellido`, CONCAT(`FechaNacimiento`) AS `FechaDeNacimiento`\
|
|
1468
|
-
FROM `SIGU`.`SIGU_Personas` WHERE `Identificador` = ?", [Resultado.
|
|
1533
|
+
FROM `SIGU`.`SIGU_Personas` WHERE `Identificador` = ?", [Resultado.Identificador]);
|
|
1469
1534
|
Resultado.Identificacion = DatosDeLaPersona[0]['Identificacion'];
|
|
1470
1535
|
Resultado.Nombre = DatosDeLaPersona[0]['Nombre'];
|
|
1471
1536
|
Resultado.PrimerApellido = DatosDeLaPersona[0]['PrimerApellido'];
|
|
@@ -1490,16 +1555,16 @@ class Miscelaneo {
|
|
|
1490
1555
|
try {
|
|
1491
1556
|
// Validación del token en la sesión
|
|
1492
1557
|
const tokenAlmacenado = await ejecutarConsultaSIGU("SELECT COUNT(*) AS `Total` FROM `SIGU`.`SIGU_Sesiones`\
|
|
1493
|
-
WHERE `Identificador` = ? AND `Token` = ?", [Resultado.
|
|
1558
|
+
WHERE `Identificador` = ? AND `Token` = ?", [Resultado.Identificador, Resultado.token]);
|
|
1494
1559
|
if (tokenAlmacenado[0]['Total'] >= 1) {
|
|
1495
1560
|
// Validación de permisos
|
|
1496
1561
|
const rolPermisoId = await this.permisoIdV2();
|
|
1497
1562
|
const permisos = await ejecutarConsultaSIGU("SELECT COUNT(*) AS `Total` FROM `SIGU`.`SIGU_PermisosPersonasV2`\
|
|
1498
|
-
WHERE `PermisoId` = ? AND `Identificador` = ?", [rolPermisoId, Resultado.
|
|
1563
|
+
WHERE `PermisoId` = ? AND `Identificador` = ?", [rolPermisoId, Resultado.Identificador]);
|
|
1499
1564
|
if (permisos[0]['Total'] === 1) {
|
|
1500
1565
|
if (permisoExtraId) {
|
|
1501
1566
|
const ValidacionPermisoExtra = await ejecutarConsultaSIGU("SELECT COUNT(*) AS `Total` FROM `SIGU`.`SIGU_PermisosExtraPersonasV2`\
|
|
1502
|
-
WHERE `PermisoExtraId` = ? AND `Identificador` = ?", [permisoExtraId, Resultado.
|
|
1567
|
+
WHERE `PermisoExtraId` = ? AND `Identificador` = ?", [permisoExtraId, Resultado.Identificador]);
|
|
1503
1568
|
if (ValidacionPermisoExtra[0]['Total'] === 1) {
|
|
1504
1569
|
return true;
|
|
1505
1570
|
}
|
|
@@ -1533,13 +1598,13 @@ class Miscelaneo {
|
|
|
1533
1598
|
// try {
|
|
1534
1599
|
// // Validación del token en la sesión
|
|
1535
1600
|
// const tokenAlmacenado = await ejecutarConsultaSIGU("SELECT COUNT(*) AS `Total` FROM `SIGU`.`SIGU_Sesiones`\
|
|
1536
|
-
// WHERE `Identificador` = ? AND `Token` = ?", [Resultado.
|
|
1601
|
+
// WHERE `Identificador` = ? AND `Token` = ?", [Resultado.Identificador, Resultado.token]);
|
|
1537
1602
|
// if (tokenAlmacenado[0]['Total'] >= 1) { // Se compara con >= para preveer multisesiones
|
|
1538
1603
|
// // Validación de permisos
|
|
1539
1604
|
// const perfilGeneralId = await this.perfilGeneralId();
|
|
1540
1605
|
// const rolPermisoId = await this.rolPermisoIdDelModulo();
|
|
1541
1606
|
// const permisos = await ejecutarConsultaSIGU("SELECT COUNT(*) AS `Total` FROM `SIGU`.`SIGU_RolesPersonas`\
|
|
1542
|
-
// WHERE `RolPermisoId` = ? AND `Identificador` = ? AND `PerfilGeneralId` = ?", [rolPermisoId, Resultado.
|
|
1607
|
+
// WHERE `RolPermisoId` = ? AND `Identificador` = ? AND `PerfilGeneralId` = ?", [rolPermisoId, Resultado.Identificador, perfilGeneralId]);
|
|
1543
1608
|
// if (permisos[0]['Total'] === 1) {
|
|
1544
1609
|
// return true;
|
|
1545
1610
|
// } else {
|
|
@@ -1863,8 +1928,8 @@ class Miscelaneo {
|
|
|
1863
1928
|
informacionDelArchivo.Etiquetas = Etiquetas;
|
|
1864
1929
|
await ejecutarConsulta("INSERT INTO `" + this.NombreDelRepositorioDeLaBaseDeDatos.slice(0, -3) + "`.`Archivos`\
|
|
1865
1930
|
VALUES (?, ?, ?, ?, ?, NOW(4), ?)"
|
|
1866
|
-
, [Respuesta.insertId, Resultado.
|
|
1867
|
-
, Etiquetas, Resultado.
|
|
1931
|
+
, [Respuesta.insertId, Resultado.Identificador, informacionDelArchivo.rutaDeArchivo, informacionDelArchivo.nombreDeArchivo
|
|
1932
|
+
, Etiquetas, Resultado.Identificador]);
|
|
1868
1933
|
return informacionDelArchivo;
|
|
1869
1934
|
}
|
|
1870
1935
|
return;
|
|
@@ -2148,7 +2213,7 @@ class Miscelaneo {
|
|
|
2148
2213
|
let Token = undefined;
|
|
2149
2214
|
if (this.NombresParalocalhost().includes(process.env.HOST) && (typeof process.env.DB_HOST_SIGU === "undefined")) {
|
|
2150
2215
|
const jwt = require('jsonwebtoken');
|
|
2151
|
-
Token = await jwt.sign({ uid: Identificador }, await this.palabraSecretaParaTokens(), { expiresIn: '10h' });
|
|
2216
|
+
Token = await jwt.sign({ uid: Identificador, Identificador }, await this.palabraSecretaParaTokens(), { expiresIn: '10h' });
|
|
2152
2217
|
await ejecutarConsultaSIGU("DELETE FROM `SIGU`.`SIGU_Sesiones` WHERE `Identificador` = ?", [Identificador]);
|
|
2153
2218
|
await ejecutarConsultaSIGU("INSERT INTO `SIGU`.`SIGU_Sesiones` VALUES (?, 'Backend', ?, NOW(4), USER())", [Identificador, Token]);
|
|
2154
2219
|
}
|
|
@@ -2198,7 +2263,7 @@ class Miscelaneo {
|
|
|
2198
2263
|
return await ejecutarConsulta("SELECT `ArchivoId`, `Identificador`, `Ruta`, CONCAT(`Nombre`, ' (', DATE_FORMAT(`LastUpdate`, '%Y-%M-%d %H:%i'), ')') AS `Nombre`, `Etiquetas`\
|
|
2199
2264
|
FROM `" + this.NombreDelRepositorioDeLaBaseDeDatos.slice(0, -3) + "`.`Archivos`\
|
|
2200
2265
|
WHERE `Identificador` = ? AND `Etiquetas` = ?"
|
|
2201
|
-
, [Resultado.
|
|
2266
|
+
, [Resultado.Identificador, Etiquetas]);
|
|
2202
2267
|
}
|
|
2203
2268
|
if (ultimaParte === 'Servicio') {
|
|
2204
2269
|
return await ejecutarConsulta("SELECT `ArchivoId`, `Identificador`, `Ruta`, CONCAT(`Nombre`, ' (', DATE_FORMAT(`LastUpdate`, '%Y-%M-%d %H:%i'), ')') AS `Nombre`, `Etiquetas`\
|
|
@@ -2258,14 +2323,14 @@ class Miscelaneo {
|
|
|
2258
2323
|
const Archivo = await ejecutarConsulta("SELECT `Ruta`\
|
|
2259
2324
|
FROM `" + this.NombreDelRepositorioDeLaBaseDeDatos.slice(0, -3) + "`.`Archivos`\
|
|
2260
2325
|
WHERE `Identificador` = ? AND `ArchivoId` = ?"
|
|
2261
|
-
, [Resultado.
|
|
2326
|
+
, [Resultado.Identificador, Datos.ArchivoId]);
|
|
2262
2327
|
fs.unlinkSync(Archivo[0]['Ruta']);
|
|
2263
2328
|
await ejecutarConsulta("DELETE FROM `" + this.NombreDelRepositorioDeLaBaseDeDatos.slice(0, -3) + "`.`Archivos`\
|
|
2264
2329
|
WHERE `Identificador` = ? AND `ArchivoId` = ?"
|
|
2265
|
-
, [Resultado.
|
|
2330
|
+
, [Resultado.Identificador, Datos.ArchivoId]);
|
|
2266
2331
|
await ejecutarConsultaSIGU("DELETE FROM `SIGU`.`SIGU_Adjuntos`\
|
|
2267
2332
|
WHERE `Identificador` = ? AND `AdjuntosId` = ?"
|
|
2268
|
-
, [Resultado.
|
|
2333
|
+
, [Resultado.Identificador, Datos.ArchivoId]);
|
|
2269
2334
|
return;
|
|
2270
2335
|
}
|
|
2271
2336
|
|
|
@@ -2288,7 +2353,7 @@ class Miscelaneo {
|
|
|
2288
2353
|
RutaDelArchivo = await ejecutarConsulta("SELECT `Ruta`\
|
|
2289
2354
|
FROM `" + this.NombreDelRepositorioDeLaBaseDeDatos.slice(0, -3) + "`.`Archivos`\
|
|
2290
2355
|
WHERE `Identificador` = ? AND `ArchivoId` = ?"
|
|
2291
|
-
, [Resultado.
|
|
2356
|
+
, [Resultado.Identificador, ArchivoId]);
|
|
2292
2357
|
}
|
|
2293
2358
|
if (ultimaParte === 'Servicio') {
|
|
2294
2359
|
RutaDelArchivo = await ejecutarConsulta("SELECT `Ruta`\
|
|
@@ -2388,8 +2453,14 @@ class Miscelaneo {
|
|
|
2388
2453
|
}
|
|
2389
2454
|
|
|
2390
2455
|
async ejecucionDiferida(callback) {
|
|
2456
|
+
const stackLines = new Error().stack.split('\n');
|
|
2457
|
+
const lineaCaller = stackLines[2] ?? '';
|
|
2458
|
+
const match = lineaCaller.match(/\((.+?):\d+:\d+\)/) ?? lineaCaller.match(/at (.+?):\d+:\d+/);
|
|
2459
|
+
const archivo = match ? require('path').basename(match[1]) : null;
|
|
2460
|
+
|
|
2391
2461
|
while (true) {
|
|
2392
2462
|
try {
|
|
2463
|
+
this._archivoFuenteActual = archivo;
|
|
2393
2464
|
await callback();
|
|
2394
2465
|
break;
|
|
2395
2466
|
} catch (error) {
|
|
@@ -2398,6 +2469,7 @@ class Miscelaneo {
|
|
|
2398
2469
|
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
2399
2470
|
}
|
|
2400
2471
|
}
|
|
2472
|
+
this._archivoFuenteActual = null;
|
|
2401
2473
|
}
|
|
2402
2474
|
|
|
2403
2475
|
// async obtenerEnlaceDelModuloPadre() {
|
|
@@ -2414,6 +2486,90 @@ class Miscelaneo {
|
|
|
2414
2486
|
async obtenerEnlaceDePerfil() {
|
|
2415
2487
|
return this.EnlaceDePerfil;
|
|
2416
2488
|
}
|
|
2489
|
+
|
|
2490
|
+
async obtenerTarjetas() {
|
|
2491
|
+
const resultados = await ejecutarConsultaSIGU(
|
|
2492
|
+
"SELECT `Datos` FROM `SIGU`.`ModulosV2Tarjetas` WHERE `Modulo` = ? AND JSON_EXTRACT(`Datos`, '$.Activa') = TRUE",
|
|
2493
|
+
[this.NombreCanonicoDelModulo]
|
|
2494
|
+
);
|
|
2495
|
+
return resultados.map(fila => typeof fila.Datos === 'string' ? JSON.parse(fila.Datos) : fila.Datos);
|
|
2496
|
+
}
|
|
2497
|
+
|
|
2498
|
+
async inicializar(solicitud) {
|
|
2499
|
+
const ConsentimientoInformado = require('./ConsentimientoInformado.js');
|
|
2500
|
+
const LastUser = await this.generarLastUser(solicitud);
|
|
2501
|
+
const [configuracion, detalleDelModulo, notificaciones, mensajesModulares, consentimiento] = await Promise.all([
|
|
2502
|
+
this.configurarFrontend(solicitud.headers.authorization),
|
|
2503
|
+
this.obtenerDetalleDelModulo(),
|
|
2504
|
+
this.obtenerNotificaciones(solicitud.headers.authorization),
|
|
2505
|
+
this.obtenerMensajesModulares(),
|
|
2506
|
+
ConsentimientoInformado.ConsentimientoInformado({ LastUser })
|
|
2507
|
+
]);
|
|
2508
|
+
return {
|
|
2509
|
+
TienePermiso: true,
|
|
2510
|
+
...configuracion,
|
|
2511
|
+
EnlaceDelManual: detalleDelModulo[0]?.EnlaceDelManual ?? '-',
|
|
2512
|
+
EnlaceDelVideo: detalleDelModulo[0]?.EnlaceDelVideo ?? '-',
|
|
2513
|
+
Notificaciones: notificaciones,
|
|
2514
|
+
MensajesModulares: mensajesModulares,
|
|
2515
|
+
Consentimiento: consentimiento
|
|
2516
|
+
};
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
async obtenerTarjetasDelContenedor(token) {
|
|
2520
|
+
const ConfiguracionDeTarjetas = require('./ConfiguracionDeTarjetas.js');
|
|
2521
|
+
const Personas = require('../Personas.js');
|
|
2522
|
+
const path = require('path');
|
|
2523
|
+
const serviciosDir = path.join(__dirname, '..');
|
|
2524
|
+
|
|
2525
|
+
const usuario = await this.obtenerDatosDelUsuario(token);
|
|
2526
|
+
const [tarjetas, configuracion, tienePermisoExtra] = await Promise.all([
|
|
2527
|
+
this.obtenerTarjetas(),
|
|
2528
|
+
ConfiguracionDeTarjetas.obtener({ Identificador: usuario.uid }),
|
|
2529
|
+
Personas.PermisoExtra(token)
|
|
2530
|
+
]);
|
|
2531
|
+
|
|
2532
|
+
const cantidadesEntradas = await Promise.all(
|
|
2533
|
+
tarjetas
|
|
2534
|
+
.filter(t => t.Archivo)
|
|
2535
|
+
.map(async t => {
|
|
2536
|
+
try {
|
|
2537
|
+
const rutaArchivo = this._buscarArchivoRecursivo(serviciosDir, t.Archivo);
|
|
2538
|
+
if (!rutaArchivo) return null;
|
|
2539
|
+
const servicio = require(rutaArchivo);
|
|
2540
|
+
if (typeof servicio.Cantidades !== 'function') return null;
|
|
2541
|
+
const cantidades = await servicio.Cantidades(token);
|
|
2542
|
+
return [t['Título'], cantidades];
|
|
2543
|
+
} catch { return null; }
|
|
2544
|
+
})
|
|
2545
|
+
);
|
|
2546
|
+
|
|
2547
|
+
const cantidadesPorTitulo = Object.fromEntries(cantidadesEntradas.filter(Boolean));
|
|
2548
|
+
|
|
2549
|
+
const titulosAExcluir = new Set(
|
|
2550
|
+
Object.entries(cantidadesPorTitulo)
|
|
2551
|
+
.filter(([, c]) => !(c.cantidadMaxima > 0))
|
|
2552
|
+
.map(([titulo]) => titulo)
|
|
2553
|
+
);
|
|
2554
|
+
|
|
2555
|
+
return tarjetas
|
|
2556
|
+
.filter(t => !t.RequierePermisoExtra || tienePermisoExtra)
|
|
2557
|
+
.filter(t => !titulosAExcluir.has(t['Título']))
|
|
2558
|
+
.map(t => {
|
|
2559
|
+
const config = configuracion.find(c => c.Titulo === t['Título']);
|
|
2560
|
+
if (config) {
|
|
2561
|
+
t['Posición'] = config.Posicion;
|
|
2562
|
+
if (config.ColorDeBorde) t.ColorDeBorde = config.ColorDeBorde;
|
|
2563
|
+
}
|
|
2564
|
+
const cantidades = cantidadesPorTitulo[t['Título']];
|
|
2565
|
+
if (cantidades) {
|
|
2566
|
+
t['Cantidad'] = cantidades.cantidad;
|
|
2567
|
+
t['CantidadMaxima'] = cantidades.cantidadMaxima;
|
|
2568
|
+
}
|
|
2569
|
+
return t;
|
|
2570
|
+
})
|
|
2571
|
+
.sort((a, b) => a['Posición'] - b['Posición']);
|
|
2572
|
+
}
|
|
2417
2573
|
}
|
|
2418
2574
|
|
|
2419
2575
|
module.exports = new Miscelaneo();
|
|
@@ -16,22 +16,23 @@
|
|
|
16
16
|
"@angular/common": "^21.0.0",
|
|
17
17
|
"@angular/compiler": "^21.0.0",
|
|
18
18
|
"@angular/core": "^21.0.0",
|
|
19
|
-
"@angular/forms": "^21.2.
|
|
20
|
-
"@angular/material": "^21.2.
|
|
19
|
+
"@angular/forms": "^21.2.11",
|
|
20
|
+
"@angular/material": "^21.2.9",
|
|
21
21
|
"@angular/platform-browser": "^21.0.0",
|
|
22
|
-
"@angular/platform-browser-dynamic": "^21.2.
|
|
23
|
-
"@angular/router": "^21.2.
|
|
22
|
+
"@angular/platform-browser-dynamic": "^21.2.11",
|
|
23
|
+
"@angular/router": "^21.2.11",
|
|
24
24
|
"chart.js": "^4.5.1",
|
|
25
|
+
"marked": "^18.0.3",
|
|
25
26
|
"ng2-charts": "^8.0.0",
|
|
26
27
|
"rxjs": "~7.8.0",
|
|
27
28
|
"tslib": "^2.3.0",
|
|
28
29
|
"zone.js": "^0.16.1"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
|
-
"@angular-devkit/build-angular": "^21.2.
|
|
32
|
+
"@angular-devkit/build-angular": "^21.2.9",
|
|
32
33
|
"@angular/build": "^21.0.3",
|
|
33
|
-
"@angular/cli": "^21.2.
|
|
34
|
-
"@angular/compiler-cli": "^21.2.
|
|
34
|
+
"@angular/cli": "^21.2.9",
|
|
35
|
+
"@angular/compiler-cli": "^21.2.11",
|
|
35
36
|
"@types/jasmine": "^5.1.15",
|
|
36
37
|
"jasmine-core": "^5.13.0",
|
|
37
38
|
"jsdom": "^27.4.0",
|
|
@@ -41,6 +42,6 @@
|
|
|
41
42
|
"karma-jasmine": "^5.1.0",
|
|
42
43
|
"karma-jasmine-html-reporter": "^2.2.0",
|
|
43
44
|
"typescript": "~5.9.2",
|
|
44
|
-
"vitest": "^4.1.
|
|
45
|
+
"vitest": "^4.1.5"
|
|
45
46
|
}
|
|
46
47
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Manual de Usuario — Módulo de Mantenimientos V2
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, OnInit } from '@angular/core';
|
|
1
|
+
import { Component, OnDestroy, OnInit } from '@angular/core';
|
|
2
2
|
import { CommonModule } from '@angular/common';
|
|
3
3
|
import { HttpClient } from '@angular/common/http';
|
|
4
4
|
import { MatIconModule } from '@angular/material/icon';
|
|
@@ -8,6 +8,8 @@ import { MatButtonModule } from '@angular/material/button';
|
|
|
8
8
|
import { MatDialog } from '@angular/material/dialog';
|
|
9
9
|
import { MensajeConfirmacionComponent } from '../mensaje-confirmacion/mensaje-confirmacion';
|
|
10
10
|
import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
11
|
+
import { Subject } from 'rxjs';
|
|
12
|
+
import { takeUntil } from 'rxjs/operators';
|
|
11
13
|
|
|
12
14
|
@Component({
|
|
13
15
|
selector: 'app-gestion-actividad',
|
|
@@ -16,10 +18,11 @@ import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
|
16
18
|
templateUrl: './gestion-actividad.component.html',
|
|
17
19
|
styleUrls: ['./gestion-actividad.component.css']
|
|
18
20
|
})
|
|
19
|
-
export class GestionActividadComponent implements OnInit {
|
|
21
|
+
export class GestionActividadComponent implements OnInit, OnDestroy {
|
|
20
22
|
public actividad: any[] = [];
|
|
21
23
|
public cargando = false;
|
|
22
24
|
private tokenActual = '';
|
|
25
|
+
private _destroy$ = new Subject<void>();
|
|
23
26
|
|
|
24
27
|
constructor(private http: HttpClient, private datosGlobalesService: DatosGlobalesService, private dialog: MatDialog) { }
|
|
25
28
|
|
|
@@ -30,7 +33,7 @@ export class GestionActividadComponent implements OnInit {
|
|
|
30
33
|
|
|
31
34
|
obtenerActividad(): void {
|
|
32
35
|
this.cargando = true;
|
|
33
|
-
this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/ListarActividades`).subscribe({
|
|
36
|
+
this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/ListarActividades`).pipe(takeUntil(this._destroy$)).subscribe({
|
|
34
37
|
next: (datos: any) => {
|
|
35
38
|
this.actividad = datos.body;
|
|
36
39
|
},
|
|
@@ -69,6 +72,11 @@ export class GestionActividadComponent implements OnInit {
|
|
|
69
72
|
},
|
|
70
73
|
});
|
|
71
74
|
}
|
|
75
|
+
ngOnDestroy(): void {
|
|
76
|
+
this._destroy$.next();
|
|
77
|
+
this._destroy$.complete();
|
|
78
|
+
}
|
|
79
|
+
|
|
72
80
|
esSesionActual(token: string): boolean {
|
|
73
81
|
|
|
74
82
|
return token === this.tokenActual;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
1
|
+
import { Component, Input, OnChanges } from '@angular/core';
|
|
2
2
|
import { BaseChartDirective } from 'ng2-charts';
|
|
3
3
|
import { ChartConfiguration, ChartData, Plugin } from 'chart.js';
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ import { ChartConfiguration, ChartData, Plugin } from 'chart.js';
|
|
|
9
9
|
templateUrl: './graficos.component.html',
|
|
10
10
|
styleUrl: './graficos.component.css'
|
|
11
11
|
})
|
|
12
|
-
export class GraficosComponent {
|
|
12
|
+
export class GraficosComponent implements OnChanges {
|
|
13
13
|
|
|
14
14
|
@Input() public TipoDeGrafico: string = '';
|
|
15
15
|
@Input() public Datos: any[] = [];
|
|
@@ -162,8 +162,6 @@ export class GraficosComponent {
|
|
|
162
162
|
}
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
-
constructor() { }
|
|
166
|
-
|
|
167
165
|
ngOnChanges() {
|
|
168
166
|
|
|
169
167
|
// ✅ Si no hay datos, limpia todo para evitar gráficos raros
|