utn-cli 2.1.0 → 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.
Files changed (29) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/commands/commit.js +14 -1
  3. package/package.json +1 -1
  4. package/templates/backend/package.json +3 -3
  5. package/templates/backend/rutas/misc.js +51 -0
  6. package/templates/backend/servicios/Nucleo/Miscelaneas.js +176 -20
  7. package/templates/frontend/package.json +9 -8
  8. package/templates/frontend/public/Manual.md +1 -0
  9. package/templates/frontend/src/app/Componentes/Nucleo/gestion-actividad/gestion-actividad.component.ts +11 -3
  10. package/templates/frontend/src/app/Componentes/Nucleo/graficos/graficos.component.ts +2 -4
  11. package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.css +318 -0
  12. package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.html +43 -0
  13. package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.ts +74 -0
  14. package/templates/frontend/src/app/Componentes/Nucleo/mensajes/mensajes.component.ts +5 -3
  15. package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component.ts +12 -4
  16. package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.ts +12 -3
  17. package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.html +13 -10
  18. package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.ts +18 -6
  19. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.html +2 -2
  20. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.ts +9 -13
  21. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.ts +7 -9
  22. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-reporte/tarjeta-reporte.component.ts +1 -6
  23. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.html +0 -2
  24. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.ts +34 -57
  25. package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.html +3 -3
  26. package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.ts +37 -203
  27. package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.html +14 -3
  28. package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.ts +52 -14
  29. package/templates/frontend/src/app/app.routes.ts +4 -0
@@ -28,8 +28,8 @@
28
28
  <div [class]="cantidadLugar">
29
29
  <p>{{ (cantidadAMostrar !== 0 ? cantidadAMostrar : '') }}</p>
30
30
  </div>
31
- @if(cantidad > 0) {
32
- <hr [id]="'porcentaje'+titulo" class="linea" [matTooltip]="'Cantidad: ' + cantidad" matTooltipPosition="above" />
31
+ @if(cantidadMaxima && cantidadMaxima > 0) {
32
+ <hr #lineaRef class="linea" [matTooltip]="cantidad + ' / ' + cantidadMaxima" matTooltipPosition="above" />
33
33
  }
34
34
  <!-- <div class="pie">
35
35
  <p>CONTINUAR</p>
@@ -1,4 +1,4 @@
1
- import { AfterViewInit, Component, Input } from "@angular/core";
1
+ import { AfterViewInit, Component, ElementRef, Input, ViewChild } from "@angular/core";
2
2
  import { MatIconModule } from "@angular/material/icon";
3
3
  import { MatCardModule } from "@angular/material/card";
4
4
  import { MatTooltipModule } from "@angular/material/tooltip";
@@ -21,8 +21,12 @@ export class TarjetaComponent implements AfterViewInit {
21
21
  @Input() cantidadLugar: string = "cantidad";
22
22
  @Input() rutaASeguir: string = '';
23
23
  @Input() cantidadMaxima: number | undefined;
24
- @Input() cantidadAMostrar: number = 0;
25
24
  @Input() ColorDeBorde: string = '';
25
+ @ViewChild('lineaRef') lineaRef?: ElementRef<HTMLHRElement>;
26
+
27
+ get cantidadAMostrar(): number {
28
+ return (this.cantidadMaxima ?? 0) - (this.cantidad ?? 0);
29
+ }
26
30
 
27
31
  constructor(private ruta: Router) { }
28
32
 
@@ -30,18 +34,10 @@ export class TarjetaComponent implements AfterViewInit {
30
34
  this.ruta.navigate([this.rutaASeguir]);
31
35
  }
32
36
 
33
- ngOnInit() {
34
- this.cantidadAMostrar = (this.cantidadMaxima ? this.cantidadMaxima : 0) - (this.cantidad ? this.cantidad : 0);
35
- }
36
-
37
37
  ngAfterViewInit(): void {
38
- if (this.cantidad) {
39
- let porcentaje = 100;
40
- if (this.cantidadMaxima) {
41
- porcentaje = (this.cantidad / this.cantidadMaxima) * 100;
42
- }
43
- const linea = document.getElementById('porcentaje' + this.titulo) as HTMLHRElement;
44
- linea.style.width = `${porcentaje}%`;
38
+ if (this.cantidadMaxima && this.cantidadMaxima > 0) {
39
+ const porcentaje = (this.cantidad / this.cantidadMaxima) * 100;
40
+ if (this.lineaRef?.nativeElement) this.lineaRef.nativeElement.style.width = `${porcentaje}%`;
45
41
  }
46
42
  }
47
43
  }
@@ -1,4 +1,4 @@
1
- import { AfterViewInit, Component, EventEmitter, Input, Output } from "@angular/core";
1
+ import { AfterViewInit, Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild } from "@angular/core";
2
2
  import { MatIconModule } from "@angular/material/icon";
3
3
  import { MatCardModule } from "@angular/material/card";
4
4
  import { MatTooltipModule } from "@angular/material/tooltip";
@@ -11,7 +11,7 @@ import { Router } from "@angular/router";
11
11
  templateUrl: "./tarjeta-personalizada.component.html",
12
12
  styleUrl: "./tarjeta-personalizada.component.css"
13
13
  })
14
- export class TarjetaPersonalizadaComponent implements AfterViewInit {
14
+ export class TarjetaPersonalizadaComponent implements OnInit, AfterViewInit {
15
15
  @Input() titulo: string = "";
16
16
  @Input() descripcion: string = "";
17
17
  @Input() icono: any = null;
@@ -25,15 +25,12 @@ export class TarjetaPersonalizadaComponent implements AfterViewInit {
25
25
  @Input() Etiqueta: string = "CONTINUAR";
26
26
  @Input() ColorDeBorde: string = "";
27
27
  @Output() etiquetaClick = new EventEmitter<void>();
28
+ @ViewChild('porcentajeRef') porcentajeRef?: ElementRef<HTMLHRElement>;
28
29
 
29
30
  constructor(private ruta: Router) { }
30
31
 
31
32
  onClick() {
32
- if(this.etiquetaClick!==undefined){
33
- this.etiquetaClick.emit();
34
- return;
35
- }
36
- this.ruta.navigate([this.rutaASeguir]);
33
+ this.etiquetaClick.emit();
37
34
  }
38
35
 
39
36
  ngOnInit() {
@@ -46,8 +43,9 @@ export class TarjetaPersonalizadaComponent implements AfterViewInit {
46
43
  if (this.cantidadMaxima) {
47
44
  porcentaje = (this.cantidad / this.cantidadMaxima) * 100;
48
45
  }
49
- const linea = document.getElementById('porcentaje' + this.titulo) as HTMLHRElement;
50
- linea.style.width = `${porcentaje}%`;
46
+ if (this.porcentajeRef?.nativeElement) {
47
+ this.porcentajeRef.nativeElement.style.width = `${porcentaje}%`;
48
+ }
51
49
  }
52
50
  }
53
51
  }
@@ -15,12 +15,7 @@ export class TarjetaReporteComponent {
15
15
  @Input() ColorDeBorde: string = '';
16
16
  @Output() GenerarReporte: EventEmitter<{ reporte: string }> = new EventEmitter();
17
17
 
18
- constructor() { }
19
-
20
18
  onClick() {
21
- this.GenerarReporte.emit({reporte: this.reporteAGenerar});
22
- }
23
-
24
- ngOnInit() {
19
+ this.GenerarReporte.emit({ reporte: this.reporteAGenerar });
25
20
  }
26
21
  }
@@ -132,11 +132,9 @@
132
132
  <mat-icon>live_tv</mat-icon>
133
133
  </button>
134
134
  }
135
- @if(EnlaceDelManual !== '-') {
136
135
  <button class="botonDeNavegacion" matTooltip="Ayuda" title="Ayuda" (click)="irAAyuda()">
137
136
  <mat-icon>help</mat-icon>
138
137
  </button>
139
- }
140
138
  <button class="botonDeNavegacion" matTooltip="Reporte" title="Reporte" (click)="irASoporte()">
141
139
  <mat-icon>support_agent</mat-icon>
142
140
  </button>
@@ -1,4 +1,4 @@
1
- import { HttpClient, HttpHeaders } from '@angular/common/http';
1
+ import { HttpClient } from '@angular/common/http';
2
2
  import { Component, OnInit, OnDestroy, NgZone } from '@angular/core';
3
3
  import { RouterOutlet, Router } from '@angular/router';
4
4
  import { DatosGlobalesService } from '../../../datos-globales.service';
@@ -65,73 +65,57 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
65
65
  }, 60000);
66
66
  });
67
67
 
68
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/validarToken').subscribe((datos: any) => {
69
- this.TienePermiso = datos.body;
68
+ this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/inicializar').subscribe((datos: any) => {
69
+ const body = datos.body;
70
+
71
+ this.TienePermiso = body.TienePermiso;
70
72
  if (!this.TienePermiso) {
71
73
  this.Entrar();
74
+ return;
72
75
  }
73
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/configurarFrontend').subscribe((datos: any) => {
74
- this.Titulo = datos.body.Titulo;
75
- this.Modulo = datos.body.Modulo;
76
- this.Version = datos.body.Version;
77
- this.Descripcion = datos.body.Descripcion;
78
- this.Detalle = datos.body.Detalle;
79
- this.NombreUsuario = datos.body.NombreUsuario || 'Perfil';
80
- })
81
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerNotificaciones', {
82
- headers: new HttpHeaders({
83
- Authorization: this.datosGlobalesService.ObtenerToken(),
84
- 'Content-Type': 'application/json'
85
- })
86
- }).subscribe((datos: any) => {
87
- this.Mensajes = datos.body;
88
- })
89
- });
90
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerDetalleDelModulo').subscribe((datos: any) => {
91
- this.EnlaceDelManual = datos.body[0].EnlaceDelManual;
92
- this.EnlaceDelVideo = datos.body[0].EnlaceDelVideo;
93
- });
94
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'ConsentimientoInformado/ConsentimientoInformado').subscribe((datos: any) => {
95
- if (datos.body.Aceptaciones === 0) {
96
- const ConsentimientoInformadoId = datos.body.Consentimiento[0].ConsentimientoInformadoId;
76
+
77
+ this.Titulo = body.Titulo;
78
+ this.Modulo = body.Modulo;
79
+ this.Version = body.Version;
80
+ this.Descripcion = body.Descripcion;
81
+ this.Detalle = body.Detalle;
82
+ this.NombreUsuario = body.NombreUsuario || 'Perfil';
83
+ this.EnlaceDelManual = body.EnlaceDelManual;
84
+ this.EnlaceDelVideo = body.EnlaceDelVideo;
85
+ this.Mensajes = body.Notificaciones;
86
+
87
+ if (body.Consentimiento?.Aceptaciones === 0) {
88
+ const ConsentimientoInformadoId = body.Consentimiento.Consentimiento[0].ConsentimientoInformadoId;
97
89
  this.dialog.open(MensajeConfirmacionHTMLComponent, {
98
90
  data: {
99
- titulo: datos.body.Consentimiento[0].Titulo,
100
- mensaje: datos.body.Consentimiento[0].Texto,
91
+ titulo: body.Consentimiento.Consentimiento[0].Titulo,
92
+ mensaje: body.Consentimiento.Consentimiento[0].Texto,
101
93
  textoCerrar: 'Cancelar',
102
94
  textoAceptar: 'Aceptar',
103
95
  onClose: () => { this.datosGlobalesService.RedirigirAPortal() },
104
96
  onAccept: () => {
105
- this.http.post(`${this.datosGlobalesService.ObtenerURL()}ConsentimientoInformado/AceptarConsentimientoInformado`
106
- , { ConsentimientoInformadoId })
107
- .subscribe({
108
- next: (datos: any) => {
109
- // this.obtenerDatosParaPoblarLaTabla();
110
- },
111
- error: (error) => {
112
- this.datosGlobalesService.RedirigirAPortal();
113
- },
97
+ this.http.post(`${this.datosGlobalesService.ObtenerURL()}ConsentimientoInformado/AceptarConsentimientoInformado`,
98
+ { ConsentimientoInformadoId }).subscribe({
99
+ error: () => { this.datosGlobalesService.RedirigirAPortal(); }
114
100
  });
115
101
  },
116
102
  },
117
103
  });
118
104
  }
119
- });
120
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerMensajesModulares').subscribe((datos: any) => {
121
- let mensajesVisualizados = this.obtenerMensajesModularesVisualizados();
122
- for (let contador = 0; contador < datos.body.length; contador++) {
123
- const index = mensajesVisualizados.indexOf(datos.body[contador]['MensajeModularId']);
124
- if (index === -1) {
105
+
106
+ const mensajesVisualizados = this.obtenerMensajesModularesVisualizados();
107
+ for (let i = 0; i < body.MensajesModulares.length; i++) {
108
+ if (!mensajesVisualizados.includes(body.MensajesModulares[i]['MensajeModularId'])) {
125
109
  this.dialog.open(MensajeConfirmacionHTMLComponent, {
126
110
  data: {
127
- titulo: datos.body[contador]['Titulo'],
128
- mensaje: datos.body[contador]['Texto'],
111
+ titulo: body.MensajesModulares[i]['Titulo'],
112
+ mensaje: body.MensajesModulares[i]['Texto'],
129
113
  textoAceptar: 'Cerrar',
130
114
  onClose: () => { },
131
115
  onAccept: () => {
132
- let mensajesVisualizados = this.obtenerMensajesModularesVisualizados();
133
- mensajesVisualizados.push(datos.body[contador]['MensajeModularId']);
134
- localStorage.setItem('MensajesModularesVisualizados', JSON.stringify(mensajesVisualizados));
116
+ const vistos = this.obtenerMensajesModularesVisualizados();
117
+ vistos.push(body.MensajesModulares[i]['MensajeModularId']);
118
+ localStorage.setItem('MensajesModularesVisualizados', JSON.stringify(vistos));
135
119
  },
136
120
  },
137
121
  });
@@ -171,14 +155,7 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
171
155
  }
172
156
 
173
157
  irAAyuda(): void {
174
- window.open(this.EnlaceDelManual, '_blank', 'noopener,noreferrer');
175
- // const link = document.createElement('a');
176
- // link.href = 'assets/Ayuda.pdf';
177
- // link.setAttribute('download', 'Ayuda.pdf');
178
- // link.setAttribute('target', '_blank');
179
- // document.body.appendChild(link);
180
- // link.click();
181
- // document.body.removeChild(link);
158
+ this.router.navigate(['/manual']);
182
159
  }
183
160
 
184
161
  irAVideo(): void {
@@ -3,9 +3,9 @@
3
3
  <div cdkDrag>
4
4
  @switch (tarjeta.type) {
5
5
  @case ('single') {
6
- <app-tarjeta [rutaASeguir]="tarjeta.rutaASeguir" [titulo]="tarjeta.titulo" [descripcion]="tarjeta.descripcion"
7
- [icono]="tarjeta.icono" [ColorDeBorde]="tarjeta.ColorDeBorde ? tarjeta.ColorDeBorde : ''"
8
- [cantidad]="tarjeta.cantidad ?? 0" [cantidadMaxima]="tarjeta.cantidadMaxima ?? 0">>
6
+ <app-tarjeta [rutaASeguir]="$any(tarjeta).rutaASeguir" [titulo]="tarjeta.titulo" [descripcion]="tarjeta.descripcion"
7
+ [icono]="$any(tarjeta).icono" [ColorDeBorde]="tarjeta.ColorDeBorde ? tarjeta.ColorDeBorde : ''"
8
+ [cantidad]="$any(tarjeta).cantidad ?? 0" [cantidadMaxima]="$any(tarjeta).cantidadMaxima ?? 0">
9
9
  </app-tarjeta>
10
10
  }
11
11
  @case ('multiple') {
@@ -7,8 +7,6 @@ import { TarjetaPersonalizadaComponent } from "../../Componentes/Nucleo/tarjeta-
7
7
  import { HttpClient } from "@angular/common/http";
8
8
  import { DatosGlobalesService } from '../../datos-globales.service';
9
9
  import { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
10
- import { forkJoin, of } from 'rxjs';
11
- import { catchError } from 'rxjs/operators';
12
10
 
13
11
  interface BaseTarjetaConfig {
14
12
  type: 'single' | 'multiple' | 'report' | 'custom';
@@ -70,217 +68,53 @@ export class ContenedorPrincipalComponent implements OnInit {
70
68
  this.cantidadMaxima = 0;
71
69
  }
72
70
 
73
- async ngOnInit() {
74
- // 1. Define base cards
75
- let baseTarjetas: AnyTarjetaConfig[] = [
76
- {
77
- type: 'single',
78
- position: 10,
79
- rutaASeguir: 'FRM-ModulosV2',
80
- titulo: 'Módulos',
81
- descripcion: 'Módulo para el mantenimiento de los módulosV2',
82
- icono: 'dashboard_2',
83
- ColorDeBorde: '#0B4FCE'
84
- },
85
- {
86
- type: 'single',
87
- position: 20,
88
- rutaASeguir: 'tablaTareasProgramadas',
89
- titulo: 'Tareas programadas',
90
- descripcion: 'Módulo para el mantenimiento de las tareas programadas',
91
- icono: 'grading'
92
- },
93
- {
94
- type: 'multiple',
95
- position: 30,
96
- titulo: 'Permisos',
97
- descripcion: 'Módulo para el mantenimiento de los permisos',
98
- rutas: [
99
- { nombre: 'Permisos', ruta: 'tablaPermisos' },
100
- { nombre: 'Permisos de personas', ruta: 'tablaPermisosDePersonas' },
101
- { nombre: 'Permisos padres de personas', ruta: 'tablaPermisosDePersonasPadres' }
102
- ]
103
- },
104
- {
105
- type: 'multiple',
106
- position: 40,
107
- titulo: 'Permisos extra',
108
- descripcion: 'Módulo para el mantenimiento de los permisos extra',
109
- rutas: [
110
- { nombre: 'Permisos extra', ruta: 'tablaPermisosExtra' },
111
- { nombre: 'Permisos extra de personas', ruta: 'tablaPermisosExtraDePersonas' },
112
- ]
113
- },
114
- {
115
- type: 'single',
116
- position: 50,
117
- rutaASeguir: 'tablaMensajesInstitucionales',
118
- titulo: 'Mensajes institucionales',
119
- descripcion: 'Módulo para el mantenimiento de mensajes institucionales',
120
- icono: 'mark_unread_chat_alt'
121
- },
122
- {
123
- type: 'single',
124
- position: 60,
125
- rutaASeguir: 'tablaMensajesModulares',
126
- titulo: 'Mensajes modulares',
127
- descripcion: 'Módulo para el mantenimiento de mensajes modulares',
128
- icono: 'mark_unread_chat_alt'
129
- },
130
- {
131
- type: 'single',
132
- position: 70,
133
- rutaASeguir: 'tablaPeriodos',
134
- titulo: 'Periodos',
135
- descripcion: 'Módulo para el mantenimiento de periodos',
136
- icono: 'edit_calendar'
137
- },
138
- {
139
- type: 'single',
140
- position: 80,
141
- rutaASeguir: 'tablaSedes',
142
- titulo: 'Sedes y recintos',
143
- descripcion: 'Módulo para el mantenimiento de sedes y recintos',
144
- icono: 'home_work'
145
- },
146
- {
147
- type: 'single',
148
- position: 90,
149
- rutaASeguir: 'tablaFlujosAprobacion',
150
- titulo: 'Flujos de aprobación',
151
- descripcion: 'Módulo para el mantenimiento de flujos de aprobación, pasos y movimientos',
152
- icono: 'library_add_check'
153
- },
154
- {
155
- type: 'single',
156
- position: 100,
157
- rutaASeguir: 'tablaLocalidades',
158
- titulo: 'Localidades',
159
- descripcion: 'Módulo para la revisión de las diferentes localidades',
160
- icono: 'location_on'
161
- },
162
- {
163
- type: 'multiple',
164
- position: 110,
165
- titulo: 'Repositorios',
166
- descripcion: 'Módulo para el mantenimiento de repositorios, variables del sistema y tareas programadas',
167
- rutas: [
168
- { nombre: 'Consentimientos informados', ruta: 'tablaConsentimientosInformados' },
169
- { nombre: 'Repositorios', ruta: 'tablaRepositorios' },
170
- { nombre: 'Accesos a repositorios', ruta: 'tablaRepositoriosAccesos' },
171
- { nombre: 'Variables del sistema', ruta: 'tablaVariablesDeSistema' },
172
- ]
173
- },
174
- {
175
- type: 'single',
176
- position: 120,
177
- rutaASeguir: 'gestionGrafico',
178
- titulo: 'Gráfico de solicitudes mensuales',
179
- descripcion: 'Módulo para la visualización del gráfico de solicitudes por el mes actual',
180
- icono: 'leaderboard'
181
- },
182
- {
183
- type: 'single',
184
- position: 130,
185
- rutaASeguir: 'gestionGraficoSesiones',
186
- titulo: 'Gráfico de las sesiones',
187
- descripcion: 'Módulo para la visualización del gráfico de sesiones',
188
- icono: 'pie_chart'
189
- },
190
- {
191
- type: 'single',
192
- position: 140,
193
- rutaASeguir: 'tablaAdministradores',
194
- titulo: 'Administradores',
195
- descripcion: 'Módulo para el mantenimiento de administradores',
196
- icono: 'supervisor_account'
197
- }
198
- ];
199
-
200
- forkJoin({
201
- permisoExtra: this.http.get(`${this.datosGlobalesService.ObtenerURL()}Personas/PermisoExtra`).pipe(
202
- catchError(error => {
203
- console.error('Problemas al intentar obtener los datos de PermisoExtra: ', error);
204
- return of({ body: null });
205
- })
206
- ),
207
- // configuracion: this.http.get(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/obtener`).pipe(
208
- // catchError(error => {
209
- // console.error('Error al obtener configuración de tarjetas:', error);
210
- // return of({ body: [] });
211
- // })
212
- // )
213
- }).subscribe({
214
- next: (results: any) => {
215
- // // 2. Handle extra permissions
216
- // if (results.permisoExtra && results.permisoExtra.body) {
217
- // const personasCard: TarjetaMultipleConfig = {
218
- // type: 'multiple',
219
- // position: 25,
220
- // titulo: 'Personas',
221
- // descripcion: 'Módulo para el mantenimiento de los datos relacionados con personas',
222
- // rutas: [
223
- // { nombre: 'Personas', ruta: 'tablaPersonas' },
224
- // { nombre: 'Correos', ruta: 'tablaCorreosPersonas' },
225
- // { nombre: 'Cuentas bancarias', ruta: 'tablaCuentasPersonas' },
226
- // { nombre: 'Teléfonos', ruta: 'tablaTelefonosPersonas' },
227
- // { nombre: 'Ubicación', ruta: 'tablaUbicacionPersona' },
228
- // ]
229
- // };
230
- // if (!baseTarjetas.find(t => t.titulo === 'Personas')) {
231
- // baseTarjetas.push(personasCard);
232
- // }
233
- // }
234
-
235
- // 3. Apply saved configurations
236
- if (results.configuracion && results.configuracion.body && results.configuracion.body.length > 0) {
237
- const savedConfigs = results.configuracion.body;
238
- baseTarjetas.forEach(tarjeta => {
239
- const config = savedConfigs.find((c: any) => c.Titulo === tarjeta.titulo);
240
- if (config) {
241
- tarjeta.position = config.Posicion;
242
- if (config.ColorDeBorde) {
243
- tarjeta.ColorDeBorde = config.ColorDeBorde;
244
- }
245
- }
246
- });
247
- }
248
- this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
71
+ ngOnInit() {
72
+ this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/obtenerTarjetasDelContenedor`).subscribe({
73
+ next: (datos: any) => {
74
+ this.tarjetas = (datos.body ?? []).map((d: any) => this.mapearTarjeta(d));
249
75
  },
250
76
  error: (error) => {
251
- console.error('Error crítico en el proceso de inicialización:', error);
252
- this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
77
+ console.error('Error al obtener las tarjetas del contenedor:', error);
78
+ this.tarjetas = [];
253
79
  }
254
80
  });
255
81
  }
256
82
 
257
- // 3. Load user-specific positions from backend
258
- this.http.get(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/obtener`).subscribe({
259
- next: (res: any) => {
260
- if (res.body && res.body.length > 0) {
261
- const savedConfigs = res.body;
262
- baseTarjetas.forEach(tarjeta => {
263
- const config = savedConfigs.find((c: any) => c.Titulo === tarjeta.titulo);
264
- if (config) {
265
- tarjeta.position = config.Posicion;
266
- if (config.ColorDeBorde) {
267
- tarjeta.ColorDeBorde = config.ColorDeBorde;
268
- }
269
- }
270
- });
271
- }
272
- this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
273
- },
274
- error: (error) => {
275
- console.error('Error al obtener configuración de tarjetas:', error);
276
- this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
277
- }
278
- });
83
+ private mapearTarjeta(d: any): AnyTarjetaConfig {
84
+ const tipoMap: Record<string, AnyTarjetaConfig['type']> = {
85
+ 'Única': 'single',
86
+ 'Múltiple': 'multiple',
87
+ 'Reporte': 'report',
88
+ 'Personalizada': 'custom'
89
+ };
90
+
91
+ const base = {
92
+ type: tipoMap[d.Tipo] ?? 'single',
93
+ position: d['Posición'],
94
+ titulo: d['Título'],
95
+ descripcion: d['Descripción'],
96
+ ColorDeBorde: d.ColorDeBorde
97
+ };
98
+
99
+ switch (d.Tipo) {
100
+ case 'Única':
101
+ return { ...base, type: 'single', rutaASeguir: d.RutaASeguir ?? '', icono: d['Ícono'] ?? '', cantidad: d.Cantidad, cantidadMaxima: d.CantidadMaxima } as TarjetaConfig;
102
+ case 'Múltiple':
103
+ return {
104
+ ...base, type: 'multiple',
105
+ rutas: (d.Rutas ?? []).map((r: any) => ({ nombre: r.Nombre, ruta: r.Ruta }))
106
+ } as TarjetaMultipleConfig;
107
+ case 'Reporte':
108
+ return { ...base, type: 'report', reporteAGenerar: d.ReporteAGenerar ?? '', icono: d['Ícono'] ?? '' } as TarjetaReporteConfig;
109
+ case 'Personalizada':
110
+ return { ...base, type: 'custom', icono: d['Ícono'] ?? '', etiqueta: d.Etiqueta ?? '', accion: d['Acción'] ?? '' } as TarjetaPersonalizadaConfig;
111
+ default:
112
+ return base as any;
113
+ }
279
114
  }
280
115
 
281
116
  drop(event: CdkDragDrop<AnyTarjetaConfig[]>) {
282
117
  moveItemInArray(this.tarjetas, event.previousIndex, event.currentIndex);
283
- // Update positions based on new order
284
118
  this.tarjetas.forEach((tarjeta, index) => {
285
119
  tarjeta.position = (index + 1) * 10;
286
120
  });
@@ -1,6 +1,17 @@
1
1
  <h1>Reportería del módulo</h1>
2
2
  <div class="contenido">
3
3
  <app-tarjeta-reporte [reporteAGenerar]="'ElReporte1'" titulo="Reporte número uno"
4
- descripcion="Acá la descripción de lo que hace el reporte número uno"
5
- icono="pie_chart" (GenerarReporte)="GenerarReporte($event)"></app-tarjeta-reporte>
6
- </div>
4
+ descripcion="Descarga un CSV con los datos de prueba del módulo." icono="pie_chart" ColorDeBorde="#3498db"
5
+ (GenerarReporte)="GenerarReporte($event)">
6
+ </app-tarjeta-reporte>
7
+
8
+ <app-tarjeta-reporte [reporteAGenerar]="'ReportePersonasHTML'" titulo="Reporte de personas"
9
+ descripcion="Abre un reporte HTML con el listado completo de personas registradas en el sistema." icono="people"
10
+ ColorDeBorde="#2ecc71" (GenerarReporte)="GenerarReporte($event)">
11
+ </app-tarjeta-reporte>
12
+
13
+ <app-tarjeta-reporte [reporteAGenerar]="'ReportePDF'" titulo="Reporte PDF de ejemplo"
14
+ descripcion="Abre un reporte PDF con el listado completo de personas registradas en el sistema."
15
+ icono="picture_as_pdf" ColorDeBorde="#e74c3c" (GenerarReporte)="GenerarReporte($event)">
16
+ </app-tarjeta-reporte>
17
+ </div>
@@ -17,20 +17,58 @@ export class GestionDeReportesComponent {
17
17
 
18
18
  GenerarReporte(event: { reporte: string }) {
19
19
  if (event.reporte === 'ElReporte1') {
20
- this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/DatosParaReporteCSV`, {
21
- responseType: 'text'
22
- }).subscribe({
23
- next: (datos: any) => {
24
- const blob = new Blob([decodeURIComponent(datos)], { type: 'text/csv;charset=utf-8;' });
25
- const enlace = document.createElement('a');
26
- enlace.href = URL.createObjectURL(blob);
27
- enlace.download = 'datos.csv';
28
- enlace.click();
29
- },
30
- error: (error) => {
31
- console.error('Problemas al intentar descargar el reporte: ', error);
32
- },
33
- });
20
+ this.descargarCSV(`${this.datosGlobalesService.ObtenerURL()}misc/DatosParaReporteCSV`, 'datos.csv');
34
21
  }
22
+ if (event.reporte === 'ReportePersonasHTML') {
23
+ this.abrirReporteHTML(`${this.datosGlobalesService.ObtenerURL()}Personas/reporteHTML`);
24
+ }
25
+ if (event.reporte === 'ReportePDF') {
26
+ this.abrirReportePDF(`${this.datosGlobalesService.ObtenerURL()}Personas/reportePDF`);
27
+ }
28
+ }
29
+
30
+ private descargarCSV(url: string, nombreArchivo: string): void {
31
+ this.http.get(url, { responseType: 'text' }).subscribe({
32
+ next: (datos: any) => {
33
+ const blob = new Blob([decodeURIComponent(datos)], { type: 'text/csv;charset=utf-8;' });
34
+ const enlace = document.createElement('a');
35
+ enlace.href = URL.createObjectURL(blob);
36
+ enlace.download = nombreArchivo;
37
+ enlace.click();
38
+ URL.revokeObjectURL(enlace.href);
39
+ },
40
+ error: (error) => {
41
+ console.error('Problemas al intentar descargar el reporte: ', error);
42
+ },
43
+ });
44
+ }
45
+
46
+ private abrirReporteHTML(url: string): void {
47
+ this.http.get(url, { responseType: 'text' }).subscribe({
48
+ next: (html: string) => {
49
+ const nuevaPestana = window.open('', '_blank');
50
+ if (nuevaPestana) {
51
+ nuevaPestana.document.write(decodeURI(html));
52
+ nuevaPestana.document.close();
53
+ } else {
54
+ console.error('No se pudo abrir la nueva pestaña. Verifica que no esté bloqueada.');
55
+ }
56
+ },
57
+ error: (error) => {
58
+ console.error('Problemas al obtener el reporte HTML: ', error);
59
+ }
60
+ });
61
+ }
62
+
63
+ private abrirReportePDF(url: string): void {
64
+ this.http.get(url, { responseType: 'blob' }).subscribe({
65
+ next: (pdfBlob: Blob) => {
66
+ const urlBlob = window.URL.createObjectURL(pdfBlob);
67
+ window.open(urlBlob, '_blank');
68
+ },
69
+ error: (error) => {
70
+ console.error('Problemas al obtener el reporte PDF: ', error);
71
+ }
72
+ });
35
73
  }
36
74
  }
@@ -9,6 +9,10 @@ export const routes: Routes = [
9
9
  path: 'Actividad',
10
10
  loadComponent: () => import('./Componentes/Nucleo/gestion-actividad/gestion-actividad.component').then(m => m.GestionActividadComponent)
11
11
  },
12
+ {
13
+ path: 'manual',
14
+ loadComponent: () => import('./Componentes/Nucleo/manual/manual.component').then(m => m.ManualComponent)
15
+ },
12
16
  {
13
17
  path: 'tabla',
14
18
  loadComponent: () => import('./Paginas/gestion-tabla/gestion-tabla.component').then(m => m.GestionTablaComponent)