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.
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 +46 -68
  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') {
@@ -68,75 +68,53 @@ export class ContenedorPrincipalComponent implements OnInit {
68
68
  this.cantidadMaxima = 0;
69
69
  }
70
70
 
71
-
72
- async ngOnInit() {
73
- // 1. Define base cards
74
- let baseTarjetas: AnyTarjetaConfig[] = [
75
-
76
- // {
77
- // type: 'single',
78
- // position: 10,
79
- // rutaASeguir: 'tablaInformacionGeneral',
80
- // titulo: 'Información general',
81
- // descripcion: 'Listado de información general',
82
- // icono: 'feed'
83
- // },
84
-
85
- ];
86
-
87
- // // 2. Check for extra permissions (Personas card)
88
- // this.http.get(`${this.datosGlobalesService.ObtenerURL()}Personas/PermisoExtra`).subscribe({
89
- // next: (datos: any) => {
90
- // if (datos.body) {
91
- // const personasCard: TarjetaMultipleConfig = {
92
- // type: 'multiple',
93
- // position: 25,
94
- // titulo: 'Personas',
95
- // descripcion: 'Módulo para el mantenimiento de los datos relacionados con personas',
96
- // rutas: [
97
- // { nombre: 'Personas', ruta: 'tablaPersonas' },
98
- // { nombre: 'Correos', ruta: 'tablaCorreosPersonas' },
99
- // { nombre: 'Cuentas bancarias', ruta: 'tablaCuentasPersonas' },
100
- // { nombre: 'Teléfonos', ruta: 'tablaTelefonosPersonas' },
101
- // { nombre: 'Ubicación', ruta: 'tablaUbicacionPersona' },
102
- // ]
103
- // };
104
- // baseTarjetas.push(personasCard);
105
- // }
106
- // },
107
- // error: (error) => {
108
- // console.error('Problemas al intentar obtener los datos: ', error);
109
- // this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
110
- // },
111
- // });
112
-
113
- // 3. Load user-specific positions from backend
114
- this.http.get(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/obtener`).subscribe({
115
- next: (res: any) => {
116
- if (res.body && res.body.length > 0) {
117
- const savedConfigs = res.body;
118
- baseTarjetas.forEach(tarjeta => {
119
- const config = savedConfigs.find((c: any) => c.Titulo === tarjeta.titulo);
120
- if (config) {
121
- tarjeta.position = config.Posicion;
122
- if (config.ColorDeBorde) {
123
- tarjeta.ColorDeBorde = config.ColorDeBorde;
124
- }
125
- }
126
- });
127
- }
128
- 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));
129
75
  },
130
76
  error: (error) => {
131
- console.error('Error al obtener configuración de tarjetas:', error);
132
- this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
77
+ console.error('Error al obtener las tarjetas del contenedor:', error);
78
+ this.tarjetas = [];
133
79
  }
134
80
  });
135
81
  }
136
82
 
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
+ }
114
+ }
115
+
137
116
  drop(event: CdkDragDrop<AnyTarjetaConfig[]>) {
138
117
  moveItemInArray(this.tarjetas, event.previousIndex, event.currentIndex);
139
- // Update positions based on new order
140
118
  this.tarjetas.forEach((tarjeta, index) => {
141
119
  tarjeta.position = (index + 1) * 10;
142
120
  });
@@ -159,6 +137,12 @@ export class ContenedorPrincipalComponent implements OnInit {
159
137
  });
160
138
  }
161
139
 
140
+ EjecutarAccionPersonalizada(accion: string) {
141
+ // if (accion === 'DescargarInformacionSociodemografica') {
142
+ // this.DescargarInformacionSociodemografica();
143
+ // }
144
+ }
145
+
162
146
  GenerarReporte(event: { reporte: string }) {
163
147
  if (event.reporte === 'ElReporte1') {
164
148
  this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/DatosParaReporteCSV`, {
@@ -179,10 +163,4 @@ export class ContenedorPrincipalComponent implements OnInit {
179
163
  });
180
164
  }
181
165
  }
182
-
183
- EjecutarAccionPersonalizada(accion: string) {
184
- // if (accion === 'DescargarInformacionSociodemografica') {
185
- // this.DescargarInformacionSociodemografica();
186
- // }
187
- }
188
- }
166
+ }
@@ -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)