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.
- package/.claude/settings.local.json +7 -0
- package/commands/commit.js +14 -1
- package/commands/frontend.js +1 -1
- package/package.json +1 -1
- package/templates/backend/package.json +3 -3
- package/templates/backend/rutas/misc.js +69 -0
- package/templates/backend/servicios/Nucleo/Miscelaneas.js +188 -22
- package/templates/backend/servicios/Nucleo/ReportePDF.js +1 -1
- package/templates/bd/docker-scripts/1-crear estructura.sql +8 -0
- package/templates/frontend/package.json +9 -8
- package/templates/frontend/public/Manual.md +1 -0
- package/templates/frontend/src/app/Componentes/Nucleo/gestion-actividad/gestion-actividad.component.ts +11 -3
- package/templates/frontend/src/app/Componentes/Nucleo/graficos/graficos.component.ts +2 -4
- package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.css +318 -0
- package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.html +43 -0
- package/templates/frontend/src/app/Componentes/Nucleo/manual/manual.component.ts +77 -0
- package/templates/frontend/src/app/Componentes/Nucleo/mensajes/mensajes.component.ts +5 -3
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-incidencias/reporte-de-incidencias.component.ts +12 -4
- package/templates/frontend/src/app/Componentes/Nucleo/reporte-de-sugerencias/reporte-de-sugerencias.component.ts +12 -3
- package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.html +13 -10
- package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.ts +18 -6
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.html +2 -2
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta/tarjeta.component.ts +9 -13
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-personalizada/tarjeta-personalizada.component.ts +7 -9
- package/templates/frontend/src/app/Componentes/Nucleo/tarjeta-reporte/tarjeta-reporte.component.ts +1 -6
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.css +72 -0
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.html +17 -4
- package/templates/frontend/src/app/Paginas/Nucleo/contenedor-componentes/contenedor-componentes.component.ts +45 -57
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.css +11 -0
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.html +4 -4
- package/templates/frontend/src/app/Paginas/contenedor-principal/contenedor-principal.component.ts +65 -69
- package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.html +14 -3
- package/templates/frontend/src/app/Paginas/gestion-de-reportes/gestion-de-reportes.component.ts +52 -14
- package/templates/frontend/src/app/app.routes.ts +4 -0
- package/templates/frontend/src/app/datos-globales.service.ts +4 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, Input } from '@angular/core';
|
|
1
|
+
import { Component, Input, OnChanges } from '@angular/core';
|
|
2
2
|
import { BaseChartDirective } from 'ng2-charts';
|
|
3
3
|
import { ChartConfiguration, ChartData, Plugin } from 'chart.js';
|
|
4
4
|
|
|
@@ -9,7 +9,7 @@ import { ChartConfiguration, ChartData, Plugin } from 'chart.js';
|
|
|
9
9
|
templateUrl: './graficos.component.html',
|
|
10
10
|
styleUrl: './graficos.component.css'
|
|
11
11
|
})
|
|
12
|
-
export class GraficosComponent {
|
|
12
|
+
export class GraficosComponent implements OnChanges {
|
|
13
13
|
|
|
14
14
|
@Input() public TipoDeGrafico: string = '';
|
|
15
15
|
@Input() public Datos: any[] = [];
|
|
@@ -162,8 +162,6 @@ export class GraficosComponent {
|
|
|
162
162
|
}
|
|
163
163
|
};
|
|
164
164
|
|
|
165
|
-
constructor() { }
|
|
166
|
-
|
|
167
165
|
ngOnChanges() {
|
|
168
166
|
|
|
169
167
|
// ✅ Si no hay datos, limpia todo para evitar gráficos raros
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/* ═══════════════════════════════════════════════════════════
|
|
2
|
+
Layout general — rompe el text-align:center del contenedor
|
|
3
|
+
══════════════════════════════════════════════════════════ */
|
|
4
|
+
:host {
|
|
5
|
+
display: block;
|
|
6
|
+
text-align: left;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.manual-layout {
|
|
10
|
+
display: grid;
|
|
11
|
+
grid-template-columns: 260px 1fr;
|
|
12
|
+
gap: 0;
|
|
13
|
+
align-items: start;
|
|
14
|
+
min-height: 100%;
|
|
15
|
+
font-family: 'Roboto', 'Helvetica Neue', sans-serif;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/* ═══════════════════════════════════════════════════════════
|
|
19
|
+
Índice lateral
|
|
20
|
+
══════════════════════════════════════════════════════════ */
|
|
21
|
+
.manual-toc {
|
|
22
|
+
position: sticky;
|
|
23
|
+
top: 0;
|
|
24
|
+
max-height: calc(100vh - 120px);
|
|
25
|
+
overflow-y: auto;
|
|
26
|
+
border-right: 2px solid #e0e8f5;
|
|
27
|
+
padding: 1.25rem 0.75rem 2rem 0;
|
|
28
|
+
background: #f7faff;
|
|
29
|
+
scrollbar-width: thin;
|
|
30
|
+
scrollbar-color: #b0c4de transparent;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.toc-titulo {
|
|
34
|
+
font-size: 0.7rem;
|
|
35
|
+
font-weight: 700;
|
|
36
|
+
letter-spacing: 0.1em;
|
|
37
|
+
text-transform: uppercase;
|
|
38
|
+
color: #002f6b;
|
|
39
|
+
padding: 0 0 0.6rem 1rem;
|
|
40
|
+
border-bottom: 1px solid #d0ddf0;
|
|
41
|
+
margin-bottom: 0.6rem;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.toc-lista {
|
|
45
|
+
list-style: none;
|
|
46
|
+
padding: 0;
|
|
47
|
+
margin: 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.toc-lista li {
|
|
51
|
+
margin: 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
.toc-enlace {
|
|
55
|
+
display: block;
|
|
56
|
+
width: 100%;
|
|
57
|
+
background: none;
|
|
58
|
+
border: none;
|
|
59
|
+
cursor: pointer;
|
|
60
|
+
text-align: left;
|
|
61
|
+
font-family: inherit;
|
|
62
|
+
font-size: 0.8rem;
|
|
63
|
+
line-height: 1.4;
|
|
64
|
+
color: #444;
|
|
65
|
+
padding: 0.3rem 0.75rem;
|
|
66
|
+
border-radius: 0 6px 6px 0;
|
|
67
|
+
transition: background 0.15s, color 0.15s;
|
|
68
|
+
white-space: normal;
|
|
69
|
+
word-break: break-word;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.toc-enlace:hover {
|
|
73
|
+
background: #dce9ff;
|
|
74
|
+
color: #002f6b;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.toc-nivel-1 .toc-enlace {
|
|
78
|
+
font-weight: 700;
|
|
79
|
+
color: #002f6b;
|
|
80
|
+
font-size: 0.82rem;
|
|
81
|
+
padding-left: 1rem;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.toc-nivel-2 .toc-enlace {
|
|
85
|
+
font-weight: 600;
|
|
86
|
+
color: #1976d2;
|
|
87
|
+
padding-left: 1.5rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.toc-nivel-3 .toc-enlace {
|
|
91
|
+
font-weight: 400;
|
|
92
|
+
color: #555;
|
|
93
|
+
padding-left: 2.25rem;
|
|
94
|
+
font-size: 0.78rem;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ═══════════════════════════════════════════════════════════
|
|
98
|
+
Área principal del artículo
|
|
99
|
+
══════════════════════════════════════════════════════════ */
|
|
100
|
+
.manual-main {
|
|
101
|
+
padding: 1.5rem 2.5rem 4rem 2rem;
|
|
102
|
+
min-width: 0;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ═══════════════════════════════════════════════════════════
|
|
106
|
+
Estados: cargando / error
|
|
107
|
+
══════════════════════════════════════════════════════════ */
|
|
108
|
+
.manual-estado {
|
|
109
|
+
padding: 1.25rem 1.5rem;
|
|
110
|
+
border-radius: 8px;
|
|
111
|
+
background: #f0f4ff;
|
|
112
|
+
color: #002f6b;
|
|
113
|
+
font-size: 0.95rem;
|
|
114
|
+
border-left: 4px solid #1976d2;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.manual-error {
|
|
118
|
+
background: #fff0f0;
|
|
119
|
+
color: #b00020;
|
|
120
|
+
border-left-color: #b00020;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ═══════════════════════════════════════════════════════════
|
|
124
|
+
Tipografía del artículo
|
|
125
|
+
══════════════════════════════════════════════════════════ */
|
|
126
|
+
.manual-articulo {
|
|
127
|
+
color: #1a1a1a;
|
|
128
|
+
font-size: 0.95rem;
|
|
129
|
+
line-height: 1.8;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* Encabezados */
|
|
133
|
+
.manual-articulo h1 {
|
|
134
|
+
font-size: 1.75rem;
|
|
135
|
+
font-weight: 700;
|
|
136
|
+
color: #002f6b;
|
|
137
|
+
margin: 0 0 1.25rem;
|
|
138
|
+
padding-bottom: 0.5rem;
|
|
139
|
+
border-bottom: 3px solid #1976d2;
|
|
140
|
+
scroll-margin-top: 1rem;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.manual-articulo h2 {
|
|
144
|
+
font-size: 1.25rem;
|
|
145
|
+
font-weight: 700;
|
|
146
|
+
color: #002f6b;
|
|
147
|
+
margin: 2.5rem 0 0.75rem;
|
|
148
|
+
padding: 0.4rem 0.9rem;
|
|
149
|
+
background: #e8f0fe;
|
|
150
|
+
border-left: 4px solid #1976d2;
|
|
151
|
+
border-radius: 0 6px 6px 0;
|
|
152
|
+
scroll-margin-top: 1rem;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.manual-articulo h3 {
|
|
156
|
+
font-size: 1.05rem;
|
|
157
|
+
font-weight: 600;
|
|
158
|
+
color: #0b4fce;
|
|
159
|
+
margin: 1.75rem 0 0.5rem;
|
|
160
|
+
scroll-margin-top: 1rem;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.manual-articulo h4 {
|
|
164
|
+
font-size: 0.95rem;
|
|
165
|
+
font-weight: 600;
|
|
166
|
+
color: #333;
|
|
167
|
+
margin: 1.25rem 0 0.4rem;
|
|
168
|
+
scroll-margin-top: 1rem;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* Párrafos */
|
|
172
|
+
.manual-articulo p {
|
|
173
|
+
margin: 0 0 0.9rem;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/* Listas */
|
|
177
|
+
.manual-articulo ul,
|
|
178
|
+
.manual-articulo ol {
|
|
179
|
+
margin: 0.25rem 0 1rem 1.4rem;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
.manual-articulo li {
|
|
183
|
+
margin-bottom: 0.3rem;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.manual-articulo li p {
|
|
187
|
+
margin: 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/* Código inline */
|
|
191
|
+
.manual-articulo code {
|
|
192
|
+
background: #eef2ff;
|
|
193
|
+
color: #1a237e;
|
|
194
|
+
border-radius: 3px;
|
|
195
|
+
padding: 0.1em 0.45em;
|
|
196
|
+
font-family: 'Consolas', 'Courier New', monospace;
|
|
197
|
+
font-size: 0.87em;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/* Bloques de código */
|
|
201
|
+
.manual-articulo pre {
|
|
202
|
+
background: #f3f4f6;
|
|
203
|
+
border: 1px solid #dde1ea;
|
|
204
|
+
border-left: 4px solid #1976d2;
|
|
205
|
+
border-radius: 0 6px 6px 0;
|
|
206
|
+
padding: 1rem 1.25rem;
|
|
207
|
+
overflow-x: auto;
|
|
208
|
+
margin: 0.75rem 0 1.25rem;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.manual-articulo pre code {
|
|
212
|
+
background: none;
|
|
213
|
+
padding: 0;
|
|
214
|
+
color: #1a1a1a;
|
|
215
|
+
font-size: 0.88em;
|
|
216
|
+
line-height: 1.65;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* Tablas */
|
|
220
|
+
.manual-articulo table {
|
|
221
|
+
width: 100%;
|
|
222
|
+
border-collapse: collapse;
|
|
223
|
+
margin: 0.75rem 0 1.5rem;
|
|
224
|
+
font-size: 0.9rem;
|
|
225
|
+
border: 1px solid #d0daf0;
|
|
226
|
+
border-radius: 6px;
|
|
227
|
+
overflow: hidden;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.manual-articulo thead {
|
|
231
|
+
background: #002f6b;
|
|
232
|
+
color: #fff;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.manual-articulo th {
|
|
236
|
+
text-align: left;
|
|
237
|
+
padding: 0.6rem 1rem;
|
|
238
|
+
font-weight: 600;
|
|
239
|
+
font-size: 0.88rem;
|
|
240
|
+
letter-spacing: 0.02em;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.manual-articulo td {
|
|
244
|
+
padding: 0.55rem 1rem;
|
|
245
|
+
border-top: 1px solid #e0e8f5;
|
|
246
|
+
vertical-align: top;
|
|
247
|
+
color: #222;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
.manual-articulo tbody tr:nth-child(even) td {
|
|
251
|
+
background: #f5f8ff;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
.manual-articulo tbody tr:hover td {
|
|
255
|
+
background: #ebf1ff;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/* Separador */
|
|
259
|
+
.manual-articulo hr {
|
|
260
|
+
border: none;
|
|
261
|
+
border-top: 1px solid #dde3f0;
|
|
262
|
+
margin: 2rem 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/* Citas / notas */
|
|
266
|
+
.manual-articulo blockquote {
|
|
267
|
+
margin: 0.75rem 0 1rem;
|
|
268
|
+
padding: 0.65rem 1rem;
|
|
269
|
+
background: #fffbea;
|
|
270
|
+
border-left: 4px solid #f9a825;
|
|
271
|
+
border-radius: 0 6px 6px 0;
|
|
272
|
+
color: #4a3c00;
|
|
273
|
+
font-size: 0.92rem;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.manual-articulo blockquote p {
|
|
277
|
+
margin: 0;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* Énfasis */
|
|
281
|
+
.manual-articulo strong {
|
|
282
|
+
font-weight: 700;
|
|
283
|
+
color: #002f6b;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* Links */
|
|
287
|
+
.manual-articulo a {
|
|
288
|
+
color: #1976d2;
|
|
289
|
+
text-decoration: underline;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.manual-articulo a:focus {
|
|
293
|
+
outline: 2px solid #1976d2;
|
|
294
|
+
outline-offset: 2px;
|
|
295
|
+
border-radius: 2px;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/* ═══════════════════════════════════════════════════════════
|
|
299
|
+
Responsivo — pantallas pequeñas
|
|
300
|
+
══════════════════════════════════════════════════════════ */
|
|
301
|
+
@media (max-width: 768px) {
|
|
302
|
+
.manual-layout {
|
|
303
|
+
grid-template-columns: 1fr;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
.manual-toc {
|
|
307
|
+
position: static;
|
|
308
|
+
max-height: none;
|
|
309
|
+
border-right: none;
|
|
310
|
+
border-bottom: 2px solid #e0e8f5;
|
|
311
|
+
padding: 1rem;
|
|
312
|
+
margin-bottom: 0.5rem;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
.manual-main {
|
|
316
|
+
padding: 1rem 1rem 3rem;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<div class="manual-layout">
|
|
2
|
+
|
|
3
|
+
<!-- ── Índice lateral ─────────────────────────────────── -->
|
|
4
|
+
<aside class="manual-toc" aria-label="Índice del manual">
|
|
5
|
+
<p class="toc-titulo">Contenido</p>
|
|
6
|
+
<nav>
|
|
7
|
+
<ul class="toc-lista">
|
|
8
|
+
@for (entrada of toc; track entrada.id) {
|
|
9
|
+
<li [class]="'toc-nivel-' + entrada.nivel">
|
|
10
|
+
<button class="toc-enlace" (click)="irA(entrada.id)" [title]="entrada.texto">
|
|
11
|
+
{{ entrada.texto }}
|
|
12
|
+
</button>
|
|
13
|
+
</li>
|
|
14
|
+
}
|
|
15
|
+
</ul>
|
|
16
|
+
</nav>
|
|
17
|
+
</aside>
|
|
18
|
+
|
|
19
|
+
<!-- ── Artículo principal ─────────────────────────────── -->
|
|
20
|
+
<main class="manual-main" role="main" aria-label="Manual de usuario">
|
|
21
|
+
|
|
22
|
+
@if (cargando) {
|
|
23
|
+
<div class="manual-estado" role="status" aria-live="polite" aria-busy="true">
|
|
24
|
+
<p>Cargando manual…</p>
|
|
25
|
+
</div>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@if (error) {
|
|
29
|
+
<div class="manual-estado manual-error" role="alert">
|
|
30
|
+
<p>
|
|
31
|
+
No se pudo cargar el manual. Verifique que el archivo
|
|
32
|
+
<code>Manual.md</code> esté en la carpeta <code>public/</code>.
|
|
33
|
+
</p>
|
|
34
|
+
</div>
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@if (!cargando && !error) {
|
|
38
|
+
<article #contenidoManual class="manual-articulo" [innerHTML]="contenido"></article>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
</main>
|
|
42
|
+
|
|
43
|
+
</div>
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
|
+
import { DomSanitizer, SafeHtml } from '@angular/platform-browser';
|
|
4
|
+
import { CommonModule } from '@angular/common';
|
|
5
|
+
import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
6
|
+
import { marked } from 'marked';
|
|
7
|
+
import { Subject } from 'rxjs';
|
|
8
|
+
import { takeUntil } from 'rxjs/operators';
|
|
9
|
+
|
|
10
|
+
interface EntradaToc {
|
|
11
|
+
nivel: number;
|
|
12
|
+
texto: string;
|
|
13
|
+
id: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const DIACRITICOS = new RegExp('[̀-ͯ]', 'g');
|
|
17
|
+
|
|
18
|
+
@Component({
|
|
19
|
+
selector: 'app-manual',
|
|
20
|
+
standalone: true,
|
|
21
|
+
imports: [CommonModule],
|
|
22
|
+
templateUrl: './manual.component.html',
|
|
23
|
+
styleUrl: './manual.component.css'
|
|
24
|
+
})
|
|
25
|
+
export class ManualComponent implements OnInit, OnDestroy {
|
|
26
|
+
public contenido: SafeHtml = '';
|
|
27
|
+
public toc: EntradaToc[] = [];
|
|
28
|
+
public cargando: boolean = true;
|
|
29
|
+
public error: boolean = false;
|
|
30
|
+
private _destroy$ = new Subject<void>();
|
|
31
|
+
@ViewChild('contenidoManual') contenidoManualRef!: ElementRef;
|
|
32
|
+
|
|
33
|
+
constructor(private http: HttpClient, private sanitizer: DomSanitizer, private datosGlobalesService: DatosGlobalesService) { }
|
|
34
|
+
|
|
35
|
+
ngOnInit(): void {
|
|
36
|
+
this.http.get(`${this.datosGlobalesService.ObtenerURL()}misc/VistaDelManual`).pipe(takeUntil(this._destroy$)).subscribe({ error: () => { } });
|
|
37
|
+
|
|
38
|
+
this.http.get('/Manual.md', { responseType: 'text' }).pipe(takeUntil(this._destroy$)).subscribe({
|
|
39
|
+
next: (markdown) => {
|
|
40
|
+
const html = marked.parse(markdown) as string;
|
|
41
|
+
|
|
42
|
+
const parser = new DOMParser();
|
|
43
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
44
|
+
|
|
45
|
+
doc.querySelectorAll('h1, h2, h3').forEach((heading) => {
|
|
46
|
+
const texto = heading.textContent ?? '';
|
|
47
|
+
const id = 'sec-' + texto
|
|
48
|
+
.toLowerCase()
|
|
49
|
+
.normalize('NFD')
|
|
50
|
+
.replace(DIACRITICOS, '')
|
|
51
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
52
|
+
.trim()
|
|
53
|
+
.replace(/\s+/g, '-');
|
|
54
|
+
heading.id = id;
|
|
55
|
+
const nivel = parseInt(heading.tagName[1], 10);
|
|
56
|
+
this.toc.push({ nivel, texto, id });
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
this.contenido = this.sanitizer.bypassSecurityTrustHtml(doc.body.innerHTML);
|
|
60
|
+
this.cargando = false;
|
|
61
|
+
},
|
|
62
|
+
error: () => {
|
|
63
|
+
this.error = true;
|
|
64
|
+
this.cargando = false;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
ngOnDestroy(): void {
|
|
70
|
+
this._destroy$.next();
|
|
71
|
+
this._destroy$.complete();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
irA(id: string): void {
|
|
75
|
+
this.contenidoManualRef?.nativeElement.querySelector('#' + id)?.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Component, Inject } from '@angular/core';
|
|
2
|
-
import { HttpClient
|
|
2
|
+
import { HttpClient } from '@angular/common/http';
|
|
3
3
|
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
|
4
4
|
import { MatIconModule } from '@angular/material/icon';
|
|
5
5
|
import { MatButtonModule } from '@angular/material/button';
|
|
@@ -8,6 +8,8 @@ import { MatDialogContent, MatDialogActions, MatDialogTitle } from '@angular/mat
|
|
|
8
8
|
import { CommonModule } from '@angular/common';
|
|
9
9
|
import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
10
10
|
|
|
11
|
+
interface FilaMensaje { llave: string; valor: string; tachado: boolean }
|
|
12
|
+
|
|
11
13
|
@Component({
|
|
12
14
|
selector: 'app-mensajes',
|
|
13
15
|
templateUrl: './mensajes.component.html',
|
|
@@ -24,7 +26,7 @@ import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
|
24
26
|
]
|
|
25
27
|
})
|
|
26
28
|
export class MensajesComponent {
|
|
27
|
-
public arregloDeDatos:
|
|
29
|
+
public arregloDeDatos: FilaMensaje[] = [];
|
|
28
30
|
|
|
29
31
|
constructor(private http: HttpClient, public dialogRef: MatDialogRef<MensajesComponent>
|
|
30
32
|
, @Inject(MAT_DIALOG_DATA) public data: any, private datosGlobalesService: DatosGlobalesService) {
|
|
@@ -35,7 +37,7 @@ export class MensajesComponent {
|
|
|
35
37
|
this.dialogRef.close();
|
|
36
38
|
}
|
|
37
39
|
|
|
38
|
-
enviar(item:
|
|
40
|
+
enviar(item: FilaMensaje) {
|
|
39
41
|
item.tachado = true;
|
|
40
42
|
this.http
|
|
41
43
|
.post(`${this.datosGlobalesService.ObtenerURL()}misc/actualizarNotificacion`, { FechaYHoraDeCreacion: item.llave }).subscribe();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component, ElementRef, OnInit, ViewChild, inject } from '@angular/core';
|
|
1
|
+
import { Component, ElementRef, OnDestroy, OnInit, ViewChild, inject } from '@angular/core';
|
|
2
2
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
3
3
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
4
4
|
import { MatInputModule } from '@angular/material/input';
|
|
@@ -6,7 +6,9 @@ import { ReactiveFormsModule } from '@angular/forms';
|
|
|
6
6
|
import { MatDialogActions, MatDialogContent, MatDialogRef, MatDialogTitle } from '@angular/material/dialog';
|
|
7
7
|
import { MatButtonModule } from '@angular/material/button';
|
|
8
8
|
import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
9
|
-
import { HttpClient
|
|
9
|
+
import { HttpClient } from '@angular/common/http';
|
|
10
|
+
import { Subject } from 'rxjs';
|
|
11
|
+
import { takeUntil } from 'rxjs/operators';
|
|
10
12
|
|
|
11
13
|
@Component({
|
|
12
14
|
selector: 'app-reporte-de-incidencias',
|
|
@@ -23,10 +25,10 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
|
23
25
|
]
|
|
24
26
|
})
|
|
25
27
|
|
|
26
|
-
export class ReporteDeIncidenciasComponent implements OnInit {
|
|
28
|
+
export class ReporteDeIncidenciasComponent implements OnInit, OnDestroy {
|
|
27
29
|
formulario!: FormGroup;
|
|
28
|
-
archivos: File[] = [];
|
|
29
30
|
readonly dialogRef = inject(MatDialogRef<ReporteDeIncidenciasComponent>);
|
|
31
|
+
private _destroy$ = new Subject<void>();
|
|
30
32
|
constructor(private fb: FormBuilder, private datosGlobalesService: DatosGlobalesService, private http: HttpClient) { }
|
|
31
33
|
@ViewChild('EntradDeArchivo') EntradDeArchivo!: ElementRef<HTMLInputElement>;
|
|
32
34
|
Archivo: any;
|
|
@@ -51,6 +53,7 @@ export class ReporteDeIncidenciasComponent implements OnInit {
|
|
|
51
53
|
const Datos = JSON.stringify(this.formulario.value);
|
|
52
54
|
this.http.post(this.datosGlobalesService.ObtenerURL() + 'misc/reporteDeIncidencia/' + Datos,
|
|
53
55
|
archivo)
|
|
56
|
+
.pipe(takeUntil(this._destroy$))
|
|
54
57
|
.subscribe({
|
|
55
58
|
next: (data: any) => {
|
|
56
59
|
alert('Mensaje enviado');
|
|
@@ -70,6 +73,11 @@ export class ReporteDeIncidenciasComponent implements OnInit {
|
|
|
70
73
|
}
|
|
71
74
|
}
|
|
72
75
|
|
|
76
|
+
ngOnDestroy(): void {
|
|
77
|
+
this._destroy$.next();
|
|
78
|
+
this._destroy$.complete();
|
|
79
|
+
}
|
|
80
|
+
|
|
73
81
|
AbrirGestorDeArchivos() {
|
|
74
82
|
this.EntradDeArchivo.nativeElement.click();
|
|
75
83
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component,
|
|
1
|
+
import { Component, OnDestroy, OnInit, inject } from '@angular/core';
|
|
2
2
|
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
|
3
3
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
4
4
|
import { MatInputModule } from '@angular/material/input';
|
|
@@ -6,7 +6,9 @@ import { ReactiveFormsModule } from '@angular/forms';
|
|
|
6
6
|
import { MatDialogActions, MatDialogContent, MatDialogRef, MatDialogTitle } from '@angular/material/dialog';
|
|
7
7
|
import { MatButtonModule } from '@angular/material/button';
|
|
8
8
|
import { DatosGlobalesService } from '../../../datos-globales.service';
|
|
9
|
-
import { HttpClient
|
|
9
|
+
import { HttpClient } from '@angular/common/http';
|
|
10
|
+
import { Subject } from 'rxjs';
|
|
11
|
+
import { takeUntil } from 'rxjs/operators';
|
|
10
12
|
|
|
11
13
|
@Component({
|
|
12
14
|
selector: 'app-reporte-de-sugerencias',
|
|
@@ -23,9 +25,10 @@ import { HttpClient, HttpHeaders } from '@angular/common/http';
|
|
|
23
25
|
]
|
|
24
26
|
})
|
|
25
27
|
|
|
26
|
-
export class ReporteDeSugerenciasComponent implements OnInit {
|
|
28
|
+
export class ReporteDeSugerenciasComponent implements OnInit, OnDestroy {
|
|
27
29
|
formulario!: FormGroup;
|
|
28
30
|
readonly dialogRef = inject(MatDialogRef<ReporteDeSugerenciasComponent>);
|
|
31
|
+
private _destroy$ = new Subject<void>();
|
|
29
32
|
constructor(private fb: FormBuilder, private datosGlobalesService: DatosGlobalesService, private http: HttpClient) { }
|
|
30
33
|
|
|
31
34
|
ngOnInit(): void {
|
|
@@ -41,6 +44,7 @@ export class ReporteDeSugerenciasComponent implements OnInit {
|
|
|
41
44
|
Enviar() {
|
|
42
45
|
const Datos = JSON.stringify(this.formulario.value);
|
|
43
46
|
this.http.post(this.datosGlobalesService.ObtenerURL() + 'misc/reporteDeSugerencia/' + Datos, {})
|
|
47
|
+
.pipe(takeUntil(this._destroy$))
|
|
44
48
|
.subscribe({
|
|
45
49
|
next: (data: any) => {
|
|
46
50
|
alert('Mensaje enviado');
|
|
@@ -51,4 +55,9 @@ export class ReporteDeSugerenciasComponent implements OnInit {
|
|
|
51
55
|
})
|
|
52
56
|
this.Cerrar();
|
|
53
57
|
}
|
|
58
|
+
|
|
59
|
+
ngOnDestroy(): void {
|
|
60
|
+
this._destroy$.next();
|
|
61
|
+
this._destroy$.complete();
|
|
62
|
+
}
|
|
54
63
|
}
|
package/templates/frontend/src/app/Componentes/Nucleo/subir-archivo/subir-archivo.component.html
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
<a #descargaLink style="display:none"></a>
|
|
1
2
|
<div class="contenedor">
|
|
2
3
|
<div class="lista">
|
|
3
4
|
@for(archivo of ListaArchivos;track $index){
|
|
@@ -17,20 +18,22 @@
|
|
|
17
18
|
}
|
|
18
19
|
</div>
|
|
19
20
|
@if(EsEditable) {
|
|
20
|
-
<div class="zona-archivo" [class.deshabilitado]="ListaArchivos.length >= CantidadMaximaDeArchivos"
|
|
21
|
-
(
|
|
21
|
+
<div class="zona-archivo" [class.deshabilitado]="ListaArchivos.length >= CantidadMaximaDeArchivos"
|
|
22
|
+
(click)="AbrirGestorDeArchivos()" (dragover)="ArrastrarAdentro($event)" (dragleave)="ArrastrarAfuera($event)"
|
|
23
|
+
(drop)="Soltar($event)">
|
|
22
24
|
@if(ListaArchivos.length >= CantidadMaximaDeArchivos) {
|
|
23
|
-
|
|
25
|
+
<p class="texto-limite">Límite de {{CantidadMaximaDeArchivos}} archivos alcanzado</p>
|
|
24
26
|
} @else {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
27
|
+
@if(!Archivo){
|
|
28
|
+
<p class="texto">Arrastra y suelta un archivo aquí o <span class="texto-clic">haz clic para subir</span></p>
|
|
29
|
+
}
|
|
30
|
+
@else{
|
|
31
|
+
<p>Archivo seleccionado: {{ Archivo.name }}</p>
|
|
32
|
+
}
|
|
31
33
|
}
|
|
32
34
|
<!-- Input oculto para seleccionar archivos manualmente -->
|
|
33
|
-
<input type="file" #EntradaDeArchivo hidden (change)="ArchivoSeleccionado($event)"
|
|
35
|
+
<input type="file" #EntradaDeArchivo hidden (change)="ArchivoSeleccionado($event)"
|
|
36
|
+
[accept]="FormatosPermitidos.join(',')">
|
|
34
37
|
</div>
|
|
35
38
|
} @else {
|
|
36
39
|
@if(ListaArchivos.length === 0) {
|