utn-cli 2.1.51 → 2.1.53

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.
Files changed (38) hide show
  1. package/package.json +1 -1
  2. package/templates/backend/rutas/misc.js +240 -0
  3. package/templates/backend/servicios/Nucleo/Miscelaneas.js +111 -16
  4. package/templates/frontend/public/conozcanos1.jpg +0 -0
  5. package/templates/frontend/public/preguntasF-1.png +0 -0
  6. package/templates/frontend/public/preguntasF-2.png +0 -0
  7. package/templates/frontend/public/preguntasF-3.png +0 -0
  8. package/templates/frontend/public/preguntasF-4.png +0 -0
  9. package/templates/frontend/public/preguntasF-5.png +0 -0
  10. package/templates/frontend/public/sabias-1.png +0 -0
  11. package/templates/frontend/public/sabias-10.png +0 -0
  12. package/templates/frontend/public/sabias-11.png +0 -0
  13. package/templates/frontend/public/sabias-2.png +0 -0
  14. package/templates/frontend/public/sabias-3.png +0 -0
  15. package/templates/frontend/public/sabias-4.png +0 -0
  16. package/templates/frontend/public/sabias-5.png +0 -0
  17. package/templates/frontend/public/sabias-6.png +0 -0
  18. package/templates/frontend/public/sabias-7.png +0 -0
  19. package/templates/frontend/public/sabias-8.png +0 -0
  20. package/templates/frontend/public/sabias-9.png +0 -0
  21. package/templates/frontend/src/app/Componentes/Nucleo/tabla/tabla.component.html +5 -4
  22. package/templates/frontend/src/app/Componentes/Nucleo/tabla/tabla.component.ts +13 -2
  23. package/templates/frontend/src/app/Paginas/Nucleo/acercade/acercade.component.css +65 -0
  24. package/templates/frontend/src/app/Paginas/Nucleo/acercade/acercade.component.html +49 -0
  25. package/templates/frontend/src/app/Paginas/Nucleo/acercade/acercade.component.ts +20 -0
  26. package/templates/frontend/src/app/Paginas/Nucleo/conozcanos/conozcanos.component.css +608 -0
  27. package/templates/frontend/src/app/Paginas/Nucleo/conozcanos/conozcanos.component.html +109 -0
  28. package/templates/frontend/src/app/Paginas/Nucleo/conozcanos/conozcanos.component.ts +63 -0
  29. package/templates/frontend/src/app/Paginas/Nucleo/politicas/politicas.component.css +158 -0
  30. package/templates/frontend/src/app/Paginas/Nucleo/politicas/politicas.component.html +120 -0
  31. package/templates/frontend/src/app/Paginas/Nucleo/politicas/politicas.component.ts +33 -0
  32. package/templates/frontend/src/app/Paginas/Nucleo/preguntasFrecuentes/preguntasFrecuentes.component.css +162 -0
  33. package/templates/frontend/src/app/Paginas/Nucleo/preguntasFrecuentes/preguntasFrecuentes.component.html +97 -0
  34. package/templates/frontend/src/app/Paginas/Nucleo/preguntasFrecuentes/preguntasFrecuentes.component.ts +36 -0
  35. package/templates/frontend/src/app/Paginas/Nucleo/sabiasque/sabiasque.component.css +229 -0
  36. package/templates/frontend/src/app/Paginas/Nucleo/sabiasque/sabiasque.component.html +396 -0
  37. package/templates/frontend/src/app/Paginas/Nucleo/sabiasque/sabiasque.component.ts +51 -0
  38. package/templates/frontend/src/app/app.routes.ts +20 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utn-cli",
3
- "version": "2.1.51",
3
+ "version": "2.1.53",
4
4
  "description": "Herramienta CLI unificada para la gestión de plantillas en SIGU.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -4,6 +4,141 @@ const Router = express.Router();
4
4
  const Miscelaneo = require('./../servicios/Nucleo/Miscelaneas.js');
5
5
  const ManejadorDeErrores = require('../servicios/Nucleo/ManejadorDeErrores.js');
6
6
 
7
+ Router.get('/ObtenerActivosPorPersona', async (solicitud, respuesta, next) => {
8
+ try {
9
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
10
+ try {
11
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_ActivosPorPersona'), error: undefined });
12
+ } catch (error) {
13
+ const MensajeDeError = 'No fue posible listar vAvatar_ActivosPorPersona';
14
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
15
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
16
+ }
17
+ }
18
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
19
+ } catch (error) { next(error); }
20
+ });
21
+
22
+ Router.get('/ObtenerArticulosActivos', async (solicitud, respuesta, next) => {
23
+ try {
24
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
25
+ try {
26
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_ArticulosActivos'), error: undefined });
27
+ } catch (error) {
28
+ const MensajeDeError = 'No fue posible listar vAvatar_ArticulosActivos';
29
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
30
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
31
+ }
32
+ }
33
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
34
+ } catch (error) { next(error); }
35
+ });
36
+
37
+ Router.get('/ObtenerCuentasPresupuestariasActivas', async (solicitud, respuesta, next) => {
38
+ try {
39
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
40
+ try {
41
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_CuentasPresupuestariasActivas'), error: undefined });
42
+ } catch (error) {
43
+ const MensajeDeError = 'No fue posible listar vAvatar_CuentasPresupuestariasActivas';
44
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
45
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
46
+ }
47
+ }
48
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
49
+ } catch (error) { next(error); }
50
+ });
51
+
52
+ Router.get('/ObtenerFormulacionPresupuestaria', async (solicitud, respuesta, next) => {
53
+ try {
54
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
55
+ try {
56
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_FormulacionPresupuestaria'), error: undefined });
57
+ } catch (error) {
58
+ const MensajeDeError = 'No fue posible listar vAvatar_FormulacionPresupuestaria';
59
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
60
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
61
+ }
62
+ }
63
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
64
+ } catch (error) { next(error); }
65
+ });
66
+
67
+ Router.get('/ObtenerMetasActivas', async (solicitud, respuesta, next) => {
68
+ try {
69
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
70
+ try {
71
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_MetasActivas'), error: undefined });
72
+ } catch (error) {
73
+ const MensajeDeError = 'No fue posible listar vAvatar_MetasActivas';
74
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
75
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
76
+ }
77
+ }
78
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
79
+ } catch (error) { next(error); }
80
+ });
81
+
82
+ Router.get('/ObtenerMetasPorPersona', async (solicitud, respuesta, next) => {
83
+ try {
84
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
85
+ try {
86
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_MetasPorPersona'), error: undefined });
87
+ } catch (error) {
88
+ const MensajeDeError = 'No fue posible listar vAvatar_MetasPorPersona';
89
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
90
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
91
+ }
92
+ }
93
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
94
+ } catch (error) { next(error); }
95
+ });
96
+
97
+ Router.get('/ObtenerSolicitudesDeCompra', async (solicitud, respuesta, next) => {
98
+ try {
99
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
100
+ try {
101
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_SolicitudesDeCompra'), error: undefined });
102
+ } catch (error) {
103
+ const MensajeDeError = 'No fue posible listar vAvatar_SolicitudesDeCompra';
104
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
105
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
106
+ }
107
+ }
108
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
109
+ } catch (error) { next(error); }
110
+ });
111
+
112
+ Router.get('/ObtenerUnidadesEjecutorasActivas', async (solicitud, respuesta, next) => {
113
+ try {
114
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
115
+ try {
116
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_UnidadesEjecutorasActivas'), error: undefined });
117
+ } catch (error) {
118
+ const MensajeDeError = 'No fue posible listar vAvatar_UnidadesEjecutorasActivas';
119
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
120
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
121
+ }
122
+ }
123
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
124
+ } catch (error) { next(error); }
125
+ });
126
+
127
+ Router.get('/ObtenerUnidadesEjecutorasPorPersona', async (solicitud, respuesta, next) => {
128
+ try {
129
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
130
+ try {
131
+ return respuesta.json({ body: await Miscelaneo.listarTablaAvatar('vAvatar_UnidadesEjecutorasPorPersona'), error: undefined });
132
+ } catch (error) {
133
+ const MensajeDeError = 'No fue posible listar vAvatar_UnidadesEjecutorasPorPersona';
134
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
135
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
136
+ }
137
+ }
138
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
139
+ } catch (error) { next(error); }
140
+ });
141
+
7
142
  Router.get('/ListarActividades', async (solicitud, respuesta, next) => {
8
143
  try {
9
144
  if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
@@ -912,4 +1047,109 @@ Router.get('/calendarioPublico/:Anio?', async (solicitud, respuesta, next) => {
912
1047
  }
913
1048
  });
914
1049
 
1050
+ Router.get('/obtenerEquipoDeDesarrolladores', async (solicitud, respuesta, next) => {
1051
+ try {
1052
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
1053
+ try {
1054
+ return respuesta.json({ body: await Miscelaneo.obtenerEquipoDeDesarrolladores(), error: undefined });
1055
+ } catch (error) {
1056
+ const MensajeDeError = 'No fue posible obtener el equipo de desarrolladores';
1057
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
1058
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
1059
+ }
1060
+ }
1061
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
1062
+ } catch (error) {
1063
+ next(error);
1064
+ }
1065
+ });
1066
+
1067
+ Router.get('/VistaDePoliticas', async (solicitud, respuesta, next) => {
1068
+ try {
1069
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
1070
+ try {
1071
+ await Miscelaneo.registrarVistaDePoliticas(solicitud.headers.authorization);
1072
+ return respuesta.json({ body: undefined, error: undefined });
1073
+ } catch (error) {
1074
+ const MensajeDeError = 'No fue posible registrar la vista de políticas de seguridad';
1075
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
1076
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
1077
+ }
1078
+ }
1079
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
1080
+ } catch (error) {
1081
+ next(error);
1082
+ }
1083
+ });
1084
+ Router.get('/VistaDeAcercaDe', async (solicitud, respuesta, next) => {
1085
+ try {
1086
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
1087
+ try {
1088
+ await Miscelaneo.registrarVistaDeAcercaDe(solicitud.headers.authorization);
1089
+ return respuesta.json({ body: undefined, error: undefined });
1090
+ } catch (error) {
1091
+ const MensajeDeError = 'No fue posible registrar la vista de Acerca de';
1092
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
1093
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
1094
+ }
1095
+ }
1096
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
1097
+ } catch (error) {
1098
+ next(error);
1099
+ }
1100
+ });
1101
+ Router.get('/VistaDeConozcanos', async (solicitud, respuesta, next) => {
1102
+ try {
1103
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
1104
+ try {
1105
+ await Miscelaneo.registrarVistaDeConozcanos(solicitud.headers.authorization);
1106
+ return respuesta.json({ body: undefined, error: undefined });
1107
+ } catch (error) {
1108
+ const MensajeDeError = 'No fue posible registrar la vista de conozcanos';
1109
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
1110
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
1111
+ }
1112
+ }
1113
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
1114
+ } catch (error) {
1115
+ next(error);
1116
+ }
1117
+ });
1118
+
1119
+ Router.get('/VistaDeSabiasQue', async (solicitud, respuesta, next) => {
1120
+ try {
1121
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
1122
+ try {
1123
+ await Miscelaneo.registrarVistaDeSabiasQue(solicitud.headers.authorization);
1124
+ return respuesta.json({ body: undefined, error: undefined });
1125
+ } catch (error) {
1126
+ const MensajeDeError = 'No fue posible registrar la vista de sabías qué';
1127
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
1128
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
1129
+ }
1130
+ }
1131
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
1132
+ } catch (error) {
1133
+ next(error);
1134
+ }
1135
+ });
1136
+
1137
+ Router.get('/VistaDePreguntasFrecuentes', async (solicitud, respuesta, next) => {
1138
+ try {
1139
+ if (await Miscelaneo.validarTokenV2(solicitud.headers.authorization) && await Miscelaneo.validarAccesoDelOrigen(solicitud)) {
1140
+ try {
1141
+ await Miscelaneo.registrarVistaDePreguntasFrecuentes(solicitud.headers.authorization);
1142
+ return respuesta.json({ body: undefined, error: undefined });
1143
+ } catch (error) {
1144
+ const MensajeDeError = 'No fue posible registrar la vista de preguntas frecuentes';
1145
+ console.error(new ManejadorDeErrores(MensajeDeError, ManejadorDeErrores.obtenerNumeroDeLinea(), true, `Dirección IP: ${solicitud.ip}`));
1146
+ return respuesta.status(500).json({ body: undefined, error: MensajeDeError });
1147
+ }
1148
+ }
1149
+ return respuesta.status(401).json({ body: undefined, error: ManejadorDeErrores.mensajeDeError401() });
1150
+ } catch (error) {
1151
+ next(error);
1152
+ }
1153
+ });
1154
+
915
1155
  module.exports = Router;
@@ -41,30 +41,30 @@ class Miscelaneo {
41
41
  this.EnlaceDeAcceso = undefined;
42
42
  };
43
43
 
44
- async PasosDeFlujoDeAprobacion(Identificador, PasoActual, Accion, Metadatos, Justificacion, LastUser) {
44
+ async listarTablaAvatar(NombreDeLaTabla) {
45
+ return await ejecutarConsultaSIGU(`SELECT * FROM \`SIGU\`.\`${NombreDeLaTabla}\``);
46
+ }
47
+
48
+ async PasosDeFlujoDeAprobacion(Identificador, PasoActual, Accion, Metadatos, Justificacion, LastUser, FlujoDeAprobacionId = undefined, RegistrarMovimiento = true) {
45
49
  const DatosARetornar = {};
46
50
 
47
51
  // Determina si la acción es una improbación para manejar el flujo de forma diferente
48
52
  const EsImprobacion = Accion === 'Improbacion';
49
53
 
50
54
  // Obtiene el ID del flujo de aprobación configurado para este módulo
51
- const idDelFlujoDeAprobacion = await this.idDelFlujoDeAprobacion();
52
-
55
+ const idDelFlujoDeAprobacion = FlujoDeAprobacionId ? FlujoDeAprobacionId : await this.idDelFlujoDeAprobacion();
53
56
  // Consulta cuál es el último paso del flujo, para saber si el detalle queda completamente aprobado
54
57
  const PasoMaximo = await ejecutarConsultaSIGU("SELECT MAX(`NumeroDePaso`) \
55
58
  AS `PasoMaximo` FROM `SIGU`.`SIGU_FlujosDeAprobacionPasos` WHERE \
56
59
  `FlujoDeAprobacionId` = ?"
57
60
  , [idDelFlujoDeAprobacion]);
58
-
59
61
  // En improbación el flujo retrocede, por lo que el paso siguiente se resuelve después;
60
62
  // en aprobación simplemente avanza al siguiente paso
61
63
  let PasoSiguiente = EsImprobacion ? PasoActual : PasoActual + 1;
62
-
63
64
  // Verifica que el usuario tenga permiso para actuar en el paso correspondiente
64
- const TienePermiso = await ejecutarConsultaSIGU("SELECT `NumeroDePasoPorImprobacion` FROM `SIGU`.`SIGU_FlujosDeAprobacionPasos` \
65
- WHERE `NumeroDePaso`=? AND (`Identificadores` LIKE '%?%' OR `Identificadores`='Persona usuaria')",
66
- [PasoSiguiente, Number(Identificador)]);
67
-
65
+ const TienePermiso = await ejecutarConsultaSIGU("SELECT `NumeroDePasoPorImprobacion`,`Identificadores` FROM `SIGU`.`SIGU_FlujosDeAprobacionPasos` \
66
+ WHERE `FlujoDeAprobacionId` = ? AND `NumeroDePaso` = ? AND (`Identificadores` LIKE ? OR `Identificadores`='Persona usuaria')",
67
+ [idDelFlujoDeAprobacion, PasoSiguiente, `%${Number(Identificador)}%`]);
68
68
  // Si el usuario no aparece como firmante autorizado del paso, se corta la ejecución
69
69
  if (TienePermiso.length == 0) {
70
70
  DatosARetornar.Mensaje = 'No tiene permisos para firmar este detalle';
@@ -73,11 +73,12 @@ class Miscelaneo {
73
73
  }
74
74
 
75
75
  // Registra el movimiento en el historial del flujo de aprobación
76
- await ejecutarConsultaSIGU("INSERT INTO `SIGU`.`SIGU_FlujosDeAprobacionMovimientos` VALUES (?, ?, ?, ?, ?, ?, NOW(4), ?)"
77
- , [idDelFlujoDeAprobacion, PasoActual
78
- , Identificador, Accion
79
- , Justificacion, JSON.stringify(Metadatos), LastUser]);
80
-
76
+ if (RegistrarMovimiento) {
77
+ await ejecutarConsultaSIGU("INSERT INTO `SIGU`.`SIGU_FlujosDeAprobacionMovimientos` VALUES (?, ?, ?, ?, ?, ?, NOW(4), ?)"
78
+ , [idDelFlujoDeAprobacion, PasoActual
79
+ , Identificador, Accion
80
+ , Justificacion, JSON.stringify(Metadatos), LastUser]);
81
+ }
81
82
  // En improbación, el paso destino viene definido en la configuración del flujo (retroceso configurado)
82
83
  PasoSiguiente = EsImprobacion ? TienePermiso[0].NumeroDePasoPorImprobacion : PasoSiguiente;
83
84
 
@@ -926,7 +927,12 @@ class Miscelaneo {
926
927
  }
927
928
 
928
929
  async obtenerMensajesInstitucionales() {
929
- return await ejecutarConsultaSIGU("SELECT `MensajeInstitucionalId`, `Titulo`, `Texto`, `FechaYHoraDeInicio`, `FechaYHoraDeFinalizacion` FROM `SIGU`.`SIGU_MensajesInstitucionales` WHERE NOW(4) BETWEEN `FechaYHoraDeInicio` AND `FechaYHoraDeFinalizacion`");
930
+ return await ejecutarConsultaSIGU("SELECT `MensajeInstitucionalId`, `Titulo`, `Texto`, `FechaYHoraDeInicio`, `FechaYHoraDeFinalizacion`,\
931
+ (SELECT CONCAT('https://framework-mantenimientosv2-storage.sigu.utn.ac.cr/', REPLACE(`adj`.`Ruta`, '/var/storage/public/', ''))\
932
+ FROM `SIGU`.`SIGU_Adjuntos` `adj`\
933
+ WHERE `adj`.`Modulo` = 'framework-mantenimientosv2' AND `adj`.`Etiqueta` = CONCAT('MensajeInstitucional-', `MensajeInstitucionalId`)\
934
+ ORDER BY `adj`.`LastUpdate` DESC LIMIT 1) AS `ImagenURL`\
935
+ FROM `SIGU`.`SIGU_MensajesInstitucionales` WHERE NOW(4) BETWEEN `FechaYHoraDeInicio` AND `FechaYHoraDeFinalizacion`");
930
936
  }
931
937
 
932
938
  async obtenerConsentimientoInformado(Modulo = undefined) {
@@ -993,6 +999,7 @@ class Miscelaneo {
993
999
  });
994
1000
  }
995
1001
 
1002
+
996
1003
  async crearNotificacion(IdentificadorDelUsuario, IdentificadorDelDestinatario, Mensaje) {
997
1004
  return await ejecutarConsultaSIGU("INSERT INTO `SIGU`.`SIGU_NotificacionesV2` VALUES (?, ?, 'Sin leer', NOW(4), NOW(4), ?)"
998
1005
  , [IdentificadorDelDestinatario, Mensaje, IdentificadorDelUsuario]);
@@ -1685,7 +1692,7 @@ class Miscelaneo {
1685
1692
  convertirACSV(ArregloDeJSON) {
1686
1693
  if (!ArregloDeJSON || ArregloDeJSON.length === 0) return '';
1687
1694
  const intentarParsearJSON = (valor) => {
1688
- if (typeof valor === 'object' && valor !== null && !Array.isArray(valor)) return valor;
1695
+ if (typeof valor === 'object' && valor !== null && !Array.isArray(valor) && !(valor instanceof Date)) return valor;
1689
1696
  if (typeof valor === 'string' && valor.trim().startsWith('{')) {
1690
1697
  try {
1691
1698
  const objeto = JSON.parse(valor);
@@ -2764,6 +2771,94 @@ class Miscelaneo {
2764
2771
  calendario.Periodos = [...periodosMap.values()];
2765
2772
  return calendario;
2766
2773
  }
2774
+
2775
+ async obtenerEquipoDeDesarrolladores() {
2776
+ const desarrolladores = await ejecutarConsultaSIGU(`
2777
+ SELECT
2778
+ e.Identificador,
2779
+ CONCAT_WS(' ', p.Nombre, p.PrimerApellido, p.SegundoApellido) AS NombreCompleto,
2780
+ e.Biografia,
2781
+ e.Perfil
2782
+ FROM SIGU.SIGU_EquipoDeDesarrolladores e
2783
+ JOIN SIGU.SIGU_Personas p ON e.Identificador = p.Identificador
2784
+ ORDER BY NombreCompleto ASC
2785
+ `);
2786
+
2787
+ for (const desarrollador of desarrolladores) {
2788
+ const adjunto = await ejecutarConsultaSIGU(
2789
+ "SELECT `Ruta` FROM `SIGU`.`SIGU_Adjuntos`\
2790
+ WHERE `Modulo` = 'framework-mantenimientosv2' AND `Etiqueta` = ?\
2791
+ ORDER BY `LastUpdate` DESC LIMIT 1",
2792
+ [`EquipoDeDesarrolladores-${desarrollador.Identificador}`]
2793
+ );
2794
+ desarrollador.FotoURL = adjunto && adjunto.length > 0
2795
+ ? 'https://framework-mantenimientosv2-storage.sigu.utn.ac.cr/' + adjunto[0].Ruta.replace('/var/storage/public/', '')
2796
+ : null;
2797
+ }
2798
+
2799
+ return desarrolladores;
2800
+ }
2801
+
2802
+ async registrarVistaDePoliticas(Token) {
2803
+ const { uid } = await this.obtenerDatosDelUsuario(Token);
2804
+ await ejecutarConsulta(
2805
+ "INSERT INTO DatosMiscelaneos (DatoMiscelaneo, Datos, LastUser) VALUES ('VistasDePoliticas', JSON_OBJECT('Total', 1), ?) ON DUPLICATE KEY UPDATE Datos = JSON_SET(Datos, '$.Total', CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) + 1), LastUser = ?",
2806
+ [uid, uid]
2807
+ );
2808
+ }
2809
+
2810
+ async registrarVistaDeAcercaDe(Token) {
2811
+ const { uid } = await this.obtenerDatosDelUsuario(Token);
2812
+ await ejecutarConsulta(
2813
+ "INSERT INTO DatosMiscelaneos (DatoMiscelaneo, Datos, LastUser) VALUES ('VistasDeAcercaDe', JSON_OBJECT('Total', 1), ?) ON DUPLICATE KEY UPDATE Datos = JSON_SET(Datos, '$.Total', CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) + 1), LastUser = ?",
2814
+ [uid, uid]
2815
+ );
2816
+ }
2817
+
2818
+ async registrarVistaDeConozcanos(Token) {
2819
+ const { uid } = await this.obtenerDatosDelUsuario(Token);
2820
+ await ejecutarConsulta(
2821
+ "INSERT INTO DatosMiscelaneos (DatoMiscelaneo, Datos, LastUser) VALUES ('VistasDeConozcanos', JSON_OBJECT('Total', 1), ?) ON DUPLICATE KEY UPDATE Datos = JSON_SET(Datos, '$.Total', CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) + 1), LastUser = ?",
2822
+ [uid, uid]
2823
+ );
2824
+ }
2825
+
2826
+ async registrarVistaDeSabiasQue(Token) {
2827
+ const { uid } = await this.obtenerDatosDelUsuario(Token);
2828
+ await ejecutarConsulta(
2829
+ "INSERT INTO DatosMiscelaneos (DatoMiscelaneo, Datos, LastUser) VALUES ('VistasDeSabiasQue', JSON_OBJECT('Total', 1), ?) ON DUPLICATE KEY UPDATE Datos = JSON_SET(Datos, '$.Total', CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) + 1), LastUser = ?",
2830
+ [uid, uid]
2831
+ );
2832
+ }
2833
+
2834
+ async registrarVistaDePreguntasFrecuentes(Token) {
2835
+ const { uid } = await this.obtenerDatosDelUsuario(Token);
2836
+ await ejecutarConsulta(
2837
+ "INSERT INTO DatosMiscelaneos (DatoMiscelaneo, Datos, LastUser) VALUES ('VistasDePreguntasFrecuentes', JSON_OBJECT('Total', 1), ?) ON DUPLICATE KEY UPDATE Datos = JSON_SET(Datos, '$.Total', CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) + 1), LastUser = ?",
2838
+ [uid, uid]
2839
+ );
2840
+ }
2841
+
2842
+ async obtenerVistas() {
2843
+ const [manual, modulo, politicas, acercade, conozcanos, sabiasque, preguntasFrecuentes] = await Promise.all([
2844
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDelManual'"),
2845
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDelModulo'"),
2846
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDePoliticas'"),
2847
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDeAcercaDe'"),
2848
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDeConozcanos'"),
2849
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDeSabiasQue'"),
2850
+ ejecutarConsulta("SELECT CAST(JSON_VALUE(Datos, '$.Total') AS UNSIGNED) AS Total FROM DatosMiscelaneos WHERE DatoMiscelaneo = 'VistasDePreguntasFrecuentes'"),
2851
+ ]);
2852
+ return {
2853
+ VistasDelManual: manual[0]?.Total ?? 0,
2854
+ VistasDelModulo: modulo[0]?.Total ?? 0,
2855
+ VistasDePoliticas: politicas[0]?.Total ?? 0,
2856
+ VistasDeAcercaDe: acercade[0]?.Total ?? 0,
2857
+ VistasDeConozcanos: conozcanos[0]?.Total ?? 0,
2858
+ VistasDeSabiasQue: sabiasque[0]?.Total ?? 0,
2859
+ VistasDePreguntasFrecuentes: preguntasFrecuentes[0]?.Total ?? 0,
2860
+ };
2861
+ }
2767
2862
  }
2768
2863
 
2769
2864
  module.exports = new Miscelaneo();
@@ -99,7 +99,7 @@
99
99
  <th mat-header-cell *matHeaderCellDef aria-label="Fila expandible"></th>
100
100
  <td mat-cell *matCellDef="let element">
101
101
  <button mat-icon-button aria-label="Expandir fila"
102
- (click)="(elementoExpandible = elementoExpandible === element ? null : element); $event.stopPropagation()">
102
+ (click)="setElementoExpandible(element); $event.stopPropagation()">
103
103
  @if (elementoExpandible === element) {
104
104
  <mat-icon>keyboard_arrow_up</mat-icon>
105
105
  } @else {
@@ -111,11 +111,11 @@
111
111
  <ng-container matColumnDef="expandedDetail">
112
112
  <td mat-cell *matCellDef="let element" [attr.colspan]="llavesColumnas.length">
113
113
  <div class="example-element-detail" [@detailExpand]="element == elementoExpandible ? 'expanded' : 'collapsed'">
114
- <table mat-table [dataSource]="element[atributoDesplegable]" multiTemplateDataRows
115
- class="mat-elevation-z8 separador">
114
+ @if(element == elementoExpandible) {
115
+ <table mat-table [dataSource]="subDataSource" matSort multiTemplateDataRows class="mat-elevation-z8 separador">
116
116
  @for (column of subColumnas; track $index) {
117
117
  <ng-container matColumnDef="{{column.Llave}}">
118
- <th mat-header-cell *matHeaderCellDef> {{column.Alias}} </th>
118
+ <th mat-header-cell *matHeaderCellDef mat-sort-header> {{column.Alias}} </th>
119
119
  <td mat-cell *matCellDef="let elementTwo" [attr.data-label]="column.Alias"> {{elementTwo[column.Llave]}}
120
120
  </td>
121
121
  </ng-container>
@@ -161,6 +161,7 @@
161
161
  <tr mat-header-row *matHeaderRowDef="llavesSubColumnas"></tr>
162
162
  <tr mat-row *matRowDef="let element; columns: llavesSubColumnas;" class="example-element-row"></tr>
163
163
  </table>
164
+ }
164
165
  </div>
165
166
  </td>
166
167
  </ng-container>
@@ -1,4 +1,4 @@
1
- import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, OnDestroy, Output, SimpleChanges, ViewChild } from '@angular/core';
1
+ import { Component, ElementRef, EventEmitter, Input, OnChanges, OnInit, OnDestroy, Output, QueryList, SimpleChanges, ViewChild, ViewChildren } from '@angular/core';
2
2
  import { MatTableDataSource, MatTableModule } from '@angular/material/table';
3
3
  import { MatIconModule } from '@angular/material/icon';
4
4
  import { MatButtonModule } from '@angular/material/button';
@@ -70,8 +70,10 @@ export class TablaComponent implements OnInit, OnChanges, OnDestroy {
70
70
  @Input() public estadosQuePermitenEdicion: string[] = [];
71
71
  @Input() public subEstadosQuePermitenEdicion: string[] = [];
72
72
 
73
- @ViewChild(MatSort) sort!: MatSort;
73
+ @ViewChildren(MatSort) allSorts!: QueryList<MatSort>;
74
+ get sort(): MatSort { return this.allSorts?.first; }
74
75
  @ViewChild(MatPaginator) paginator!: MatPaginator;
76
+ public subDataSource: MatTableDataSource<any> = new MatTableDataSource<any>([]);
75
77
  @Output() GuardarAccion: EventEmitter<any> = new EventEmitter<any>();
76
78
  @Output() DescargarInformacion: EventEmitter<any> = new EventEmitter<any>();
77
79
  @ViewChild('valorFiltroInput') filtroInput!: ElementRef<HTMLInputElement>;
@@ -207,6 +209,10 @@ export class TablaComponent implements OnInit, OnChanges, OnDestroy {
207
209
  if (!this.paginarResultados) {
208
210
  this.dataSource.paginator = this.paginator;
209
211
  }
212
+ this.subscriptions.add(this.allSorts.changes.subscribe(() => {
213
+ const subSort = this.allSorts.get(1);
214
+ if (subSort) this.subDataSource.sort = subSort;
215
+ }));
210
216
  this.subscriptions.add(this.paginator.page.subscribe((event: PageEvent) => {
211
217
  localStorage.setItem('pageSize', event.pageSize.toString());
212
218
 
@@ -249,6 +255,11 @@ export class TablaComponent implements OnInit, OnChanges, OnDestroy {
249
255
  }));
250
256
  }
251
257
 
258
+ setElementoExpandible(element: any): void {
259
+ this.elementoExpandible = this.elementoExpandible === element ? null : element;
260
+ this.subDataSource.data = this.elementoExpandible ? (this.elementoExpandible[this.atributoDesplegable] || []) : [];
261
+ }
262
+
252
263
  estaAtenuado(element: any, textoAyuda: string): boolean {
253
264
  if (!element?.SeccionesTrabajadas) return false;
254
265
  try {
@@ -0,0 +1,65 @@
1
+ .politicas-container {
2
+ max-width: 1000px;
3
+ padding-bottom: 20px;
4
+ font-family: 'Roboto', sans-serif;
5
+ background-color: white;
6
+ text-align: left;
7
+ margin: 0 auto;
8
+ }
9
+
10
+ .subtitle {
11
+ background-color: #0b4794;
12
+ font-size: 25px;
13
+ color: white;
14
+ padding: 18px;
15
+ margin: 20px 0;
16
+ text-align: center;
17
+ }
18
+ .subtitle p {
19
+ margin: 5px 0;
20
+ }
21
+ .description {
22
+ width: 100%;
23
+ padding-left: 30px;
24
+ padding-right: 30px;
25
+ box-sizing: border-box;
26
+ margin-top: 30px;
27
+ }
28
+
29
+ .description p {
30
+ margin: 15px 0;
31
+ font-size: 16px;
32
+ font-weight: 400;
33
+ color: #4D4D4D;
34
+ line-height: 1.6;
35
+ }
36
+
37
+ .description ul {
38
+ padding-left: 20px;
39
+ margin-top: 10px;
40
+ }
41
+
42
+ .description li {
43
+ list-style-type: disc;
44
+ margin-bottom: 10px;
45
+ color: #4D4D4D;
46
+ font-size: 16px;
47
+ }
48
+ .description li::marker {
49
+ color: #0b4794;
50
+ }
51
+ @media (max-width: 768px) {
52
+ .description {
53
+ padding-left: 20px;
54
+ padding-right: 20px;
55
+ }
56
+
57
+ .description p {
58
+ font-size: 14px;
59
+ }
60
+
61
+ .description li{
62
+ font-size: 14px;
63
+ }
64
+ }
65
+