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,11 +1,13 @@
1
- import { Component, ElementRef, inject, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
1
+ import { Component, ElementRef, inject, OnDestroy, OnInit, ViewChild, Output, EventEmitter } from '@angular/core';
2
2
  import { MatButtonModule } from '@angular/material/button';
3
3
  import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
4
- import { HttpClient, HttpHeaders } from '@angular/common/http';
4
+ import { HttpClient } from '@angular/common/http';
5
5
  import { MatIconModule } from '@angular/material/icon';
6
6
  import { DatosGlobalesService } from '../../../datos-globales.service';
7
7
  import { MensajeConfirmacionComponent } from '../mensaje-confirmacion/mensaje-confirmacion';
8
8
  import { MatDialog } from '@angular/material/dialog';
9
+ import { Subject } from 'rxjs';
10
+ import { takeUntil } from 'rxjs/operators';
9
11
 
10
12
  @Component({
11
13
  selector: 'app-subir-archivo',
@@ -13,14 +15,16 @@ import { MatDialog } from '@angular/material/dialog';
13
15
  templateUrl: './subir-archivo.component.html',
14
16
  styleUrl: './subir-archivo.component.css'
15
17
  })
16
- export class SubirArchivoComponent implements OnInit {
18
+ export class SubirArchivoComponent implements OnInit, OnDestroy {
17
19
  readonly dialogRef = inject(MatDialogRef<SubirArchivoComponent>);
18
20
  Archivo: any;
19
21
  ListaArchivos: any[] = []
20
22
  HaCargadoArchivos: boolean = false;
21
23
  CantidadMaximaDeArchivos: number = 99;
24
+ private _destroy$ = new Subject<void>();
22
25
  @Output() HaCargadoArchivosChange = new EventEmitter<boolean>();
23
26
  @ViewChild('EntradaDeArchivo') EntradaDeArchivo!: ElementRef<HTMLInputElement>;
27
+ @ViewChild('descargaLink') descargaLinkRef!: ElementRef<HTMLAnchorElement>;
24
28
  readonly data = inject(MAT_DIALOG_DATA);
25
29
  Permiso = '--Permiso=' + this.data.Permiso;
26
30
  Etiqueta = this.data.Etiqueta + this.Permiso;
@@ -185,6 +189,7 @@ export class SubirArchivoComponent implements OnInit {
185
189
  CargarArchivo(archivo: any, Etiqueta: string) {
186
190
  this.http.post(this.datosGlobalesService.ObtenerURL() + this.RutaParaCargar + Etiqueta,
187
191
  archivo)
192
+ .pipe(takeUntil(this._destroy$))
188
193
  .subscribe({
189
194
  next: (data: any) => {
190
195
  this.Archivo = undefined;
@@ -199,6 +204,7 @@ export class SubirArchivoComponent implements OnInit {
199
204
 
200
205
  ListarArchivos(Etiqueta: string) {
201
206
  this.http.get(this.datosGlobalesService.ObtenerURL() + this.RutaParaListar + Etiqueta)
207
+ .pipe(takeUntil(this._destroy$))
202
208
  .subscribe({
203
209
  next: (data: any) => {
204
210
  this.ListaArchivos = data.body;
@@ -214,19 +220,20 @@ export class SubirArchivoComponent implements OnInit {
214
220
  DescargarArchivo(ArchivoId: string, Nombre: string) {
215
221
  this.http.get(this.datosGlobalesService.ObtenerURL() + this.RutaParaDescargar + ArchivoId + this.Permiso
216
222
  , { responseType: 'blob' })
223
+ .pipe(takeUntil(this._destroy$))
217
224
  .subscribe((pdfBlob) => {
218
225
  const url = window.URL.createObjectURL(pdfBlob);
219
- const a = document.createElement('a');
226
+ const a = this.descargaLinkRef.nativeElement;
220
227
  a.href = url;
221
228
  a.download = Nombre;
222
- document.body.appendChild(a);
223
229
  a.click();
224
- document.body.removeChild(a);
230
+ window.URL.revokeObjectURL(url);
225
231
  })
226
232
  }
227
233
 
228
234
  BorrarArchivo(ArchivoId: string) {
229
235
  this.http.delete(this.datosGlobalesService.ObtenerURL() + 'misc/borrarArchivo/' + ArchivoId)
236
+ .pipe(takeUntil(this._destroy$))
230
237
  .subscribe({
231
238
  next: (data: any) => {
232
239
  this.MetaDatosDelArchivoCargado = null;
@@ -238,6 +245,11 @@ export class SubirArchivoComponent implements OnInit {
238
245
  })
239
246
  }
240
247
 
248
+ ngOnDestroy(): void {
249
+ this._destroy$.next();
250
+ this._destroy$.complete();
251
+ }
252
+
241
253
  Cancelar() {
242
254
  this.dialogRef.close(this.MetaDatosDelArchivoCargado);
243
255
  }
@@ -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
  }
@@ -2,6 +2,15 @@
2
2
  .cabecera2 {
3
3
  width: 100%;
4
4
  border-bottom: 1px solid #707070;
5
+ display: flex;
6
+ align-items: center;
7
+ justify-content: space-between;
8
+ gap: 16px;
9
+ }
10
+
11
+ .cabecera2-info {
12
+ flex: 1;
13
+ min-width: 0;
5
14
  }
6
15
 
7
16
  .cabecera2 p {
@@ -9,6 +18,7 @@
9
18
  font-family: "Roboto";
10
19
  letter-spacing: 0;
11
20
  color: #000;
21
+ margin: 0;
12
22
  }
13
23
 
14
24
  .cabecera2 .titulo2 {
@@ -20,6 +30,68 @@
20
30
  font-size: small;
21
31
  }
22
32
 
33
+ /* Filtro de tarjetas */
34
+ .filtro-tarjetas {
35
+ display: flex;
36
+ align-items: center;
37
+ gap: 4px;
38
+ background: #f0f4f8;
39
+ border: 1px solid #c4d0de;
40
+ border-radius: 8px;
41
+ padding: 4px 8px;
42
+ flex-shrink: 0;
43
+ transition: border-color 0.2s ease, box-shadow 0.2s ease;
44
+ }
45
+
46
+ .filtro-tarjetas:focus-within {
47
+ border-color: #1976d2;
48
+ box-shadow: 0 0 0 2px rgba(25, 118, 210, 0.15);
49
+ }
50
+
51
+ .filtro-icono {
52
+ font-size: 18px !important;
53
+ width: 18px !important;
54
+ height: 18px !important;
55
+ color: #6b7c8f;
56
+ flex-shrink: 0;
57
+ }
58
+
59
+ .filtro-input {
60
+ border: none;
61
+ background: transparent;
62
+ outline: none;
63
+ font-size: 14px;
64
+ font-family: 'Roboto', sans-serif;
65
+ color: #333;
66
+ width: 180px;
67
+ }
68
+
69
+ .filtro-input::placeholder {
70
+ color: #a0aab4;
71
+ }
72
+
73
+ .filtro-limpiar {
74
+ display: flex;
75
+ align-items: center;
76
+ border: none;
77
+ background: none;
78
+ cursor: pointer;
79
+ padding: 0;
80
+ color: #6b7c8f;
81
+ transition: color 0.15s ease;
82
+ flex-shrink: 0;
83
+ }
84
+
85
+ .filtro-limpiar mat-icon {
86
+ font-size: 16px !important;
87
+ width: 16px !important;
88
+ height: 16px !important;
89
+ }
90
+
91
+ .filtro-limpiar:hover {
92
+ color: #c0392b;
93
+ }
94
+
23
95
  /* Botones */
24
96
  .botonesDeNavegacion {
25
97
  padding: 10px;
@@ -66,8 +66,23 @@
66
66
  <div class="contenido">
67
67
  @if(TienePermiso){
68
68
  <div class="cabecera2">
69
- <p class="titulo2">{{ Descripcion }}</p>
70
- <p class="descripcion2">{{ Detalle }}</p>
69
+ <div class="cabecera2-info">
70
+ <p class="titulo2">{{ Descripcion }}</p>
71
+ <p class="descripcion2">{{ Detalle }}</p>
72
+ </div>
73
+ @if (esDashboard) {
74
+ <div class="filtro-tarjetas">
75
+ <mat-icon class="filtro-icono">search</mat-icon>
76
+ <input class="filtro-input" type="text" placeholder="Buscar tarjeta..." [value]="filtro"
77
+ (input)="onFiltroChange($event)" aria-label="Buscar tarjeta por nombre" />
78
+ @if (filtro) {
79
+ <button class="filtro-limpiar" (click)="limpiarFiltro()" title="Limpiar búsqueda"
80
+ aria-label="Limpiar búsqueda">
81
+ <mat-icon>close</mat-icon>
82
+ </button>
83
+ }
84
+ </div>
85
+ }
71
86
  </div>
72
87
  <ng-content><router-outlet /></ng-content>
73
88
  }@else{
@@ -132,11 +147,9 @@
132
147
  <mat-icon>live_tv</mat-icon>
133
148
  </button>
134
149
  }
135
- @if(EnlaceDelManual !== '-') {
136
150
  <button class="botonDeNavegacion" matTooltip="Ayuda" title="Ayuda" (click)="irAAyuda()">
137
151
  <mat-icon>help</mat-icon>
138
152
  </button>
139
- }
140
153
  <button class="botonDeNavegacion" matTooltip="Reporte" title="Reporte" (click)="irASoporte()">
141
154
  <mat-icon>support_agent</mat-icon>
142
155
  </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';
@@ -37,6 +37,17 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
37
37
  public AnimarUsuariosActivos: boolean = true;
38
38
  private intervaloUsuarios: any;
39
39
 
40
+ get esDashboard(): boolean { return this.router.url === '/'; }
41
+ get filtro(): string { return this.datosGlobalesService.filtroDeTarjetas$.value; }
42
+
43
+ onFiltroChange(event: Event): void {
44
+ this.datosGlobalesService.filtroDeTarjetas$.next((event.target as HTMLInputElement).value);
45
+ }
46
+
47
+ limpiarFiltro(): void {
48
+ this.datosGlobalesService.filtroDeTarjetas$.next('');
49
+ }
50
+
40
51
  constructor(private http: HttpClient, private datosGlobalesService: DatosGlobalesService, private location: Location, private dialog: MatDialog, private router: Router, private ngZone: NgZone) {
41
52
  if (datosGlobalesService.ObtenerToken() === '') {
42
53
  datosGlobalesService.RedirigirALogin();
@@ -65,73 +76,57 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
65
76
  }, 60000);
66
77
  });
67
78
 
68
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/validarToken').subscribe((datos: any) => {
69
- this.TienePermiso = datos.body;
79
+ this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/inicializar').subscribe((datos: any) => {
80
+ const body = datos.body;
81
+
82
+ this.TienePermiso = body.TienePermiso;
70
83
  if (!this.TienePermiso) {
71
84
  this.Entrar();
85
+ return;
72
86
  }
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;
87
+
88
+ this.Titulo = body.Titulo;
89
+ this.Modulo = body.Modulo;
90
+ this.Version = body.Version;
91
+ this.Descripcion = body.Descripcion;
92
+ this.Detalle = body.Detalle;
93
+ this.NombreUsuario = body.NombreUsuario || 'Perfil';
94
+ this.EnlaceDelManual = body.EnlaceDelManual;
95
+ this.EnlaceDelVideo = body.EnlaceDelVideo;
96
+ this.Mensajes = body.Notificaciones;
97
+
98
+ if (body.Consentimiento?.Aceptaciones === 0) {
99
+ const ConsentimientoInformadoId = body.Consentimiento.Consentimiento[0].ConsentimientoInformadoId;
97
100
  this.dialog.open(MensajeConfirmacionHTMLComponent, {
98
101
  data: {
99
- titulo: datos.body.Consentimiento[0].Titulo,
100
- mensaje: datos.body.Consentimiento[0].Texto,
102
+ titulo: body.Consentimiento.Consentimiento[0].Titulo,
103
+ mensaje: body.Consentimiento.Consentimiento[0].Texto,
101
104
  textoCerrar: 'Cancelar',
102
105
  textoAceptar: 'Aceptar',
103
106
  onClose: () => { this.datosGlobalesService.RedirigirAPortal() },
104
107
  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
- },
108
+ this.http.post(`${this.datosGlobalesService.ObtenerURL()}ConsentimientoInformado/AceptarConsentimientoInformado`,
109
+ { ConsentimientoInformadoId }).subscribe({
110
+ error: () => { this.datosGlobalesService.RedirigirAPortal(); }
114
111
  });
115
112
  },
116
113
  },
117
114
  });
118
115
  }
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) {
116
+
117
+ const mensajesVisualizados = this.obtenerMensajesModularesVisualizados();
118
+ for (let i = 0; i < body.MensajesModulares.length; i++) {
119
+ if (!mensajesVisualizados.includes(body.MensajesModulares[i]['MensajeModularId'])) {
125
120
  this.dialog.open(MensajeConfirmacionHTMLComponent, {
126
121
  data: {
127
- titulo: datos.body[contador]['Titulo'],
128
- mensaje: datos.body[contador]['Texto'],
122
+ titulo: body.MensajesModulares[i]['Titulo'],
123
+ mensaje: body.MensajesModulares[i]['Texto'],
129
124
  textoAceptar: 'Cerrar',
130
125
  onClose: () => { },
131
126
  onAccept: () => {
132
- let mensajesVisualizados = this.obtenerMensajesModularesVisualizados();
133
- mensajesVisualizados.push(datos.body[contador]['MensajeModularId']);
134
- localStorage.setItem('MensajesModularesVisualizados', JSON.stringify(mensajesVisualizados));
127
+ const vistos = this.obtenerMensajesModularesVisualizados();
128
+ vistos.push(body.MensajesModulares[i]['MensajeModularId']);
129
+ localStorage.setItem('MensajesModularesVisualizados', JSON.stringify(vistos));
135
130
  },
136
131
  },
137
132
  });
@@ -171,14 +166,7 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy {
171
166
  }
172
167
 
173
168
  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);
169
+ this.router.navigate(['/manual']);
182
170
  }
183
171
 
184
172
  irAVideo(): void {
@@ -14,6 +14,17 @@
14
14
  box-sizing: border-box;
15
15
  }
16
16
 
17
+ .tarjeta-opaca {
18
+ opacity: 0.15;
19
+ filter: grayscale(50%);
20
+ transition: opacity 0.25s ease, filter 0.25s ease;
21
+ pointer-events: none;
22
+ }
23
+
24
+ .contenido>div[cdkDrag] {
25
+ transition: opacity 0.25s ease, filter 0.25s ease;
26
+ }
27
+
17
28
  .cdk-drag-preview {
18
29
  box-sizing: border-box;
19
30
  border-radius: 8px;
@@ -1,11 +1,11 @@
1
1
  <div class="contenido" cdkDropList cdkDropListOrientation="mixed" (cdkDropListDropped)="drop($event)">
2
2
  @for (tarjeta of tarjetas; track tarjeta.titulo) {
3
- <div cdkDrag>
3
+ <div cdkDrag [class.tarjeta-opaca]="esDimmed(tarjeta)">
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') {