utn-cli 2.1.39 → 2.1.40

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.40",
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,28 @@ 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 { MatDividerModule } from '@angular/material/divider';
9
10
  import { ReporteDeIncidenciasComponent } from '../../../Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component';
10
11
  import { MensajesComponent } from '../../../Componentes/Nucleo/mensajes/mensajes.component';
11
12
  import { MensajeConfirmacionHTMLComponent } from '../../../Componentes/Nucleo/mensaje-confirmacion-html/mensaje-confirmacion-html';
12
13
  import { ReporteDeSugerenciasComponent } from '../../../Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component';
13
14
  import { EstadisticasDelModuloComponent } from '../../../Componentes/Nucleo/estadisticas-del-modulo/estadisticas-del-modulo.component';
15
+ import { ReordenarMenuComponent, ItemDeMenuDialog } from '../../../Componentes/Nucleo/reordenar-menu/reordenar-menu.component';
14
16
 
15
17
  import { MatMenuModule } from '@angular/material/menu';
16
18
 
19
+ interface ItemDeMenu {
20
+ id: string;
21
+ etiqueta: string;
22
+ icono: string;
23
+ accion: () => void;
24
+ requierePermiso: boolean;
25
+ posicion: number;
26
+ }
27
+
17
28
  @Component({
18
29
  selector: 'app-contenedor-componentes',
19
- imports: [RouterOutlet, MatIconModule, MatTooltipModule, CommonModule, MatMenuModule],
30
+ imports: [RouterOutlet, MatIconModule, MatTooltipModule, CommonModule, MatMenuModule, MatDividerModule],
20
31
  templateUrl: './contenedor-componentes.component.html',
21
32
  styleUrl: './contenedor-componentes.component.css'
22
33
  })
@@ -41,6 +52,12 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
41
52
  public AnimarUsuariosActivos: boolean = true;
42
53
  private intervaloUsuarios: any;
43
54
 
55
+ public itemsDeMenu: ItemDeMenu[] = [];
56
+
57
+ get itemsDeMenuVisibles(): ItemDeMenu[] {
58
+ return this.itemsDeMenu.filter(i => !i.requierePermiso || this.TienePermiso);
59
+ }
60
+
44
61
  get esDashboard(): boolean { return this.router.url === '/'; }
45
62
  get tieneTarjetas(): boolean { return this.datosGlobalesService.cantidadDeTarjetas$.value > 0; }
46
63
  get filtro(): string { return this.datosGlobalesService.filtroDeTarjetas$.value; }
@@ -69,7 +86,78 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
69
86
  return datos ? JSON.parse(datos) : [];
70
87
  }
71
88
 
89
+ private inicializarItemsDeMenu(): void {
90
+ this.itemsDeMenu = [
91
+ { id: 'nav_sugerencias', etiqueta: 'Sugerencias', icono: 'lightbulb', accion: () => this.irASugerencias(), requierePermiso: false, posicion: 10 },
92
+ { id: 'nav_actividad', etiqueta: 'Actividad de la cuenta', icono: 'history', accion: () => this.irAActividad(), requierePermiso: true, posicion: 20 },
93
+ { id: 'nav_estadisticas', etiqueta: 'Estadísticas del módulo', icono: 'bar_chart', accion: () => this.irAEstadisticasDelModulo(), requierePermiso: true, posicion: 30 },
94
+ { id: 'nav_accesibilidad', etiqueta: 'Accesibilidad', icono: 'accessibility', accion: () => this.irAAccesibilidad(), requierePermiso: false, posicion: 40 },
95
+ { id: 'nav_declaracion_ia', etiqueta: 'Declaración de IA', icono: 'smart_toy', accion: () => this.irADeclaracionIA(), requierePermiso: false, posicion: 50 },
96
+ ];
97
+ }
98
+
99
+ private cargarOrdenDelMenu(): void {
100
+ this.http.get(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/obtener`).subscribe({
101
+ next: (datos: any) => {
102
+ const config: { Titulo: string; Posicion: number }[] = datos.body ?? [];
103
+ const navConfig = config.filter((c: any) => c.Titulo.startsWith('nav_'));
104
+ if (navConfig.length === 0) return;
105
+ navConfig.forEach((c: any) => {
106
+ const item = this.itemsDeMenu.find(i => i.id === c.Titulo);
107
+ if (item) item.posicion = c.Posicion;
108
+ });
109
+ this.itemsDeMenu.sort((a, b) => a.posicion - b.posicion);
110
+ },
111
+ error: () => { }
112
+ });
113
+ }
114
+
115
+ abrirDialogReordenarMenu(): void {
116
+ const itemsVisibles: ItemDeMenuDialog[] = this.itemsDeMenuVisibles.map(i => ({
117
+ id: i.id,
118
+ etiqueta: i.etiqueta,
119
+ icono: i.icono
120
+ }));
121
+
122
+ this.dialog.open(ReordenarMenuComponent, { data: { items: itemsVisibles } })
123
+ .afterClosed()
124
+ .subscribe((resultado: ItemDeMenuDialog[] | undefined) => {
125
+ if (!resultado) return;
126
+ this.persistirOrdenDelMenu(resultado);
127
+ });
128
+ }
129
+
130
+ private persistirOrdenDelMenu(itemsOrdenados: ItemDeMenuDialog[]): void {
131
+ const idsOrdenados = itemsOrdenados.map(i => i.id);
132
+ const itemsNoVisibles = this.itemsDeMenu.filter(i => !idsOrdenados.includes(i.id));
133
+ const itemsVisiblesReordenados = idsOrdenados
134
+ .map(id => this.itemsDeMenu.find(i => i.id === id)!)
135
+ .filter(Boolean);
136
+
137
+ itemsVisiblesReordenados.forEach((item, index) => {
138
+ item.posicion = (index + 1) * 10;
139
+ });
140
+ itemsNoVisibles.forEach((item, index) => {
141
+ item.posicion = (itemsVisiblesReordenados.length + index + 1) * 10;
142
+ });
143
+
144
+ this.itemsDeMenu = [...itemsVisiblesReordenados, ...itemsNoVisibles];
145
+
146
+ const Configuraciones = this.itemsDeMenu.map(i => ({
147
+ Titulo: i.id,
148
+ Posicion: i.posicion,
149
+ ColorDeBorde: null
150
+ }));
151
+
152
+ this.http.post(`${this.datosGlobalesService.ObtenerURL()}ConfiguracionDeTarjetas/actualizar`, {
153
+ Configuraciones
154
+ }).subscribe({ error: () => { } });
155
+ }
156
+
72
157
  ngOnInit() {
158
+ this.inicializarItemsDeMenu();
159
+ this.cargarOrdenDelMenu();
160
+
73
161
  const url = window.location.href.toLowerCase();
74
162
  if (url.includes('calidad')) {
75
163
  this.claseDelContenedor = 'contenedor calidad';