utn-cli 2.1.38 → 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.
Files changed (18) hide show
  1. package/package.json +1 -1
  2. package/templates/backend/rutas/misc.js +14 -0
  3. package/templates/backend/servicios/Nucleo/Miscelaneas.js +125 -0
  4. package/templates/frontend/src/app/Componentes/Nucleo/calendario-publico/calendario-publico.component.css +236 -0
  5. package/templates/frontend/src/app/Componentes/Nucleo/calendario-publico/calendario-publico.component.html +172 -0
  6. package/templates/frontend/src/app/Componentes/Nucleo/calendario-publico/calendario-publico.component.ts +106 -0
  7. package/templates/frontend/src/app/Componentes/Nucleo/reordenar-menu/reordenar-menu.component.css +65 -0
  8. package/templates/frontend/src/app/Componentes/Nucleo/reordenar-menu/reordenar-menu.component.html +17 -0
  9. package/templates/frontend/src/app/Componentes/Nucleo/reordenar-menu/reordenar-menu.component.ts +41 -0
  10. package/templates/frontend/src/app/Paginas/Nucleo/accesibilidad/accesibilidad.component.css +239 -0
  11. package/templates/frontend/src/app/Paginas/Nucleo/accesibilidad/accesibilidad.component.html +289 -0
  12. package/templates/frontend/src/app/Paginas/Nucleo/accesibilidad/accesibilidad.component.ts +28 -0
  13. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.html +13 -12
  14. package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.ts +102 -1
  15. package/templates/frontend/src/app/Paginas/Nucleo/declaracion-ia/declaracion-ia.component.css +149 -0
  16. package/templates/frontend/src/app/Paginas/Nucleo/declaracion-ia/declaracion-ia.component.html +69 -0
  17. package/templates/frontend/src/app/Paginas/Nucleo/declaracion-ia/declaracion-ia.component.ts +11 -0
  18. package/templates/frontend/src/app/app.routes.ts +8 -0
@@ -130,20 +130,17 @@
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
  }
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>
143
+ </button>
147
144
  </mat-menu>
148
145
 
149
146
  @if(EnlaceDelVideo !== '-') {
@@ -154,6 +151,10 @@
154
151
  <button class="botonDeNavegacion" matTooltip="Ayuda" title="Ayuda" (click)="irAAyuda()">
155
152
  <mat-icon>help</mat-icon>
156
153
  </button>
154
+ <button class="botonDeNavegacion" matTooltip="Calensario institucional" title="Calendario institucional"
155
+ (click)="irACalendario()">
156
+ <mat-icon>calendar_view_week</mat-icon>
157
+ </button>
157
158
  <button class="botonDeNavegacion" matTooltip="Reporte" title="Reporte" (click)="irASoporte()">
158
159
  <mat-icon>support_agent</mat-icon>
159
160
  </button>
@@ -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,10 +52,21 @@ 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; }
47
64
 
65
+ async irACalendario(): Promise<void> {
66
+ const { CalendarioPublicoComponent } = await import('../../../Componentes/Nucleo/calendario-publico/calendario-publico.component');
67
+ this.dialog.open(CalendarioPublicoComponent, { width: '760px', maxWidth: '95vw' });
68
+ }
69
+
48
70
  onFiltroChange(event: Event): void {
49
71
  this.datosGlobalesService.filtroDeTarjetas$.next((event.target as HTMLInputElement).value);
50
72
  }
@@ -64,7 +86,78 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
64
86
  return datos ? JSON.parse(datos) : [];
65
87
  }
66
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
+
67
157
  ngOnInit() {
158
+ this.inicializarItemsDeMenu();
159
+ this.cargarOrdenDelMenu();
160
+
68
161
  const url = window.location.href.toLowerCase();
69
162
  if (url.includes('calidad')) {
70
163
  this.claseDelContenedor = 'contenedor calidad';
@@ -177,6 +270,14 @@ export class ContenedorComponentesComponent implements OnInit, OnDestroy, AfterV
177
270
  this.router.navigate(['/manual']);
178
271
  }
179
272
 
273
+ irAAccesibilidad(): void {
274
+ this.router.navigate(['/accesibilidad']);
275
+ }
276
+
277
+ irADeclaracionIA(): void {
278
+ this.router.navigate(['/ia']);
279
+ }
280
+
180
281
  irAVideo(): void {
181
282
  window.open(this.EnlaceDelVideo, '_blank', 'noopener,noreferrer');
182
283
  }
@@ -0,0 +1,149 @@
1
+ .ia-layout {
2
+ max-width: 820px;
3
+ margin: 0 auto;
4
+ padding: 24px 16px;
5
+ font-family: 'Roboto', sans-serif;
6
+ }
7
+
8
+ .ia-main {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 28px;
12
+ }
13
+
14
+ /* Encabezado */
15
+ .ia-encabezado {
16
+ display: flex;
17
+ align-items: flex-start;
18
+ gap: 14px;
19
+ padding: 20px 24px;
20
+ background: linear-gradient(135deg, #002f6b 0%, #1565c0 100%);
21
+ color: white;
22
+ border-radius: 12px;
23
+ }
24
+
25
+ .ia-icono-header {
26
+ font-size: 40px !important;
27
+ width: 40px !important;
28
+ height: 40px !important;
29
+ flex-shrink: 0;
30
+ margin-top: 4px;
31
+ opacity: 0.9;
32
+ }
33
+
34
+ .ia-encabezado h1 {
35
+ margin: 0 0 6px 0;
36
+ font-size: 22px;
37
+ font-weight: 700;
38
+ }
39
+
40
+ .ia-encabezado p {
41
+ margin: 0;
42
+ font-size: 13px;
43
+ opacity: 0.85;
44
+ }
45
+
46
+ /* Secciones */
47
+ .ia-seccion {
48
+ background: white;
49
+ border: 1px solid #d0daea;
50
+ border-radius: 10px;
51
+ padding: 20px 24px;
52
+ }
53
+
54
+ .ia-titulo-seccion {
55
+ display: flex;
56
+ align-items: center;
57
+ gap: 8px;
58
+ font-size: 17px;
59
+ font-weight: 700;
60
+ color: #002f6b;
61
+ border-bottom: 2px solid #002f6b;
62
+ padding-bottom: 8px;
63
+ margin: 0 0 14px 0;
64
+ }
65
+
66
+ .ia-titulo-seccion mat-icon {
67
+ font-size: 20px !important;
68
+ width: 20px !important;
69
+ height: 20px !important;
70
+ color: #1976d2;
71
+ }
72
+
73
+ .ia-seccion p {
74
+ margin: 0 0 12px 0;
75
+ color: #333;
76
+ line-height: 1.65;
77
+ font-size: 14px;
78
+ }
79
+
80
+ .ia-seccion p:last-child {
81
+ margin-bottom: 0;
82
+ }
83
+
84
+ /* Bloque de declaración principal */
85
+ .ia-declaracion {
86
+ border-color: #2e7d32;
87
+ border-left: 5px solid #2e7d32;
88
+ background: #f1f8f1;
89
+ }
90
+
91
+ .ia-declaracion-contenido {
92
+ display: flex;
93
+ align-items: flex-start;
94
+ gap: 16px;
95
+ }
96
+
97
+ .ia-declaracion-icono {
98
+ font-size: 36px !important;
99
+ width: 36px !important;
100
+ height: 36px !important;
101
+ color: #2e7d32;
102
+ flex-shrink: 0;
103
+ margin-top: 2px;
104
+ }
105
+
106
+ .ia-declaracion h2 {
107
+ margin: 0 0 10px 0;
108
+ font-size: 18px;
109
+ font-weight: 700;
110
+ color: #1b5e20;
111
+ }
112
+
113
+ /* Lista */
114
+ .ia-lista {
115
+ padding-left: 20px;
116
+ margin: 10px 0 0 0;
117
+ color: #333;
118
+ font-size: 14px;
119
+ line-height: 1.7;
120
+ }
121
+
122
+ .ia-lista li {
123
+ margin-bottom: 4px;
124
+ }
125
+
126
+ /* Pie */
127
+ .ia-pie {
128
+ display: flex;
129
+ align-items: center;
130
+ gap: 8px;
131
+ font-size: 12px;
132
+ color: #666;
133
+ padding: 12px 0 0 0;
134
+ border-top: 1px solid #e0e7ef;
135
+ }
136
+
137
+ .ia-pie mat-icon {
138
+ font-size: 16px !important;
139
+ width: 16px !important;
140
+ height: 16px !important;
141
+ color: #999;
142
+ }
143
+
144
+ @media (max-width: 600px) {
145
+ .ia-declaracion-contenido {
146
+ flex-direction: column;
147
+ gap: 10px;
148
+ }
149
+ }
@@ -0,0 +1,69 @@
1
+ <div class="ia-layout">
2
+
3
+ <main class="ia-main" role="main" aria-label="Declaración de uso de inteligencia artificial">
4
+
5
+ <header class="ia-encabezado">
6
+ <mat-icon class="ia-icono-header">smart_toy</mat-icon>
7
+ <div>
8
+ <h1>Declaración de uso de inteligencia artificial</h1>
9
+ </div>
10
+ </header>
11
+
12
+ <!-- Declaración principal -->
13
+ <section class="ia-seccion ia-declaracion" aria-labelledby="titulo-declaracion">
14
+ <div class="ia-declaracion-contenido">
15
+ <mat-icon class="ia-declaracion-icono">verified</mat-icon>
16
+ <div>
17
+ <h2 id="titulo-declaracion">Este módulo no utiliza inteligencia artificial</h2>
18
+ <p>
19
+ Este módulo no incorpora ninguna aplicación,
20
+ modelo o algoritmo de inteligencia artificial como parte de su funcionamiento.
21
+ </p>
22
+ <p>
23
+ Todas las operaciones de este módulo se ejecutan mediante lógica determinista y procedimientos
24
+ establecidos, sin intervención de sistemas de aprendizaje automático ni de
25
+ procesamiento automatizado de decisiones.
26
+ </p>
27
+ </div>
28
+ </div>
29
+ </section>
30
+
31
+ <!-- Qué no se considera IA de la aplicación -->
32
+ <section class="ia-seccion" aria-labelledby="titulo-alcance">
33
+ <h2 id="titulo-alcance" class="ia-titulo-seccion">
34
+ <mat-icon>info</mat-icon> Alcance de esta declaración
35
+ </h2>
36
+ <p>
37
+ Esta declaración se refiere a la funcionalidad del módulo tal como es accesible por los
38
+ usuarios finales. No cubre herramientas internas de desarrollo de software utilizadas
39
+ por el equipo técnico durante la construcción del sistema, las cuales son externas al
40
+ módulo y no procesan datos de los usuarios.
41
+ </p>
42
+ </section>
43
+
44
+ <!-- Qué haría que esto cambie -->
45
+ <section class="ia-seccion" aria-labelledby="titulo-cambios">
46
+ <h2 id="titulo-cambios" class="ia-titulo-seccion">
47
+ <mat-icon>update</mat-icon> Actualización de esta declaración
48
+ </h2>
49
+ <p>
50
+ Si en versiones futuras se incorporara cualquier componente de inteligencia artificial
51
+ —como asistentes de planificación, sugerencias automáticas o análisis predictivo—,
52
+ esta sección será actualizada para documentar:
53
+ </p>
54
+ <ul class="ia-lista">
55
+ <li>Las razones para su incorporación.</li>
56
+ <li>Los modelos algorítmicos empleados.</li>
57
+ <li>Los datos que orientan dichos modelos.</li>
58
+ <li>La lógica de utilización y sus limitaciones.</li>
59
+ </ul>
60
+ </section>
61
+
62
+ <!-- Pie con fecha -->
63
+ <footer class="ia-pie">
64
+ <mat-icon>calendar_today</mat-icon>
65
+ <span>Declaración vigente a partir del 19 de mayo de 2026. Universidad Técnica Nacional.</span>
66
+ </footer>
67
+
68
+ </main>
69
+ </div>
@@ -0,0 +1,11 @@
1
+ import { Component } from '@angular/core';
2
+ import { MatIconModule } from '@angular/material/icon';
3
+
4
+ @Component({
5
+ selector: 'app-declaracion-ia',
6
+ standalone: true,
7
+ imports: [MatIconModule],
8
+ templateUrl: './declaracion-ia.component.html',
9
+ styleUrl: './declaracion-ia.component.css'
10
+ })
11
+ export class DeclaracionIaComponent { }
@@ -13,4 +13,12 @@ export const routes: Routes = [
13
13
  path: 'manual',
14
14
  loadComponent: () => import('./Componentes/Nucleo/manual/manual.component').then(m => m.ManualComponent)
15
15
  },
16
+ {
17
+ path: 'accesibilidad',
18
+ loadComponent: () => import('./Paginas/Nucleo/accesibilidad/accesibilidad.component').then(m => m.AccesibilidadComponent)
19
+ },
20
+ {
21
+ path: 'ia',
22
+ loadComponent: () => import('./Paginas/Nucleo/declaracion-ia/declaracion-ia.component').then(m => m.DeclaracionIaComponent)
23
+ },
16
24
  ];