utn-cli 2.1.1 → 2.1.3

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 (35) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/commands/commit.js +14 -1
  3. package/commands/frontend.js +1 -1
  4. package/package.json +1 -1
  5. package/templates/backend/package.json +3 -3
  6. package/templates/backend/rutas/misc.js +69 -0
  7. package/templates/backend/servicios/Nucleo/Miscelaneas.js +188 -22
  8. package/templates/backend/servicios/Nucleo/ReportePDF.js +1 -1
  9. package/templates/bd/docker-scripts/1-crear estructura.sql +8 -0
  10. package/templates/frontend/package.json +9 -8
  11. package/templates/frontend/public/Manual.md +1 -0
  12. package/templates/frontend/src/app/Componentes/Nucleo/gestion-actividad/gestion-actividad.component.ts +11 -3
  13. package/templates/frontend/src/app/Componentes/Nucleo/graficos/graficos.component.ts +2 -4
  14. package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.css +318 -0
  15. package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.html +43 -0
  16. package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.ts +77 -0
  17. package/templates/frontend/src/app/Componentes/Nucleo/mensajes/mensajes.component.ts +5 -3
  18. package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component.ts +12 -4
  19. package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.ts +12 -3
  20. package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.html +13 -10
  21. package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.ts +18 -6
  22. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.html +2 -2
  23. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.ts +9 -13
  24. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.ts +7 -9
  25. package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-reporte/tarjeta-reporte.component.ts +1 -6
  26. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.css +72 -0
  27. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.html +17 -4
  28. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.ts +45 -57
  29. package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.css +11 -0
  30. package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.html +4 -4
  31. package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.ts +65 -69
  32. package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.html +14 -3
  33. package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.ts +52 -14
  34. package/templates/frontend/src/app/app.routes.ts +4 -0
  35. package/templates/frontend/src/app/datos-globales.service.ts +4 -1
@@ -1,5 +1,7 @@
1
- import { Component, OnInit } from "@angular/core";
1
+ import { Component, OnInit, OnDestroy } from "@angular/core";
2
2
  import { CommonModule } from "@angular/common";
3
+ import { Subject } from "rxjs";
4
+ import { takeUntil } from "rxjs/operators";
3
5
  import { TarjetaComponent } from "../../Componentes/Nucleo/tarjeta/tarjeta.component";
4
6
  import { TarjetaMultipleComponent } from "../../Componentes/Nucleo/tarjeta-multiple/tarjeta-multiple.component";
5
7
  import { TarjetaReporteComponent } from "../../Componentes/Nucleo/tarjeta-reporte/tarjeta-reporte.component";
@@ -58,85 +60,79 @@ type AnyTarjetaConfig = TarjetaConfig | TarjetaMultipleConfig | TarjetaReporteCo
58
60
  styleUrl: "./contenedor-principal.component.css"
59
61
  })
60
62
 
61
- export class ContenedorPrincipalComponent implements OnInit {
63
+ export class ContenedorPrincipalComponent implements OnInit, OnDestroy {
62
64
  public cantidad: number = 0;
63
65
  public cantidadMaxima: number = 0;
64
66
  public tarjetas: AnyTarjetaConfig[] = [];
67
+ public filtro: string = '';
68
+ private destroy$ = new Subject<void>();
65
69
 
66
70
  constructor(private http: HttpClient, private datosGlobalesService: DatosGlobalesService) {
67
71
  this.cantidad = 0;
68
72
  this.cantidadMaxima = 0;
69
73
  }
70
74
 
75
+ ngOnDestroy(): void {
76
+ this.destroy$.next();
77
+ this.destroy$.complete();
78
+ }
71
79
 
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);
80
+ esDimmed(tarjeta: AnyTarjetaConfig): boolean {
81
+ if (!this.filtro.trim()) return false;
82
+ return !tarjeta.titulo.toLowerCase().includes(this.filtro.toLowerCase());
83
+ }
84
+
85
+ ngOnInit() {
86
+ this.datosGlobalesService.filtroDeTarjetas$
87
+ .pipe(takeUntil(this.destroy$))
88
+ .subscribe(f => this.filtro = f);
89
+
90
+ this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/obtenerTarjetasDelContenedor`).subscribe({
91
+ next: (datos: any) => {
92
+ this.tarjetas = (datos.body ?? []).map((d: any) => this.mapearTarjeta(d));
129
93
  },
130
94
  error: (error) => {
131
- console.error('Error al obtener configuración de tarjetas:', error);
132
- this.tarjetas = baseTarjetas.sort((a, b) => a.position - b.position);
95
+ console.error('Error al obtener las tarjetas del contenedor:', error);
96
+ this.tarjetas = [];
133
97
  }
134
98
  });
135
99
  }
136
100
 
101
+ private mapearTarjeta(d: any): AnyTarjetaConfig {
102
+ const tipoMap: Record<string, AnyTarjetaConfig['type']> = {
103
+ 'Única': 'single',
104
+ 'Múltiple': 'multiple',
105
+ 'Reporte': 'report',
106
+ 'Personalizada': 'custom'
107
+ };
108
+
109
+ const base = {
110
+ type: tipoMap[d.Tipo] ?? 'single',
111
+ position: d['Posición'],
112
+ titulo: d['Título'],
113
+ descripcion: d['Descripción'],
114
+ ColorDeBorde: d.ColorDeBorde
115
+ };
116
+
117
+ switch (d.Tipo) {
118
+ case 'Única':
119
+ return { ...base, type: 'single', rutaASeguir: d.RutaASeguir ?? '', icono: d['Ícono'] ?? '', cantidad: d.Cantidad, cantidadMaxima: d.CantidadMaxima } as TarjetaConfig;
120
+ case 'Múltiple':
121
+ return {
122
+ ...base, type: 'multiple',
123
+ rutas: (d.Rutas ?? []).map((r: any) => ({ nombre: r.Nombre, ruta: r.Ruta }))
124
+ } as TarjetaMultipleConfig;
125
+ case 'Reporte':
126
+ return { ...base, type: 'report', reporteAGenerar: d.ReporteAGenerar ?? '', icono: d['Ícono'] ?? '' } as TarjetaReporteConfig;
127
+ case 'Personalizada':
128
+ return { ...base, type: 'custom', icono: d['Ícono'] ?? '', etiqueta: d.Etiqueta ?? '', accion: d['Acción'] ?? '' } as TarjetaPersonalizadaConfig;
129
+ default:
130
+ return base as any;
131
+ }
132
+ }
133
+
137
134
  drop(event: CdkDragDrop<AnyTarjetaConfig[]>) {
138
135
  moveItemInArray(this.tarjetas, event.previousIndex, event.currentIndex);
139
- // Update positions based on new order
140
136
  this.tarjetas.forEach((tarjeta, index) => {
141
137
  tarjeta.position = (index + 1) * 10;
142
138
  });
@@ -159,6 +155,12 @@ export class ContenedorPrincipalComponent implements OnInit {
159
155
  });
160
156
  }
161
157
 
158
+ EjecutarAccionPersonalizada(accion: string) {
159
+ // if (accion === 'DescargarInformacionSociodemografica') {
160
+ // this.DescargarInformacionSociodemografica();
161
+ // }
162
+ }
163
+
162
164
  GenerarReporte(event: { reporte: string }) {
163
165
  if (event.reporte === 'ElReporte1') {
164
166
  this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/DatosParaReporteCSV`, {
@@ -179,10 +181,4 @@ export class ContenedorPrincipalComponent implements OnInit {
179
181
  });
180
182
  }
181
183
  }
182
-
183
- EjecutarAccionPersonalizada(accion: string) {
184
- // if (accion === 'DescargarInformacionSociodemografica') {
185
- // this.DescargarInformacionSociodemografica();
186
- // }
187
- }
188
- }
184
+ }
@@ -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)
@@ -1,16 +1,19 @@
1
1
  import { Injectable } from '@angular/core';
2
+ import { BehaviorSubject } from 'rxjs';
2
3
 
3
4
  @Injectable({
4
5
  providedIn: 'root'
5
6
  })
6
7
  export class DatosGlobalesService {
7
8
 
9
+ readonly filtroDeTarjetas$ = new BehaviorSubject<string>('');
10
+
8
11
  constructor() { }
9
12
 
10
13
  ObtenerToken() {
11
14
  const baseUrl = this.ObtenerURL();
12
15
  if (baseUrl === 'http://localhost/') {
13
- return 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxMiIsImlhdCI6MTc2MzEzMDc0NSwiZXhwIjoxNzYzMTY2NzQ1fQ.0Ferkg8T4bOGmpCR2RWdKxUwzOJr2xuqNBbX502D7vs';
16
+ return 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOiIxMiIsIklkZW50aWZpY2Fkb3IiOiIxMiIsImlhdCI6MTc3Nzk4OTgwOSwiZXhwIjoxNzc4MDI1ODA5fQ.T1kUm7H4hwbapQ3eFok31GVvezkPitdTlJL96MorozY';
14
17
  }
15
18
  const match = document.cookie.match(/(?:^|;\s*)_siguid=([^;]+)/);
16
19
  let token = match ? decodeURIComponent(match[1]) : '';