utn-cli 2.1.39 → 2.1.41

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "utn-cli",
3
- "version": "2.1.39",
3
+ "version": "2.1.41",
4
4
  "description": "Herramienta CLI unificada para la gestión de plantillas en SIGU.",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -0,0 +1,65 @@
1
+ .instruccion {
2
+ font-size: 0.875rem;
3
+ color: #555;
4
+ margin: 0 0 1rem 0;
5
+ }
6
+
7
+ .lista-items {
8
+ display: flex;
9
+ flex-direction: column;
10
+ gap: 0.5rem;
11
+ min-width: 280px;
12
+ }
13
+
14
+ .item {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 0.75rem;
18
+ padding: 0.75rem 1rem;
19
+ border-radius: 8px;
20
+ background: #f5f8ff;
21
+ border-left: 4px solid #1976d2;
22
+ }
23
+
24
+ .handle {
25
+ color: #999;
26
+ cursor: grab;
27
+ flex-shrink: 0;
28
+ }
29
+
30
+ .handle:active {
31
+ cursor: grabbing;
32
+ }
33
+
34
+ .item-icono {
35
+ color: #1976d2;
36
+ flex-shrink: 0;
37
+ }
38
+
39
+ .item-etiqueta {
40
+ flex: 1;
41
+ font-size: 0.95rem;
42
+ }
43
+
44
+ .cdk-drag-preview {
45
+ display: flex;
46
+ align-items: center;
47
+ gap: 0.75rem;
48
+ padding: 0.75rem 1rem;
49
+ border-radius: 8px;
50
+ background: white;
51
+ border-left: 4px solid #1976d2;
52
+ box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
53
+ }
54
+
55
+ .cdk-drag-placeholder {
56
+ opacity: 0.3;
57
+ }
58
+
59
+ .cdk-drag-animating {
60
+ transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
61
+ }
62
+
63
+ .lista-items.cdk-drop-list-dragging .item:not(.cdk-drag-placeholder) {
64
+ transition: transform 250ms cubic-bezier(0, 0, 0.2, 1);
65
+ }
@@ -0,0 +1,17 @@
1
+ <h2 mat-dialog-title>Reordenar menú</h2>
2
+ <mat-dialog-content>
3
+ <p class="instruccion">Arrastrá los elementos para cambiar el orden.</p>
4
+ <div cdkDropList (cdkDropListDropped)="drop($event)" class="lista-items">
5
+ @for (item of items; track item.id) {
6
+ <div cdkDrag class="item">
7
+ <mat-icon cdkDragHandle class="handle">drag_indicator</mat-icon>
8
+ <mat-icon class="item-icono">{{ item.icono }}</mat-icon>
9
+ <span class="item-etiqueta">{{ item.etiqueta }}</span>
10
+ </div>
11
+ }
12
+ </div>
13
+ </mat-dialog-content>
14
+ <mat-dialog-actions>
15
+ <button mat-button (click)="cancelar()">Cancelar</button>
16
+ <button mat-flat-button color="primary" (click)="guardar()">Guardar</button>
17
+ </mat-dialog-actions>
@@ -0,0 +1,41 @@
1
+ import { Component, Inject } from '@angular/core';
2
+ import { MAT_DIALOG_DATA, MatDialogRef, MatDialogTitle, MatDialogContent, MatDialogActions } from '@angular/material/dialog';
3
+ import { MatButtonModule } from '@angular/material/button';
4
+ import { MatIconModule } from '@angular/material/icon';
5
+ import { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
6
+
7
+ export interface ItemDeMenuDialog {
8
+ id: string;
9
+ etiqueta: string;
10
+ icono: string;
11
+ }
12
+
13
+ @Component({
14
+ selector: 'app-reordenar-menu',
15
+ standalone: true,
16
+ imports: [MatDialogTitle, MatDialogContent, MatDialogActions, MatButtonModule, MatIconModule, DragDropModule],
17
+ templateUrl: './reordenar-menu.component.html',
18
+ styleUrl: './reordenar-menu.component.css'
19
+ })
20
+ export class ReordenarMenuComponent {
21
+ items: ItemDeMenuDialog[];
22
+
23
+ constructor(
24
+ @Inject(MAT_DIALOG_DATA) public data: { items: ItemDeMenuDialog[] },
25
+ private dialogRef: MatDialogRef<ReordenarMenuComponent>
26
+ ) {
27
+ this.items = [...data.items];
28
+ }
29
+
30
+ drop(event: CdkDragDrop<ItemDeMenuDialog[]>): void {
31
+ moveItemInArray(this.items, event.previousIndex, event.currentIndex);
32
+ }
33
+
34
+ guardar(): void {
35
+ this.dialogRef.close(this.items);
36
+ }
37
+
38
+ cancelar(): void {
39
+ this.dialogRef.close();
40
+ }
41
+ }
@@ -130,27 +130,16 @@
130
130
  </button>
131
131
 
132
132
  <mat-menu #menuAplicaciones="matMenu">
133
- <button mat-menu-item (click)="irASugerencias()" title="Sugerencias">
134
- <mat-icon>lightbulb</mat-icon>
135
- <span>Sugerencias</span>
136
- </button>
137
- @if(TienePermiso) {
138
- <button mat-menu-item (click)="irAActividad()" title="Actividad de la cuenta">
139
- <mat-icon>history</mat-icon>
140
- <span>Actividad de la cuenta</span>
141
- </button>
142
- <button mat-menu-item (click)="irAEstadisticasDelModulo()" title="Estadísticas del módulo">
143
- <mat-icon>bar_chart</mat-icon>
144
- <span>Estadísticas del módulo</span>
133
+ @for (item of itemsDeMenuVisibles; track item.id) {
134
+ <button mat-menu-item (click)="item.accion()" [title]="item.etiqueta">
135
+ <mat-icon>{{ item.icono }}</mat-icon>
136
+ <span>{{ item.etiqueta }}</span>
145
137
  </button>
146
138
  }
147
- <button mat-menu-item (click)="irAAccesibilidad()" title="Accesibilidad mediante teclado">
148
- <mat-icon>accessibility</mat-icon>
149
- <span>Accesibilidad</span>
150
- </button>
151
- <button mat-menu-item (click)="irADeclaracionIA()" title="Declaración de uso de inteligencia artificial">
152
- <mat-icon>smart_toy</mat-icon>
153
- <span>Declaración de IA</span>
139
+ <mat-divider></mat-divider>
140
+ <button mat-menu-item (click)="abrirDialogReordenarMenu()" title="Reordenar elementos del menú">
141
+ <mat-icon>sort</mat-icon>
142
+ <span>Reordenar</span>
154
143
  </button>
155
144
  </mat-menu>
156
145
 
@@ -6,17 +6,23 @@ import { Location, CommonModule } from '@angular/common';
6
6
  import { MatIconModule } from '@angular/material/icon';
7
7
  import { MatTooltipModule } from '@angular/material/tooltip';
8
8
  import { MatDialog } from '@angular/material/dialog';
9
- import { ReporteDeIncidenciasComponent } from '../../../Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component';
10
- import { MensajesComponent } from '../../../Componentes/Nucleo/mensajes/mensajes.component';
11
- import { MensajeConfirmacionHTMLComponent } from '../../../Componentes/Nucleo/mensaje-confirmacion-html/mensaje-confirmacion-html';
12
- import { ReporteDeSugerenciasComponent } from '../../../Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component';
13
- import { EstadisticasDelModuloComponent } from '../../../Componentes/Nucleo/estadisticas-del-modulo/estadisticas-del-modulo.component';
9
+ import { MatDividerModule } from '@angular/material/divider';
10
+ import type { ItemDeMenuDialog } from '../../../Componentes/Nucleo/reordenar-menu/reordenar-menu.component';
14
11
 
15
12
  import { MatMenuModule } from '@angular/material/menu';
16
13
 
14
+ interface ItemDeMenu {
15
+ id: string;
16
+ etiqueta: string;
17
+ icono: string;
18
+ accion: () => void;
19
+ requierePermiso: boolean;
20
+ posicion: number;
21
+ }
22
+
17
23
  @Component({
18
24
  selector: 'app-contenedor-componentes',
19
- imports: [RouterOutlet, MatIconModule, MatTooltipModule, CommonModule, MatMenuModule],
25
+ imports: [RouterOutlet, MatIconModule, MatTooltipModule, CommonModule, MatMenuModule, MatDividerModule],
20
26
  templateUrl: './contenedor-componentes.component.html',
21
27
  styleUrl: './contenedor-componentes.component.css'
22
28
  })
@@ -41,6 +47,12 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
41
47
  public AnimarUsuariosActivos: boolean = true;
42
48
  private intervaloUsuarios: any;
43
49
 
50
+ public itemsDeMenu: ItemDeMenu[] = [];
51
+
52
+ get itemsDeMenuVisibles(): ItemDeMenu[] {
53
+ return this.itemsDeMenu.filter(i => !i.requierePermiso || this.TienePermiso);
54
+ }
55
+
44
56
  get esDashboard(): boolean { return this.router.url === '/'; }
45
57
  get tieneTarjetas(): boolean { return this.datosGlobalesService.cantidadDeTarjetas$.value > 0; }
46
58
  get filtro(): string { return this.datosGlobalesService.filtroDeTarjetas$.value; }
@@ -69,7 +81,79 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
69
81
  return datos ? JSON.parse(datos) : [];
70
82
  }
71
83
 
84
+ private inicializarItemsDeMenu(): void {
85
+ this.itemsDeMenu = [
86
+ { id: 'nav_sugerencias', etiqueta: 'Sugerencias', icono: 'lightbulb', accion: () => this.irASugerencias(), requierePermiso: false, posicion: 10 },
87
+ { id: 'nav_actividad', etiqueta: 'Actividad de la cuenta', icono: 'history', accion: () => this.irAActividad(), requierePermiso: true, posicion: 20 },
88
+ { id: 'nav_estadisticas', etiqueta: 'Estadísticas del módulo', icono: 'bar_chart', accion: () => this.irAEstadisticasDelModulo(), requierePermiso: true, posicion: 30 },
89
+ { id: 'nav_accesibilidad', etiqueta: 'Accesibilidad', icono: 'accessibility', accion: () => this.irAAccesibilidad(), requierePermiso: false, posicion: 40 },
90
+ { id: 'nav_declaracion_ia', etiqueta: 'Declaración de IA', icono: 'smart_toy', accion: () => this.irADeclaracionIA(), requierePermiso: false, posicion: 50 },
91
+ ];
92
+ }
93
+
94
+ private cargarOrdenDelMenu(): void {
95
+ this.http.get(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/obtener`).subscribe({
96
+ next: (datos: any) => {
97
+ const config: { Titulo: string; Posicion: number }[] = datos.body ?? [];
98
+ const navConfig = config.filter((c: any) => c.Titulo.startsWith('nav_'));
99
+ if (navConfig.length === 0) return;
100
+ navConfig.forEach((c: any) => {
101
+ const item = this.itemsDeMenu.find(i => i.id === c.Titulo);
102
+ if (item) item.posicion = c.Posicion;
103
+ });
104
+ this.itemsDeMenu.sort((a, b) => a.posicion - b.posicion);
105
+ },
106
+ error: () => { }
107
+ });
108
+ }
109
+
110
+ async abrirDialogReordenarMenu(): Promise<void> {
111
+ const itemsVisibles: ItemDeMenuDialog[] = this.itemsDeMenuVisibles.map(i => ({
112
+ id: i.id,
113
+ etiqueta: i.etiqueta,
114
+ icono: i.icono
115
+ }));
116
+
117
+ const { ReordenarMenuComponent } = await import('../../../Componentes/Nucleo/reordenar-menu/reordenar-menu.component');
118
+ this.dialog.open(ReordenarMenuComponent, { data: { items: itemsVisibles } })
119
+ .afterClosed()
120
+ .subscribe((resultado: ItemDeMenuDialog[] | undefined) => {
121
+ if (!resultado) return;
122
+ this.persistirOrdenDelMenu(resultado);
123
+ });
124
+ }
125
+
126
+ private persistirOrdenDelMenu(itemsOrdenados: ItemDeMenuDialog[]): void {
127
+ const idsOrdenados = itemsOrdenados.map(i => i.id);
128
+ const itemsNoVisibles = this.itemsDeMenu.filter(i => !idsOrdenados.includes(i.id));
129
+ const itemsVisiblesReordenados = idsOrdenados
130
+ .map(id => this.itemsDeMenu.find(i => i.id === id)!)
131
+ .filter(Boolean);
132
+
133
+ itemsVisiblesReordenados.forEach((item, index) => {
134
+ item.posicion = (index + 1) * 10;
135
+ });
136
+ itemsNoVisibles.forEach((item, index) => {
137
+ item.posicion = (itemsVisiblesReordenados.length + index + 1) * 10;
138
+ });
139
+
140
+ this.itemsDeMenu = [...itemsVisiblesReordenados, ...itemsNoVisibles];
141
+
142
+ const Configuraciones = this.itemsDeMenu.map(i => ({
143
+ Titulo: i.id,
144
+ Posicion: i.posicion,
145
+ ColorDeBorde: null
146
+ }));
147
+
148
+ this.http.post(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/actualizar`, {
149
+ Configuraciones
150
+ }).subscribe({ error: () => { } });
151
+ }
152
+
72
153
  ngOnInit() {
154
+ this.inicializarItemsDeMenu();
155
+ this.cargarOrdenDelMenu();
156
+
73
157
  const url = window.location.href.toLowerCase();
74
158
  if (url.includes('calidad')) {
75
159
  this.claseDelContenedor = 'contenedor calidad';
@@ -86,7 +170,7 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
86
170
  }, 60000);
87
171
  });
88
172
 
89
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/inicializar').subscribe((datos: any) => {
173
+ this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/inicializar').subscribe(async (datos: any) => {
90
174
  const body = datos.body;
91
175
 
92
176
  this.TienePermiso = body.TienePermiso;
@@ -108,6 +192,8 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
108
192
  this.EnlaceDelVideo = body.EnlaceDelVideo;
109
193
  this.Mensajes = body.Notificaciones;
110
194
 
195
+ const { MensajeConfirmacionHTMLComponent } = await import('../../../Componentes/Nucleo/mensaje-confirmacion-html/mensaje-confirmacion-html');
196
+
111
197
  if (body.Consentimiento?.Aceptaciones === 0) {
112
198
  const ConsentimientoInformadoId = body.Consentimiento.Consentimiento[0].ConsentimientoInformadoId;
113
199
  this.dialog.open(MensajeConfirmacionHTMLComponent, {
@@ -172,10 +258,10 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
172
258
  }
173
259
 
174
260
  irAMensajes(): void {
175
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerNotificaciones').subscribe((datos: any) => {
176
- let data = datos.body;
177
- this.dialog.open(MensajesComponent, { data });
178
- })
261
+ this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerNotificaciones').subscribe(async (datos: any) => {
262
+ const { MensajesComponent } = await import('../../../Componentes/Nucleo/mensajes/mensajes.component');
263
+ this.dialog.open(MensajesComponent, { data: datos.body });
264
+ });
179
265
  }
180
266
 
181
267
  irAAyuda(): void {
@@ -194,7 +280,8 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
194
280
  window.open(this.EnlaceDelVideo, '_blank', 'noopener,noreferrer');
195
281
  }
196
282
 
197
- irASoporte(): void {
283
+ async irASoporte(): Promise<void> {
284
+ const { ReporteDeIncidenciasComponent } = await import('../../../Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component');
198
285
  this.dialog.open(ReporteDeIncidenciasComponent);
199
286
  }
200
287
 
@@ -221,12 +308,14 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
221
308
  })
222
309
  }
223
310
 
224
- irASugerencias(): void {
311
+ async irASugerencias(): Promise<void> {
312
+ const { ReporteDeSugerenciasComponent } = await import('../../../Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component');
225
313
  this.dialog.open(ReporteDeSugerenciasComponent);
226
314
  }
227
315
 
228
316
  irAEstadisticasDelModulo(): void {
229
- this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerVistas').subscribe((datos: any) => {
317
+ this.http.get(this.datosGlobalesService.ObtenerURL() + 'misc/obtenerVistas').subscribe(async (datos: any) => {
318
+ const { EstadisticasDelModuloComponent } = await import('../../../Componentes/Nucleo/estadisticas-del-modulo/estadisticas-del-modulo.component');
230
319
  this.dialog.open(EstadisticasDelModuloComponent, { data: datos.body });
231
320
  });
232
321
  }