utn-cli 2.0.78 → 2.0.79
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 +1 -1
- package/templates/backend/server.js +2 -1
- package/templates/backend/servicios/Nucleo/Miscelaneas.js +23 -14
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.css +133 -0
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.html +35 -0
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.ts +53 -0
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.html +27 -31
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.ts +14 -4
package/package.json
CHANGED
|
@@ -11,7 +11,8 @@ app.use(express.json());
|
|
|
11
11
|
app.use(helmet());
|
|
12
12
|
app.use(async (solicitud, respuesta, next) => {
|
|
13
13
|
let Usuario = null;
|
|
14
|
-
|
|
14
|
+
// Si length es > a 43 es un JWT, de lo contario es un UUID
|
|
15
|
+
if (solicitud.headers.authorization.length > 43) {
|
|
15
16
|
Usuario = await Miscelaneo.obtenerDatosDelUsuario(solicitud.headers.authorization) ?? null;
|
|
16
17
|
} else {
|
|
17
18
|
Usuario = solicitud.user ?? null;
|
|
@@ -160,10 +160,10 @@ class Miscelaneo {
|
|
|
160
160
|
</div>`;
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
-
GenerarReporteHTMLRegistrosVerticales(ElementosParaLaTabla, ParametrosExcluidos = [], ParametrosExtra = []) {
|
|
164
|
-
//Ejemplo ElementosParaLaTabla:
|
|
163
|
+
GenerarReporteHTMLRegistrosVerticales(ElementosParaLaTabla, ParametrosExcluidos = [], ParametrosExtra = [], MostrarEncabezado = true) {
|
|
164
|
+
// Ejemplo ElementosParaLaTabla:
|
|
165
165
|
// [
|
|
166
|
-
//
|
|
166
|
+
// {
|
|
167
167
|
// "Fecha del traslado": "16-03-2026",
|
|
168
168
|
// "Justificacion": "Traslado por mantenimiento",
|
|
169
169
|
// "Dependencia que entrega": "Juan Pérez",
|
|
@@ -192,16 +192,21 @@ class Miscelaneo {
|
|
|
192
192
|
// Estado: "Excelente",
|
|
193
193
|
// IdentificadorOrigen: 101,
|
|
194
194
|
// IdentificadorDestino: 202
|
|
195
|
-
// }
|
|
196
195
|
// ]
|
|
197
|
-
// Espera un Array
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
196
|
+
// Espera un Array de objetos.
|
|
197
|
+
//
|
|
198
|
+
// Ejemplo ParametrosExcluidos:
|
|
199
|
+
// const ParametrosExcluidos = ['IdentificadorOrigen', 'Justificacion', 'IdentificadorDestino'];
|
|
200
|
+
// Valores devueltos por el QUERY que no sean necesarios mostrar en la tabla.
|
|
201
|
+
//
|
|
202
|
+
// Ejemplo ParametrosExtra:
|
|
203
|
+
// const ParametrosExtra = ['Toma física'];
|
|
204
|
+
// En caso de necesitar un espacio extra en la tabla que no se encuentra en la lista principal.
|
|
205
|
+
//
|
|
206
|
+
// Ejemplo MostrarEncabezado:
|
|
207
|
+
// MostrarEncabezado = true (Valor por defecto, muestra la barra azul "Registro N° 1")
|
|
208
|
+
// MostrarEncabezado = false (Oculta la barra azul, ideal para usar la función con un único registro)
|
|
201
209
|
|
|
202
|
-
//Ejemplo ParametrosExtra:
|
|
203
|
-
//const ParametrosExtra = ['Toma física'];
|
|
204
|
-
//En caso de necesitar un espacio extra en la tabla que no se encuentra en la lista principal.
|
|
205
210
|
if (!ElementosParaLaTabla?.length) return '<p>No hay datos para mostrar.</p>';
|
|
206
211
|
|
|
207
212
|
const baseColumnas = Object.keys(ElementosParaLaTabla[0]).filter(col => !ParametrosExcluidos.includes(col));
|
|
@@ -221,12 +226,16 @@ class Miscelaneo {
|
|
|
221
226
|
`;
|
|
222
227
|
}).join('');
|
|
223
228
|
|
|
224
|
-
|
|
225
|
-
<div style="margin-top: 20px; page-break-inside: avoid; border: 1px solid #ccc; border-radius: 8px; overflow: hidden;">
|
|
226
|
-
|
|
229
|
+
const encabezadoHTML = MostrarEncabezado ? `
|
|
227
230
|
<div style="background-color: #002f6b; color: white; padding: 6px 10px; font-weight: bold; font-size: 14px;">
|
|
228
231
|
Registro N° ${index + 1}
|
|
229
232
|
</div>
|
|
233
|
+
` : '';
|
|
234
|
+
|
|
235
|
+
htmlFinal += `
|
|
236
|
+
<div style="margin-top: 20px; page-break-inside: avoid; border: 1px solid #ccc; border-radius: 8px; overflow: hidden;">
|
|
237
|
+
|
|
238
|
+
${encabezadoHTML}
|
|
230
239
|
|
|
231
240
|
<table style="border: none; border-radius: 0; margin-top: 0;">
|
|
232
241
|
<tbody>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.contenedor {
|
|
8
|
+
width: 100%;
|
|
9
|
+
/* Ocupa todo el espacio que le dé el padre (cdkDrag) */
|
|
10
|
+
background: #ffffff 0% 0% no-repeat padding-box;
|
|
11
|
+
box-shadow: 0px 3px 6px #00000029;
|
|
12
|
+
border-radius: 10px;
|
|
13
|
+
opacity: 1;
|
|
14
|
+
padding: 16px;
|
|
15
|
+
cursor: pointer;
|
|
16
|
+
display: flex;
|
|
17
|
+
flex-direction: column;
|
|
18
|
+
height: 100%;
|
|
19
|
+
/* Asegura que todas las tarjetas tengan la misma altura si se desea */
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* Eliminamos los media queries antiguos que forzaban anchos en vw */
|
|
24
|
+
|
|
25
|
+
.contenedor .cabecera_contenedor {
|
|
26
|
+
display: flex;
|
|
27
|
+
flex-direction: row;
|
|
28
|
+
align-items: flex-start;
|
|
29
|
+
/* Alinea los elementos al inicio (arriba) */
|
|
30
|
+
justify-content: space-between;
|
|
31
|
+
/* Separa el texto del icono */
|
|
32
|
+
gap: 8px;
|
|
33
|
+
/* Espacio mínimo entre texto e icono */
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
.contenedor .cabecera_contenedor .icono {
|
|
37
|
+
background: #eef2ff 0% 0% no-repeat padding-box;
|
|
38
|
+
opacity: 1;
|
|
39
|
+
border-radius: 50%;
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: center;
|
|
43
|
+
width: 48px;
|
|
44
|
+
height: 48px;
|
|
45
|
+
min-width: 48px;
|
|
46
|
+
/* Evita que flexbox lo encoja */
|
|
47
|
+
color: #1b3069;
|
|
48
|
+
font-size: x-small;
|
|
49
|
+
font-weight: bold;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.contenedor .cabecera_contenedor .icono .numero {
|
|
53
|
+
font-size: large;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.contenedor .cabecera {
|
|
57
|
+
flex: 1;
|
|
58
|
+
/* Permite que el texto ocupe todo el espacio sobrante */
|
|
59
|
+
min-width: 0;
|
|
60
|
+
/* Permite truncar texto si es muy largo en móviles */
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.contenedor .cabecera .titulo {
|
|
64
|
+
text-align: left;
|
|
65
|
+
font-family: "Roboto";
|
|
66
|
+
font-size: large;
|
|
67
|
+
font-weight: 600;
|
|
68
|
+
letter-spacing: 0px;
|
|
69
|
+
color: #000000;
|
|
70
|
+
opacity: 1;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
.contenedor .cabecera .descripcion {
|
|
74
|
+
text-align: left;
|
|
75
|
+
font-family: "Roboto";
|
|
76
|
+
font-size: small;
|
|
77
|
+
letter-spacing: 0px;
|
|
78
|
+
color: #000000;
|
|
79
|
+
opacity: 1;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.contenedor .contenido {
|
|
83
|
+
text-align: left;
|
|
84
|
+
font-family: "Roboto";
|
|
85
|
+
font-size: small;
|
|
86
|
+
letter-spacing: 0px;
|
|
87
|
+
color: #000000;
|
|
88
|
+
opacity: 1;
|
|
89
|
+
margin-left: 3%;
|
|
90
|
+
margin-top: 5%;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.contenedor .cantidad {
|
|
94
|
+
min-height: 20px;
|
|
95
|
+
width: 100%;
|
|
96
|
+
justify-content: center;
|
|
97
|
+
font-family: "Roboto";
|
|
98
|
+
font-size: large;
|
|
99
|
+
font-weight: 600;
|
|
100
|
+
letter-spacing: 0px;
|
|
101
|
+
color: #000000;
|
|
102
|
+
opacity: 1;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.contenedor .izquierda {
|
|
106
|
+
margin-top: 20%;
|
|
107
|
+
align-items: start;
|
|
108
|
+
color: #1b3069;
|
|
109
|
+
font-weight: 700;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.contenedor .pie {
|
|
113
|
+
border-top: 2px solid #eef2ff;
|
|
114
|
+
opacity: 1;
|
|
115
|
+
padding: 5%;
|
|
116
|
+
position: relative;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.contenedor .linea {
|
|
120
|
+
width: 0%;
|
|
121
|
+
border: 1px solid #95d03a;
|
|
122
|
+
opacity: 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.contenedor .pie p {
|
|
126
|
+
text-align: left;
|
|
127
|
+
font: normal normal normal 15px/20px Roboto;
|
|
128
|
+
letter-spacing: 0px;
|
|
129
|
+
color: #1b3069;
|
|
130
|
+
opacity: 1;
|
|
131
|
+
font-size: small;
|
|
132
|
+
text-decoration: none;
|
|
133
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
<div class="contenedor" (click)="onClick()" [style.border-left]="ColorDeBorde ? '10px solid ' + ColorDeBorde : ''">
|
|
2
|
+
<div class="cabecera_contenedor">
|
|
3
|
+
<div class="cabecera">
|
|
4
|
+
<p class="titulo">
|
|
5
|
+
{{ titulo }}
|
|
6
|
+
</p>
|
|
7
|
+
<p class="descripcion">
|
|
8
|
+
{{ descripcion }}
|
|
9
|
+
</p>
|
|
10
|
+
</div>
|
|
11
|
+
<div class="icono">
|
|
12
|
+
@if(icono){
|
|
13
|
+
<mat-icon [fontIcon]="icono"></mat-icon>
|
|
14
|
+
} @if(numero){
|
|
15
|
+
<p class="numero">{{ numero }}</p>
|
|
16
|
+
}
|
|
17
|
+
</div>
|
|
18
|
+
</div>
|
|
19
|
+
<div class="contenido">
|
|
20
|
+
@if(contenido.length>0){ @for(item of contenido[0];track item){
|
|
21
|
+
<p>{{ item }}</p>
|
|
22
|
+
}
|
|
23
|
+
<hr />
|
|
24
|
+
@for(item of contenido[1];track item){
|
|
25
|
+
<p>{{ item }}</p>
|
|
26
|
+
} }
|
|
27
|
+
</div>
|
|
28
|
+
<div [class]="cantidadLugar">
|
|
29
|
+
<p>{{ (cantidadAMostrar !== 0 ? cantidadAMostrar : '') }}</p>
|
|
30
|
+
</div>
|
|
31
|
+
<!-- <hr [id]="'porcentaje'+titulo" class="linea" [matTooltip]="'Cantidad: ' + cantidad" matTooltipPosition="above"/>
|
|
32
|
+
<div class="pie">
|
|
33
|
+
<p>{{Etiqueta}}</p>
|
|
34
|
+
</div> -->
|
|
35
|
+
</div>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { AfterViewInit, Component, EventEmitter, Input, Output } from "@angular/core";
|
|
2
|
+
import { MatIconModule } from "@angular/material/icon";
|
|
3
|
+
import { MatCardModule } from "@angular/material/card";
|
|
4
|
+
import { MatTooltipModule } from "@angular/material/tooltip";
|
|
5
|
+
import { Router } from "@angular/router";
|
|
6
|
+
|
|
7
|
+
@Component({
|
|
8
|
+
selector: "app-tarjeta-personalizada",
|
|
9
|
+
standalone: true,
|
|
10
|
+
imports: [MatIconModule, MatCardModule, MatTooltipModule],
|
|
11
|
+
templateUrl: "./tarjeta-personalizada.component.html",
|
|
12
|
+
styleUrl: "./tarjeta-personalizada.component.css"
|
|
13
|
+
})
|
|
14
|
+
export class TarjetaPersonalizadaComponent implements AfterViewInit {
|
|
15
|
+
@Input() titulo: string = "";
|
|
16
|
+
@Input() descripcion: string = "";
|
|
17
|
+
@Input() icono: any = null;
|
|
18
|
+
@Input() numero: any = null;
|
|
19
|
+
@Input() contenido: any = [];
|
|
20
|
+
@Input() cantidad: number | undefined;
|
|
21
|
+
@Input() cantidadLugar: string = "cantidad";
|
|
22
|
+
@Input() rutaASeguir: string = '';
|
|
23
|
+
@Input() cantidadMaxima: number | undefined;
|
|
24
|
+
@Input() cantidadAMostrar: number = 0;
|
|
25
|
+
@Input() Etiqueta: string = "CONTINUAR";
|
|
26
|
+
@Input() ColorDeBorde: string = "";
|
|
27
|
+
@Output() etiquetaClick = new EventEmitter<void>();
|
|
28
|
+
|
|
29
|
+
constructor(private ruta: Router) { }
|
|
30
|
+
|
|
31
|
+
onClick() {
|
|
32
|
+
if(this.etiquetaClick!==undefined){
|
|
33
|
+
this.etiquetaClick.emit();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
this.ruta.navigate([this.rutaASeguir]);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ngOnInit() {
|
|
40
|
+
this.cantidadAMostrar = (this.cantidadMaxima ? this.cantidadMaxima : 0) - (this.cantidad ? this.cantidad : 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
ngAfterViewInit(): void {
|
|
44
|
+
if (this.cantidad) {
|
|
45
|
+
let porcentaje = 100;
|
|
46
|
+
if (this.cantidadMaxima) {
|
|
47
|
+
porcentaje = (this.cantidad / this.cantidadMaxima) * 100;
|
|
48
|
+
}
|
|
49
|
+
const linea = document.getElementById('porcentaje' + this.titulo) as HTMLHRElement;
|
|
50
|
+
linea.style.width = `${porcentaje}%`;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.html
CHANGED
|
@@ -1,35 +1,31 @@
|
|
|
1
1
|
<div class="contenido" cdkDropList cdkDropListOrientation="mixed" (cdkDropListDropped)="drop($event)">
|
|
2
2
|
@for (tarjeta of tarjetas; track tarjeta.titulo) {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
</app-tarjeta-reporte>
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
</div>
|
|
3
|
+
<div cdkDrag>
|
|
4
|
+
@switch (tarjeta.type) {
|
|
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">>
|
|
9
|
+
</app-tarjeta>
|
|
10
|
+
}
|
|
11
|
+
@case ('multiple') {
|
|
12
|
+
<app-tarjeta-multiple [titulo]="tarjeta.titulo" [descripcion]="tarjeta.descripcion" [rutas]="tarjeta.rutas"
|
|
13
|
+
[ColorDeBorde]="tarjeta.ColorDeBorde ? tarjeta.ColorDeBorde : ''">
|
|
14
|
+
</app-tarjeta-multiple>
|
|
15
|
+
}
|
|
16
|
+
@case ('report') {
|
|
17
|
+
<app-tarjeta-reporte [reporteAGenerar]="tarjeta.reporteAGenerar" [titulo]="tarjeta.titulo"
|
|
18
|
+
[descripcion]="tarjeta.descripcion" [icono]="tarjeta.icono"
|
|
19
|
+
[ColorDeBorde]="tarjeta.ColorDeBorde ? tarjeta.ColorDeBorde : ''" (GenerarReporte)="GenerarReporte($event)">
|
|
20
|
+
</app-tarjeta-reporte>
|
|
21
|
+
}
|
|
22
|
+
@case ('custom') {
|
|
23
|
+
<app-tarjeta-personalizada [titulo]="tarjeta.titulo" [descripcion]="tarjeta.descripcion" [icono]="tarjeta.icono"
|
|
24
|
+
[Etiqueta]="tarjeta.etiqueta" [ColorDeBorde]="tarjeta.ColorDeBorde ? tarjeta.ColorDeBorde : ''"
|
|
25
|
+
(etiquetaClick)="EjecutarAccionPersonalizada(tarjeta.accion)">
|
|
26
|
+
</app-tarjeta-personalizada>
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
</div>
|
|
34
30
|
}
|
|
35
31
|
</div>
|
package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.ts
CHANGED
|
@@ -3,16 +3,17 @@ import { CommonModule } from "@angular/common";
|
|
|
3
3
|
import { TarjetaComponent } from "../../Componentes/Nucleo/tarjeta/tarjeta.component";
|
|
4
4
|
import { TarjetaMultipleComponent } from "../../Componentes/Nucleo/tarjeta-multiple/tarjeta-multiple.component";
|
|
5
5
|
import { TarjetaReporteComponent } from "../../Componentes/Nucleo/tarjeta-reporte/tarjeta-reporte.component";
|
|
6
|
+
import { TarjetaPersonalizadaComponent } from "../../Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component";
|
|
6
7
|
import { HttpClient } from "@angular/common/http";
|
|
7
8
|
import { DatosGlobalesService } from '../../datos-globales.service';
|
|
8
9
|
import { DragDropModule, CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop';
|
|
9
10
|
|
|
10
11
|
interface BaseTarjetaConfig {
|
|
11
|
-
type: 'single' | 'multiple' | 'report';
|
|
12
|
+
type: 'single' | 'multiple' | 'report' | 'custom';
|
|
12
13
|
position: number;
|
|
13
14
|
titulo: string;
|
|
14
15
|
descripcion: string;
|
|
15
|
-
acceso?: boolean;
|
|
16
|
+
acceso?: boolean;
|
|
16
17
|
ColorDeBorde?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -20,6 +21,8 @@ interface TarjetaConfig extends BaseTarjetaConfig {
|
|
|
20
21
|
type: 'single';
|
|
21
22
|
rutaASeguir: string;
|
|
22
23
|
icono: string;
|
|
24
|
+
cantidad?: number;
|
|
25
|
+
cantidadMaxima?: number;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
interface RutaMenuItem {
|
|
@@ -38,12 +41,19 @@ interface TarjetaReporteConfig extends BaseTarjetaConfig {
|
|
|
38
41
|
icono: string;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
interface TarjetaPersonalizadaConfig extends BaseTarjetaConfig {
|
|
45
|
+
type: 'custom';
|
|
46
|
+
icono: string;
|
|
47
|
+
etiqueta: string;
|
|
48
|
+
accion: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
type AnyTarjetaConfig = TarjetaConfig | TarjetaMultipleConfig | TarjetaReporteConfig | TarjetaPersonalizadaConfig;
|
|
42
52
|
|
|
43
53
|
@Component({
|
|
44
54
|
selector: "app-contenedor-principal",
|
|
45
55
|
standalone: true,
|
|
46
|
-
imports: [TarjetaComponent, TarjetaMultipleComponent, TarjetaReporteComponent, CommonModule, DragDropModule],
|
|
56
|
+
imports: [TarjetaComponent, TarjetaMultipleComponent, TarjetaReporteComponent, TarjetaPersonalizadaComponent, CommonModule, DragDropModule],
|
|
47
57
|
templateUrl: "./contenedor-principal.component.html",
|
|
48
58
|
styleUrl: "./contenedor-principal.component.css"
|
|
49
59
|
})
|