ngx-dsxlibrary 2.21.71 → 2.21.72
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/README.md +51 -0
- package/fesm2022/ngx-dsxlibrary.mjs +1860 -278
- package/fesm2022/ngx-dsxlibrary.mjs.map +1 -1
- package/ngx-dsxlibrary-2.21.72.tgz +0 -0
- package/package.json +1 -1
- package/src/assets/css/primeng_dsx_table.css +1 -2
- package/src/assets/css/sweetAlert2.css +16 -0
- package/types/ngx-dsxlibrary.d.ts +649 -81
- package/ngx-dsxlibrary-2.21.71.tgz +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, forwardRef, Component, EventEmitter, effect, Output, viewChild, signal, Input, model, inject, computed, Injectable, isDevMode, InjectionToken, output, HostBinding, ChangeDetectorRef, untracked,
|
|
2
|
+
import { input, forwardRef, Component, EventEmitter, effect, Output, viewChild, signal, Input, model, inject, computed, Injectable, isDevMode, InjectionToken, output, HostBinding, ChangeDetectorRef, untracked, ViewEncapsulation, HostListener, Directive, NgZone, ElementRef, ChangeDetectionStrategy, NgModule, Pipe, Injector, ViewChild } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { FormsModule, NG_VALUE_ACCESSOR, AbstractControl, FormControl, NG_VALIDATORS, FormBuilder, Validators, ReactiveFormsModule, NgControl, FormGroup } from '@angular/forms';
|
|
5
5
|
import * as i2 from 'primeng/togglebutton';
|
|
6
6
|
import { ToggleButtonModule } from 'primeng/togglebutton';
|
|
7
7
|
import * as i1$1 from '@angular/common';
|
|
8
|
-
import { CommonModule, AsyncPipe, DecimalPipe, Location
|
|
8
|
+
import { CommonModule, AsyncPipe, JsonPipe, DatePipe, DecimalPipe, Location } from '@angular/common';
|
|
9
9
|
import * as i2$1 from 'primeng/tree';
|
|
10
10
|
import { TreeModule } from 'primeng/tree';
|
|
11
11
|
import * as i3 from 'primeng/api';
|
|
@@ -60,6 +60,7 @@ import { DrawerModule } from 'primeng/drawer';
|
|
|
60
60
|
import { FieldsetModule } from 'primeng/fieldset';
|
|
61
61
|
import { IconFieldModule } from 'primeng/iconfield';
|
|
62
62
|
import { InputIconModule } from 'primeng/inputicon';
|
|
63
|
+
import * as i1$8 from 'primeng/inputmask';
|
|
63
64
|
import { InputMaskModule } from 'primeng/inputmask';
|
|
64
65
|
import { InputNumberModule, InputNumber } from 'primeng/inputnumber';
|
|
65
66
|
import { InputTextModule } from 'primeng/inputtext';
|
|
@@ -4978,7 +4979,6 @@ class FileComponent {
|
|
|
4978
4979
|
existingFileName = input(null, ...(ngDevMode ? [{ debugName: "existingFileName" }] : /* istanbul ignore next */ []));
|
|
4979
4980
|
overrideFileName = input(null, ...(ngDevMode ? [{ debugName: "overrideFileName" }] : /* istanbul ignore next */ []));
|
|
4980
4981
|
debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
|
|
4981
|
-
required = input(true, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
|
|
4982
4982
|
accept = input('.xls,.xlsx', ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
|
|
4983
4983
|
pTooltipOverride = input('Seleccione un archivo', ...(ngDevMode ? [{ debugName: "pTooltipOverride" }] : /* istanbul ignore next */ []));
|
|
4984
4984
|
tooltipPositionOverride = input('top', ...(ngDevMode ? [{ debugName: "tooltipPositionOverride" }] : /* istanbul ignore next */ []));
|
|
@@ -4990,6 +4990,7 @@ class FileComponent {
|
|
|
4990
4990
|
// Signals de estado
|
|
4991
4991
|
isReplacing = signal(false, ...(ngDevMode ? [{ debugName: "isReplacing" }] : /* istanbul ignore next */ []));
|
|
4992
4992
|
disabled = signal(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
4993
|
+
errorMessage = signal(null, ...(ngDevMode ? [{ debugName: "errorMessage" }] : /* istanbul ignore next */ []));
|
|
4993
4994
|
isViewActive = signal(false, ...(ngDevMode ? [{ debugName: "isViewActive" }] : /* istanbul ignore next */ []));
|
|
4994
4995
|
value = null;
|
|
4995
4996
|
touched = false;
|
|
@@ -4997,7 +4998,31 @@ class FileComponent {
|
|
|
4997
4998
|
// Dependencias
|
|
4998
4999
|
cdr = inject(ChangeDetectorRef);
|
|
4999
5000
|
fileUpload = viewChild('fileUpload', ...(ngDevMode ? [{ debugName: "fileUpload" }] : /* istanbul ignore next */ []));
|
|
5000
|
-
//
|
|
5001
|
+
// --- Para el manejo de errores del formulario y debug ---
|
|
5002
|
+
_control = null;
|
|
5003
|
+
// Computed para el control de errores
|
|
5004
|
+
errorControl = computed(() => {
|
|
5005
|
+
return this._control;
|
|
5006
|
+
}, ...(ngDevMode ? [{ debugName: "errorControl" }] : /* istanbul ignore next */ []));
|
|
5007
|
+
// Signal para errores del control
|
|
5008
|
+
controlErrors = signal(null, ...(ngDevMode ? [{ debugName: "controlErrors" }] : /* istanbul ignore next */ []));
|
|
5009
|
+
// Estado del control
|
|
5010
|
+
controlValid = signal(true, ...(ngDevMode ? [{ debugName: "controlValid" }] : /* istanbul ignore next */ []));
|
|
5011
|
+
controlTouched = signal(false, ...(ngDevMode ? [{ debugName: "controlTouched" }] : /* istanbul ignore next */ []));
|
|
5012
|
+
controlDirty = signal(false, ...(ngDevMode ? [{ debugName: "controlDirty" }] : /* istanbul ignore next */ []));
|
|
5013
|
+
// --- Detección de modo desarrollo ---
|
|
5014
|
+
get isDevMode() {
|
|
5015
|
+
return (typeof ngDevMode !== 'undefined' &&
|
|
5016
|
+
ngDevMode !== null &&
|
|
5017
|
+
ngDevMode !== false);
|
|
5018
|
+
}
|
|
5019
|
+
// ✅ Método para detectar si el control es requerido
|
|
5020
|
+
isRequired() {
|
|
5021
|
+
if (!this._control)
|
|
5022
|
+
return false;
|
|
5023
|
+
return this._control.errors?.['required'] ?? false;
|
|
5024
|
+
}
|
|
5025
|
+
// Computed properties
|
|
5001
5026
|
hasExistingFile = computed(() => {
|
|
5002
5027
|
if (!this.isViewActive())
|
|
5003
5028
|
return false;
|
|
@@ -5033,14 +5058,27 @@ class FileComponent {
|
|
|
5033
5058
|
return true;
|
|
5034
5059
|
return false;
|
|
5035
5060
|
}, ...(ngDevMode ? [{ debugName: "isReplaceButtonDisabled" }] : /* istanbul ignore next */ []));
|
|
5036
|
-
|
|
5061
|
+
allowedFileTypeLabel = computed(() => {
|
|
5062
|
+
if (!this.isViewActive())
|
|
5063
|
+
return 'archivo';
|
|
5064
|
+
const acceptStr = this.accept();
|
|
5065
|
+
if (!acceptStr)
|
|
5066
|
+
return 'archivo';
|
|
5067
|
+
const extensions = acceptStr.split(',').map((ext) => ext.trim());
|
|
5068
|
+
const cleanExtensions = extensions
|
|
5069
|
+
.map((ext) => ext.replace('.', '').toUpperCase())
|
|
5070
|
+
.filter((ext) => ext.length > 0);
|
|
5071
|
+
if (cleanExtensions.length === 0)
|
|
5072
|
+
return 'archivo';
|
|
5073
|
+
if (cleanExtensions.length === 1)
|
|
5074
|
+
return cleanExtensions[0];
|
|
5075
|
+
return cleanExtensions.join(' o ');
|
|
5076
|
+
}, ...(ngDevMode ? [{ debugName: "allowedFileTypeLabel" }] : /* istanbul ignore next */ []));
|
|
5077
|
+
// --- Constructor ---
|
|
5037
5078
|
constructor() {
|
|
5038
|
-
// Efecto 1: Re-evaluar validación cuando cambie el estado relevante
|
|
5039
5079
|
effect(() => {
|
|
5040
|
-
// Solo ejecutar si la vista está activa
|
|
5041
5080
|
if (!this.isViewActive())
|
|
5042
5081
|
return;
|
|
5043
|
-
// "Tocar" estas signals para que el efecto dependa de ellas
|
|
5044
5082
|
const hasFile = this.hasExistingFile();
|
|
5045
5083
|
const replacing = this.isReplacing();
|
|
5046
5084
|
const fileName = this.existingFileName();
|
|
@@ -5051,14 +5089,12 @@ class FileComponent {
|
|
|
5051
5089
|
fileName,
|
|
5052
5090
|
});
|
|
5053
5091
|
if (this.control) {
|
|
5054
|
-
// Usar untracked para no crear bucles infinitos
|
|
5055
5092
|
untracked(() => {
|
|
5056
5093
|
this.control?.updateValueAndValidity({ emitEvent: false });
|
|
5057
5094
|
});
|
|
5058
5095
|
}
|
|
5059
5096
|
this.cdr.detectChanges();
|
|
5060
5097
|
});
|
|
5061
|
-
// Efecto 2: Cancelar reemplazo si el componente se deshabilita
|
|
5062
5098
|
effect(() => {
|
|
5063
5099
|
if (!this.isViewActive())
|
|
5064
5100
|
return;
|
|
@@ -5072,104 +5108,341 @@ class FileComponent {
|
|
|
5072
5108
|
});
|
|
5073
5109
|
}
|
|
5074
5110
|
ngAfterViewInit() {
|
|
5075
|
-
// Activar el componente después de que la vista esté lista
|
|
5076
5111
|
setTimeout(() => {
|
|
5077
5112
|
this.isViewActive.set(true);
|
|
5078
|
-
if (this.debug())
|
|
5079
|
-
console.log('[FileComponent] View activated');
|
|
5113
|
+
if (this.debug()) {
|
|
5114
|
+
console.log('[FileComponent] ✅ View activated');
|
|
5115
|
+
console.log('[FileComponent] 📋 Configuración actual:', {
|
|
5116
|
+
accept: this.accept(),
|
|
5117
|
+
maxFileSize: this.maxFileSize(),
|
|
5118
|
+
debug: this.debug(),
|
|
5119
|
+
isDevMode: this.isDevMode,
|
|
5120
|
+
});
|
|
5121
|
+
}
|
|
5080
5122
|
});
|
|
5081
5123
|
}
|
|
5082
5124
|
ngOnDestroy() {
|
|
5083
5125
|
this.isViewActive.set(false);
|
|
5084
5126
|
if (this.debug())
|
|
5085
|
-
console.log('[FileComponent] Component destroyed');
|
|
5127
|
+
console.log('[FileComponent] 🗑️ Component destroyed');
|
|
5128
|
+
}
|
|
5129
|
+
// --- Método para validar archivo manualmente ---
|
|
5130
|
+
validateFile(file) {
|
|
5131
|
+
if (this.debug()) {
|
|
5132
|
+
this.log('🔍 validateFile - Iniciando validación manual', {
|
|
5133
|
+
fileName: file.name,
|
|
5134
|
+
fileSize: file.size,
|
|
5135
|
+
fileType: file.type,
|
|
5136
|
+
accept: this.accept(),
|
|
5137
|
+
maxSize: this.maxFileSize(),
|
|
5138
|
+
});
|
|
5139
|
+
}
|
|
5140
|
+
// Validar tipo de archivo
|
|
5141
|
+
const acceptStr = this.accept();
|
|
5142
|
+
if (acceptStr) {
|
|
5143
|
+
const acceptedExtensions = acceptStr
|
|
5144
|
+
.split(',')
|
|
5145
|
+
.map((ext) => ext.trim().toLowerCase());
|
|
5146
|
+
const fileExtension = '.' + file.name.split('.').pop()?.toLowerCase();
|
|
5147
|
+
if (this.debug()) {
|
|
5148
|
+
this.log('🔍 validateFile - Verificando extensión', {
|
|
5149
|
+
fileExtension,
|
|
5150
|
+
acceptedExtensions,
|
|
5151
|
+
matches: acceptedExtensions.includes(fileExtension),
|
|
5152
|
+
});
|
|
5153
|
+
}
|
|
5154
|
+
if (!acceptedExtensions.includes(fileExtension)) {
|
|
5155
|
+
const detail = this.invalidDetail().replace('{0}', this.allowedFileTypeLabel());
|
|
5156
|
+
const message = `${this.invalidSummary()} ${detail}`;
|
|
5157
|
+
if (this.debug())
|
|
5158
|
+
this.log('❌ validateFile - Tipo de archivo no permitido', {
|
|
5159
|
+
message,
|
|
5160
|
+
});
|
|
5161
|
+
return { valid: false, error: message };
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
// Validar tamaño
|
|
5165
|
+
const maxSizeBytes = this.maxFileSize() * 1024 * 1024;
|
|
5166
|
+
if (this.debug()) {
|
|
5167
|
+
this.log('🔍 validateFile - Verificando tamaño', {
|
|
5168
|
+
fileSize: file.size,
|
|
5169
|
+
maxSizeBytes,
|
|
5170
|
+
maxSizeMB: this.maxFileSize(),
|
|
5171
|
+
});
|
|
5172
|
+
}
|
|
5173
|
+
if (file.size > maxSizeBytes) {
|
|
5174
|
+
const detail = this.invalidSizeDetail().replace('{0}', `${this.maxFileSize()} MB`);
|
|
5175
|
+
const message = `${this.invalidSizeSummary()} ${detail}`;
|
|
5176
|
+
if (this.debug())
|
|
5177
|
+
this.log('❌ validateFile - Tamaño excedido', { message });
|
|
5178
|
+
return { valid: false, error: message };
|
|
5179
|
+
}
|
|
5180
|
+
if (this.debug())
|
|
5181
|
+
this.log('✅ validateFile - Archivo válido');
|
|
5182
|
+
return { valid: true };
|
|
5183
|
+
}
|
|
5184
|
+
// --- Métodos para el manejo de errores ---
|
|
5185
|
+
showError() {
|
|
5186
|
+
return !!(this._control && this._control.invalid && this._control.touched);
|
|
5187
|
+
}
|
|
5188
|
+
// Actualizar estado del control
|
|
5189
|
+
updateControlState() {
|
|
5190
|
+
if (this._control) {
|
|
5191
|
+
this.controlValid.set(this._control.valid);
|
|
5192
|
+
this.controlTouched.set(this._control.touched);
|
|
5193
|
+
this.controlDirty.set(this._control.dirty);
|
|
5194
|
+
this.controlErrors.set(this._control.errors);
|
|
5195
|
+
}
|
|
5196
|
+
}
|
|
5197
|
+
// Forzar recarga (para debug)
|
|
5198
|
+
forceReload() {
|
|
5199
|
+
if (this.debug()) {
|
|
5200
|
+
console.log('[FileComponent] 🔄 Force reload');
|
|
5201
|
+
if (this._control) {
|
|
5202
|
+
this._control.updateValueAndValidity();
|
|
5203
|
+
this.updateControlState();
|
|
5204
|
+
}
|
|
5205
|
+
this.cdr.detectChanges();
|
|
5206
|
+
}
|
|
5086
5207
|
}
|
|
5087
|
-
// --- Métodos públicos
|
|
5208
|
+
// --- Métodos públicos ---
|
|
5088
5209
|
onSelect(event) {
|
|
5089
|
-
|
|
5210
|
+
const startTime = performance.now();
|
|
5211
|
+
if (this.debug()) {
|
|
5212
|
+
this.log('📤 onSelect - Evento recibido', {
|
|
5213
|
+
files: event.files,
|
|
5214
|
+
currentFiles: event.currentFiles,
|
|
5215
|
+
isFileUploadEnabled: this.isFileUploadEnabled(),
|
|
5216
|
+
});
|
|
5217
|
+
}
|
|
5218
|
+
if (!this.isViewActive()) {
|
|
5219
|
+
if (this.debug())
|
|
5220
|
+
this.log('⚠️ Componente no activo, ignorando selección');
|
|
5090
5221
|
return;
|
|
5222
|
+
}
|
|
5091
5223
|
if (!this.isFileUploadEnabled()) {
|
|
5092
|
-
console.warn('File upload is disabled');
|
|
5224
|
+
console.warn('[FileComponent] ⚠️ File upload is disabled');
|
|
5093
5225
|
this.fileUpload()?.clear();
|
|
5094
5226
|
return;
|
|
5095
5227
|
}
|
|
5096
5228
|
const file = event.files?.[0] ?? null;
|
|
5097
|
-
if (
|
|
5098
|
-
this.
|
|
5229
|
+
if (!file) {
|
|
5230
|
+
if (this.debug())
|
|
5231
|
+
this.log('⚠️ No se seleccionó ningún archivo');
|
|
5232
|
+
return;
|
|
5233
|
+
}
|
|
5234
|
+
if (this.debug()) {
|
|
5235
|
+
this.log('📄 Archivo seleccionado', {
|
|
5236
|
+
name: file.name,
|
|
5237
|
+
size: file.size,
|
|
5238
|
+
type: file.type,
|
|
5239
|
+
sizeFormatted: this.formatFileSize(file.size),
|
|
5240
|
+
});
|
|
5241
|
+
}
|
|
5242
|
+
// Validar archivo manualmente
|
|
5243
|
+
const validation = this.validateFile(file);
|
|
5244
|
+
if (!validation.valid) {
|
|
5245
|
+
// Archivo inválido - mostrar error y limpiar
|
|
5246
|
+
this.errorMessage.set(validation.error || 'Archivo inválido');
|
|
5247
|
+
this.fileUpload()?.clear();
|
|
5248
|
+
this.value = null;
|
|
5249
|
+
this.onChange(null);
|
|
5250
|
+
if (this.debug()) {
|
|
5251
|
+
this.log('❌ Archivo inválido - Limpiando selección', {
|
|
5252
|
+
error: validation.error,
|
|
5253
|
+
});
|
|
5254
|
+
}
|
|
5255
|
+
// Auto-ocultar error después de 5 segundos
|
|
5256
|
+
setTimeout(() => {
|
|
5257
|
+
this.errorMessage.set(null);
|
|
5258
|
+
if (this.debug())
|
|
5259
|
+
this.log('⏰ Mensaje de error auto-ocultado después de 5s');
|
|
5260
|
+
}, 5000);
|
|
5261
|
+
// Actualizar estado del control
|
|
5262
|
+
this.updateControlState();
|
|
5263
|
+
return;
|
|
5264
|
+
}
|
|
5265
|
+
// Archivo válido
|
|
5266
|
+
this.errorMessage.set(null);
|
|
5099
5267
|
this.value = file;
|
|
5100
5268
|
this.onChange(file);
|
|
5101
5269
|
this.markAsTouched();
|
|
5102
5270
|
if (this.isReplacing() && file) {
|
|
5103
5271
|
this.isReplacing.set(false);
|
|
5272
|
+
if (this.debug())
|
|
5273
|
+
this.log('🔄 Reemplazo completado');
|
|
5104
5274
|
}
|
|
5105
5275
|
if (this.control) {
|
|
5106
5276
|
this.control.updateValueAndValidity();
|
|
5107
5277
|
}
|
|
5278
|
+
// Actualizar estado del control
|
|
5279
|
+
this.updateControlState();
|
|
5280
|
+
const endTime = performance.now();
|
|
5281
|
+
if (this.debug()) {
|
|
5282
|
+
this.log(`⏱️ onSelect completado en ${(endTime - startTime).toFixed(2)}ms`);
|
|
5283
|
+
}
|
|
5108
5284
|
}
|
|
5109
5285
|
clear() {
|
|
5286
|
+
if (this.debug())
|
|
5287
|
+
this.log('🧹 clear() - Limpiando componente');
|
|
5110
5288
|
if (!this.isViewActive())
|
|
5111
5289
|
return;
|
|
5112
5290
|
this.fileUpload()?.clear();
|
|
5113
5291
|
this.value = null;
|
|
5292
|
+
this.errorMessage.set(null);
|
|
5114
5293
|
this.onChange(null);
|
|
5115
5294
|
this.markAsTouched();
|
|
5116
5295
|
if (this.control)
|
|
5117
5296
|
this.control.updateValueAndValidity();
|
|
5297
|
+
this.updateControlState();
|
|
5298
|
+
if (this.debug())
|
|
5299
|
+
this.log('✅ Limpieza completada');
|
|
5118
5300
|
}
|
|
5119
|
-
|
|
5301
|
+
clearFile() {
|
|
5302
|
+
if (this.debug())
|
|
5303
|
+
this.log('🧹 clearFile() - Limpiando archivo seleccionado');
|
|
5120
5304
|
if (!this.isViewActive())
|
|
5121
5305
|
return;
|
|
5306
|
+
this.fileUpload()?.clear();
|
|
5307
|
+
this.value = null;
|
|
5308
|
+
this.errorMessage.set(null);
|
|
5309
|
+
this.onChange(null);
|
|
5310
|
+
this.markAsTouched();
|
|
5311
|
+
if (this.control)
|
|
5312
|
+
this.control.updateValueAndValidity();
|
|
5313
|
+
this.updateControlState();
|
|
5122
5314
|
if (this.debug())
|
|
5123
|
-
this.log('
|
|
5315
|
+
this.log('✅ Archivo limpiado');
|
|
5316
|
+
}
|
|
5317
|
+
writeValue(value) {
|
|
5318
|
+
if (!this.isViewActive())
|
|
5319
|
+
return;
|
|
5320
|
+
if (this.debug()) {
|
|
5321
|
+
this.log('✍️ writeValue', {
|
|
5322
|
+
value: value?.name || null,
|
|
5323
|
+
size: value?.size ? this.formatFileSize(value.size) : null,
|
|
5324
|
+
});
|
|
5325
|
+
}
|
|
5124
5326
|
this.value = value;
|
|
5125
5327
|
}
|
|
5126
5328
|
registerOnChange(fn) {
|
|
5329
|
+
if (this.debug())
|
|
5330
|
+
this.log('📝 registerOnChange');
|
|
5127
5331
|
this.onChange = fn;
|
|
5128
5332
|
}
|
|
5129
5333
|
registerOnTouched(fn) {
|
|
5334
|
+
if (this.debug())
|
|
5335
|
+
this.log('📝 registerOnTouched');
|
|
5130
5336
|
this.onTouched = fn;
|
|
5131
5337
|
}
|
|
5132
5338
|
setDisabledState(isDisabled) {
|
|
5133
5339
|
if (!this.isViewActive())
|
|
5134
5340
|
return;
|
|
5341
|
+
if (this.debug()) {
|
|
5342
|
+
this.log('🔒 setDisabledState', {
|
|
5343
|
+
isDisabled,
|
|
5344
|
+
previousState: this.disabled(),
|
|
5345
|
+
});
|
|
5346
|
+
}
|
|
5135
5347
|
this.disabled.set(isDisabled);
|
|
5136
5348
|
}
|
|
5137
5349
|
startReplace() {
|
|
5350
|
+
if (this.debug())
|
|
5351
|
+
this.log('🔄 startReplace - Iniciando reemplazo');
|
|
5138
5352
|
if (!this.isViewActive())
|
|
5139
5353
|
return;
|
|
5140
|
-
if (this.disabled())
|
|
5354
|
+
if (this.disabled()) {
|
|
5355
|
+
if (this.debug())
|
|
5356
|
+
this.log('⛔ Reemplazo cancelado - componente deshabilitado');
|
|
5141
5357
|
return;
|
|
5358
|
+
}
|
|
5142
5359
|
this.fileUpload()?.clear();
|
|
5143
5360
|
this.value = null;
|
|
5361
|
+
this.errorMessage.set(null);
|
|
5144
5362
|
this.onChange(null);
|
|
5145
5363
|
this.isReplacing.set(true);
|
|
5146
5364
|
if (this.debug())
|
|
5147
|
-
this.log('
|
|
5365
|
+
this.log('✅ Reemplazo iniciado');
|
|
5148
5366
|
}
|
|
5149
5367
|
cancelReplace() {
|
|
5368
|
+
if (this.debug())
|
|
5369
|
+
this.log('❌ cancelReplace - Cancelando reemplazo');
|
|
5150
5370
|
if (!this.isViewActive())
|
|
5151
5371
|
return;
|
|
5152
5372
|
this.isReplacing.set(false);
|
|
5153
5373
|
this.fileUpload()?.clear();
|
|
5154
5374
|
this.value = null;
|
|
5375
|
+
this.errorMessage.set(null);
|
|
5155
5376
|
this.onChange(null);
|
|
5156
5377
|
this.markAsTouched();
|
|
5157
5378
|
if (this.debug())
|
|
5158
|
-
this.log('
|
|
5379
|
+
this.log('✅ Reemplazo cancelado');
|
|
5380
|
+
}
|
|
5381
|
+
choose(event, chooseCallback) {
|
|
5382
|
+
if (this.debug())
|
|
5383
|
+
this.log('🔍 choose - Abriendo selector de archivos');
|
|
5384
|
+
chooseCallback();
|
|
5385
|
+
}
|
|
5386
|
+
formatFileSize(bytes) {
|
|
5387
|
+
if (!bytes)
|
|
5388
|
+
return '';
|
|
5389
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
5390
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
5391
|
+
const value = (bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0);
|
|
5392
|
+
return `${value} ${sizes[i]}`;
|
|
5159
5393
|
}
|
|
5160
5394
|
// --- Validación ---
|
|
5161
5395
|
validate(control) {
|
|
5396
|
+
const startTime = performance.now();
|
|
5397
|
+
this._control = control;
|
|
5162
5398
|
this.control = control;
|
|
5163
|
-
if (!this.isViewActive())
|
|
5399
|
+
if (!this.isViewActive()) {
|
|
5400
|
+
if (this.debug())
|
|
5401
|
+
this.log('⚠️ validate - Componente no activo');
|
|
5164
5402
|
return null;
|
|
5165
|
-
|
|
5403
|
+
}
|
|
5404
|
+
if (this.debug()) {
|
|
5405
|
+
this.log('🔍 validate - Ejecutando validación', {
|
|
5406
|
+
isRequired: this.isRequired(),
|
|
5407
|
+
hasExistingFile: this.hasExistingFile(),
|
|
5408
|
+
isReplacing: this.isReplacing(),
|
|
5409
|
+
hasValue: !!this.value,
|
|
5410
|
+
valueName: this.value?.name || null,
|
|
5411
|
+
errorMessage: this.errorMessage(),
|
|
5412
|
+
});
|
|
5413
|
+
}
|
|
5414
|
+
// Actualizar estado del control
|
|
5415
|
+
this.updateControlState();
|
|
5416
|
+
// Si hay un mensaje de error, consideramos que la validación falla
|
|
5417
|
+
if (this.errorMessage()) {
|
|
5418
|
+
if (this.debug())
|
|
5419
|
+
this.log('❌ validate - Error: Hay un mensaje de error activo');
|
|
5420
|
+
return { invalidFile: true };
|
|
5421
|
+
}
|
|
5422
|
+
if (!this.isRequired()) {
|
|
5423
|
+
if (this.debug())
|
|
5424
|
+
this.log('✅ validate - No requerido, pasa validación');
|
|
5166
5425
|
return null;
|
|
5167
|
-
|
|
5426
|
+
}
|
|
5427
|
+
if (this.hasExistingFile()) {
|
|
5428
|
+
if (this.debug())
|
|
5429
|
+
this.log('✅ validate - Archivo existente, pasa validación');
|
|
5168
5430
|
return null;
|
|
5169
|
-
|
|
5431
|
+
}
|
|
5432
|
+
if (this.isReplacing() && !this.value) {
|
|
5433
|
+
if (this.debug())
|
|
5434
|
+
this.log('❌ validate - Error: Reemplazando pero sin archivo');
|
|
5170
5435
|
return { required: true };
|
|
5171
|
-
|
|
5436
|
+
}
|
|
5437
|
+
if (!this.hasExistingFile() && !this.value) {
|
|
5438
|
+
if (this.debug())
|
|
5439
|
+
this.log('❌ validate - Error: Sin archivo seleccionado');
|
|
5172
5440
|
return { required: true };
|
|
5441
|
+
}
|
|
5442
|
+
const endTime = performance.now();
|
|
5443
|
+
if (this.debug()) {
|
|
5444
|
+
this.log(`✅ validate - Validación exitosa en ${(endTime - startTime).toFixed(2)}ms`);
|
|
5445
|
+
}
|
|
5173
5446
|
return null;
|
|
5174
5447
|
}
|
|
5175
5448
|
// --- Privados ---
|
|
@@ -5179,13 +5452,15 @@ class FileComponent {
|
|
|
5179
5452
|
if (!this.touched) {
|
|
5180
5453
|
this.touched = true;
|
|
5181
5454
|
this.onTouched();
|
|
5455
|
+
if (this.debug())
|
|
5456
|
+
this.log('👆 markAsTouched - Componente marcado como touched');
|
|
5182
5457
|
}
|
|
5183
5458
|
}
|
|
5184
5459
|
log(method, data) {
|
|
5185
5460
|
console.log(`[FileComponent] ${method}`, data || '');
|
|
5186
5461
|
}
|
|
5187
5462
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5188
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FileComponent, isStandalone: true, selector: "dsx-file-upload", inputs: { existingFile: { classPropertyName: "existingFile", publicName: "existingFile", isSignal: true, isRequired: false, transformFunction: null }, existingFileName: { classPropertyName: "existingFileName", publicName: "existingFileName", isSignal: true, isRequired: false, transformFunction: null }, overrideFileName: { classPropertyName: "overrideFileName", publicName: "overrideFileName", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null },
|
|
5463
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FileComponent, isStandalone: true, selector: "dsx-file-upload", inputs: { existingFile: { classPropertyName: "existingFile", publicName: "existingFile", isSignal: true, isRequired: false, transformFunction: null }, existingFileName: { classPropertyName: "existingFileName", publicName: "existingFileName", isSignal: true, isRequired: false, transformFunction: null }, overrideFileName: { classPropertyName: "overrideFileName", publicName: "overrideFileName", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, pTooltipOverride: { classPropertyName: "pTooltipOverride", publicName: "pTooltipOverride", isSignal: true, isRequired: false, transformFunction: null }, tooltipPositionOverride: { classPropertyName: "tooltipPositionOverride", publicName: "tooltipPositionOverride", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, invalidSummary: { classPropertyName: "invalidSummary", publicName: "invalidSummary", isSignal: true, isRequired: false, transformFunction: null }, invalidDetail: { classPropertyName: "invalidDetail", publicName: "invalidDetail", isSignal: true, isRequired: false, transformFunction: null }, invalidSizeSummary: { classPropertyName: "invalidSizeSummary", publicName: "invalidSizeSummary", isSignal: true, isRequired: false, transformFunction: null }, invalidSizeDetail: { classPropertyName: "invalidSizeDetail", publicName: "invalidSizeDetail", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
5189
5464
|
{
|
|
5190
5465
|
provide: NG_VALUE_ACCESSOR,
|
|
5191
5466
|
useExisting: forwardRef(() => FileComponent),
|
|
@@ -5196,11 +5471,11 @@ class FileComponent {
|
|
|
5196
5471
|
useExisting: forwardRef(() => FileComponent),
|
|
5197
5472
|
multi: true,
|
|
5198
5473
|
},
|
|
5199
|
-
], viewQueries: [{ propertyName: "fileUpload", first: true, predicate: ["fileUpload"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\">\r\n \uD83D\uDCC4 {{ overrideFileName() ?? existingFileName() }}\r\n </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n #fileUpload\r\n mode=\"basic\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"pTooltipOverride()\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n />\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"], dependencies: [{ kind: "component", type: FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }] });
|
|
5474
|
+
], viewQueries: [{ propertyName: "fileUpload", first: true, predicate: ["fileUpload"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\">\r\n \uD83D\uDCC4 {{ overrideFileName() ?? existingFileName() }}\r\n </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex flex-col gap-1\">\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n fluid\r\n #fileUpload\r\n chooseIcon=\"fa-solid fa-file-arrow-up\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"`${pTooltipOverride()} ${allowedFileTypeLabel()}`\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n >\r\n <ng-template\r\n #header\r\n let-files\r\n let-chooseCallback=\"chooseCallback\"\r\n let-clearCallback=\"clearCallback\"\r\n >\r\n <!-- Mostrar el bot\u00F3n de upload SOLO si NO hay archivo seleccionado -->\r\n @if (!files || files.length === 0) {\r\n <p-button\r\n (onClick)=\"choose($event, chooseCallback)\"\r\n icon=\"fa-solid fa-cloud-arrow-up\"\r\n [rounded]=\"true\"\r\n [outlined]=\"true\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n />\r\n }\r\n\r\n <span class=\"file-upload-placeholder\">\r\n <i class=\"fa-regular fa-file-lines placeholder-icon\"></i>\r\n <span class=\"placeholder-text\">\r\n @if (files && files.length > 0) {\r\n <span class=\"file-selected\">\r\n <i class=\"fa-regular fa-file-pdf file-icon\"></i>\r\n {{ files[0]?.name }}\r\n <span class=\"file-size\"\r\n >({{ formatFileSize(files[0]?.size) }})</span\r\n >\r\n </span>\r\n } @else if (allowedFileTypeLabel() !== \"archivo\") {\r\n <span class=\"empty-warning\">Ning\u00FAn</span>\r\n <span class=\"file-type empty-warning-type\">{{\r\n allowedFileTypeLabel()\r\n }}</span>\r\n <span class=\"empty-warning\">seleccionado</span>\r\n } @else {\r\n <span class=\"empty-warning\">Ning\u00FAn archivo seleccionado</span>\r\n }\r\n </span>\r\n </span>\r\n\r\n <!-- Mostrar bot\u00F3n de limpiar SOLO si hay archivo seleccionado -->\r\n @if (files && files.length > 0) {\r\n <p-button\r\n icon=\"fa-solid fa-xmark\"\r\n [rounded]=\"true\"\r\n [outlined]=\"true\"\r\n (onClick)=\"clearFile()\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n styleClass=\"clear-button\"\r\n />\r\n }\r\n </ng-template>\r\n <!-- Ocultar la vista previa por defecto -->\r\n <ng-template #content></ng-template>\r\n <ng-template #file></ng-template>\r\n </p-fileUpload>\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n\r\n <!-- Mensajes de error (validaci\u00F3n de PrimeNG) -->\r\n @if (errorMessage()) {\r\n <div class=\"file-error-message\">\r\n <i class=\"pi pi-exclamation-circle\"></i>\r\n {{ errorMessage() }}\r\n </div>\r\n }\r\n\r\n <!-- Mensaje de error del formulario (dsx-message-error) -->\r\n @if (showError() && errorControl()) {\r\n <dsx-message-error [control]=\"errorControl()\" />\r\n }\r\n </div>\r\n}\r\n\r\n<!-- Panel de Debug -->\r\n@if (debug() && isDevMode) {\r\n <div class=\"debug-panel\">\r\n <div class=\"debug-header\">\r\n <div class=\"debug-info\">\r\n <strong>\uD83D\uDD0D Debug File Upload:</strong>\r\n <span class=\"debug-item\"\r\n >Archivo: {{ value ? value.name : \"Ninguno\" }}</span\r\n >\r\n <span class=\"debug-item\"\r\n >Tama\u00F1o: {{ value ? formatFileSize(value.size) : \"-\" }}</span\r\n >\r\n <span class=\"debug-item\">Tipo: {{ value ? value.type : \"-\" }}</span>\r\n <span\r\n class=\"debug-item\"\r\n [style.color]=\"controlValid() ? '#10b981' : '#ef4444'\"\r\n >\r\n {{ controlValid() ? \"\u2705 V\u00E1lido\" : \"\u274C Inv\u00E1lido\" }}\r\n </span>\r\n <!-- Cambiar esta l\u00EDnea en el panel de debug -->\r\n <span class=\"debug-item\"\r\n >Required: {{ isRequired() ? \"\u2705\" : \"\u274C\" }}</span\r\n >\r\n <span class=\"debug-item\"\r\n >Reemplazando: {{ isReplacing() ? \"\u2705\" : \"\u274C\" }}</span\r\n >\r\n </div>\r\n <div class=\"debug-actions\">\r\n <button (click)=\"forceReload()\" class=\"debug-btn\" type=\"button\">\r\n \uD83D\uDD04 Recargar\r\n </button>\r\n <button (click)=\"clearFile()\" class=\"debug-btn\" type=\"button\">\r\n \uD83E\uDDF9 Limpiar\r\n </button>\r\n </div>\r\n </div>\r\n @if (errorMessage()) {\r\n <div class=\"debug-error\">\r\n <strong>\u26A0\uFE0F Error activo:</strong> {{ errorMessage() }}\r\n </div>\r\n }\r\n @if (controlErrors()) {\r\n <div class=\"debug-errors\">\r\n <strong>\u274C Errores del formulario:</strong>\r\n <pre>{{ controlErrors() | json }}</pre>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}::ng-deep .p-fileupload .p-fileupload-header{display:flex!important;align-items:center!important;gap:.75rem!important;padding:0!important;border:none!important;background:transparent!important}::ng-deep .p-fileupload .p-fileupload-header .p-button{background:transparent!important;border:2px dashed #d1d5db!important;border-radius:8px!important;padding:.5rem 1rem!important;margin:.5rem!important;min-height:40px!important;min-width:40px!important;transition:all .25s ease!important;color:#6b7280!important}::ng-deep .p-fileupload .p-fileupload-header .p-button:not(:disabled):hover{border-color:#3b82f6!important;background:#eff6ff!important;box-shadow:0 0 0 3px #3b82f61a!important;color:#3b82f6!important}::ng-deep .p-fileupload .p-fileupload-header .p-button:disabled{opacity:.5!important;cursor:not-allowed!important;border-color:#e5e7eb!important;color:#9ca3af!important}::ng-deep .p-fileupload .p-fileupload-header .p-button .p-button-icon{font-size:1.2rem!important;transition:color .25s ease!important}.file-upload-placeholder{display:flex;align-items:center;gap:.75rem;pointer-events:none;white-space:nowrap;padding:0 .5rem}.placeholder-icon{font-size:1.35rem;color:#94a3b8;transition:all .3s ease}.placeholder-text{color:#94a3b8;font-size:.875rem;font-weight:400;transition:all .3s ease;letter-spacing:.01em}.placeholder-text .file-type{color:#64748b;font-size:.8rem;font-weight:600;transition:all .3s ease;letter-spacing:.03em;text-transform:uppercase;background:#f1f5f9;padding:.1rem .4rem;border-radius:4px;margin:0 .1rem}.placeholder-text .empty-warning{color:#d97706;font-weight:500;transition:color .3s ease}.placeholder-text .file-type.empty-warning-type{color:#b45309;background:#fef3c7;font-weight:600}.file-selected{display:flex;align-items:center;gap:.5rem;color:#1f2937;font-weight:500;font-size:.85rem}.file-selected .file-icon{font-size:1.2rem;color:#bb1589}.file-selected .file-size{color:#9ca3af;font-weight:400;font-size:.75rem;margin-left:.25rem}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .placeholder-icon{color:#3b82f6}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .placeholder-text{color:#1f2937}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .file-type{color:#2563eb;background:#dbeafe}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .placeholder-text .empty-warning{color:#2563eb}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .file-type.empty-warning-type{color:#2563eb;background:#dbeafe}::ng-deep .p-fileupload .p-fileupload-buttonbar{display:none!important}::ng-deep .p-fileupload .p-fileupload-content{padding:0!important;border:none!important;background:transparent!important}::ng-deep .p-button.clear-button{background:transparent!important;border:none!important;border-radius:8px!important;padding:.5rem!important;min-width:40px!important;height:40px!important;transition:all .25s ease!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:none!important}::ng-deep .p-fileupload .p-fileupload-header .p-button.clear-button:not(:disabled):hover{background:none!important;border:none!important;box-shadow:none!important;transform:scale(1.05)!important}::ng-deep .p-button.clear-button .p-button-icon{color:#6b7280!important;font-size:1.1rem!important;transition:all .25s ease!important}::ng-deep .p-button.clear-button:not(:disabled):hover .p-button-icon{color:#ef4444!important}::ng-deep .p-button.clear-button:disabled{opacity:.5!important;cursor:not-allowed!important}.file-error-message{display:flex;align-items:center;gap:.5rem;color:#dc2626;font-size:.875rem;font-weight:400;padding:.25rem .75rem;background:#fef2f2;border-radius:6px;border:1px solid #fecaca;animation:slideDown .3s ease}.file-error-message i{font-size:1rem;color:#dc2626;flex-shrink:0}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.debug-panel{margin-top:8px;font-size:11px;color:#374151;background:#f8fafc;padding:8px 12px;border-radius:6px;font-family:monospace;border:1px solid #e2e8f0;width:100%}.debug-header{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap}.debug-info{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.debug-item{font-size:11px;color:#475569}.debug-item strong{color:#1e293b}.debug-actions{display:flex;gap:6px}.debug-btn{font-size:10px;padding:2px 10px;border-radius:4px;border:1px solid #cbd5e1;background:#fff;cursor:pointer;transition:all .2s ease;color:#475569;font-family:monospace}.debug-btn:hover{background:#f1f5f9;border-color:#94a3b8}.debug-error,.debug-errors{margin-top:6px;padding:4px 8px;background:#fef2f2;border-radius:4px;color:#dc2626;font-size:11px;border:1px solid #fecaca}.debug-errors pre{margin:4px 0 0;font-size:10px;color:#991b1b;white-space:pre-wrap;word-break:break-all}@media(max-width:640px){.placeholder-icon{font-size:1.1rem!important}.placeholder-text{font-size:.75rem!important}.placeholder-text .file-type{font-size:.7rem!important;padding:.05rem .3rem!important}.file-selected{font-size:.75rem!important}.file-selected .file-icon{font-size:1rem!important}.file-selected .file-size{font-size:.65rem!important}::ng-deep .p-fileupload .p-fileupload-header .p-button{padding:.4rem .75rem!important;min-height:34px!important;min-width:34px!important}::ng-deep .p-button.clear-button{min-width:34px!important;height:34px!important}.file-error-message{font-size:.75rem!important;padding:.2rem .5rem!important}.debug-panel{font-size:10px;padding:6px 8px}.debug-item{font-size:10px}.debug-btn{font-size:9px;padding:2px 6px}}\n"], dependencies: [{ kind: "component", type: FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }, { kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "pipe", type: JsonPipe, name: "json" }] });
|
|
5200
5475
|
}
|
|
5201
5476
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FileComponent, decorators: [{
|
|
5202
5477
|
type: Component,
|
|
5203
|
-
args: [{ selector: 'dsx-file-upload', imports: [FileUpload, Tooltip, Button], providers: [
|
|
5478
|
+
args: [{ selector: 'dsx-file-upload', imports: [FileUpload, Tooltip, Button, JsonPipe, AppMessageErrorComponent], providers: [
|
|
5204
5479
|
{
|
|
5205
5480
|
provide: NG_VALUE_ACCESSOR,
|
|
5206
5481
|
useExisting: forwardRef(() => FileComponent),
|
|
@@ -5211,119 +5486,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
5211
5486
|
useExisting: forwardRef(() => FileComponent),
|
|
5212
5487
|
multi: true,
|
|
5213
5488
|
},
|
|
5214
|
-
], template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\">\r\n \uD83D\uDCC4 {{ overrideFileName() ?? existingFileName() }}\r\n </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n #fileUpload\r\n mode=\"basic\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"pTooltipOverride()\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n />\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"] }]
|
|
5215
|
-
}], ctorParameters: () => [], propDecorators: { existingFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFile", required: false }] }], existingFileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFileName", required: false }] }], overrideFileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "overrideFileName", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }],
|
|
5216
|
-
|
|
5217
|
-
class JsonHighlightPipe {
|
|
5218
|
-
/**
|
|
5219
|
-
* Stringifica el JSON manteniendo arrays en una línea para una vista más compacta
|
|
5220
|
-
*/
|
|
5221
|
-
compactStringify(obj, indent = 0) {
|
|
5222
|
-
const spaces = ' '.repeat(indent);
|
|
5223
|
-
if (obj === null) {
|
|
5224
|
-
return 'null';
|
|
5225
|
-
}
|
|
5226
|
-
if (typeof obj !== 'object') {
|
|
5227
|
-
if (typeof obj === 'string') {
|
|
5228
|
-
return JSON.stringify(obj);
|
|
5229
|
-
}
|
|
5230
|
-
return String(obj);
|
|
5231
|
-
}
|
|
5232
|
-
if (obj instanceof Date) {
|
|
5233
|
-
return JSON.stringify(obj.toISOString());
|
|
5234
|
-
}
|
|
5235
|
-
if (Array.isArray(obj)) {
|
|
5236
|
-
// Mantener arrays en una línea
|
|
5237
|
-
const items = obj.map((item) => {
|
|
5238
|
-
if (item instanceof Date) {
|
|
5239
|
-
return JSON.stringify(item.toISOString());
|
|
5240
|
-
}
|
|
5241
|
-
if (typeof item === 'object' && item !== null) {
|
|
5242
|
-
return this.compactStringify(item, indent);
|
|
5243
|
-
}
|
|
5244
|
-
return typeof item === 'string'
|
|
5245
|
-
? JSON.stringify(item)
|
|
5246
|
-
: item === null
|
|
5247
|
-
? 'null'
|
|
5248
|
-
: String(item);
|
|
5249
|
-
});
|
|
5250
|
-
return `[${items.join(', ')}]`;
|
|
5251
|
-
}
|
|
5252
|
-
// Para objetos, mantener la indentación
|
|
5253
|
-
const keys = Object.keys(obj);
|
|
5254
|
-
if (keys.length === 0) {
|
|
5255
|
-
return '{}';
|
|
5256
|
-
}
|
|
5257
|
-
const innerIndent = indent + 2;
|
|
5258
|
-
const innerSpaces = ' '.repeat(innerIndent);
|
|
5259
|
-
const lines = keys.map((key) => {
|
|
5260
|
-
const value = obj[key];
|
|
5261
|
-
const stringified = value instanceof Date
|
|
5262
|
-
? JSON.stringify(value.toISOString())
|
|
5263
|
-
: typeof value === 'object' && value !== null
|
|
5264
|
-
? this.compactStringify(value, innerIndent)
|
|
5265
|
-
: typeof value === 'string'
|
|
5266
|
-
? JSON.stringify(value)
|
|
5267
|
-
: value === null
|
|
5268
|
-
? 'null'
|
|
5269
|
-
: String(value);
|
|
5270
|
-
return `${innerSpaces}"${key}": ${stringified}`;
|
|
5271
|
-
});
|
|
5272
|
-
return `{\n${lines.join(',\n')}\n${spaces}}`;
|
|
5273
|
-
}
|
|
5274
|
-
instanceId = Math.random().toString(36).slice(2, 8);
|
|
5275
|
-
executions = 0;
|
|
5276
|
-
transform(value) {
|
|
5277
|
-
if (isDevMode()) {
|
|
5278
|
-
this.executions++;
|
|
5279
|
-
if (this.executions % 10 === 0) {
|
|
5280
|
-
console.log(`[jsonHighlight:${this.instanceId}] ${this.executions}`);
|
|
5281
|
-
}
|
|
5282
|
-
}
|
|
5283
|
-
if (!value) {
|
|
5284
|
-
return '';
|
|
5285
|
-
}
|
|
5286
|
-
const json = this.compactStringify(value);
|
|
5287
|
-
// Detecta fechas ISO: "YYYY-MM-DDTHH:MM:SS.mmmZ"
|
|
5288
|
-
const ISO_DATE_RE = /^"(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2}\.\d{3}Z)"$/;
|
|
5289
|
-
// Aplica estilo y colores para diferenciar claves, valores, booleanos, números, etc.
|
|
5290
|
-
return json
|
|
5291
|
-
.replace(/&/g, '&')
|
|
5292
|
-
.replace(/</g, '<')
|
|
5293
|
-
.replace(/>/g, '>')
|
|
5294
|
-
.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?)/g, (match) => {
|
|
5295
|
-
// Si es una clave (tiene dos puntos al final)
|
|
5296
|
-
if (/:$/.test(match)) {
|
|
5297
|
-
return `<span class="json-key">${match}</span>`;
|
|
5298
|
-
}
|
|
5299
|
-
// Detectar fechas ISO (YYYY-MM-DDTHH:MM:SS.mmmZ)
|
|
5300
|
-
const isoMatch = match.match(ISO_DATE_RE);
|
|
5301
|
-
if (isoMatch) {
|
|
5302
|
-
return `<span class="json-string json-date">"<span class="json-date-part">${isoMatch[1]}</span><span class="json-date-sep">T</span><span class="json-date-time">${isoMatch[2]}</span>"</span>`;
|
|
5303
|
-
}
|
|
5304
|
-
// Si es un string (no tiene dos puntos)
|
|
5305
|
-
return `<span class="json-string">${match}</span>`;
|
|
5306
|
-
})
|
|
5307
|
-
.replace(/\b(true|false|null)\b/g, (match) => {
|
|
5308
|
-
if (match === 'null') {
|
|
5309
|
-
return '<span class="json-null">null</span>';
|
|
5310
|
-
}
|
|
5311
|
-
return `<span class="json-boolean">${match}</span>`;
|
|
5312
|
-
})
|
|
5313
|
-
.replace(/\b(\d+\.?\d*)\b/g, '<span class="json-number">$1</span>')
|
|
5314
|
-
.replace(/[{}\[\]]/g, '<span class="json-bracket">$&</span>')
|
|
5315
|
-
.replace(/,/g, '<span class="json-comma">$&</span>')
|
|
5316
|
-
.replace(/:/g, '<span class="json-colon">$&</span>');
|
|
5317
|
-
}
|
|
5318
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonHighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
5319
|
-
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: JsonHighlightPipe, isStandalone: true, name: "jsonHighlight" });
|
|
5320
|
-
}
|
|
5321
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonHighlightPipe, decorators: [{
|
|
5322
|
-
type: Pipe,
|
|
5323
|
-
args: [{
|
|
5324
|
-
name: 'jsonHighlight',
|
|
5325
|
-
}]
|
|
5326
|
-
}] });
|
|
5489
|
+
], template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\">\r\n \uD83D\uDCC4 {{ overrideFileName() ?? existingFileName() }}\r\n </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex flex-col gap-1\">\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n fluid\r\n #fileUpload\r\n chooseIcon=\"fa-solid fa-file-arrow-up\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"`${pTooltipOverride()} ${allowedFileTypeLabel()}`\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n >\r\n <ng-template\r\n #header\r\n let-files\r\n let-chooseCallback=\"chooseCallback\"\r\n let-clearCallback=\"clearCallback\"\r\n >\r\n <!-- Mostrar el bot\u00F3n de upload SOLO si NO hay archivo seleccionado -->\r\n @if (!files || files.length === 0) {\r\n <p-button\r\n (onClick)=\"choose($event, chooseCallback)\"\r\n icon=\"fa-solid fa-cloud-arrow-up\"\r\n [rounded]=\"true\"\r\n [outlined]=\"true\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n />\r\n }\r\n\r\n <span class=\"file-upload-placeholder\">\r\n <i class=\"fa-regular fa-file-lines placeholder-icon\"></i>\r\n <span class=\"placeholder-text\">\r\n @if (files && files.length > 0) {\r\n <span class=\"file-selected\">\r\n <i class=\"fa-regular fa-file-pdf file-icon\"></i>\r\n {{ files[0]?.name }}\r\n <span class=\"file-size\"\r\n >({{ formatFileSize(files[0]?.size) }})</span\r\n >\r\n </span>\r\n } @else if (allowedFileTypeLabel() !== \"archivo\") {\r\n <span class=\"empty-warning\">Ning\u00FAn</span>\r\n <span class=\"file-type empty-warning-type\">{{\r\n allowedFileTypeLabel()\r\n }}</span>\r\n <span class=\"empty-warning\">seleccionado</span>\r\n } @else {\r\n <span class=\"empty-warning\">Ning\u00FAn archivo seleccionado</span>\r\n }\r\n </span>\r\n </span>\r\n\r\n <!-- Mostrar bot\u00F3n de limpiar SOLO si hay archivo seleccionado -->\r\n @if (files && files.length > 0) {\r\n <p-button\r\n icon=\"fa-solid fa-xmark\"\r\n [rounded]=\"true\"\r\n [outlined]=\"true\"\r\n (onClick)=\"clearFile()\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n styleClass=\"clear-button\"\r\n />\r\n }\r\n </ng-template>\r\n <!-- Ocultar la vista previa por defecto -->\r\n <ng-template #content></ng-template>\r\n <ng-template #file></ng-template>\r\n </p-fileUpload>\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n\r\n <!-- Mensajes de error (validaci\u00F3n de PrimeNG) -->\r\n @if (errorMessage()) {\r\n <div class=\"file-error-message\">\r\n <i class=\"pi pi-exclamation-circle\"></i>\r\n {{ errorMessage() }}\r\n </div>\r\n }\r\n\r\n <!-- Mensaje de error del formulario (dsx-message-error) -->\r\n @if (showError() && errorControl()) {\r\n <dsx-message-error [control]=\"errorControl()\" />\r\n }\r\n </div>\r\n}\r\n\r\n<!-- Panel de Debug -->\r\n@if (debug() && isDevMode) {\r\n <div class=\"debug-panel\">\r\n <div class=\"debug-header\">\r\n <div class=\"debug-info\">\r\n <strong>\uD83D\uDD0D Debug File Upload:</strong>\r\n <span class=\"debug-item\"\r\n >Archivo: {{ value ? value.name : \"Ninguno\" }}</span\r\n >\r\n <span class=\"debug-item\"\r\n >Tama\u00F1o: {{ value ? formatFileSize(value.size) : \"-\" }}</span\r\n >\r\n <span class=\"debug-item\">Tipo: {{ value ? value.type : \"-\" }}</span>\r\n <span\r\n class=\"debug-item\"\r\n [style.color]=\"controlValid() ? '#10b981' : '#ef4444'\"\r\n >\r\n {{ controlValid() ? \"\u2705 V\u00E1lido\" : \"\u274C Inv\u00E1lido\" }}\r\n </span>\r\n <!-- Cambiar esta l\u00EDnea en el panel de debug -->\r\n <span class=\"debug-item\"\r\n >Required: {{ isRequired() ? \"\u2705\" : \"\u274C\" }}</span\r\n >\r\n <span class=\"debug-item\"\r\n >Reemplazando: {{ isReplacing() ? \"\u2705\" : \"\u274C\" }}</span\r\n >\r\n </div>\r\n <div class=\"debug-actions\">\r\n <button (click)=\"forceReload()\" class=\"debug-btn\" type=\"button\">\r\n \uD83D\uDD04 Recargar\r\n </button>\r\n <button (click)=\"clearFile()\" class=\"debug-btn\" type=\"button\">\r\n \uD83E\uDDF9 Limpiar\r\n </button>\r\n </div>\r\n </div>\r\n @if (errorMessage()) {\r\n <div class=\"debug-error\">\r\n <strong>\u26A0\uFE0F Error activo:</strong> {{ errorMessage() }}\r\n </div>\r\n }\r\n @if (controlErrors()) {\r\n <div class=\"debug-errors\">\r\n <strong>\u274C Errores del formulario:</strong>\r\n <pre>{{ controlErrors() | json }}</pre>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}::ng-deep .p-fileupload .p-fileupload-header{display:flex!important;align-items:center!important;gap:.75rem!important;padding:0!important;border:none!important;background:transparent!important}::ng-deep .p-fileupload .p-fileupload-header .p-button{background:transparent!important;border:2px dashed #d1d5db!important;border-radius:8px!important;padding:.5rem 1rem!important;margin:.5rem!important;min-height:40px!important;min-width:40px!important;transition:all .25s ease!important;color:#6b7280!important}::ng-deep .p-fileupload .p-fileupload-header .p-button:not(:disabled):hover{border-color:#3b82f6!important;background:#eff6ff!important;box-shadow:0 0 0 3px #3b82f61a!important;color:#3b82f6!important}::ng-deep .p-fileupload .p-fileupload-header .p-button:disabled{opacity:.5!important;cursor:not-allowed!important;border-color:#e5e7eb!important;color:#9ca3af!important}::ng-deep .p-fileupload .p-fileupload-header .p-button .p-button-icon{font-size:1.2rem!important;transition:color .25s ease!important}.file-upload-placeholder{display:flex;align-items:center;gap:.75rem;pointer-events:none;white-space:nowrap;padding:0 .5rem}.placeholder-icon{font-size:1.35rem;color:#94a3b8;transition:all .3s ease}.placeholder-text{color:#94a3b8;font-size:.875rem;font-weight:400;transition:all .3s ease;letter-spacing:.01em}.placeholder-text .file-type{color:#64748b;font-size:.8rem;font-weight:600;transition:all .3s ease;letter-spacing:.03em;text-transform:uppercase;background:#f1f5f9;padding:.1rem .4rem;border-radius:4px;margin:0 .1rem}.placeholder-text .empty-warning{color:#d97706;font-weight:500;transition:color .3s ease}.placeholder-text .file-type.empty-warning-type{color:#b45309;background:#fef3c7;font-weight:600}.file-selected{display:flex;align-items:center;gap:.5rem;color:#1f2937;font-weight:500;font-size:.85rem}.file-selected .file-icon{font-size:1.2rem;color:#bb1589}.file-selected .file-size{color:#9ca3af;font-weight:400;font-size:.75rem;margin-left:.25rem}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .placeholder-icon{color:#3b82f6}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .placeholder-text{color:#1f2937}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .file-type{color:#2563eb;background:#dbeafe}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .placeholder-text .empty-warning{color:#2563eb}::ng-deep .p-fileupload .p-fileupload-header:has(.p-button:not(:disabled):hover) .file-type.empty-warning-type{color:#2563eb;background:#dbeafe}::ng-deep .p-fileupload .p-fileupload-buttonbar{display:none!important}::ng-deep .p-fileupload .p-fileupload-content{padding:0!important;border:none!important;background:transparent!important}::ng-deep .p-button.clear-button{background:transparent!important;border:none!important;border-radius:8px!important;padding:.5rem!important;min-width:40px!important;height:40px!important;transition:all .25s ease!important;display:flex!important;align-items:center!important;justify-content:center!important;box-shadow:none!important}::ng-deep .p-fileupload .p-fileupload-header .p-button.clear-button:not(:disabled):hover{background:none!important;border:none!important;box-shadow:none!important;transform:scale(1.05)!important}::ng-deep .p-button.clear-button .p-button-icon{color:#6b7280!important;font-size:1.1rem!important;transition:all .25s ease!important}::ng-deep .p-button.clear-button:not(:disabled):hover .p-button-icon{color:#ef4444!important}::ng-deep .p-button.clear-button:disabled{opacity:.5!important;cursor:not-allowed!important}.file-error-message{display:flex;align-items:center;gap:.5rem;color:#dc2626;font-size:.875rem;font-weight:400;padding:.25rem .75rem;background:#fef2f2;border-radius:6px;border:1px solid #fecaca;animation:slideDown .3s ease}.file-error-message i{font-size:1rem;color:#dc2626;flex-shrink:0}@keyframes slideDown{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.debug-panel{margin-top:8px;font-size:11px;color:#374151;background:#f8fafc;padding:8px 12px;border-radius:6px;font-family:monospace;border:1px solid #e2e8f0;width:100%}.debug-header{display:flex;justify-content:space-between;align-items:center;gap:8px;flex-wrap:wrap}.debug-info{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.debug-item{font-size:11px;color:#475569}.debug-item strong{color:#1e293b}.debug-actions{display:flex;gap:6px}.debug-btn{font-size:10px;padding:2px 10px;border-radius:4px;border:1px solid #cbd5e1;background:#fff;cursor:pointer;transition:all .2s ease;color:#475569;font-family:monospace}.debug-btn:hover{background:#f1f5f9;border-color:#94a3b8}.debug-error,.debug-errors{margin-top:6px;padding:4px 8px;background:#fef2f2;border-radius:4px;color:#dc2626;font-size:11px;border:1px solid #fecaca}.debug-errors pre{margin:4px 0 0;font-size:10px;color:#991b1b;white-space:pre-wrap;word-break:break-all}@media(max-width:640px){.placeholder-icon{font-size:1.1rem!important}.placeholder-text{font-size:.75rem!important}.placeholder-text .file-type{font-size:.7rem!important;padding:.05rem .3rem!important}.file-selected{font-size:.75rem!important}.file-selected .file-icon{font-size:1rem!important}.file-selected .file-size{font-size:.65rem!important}::ng-deep .p-fileupload .p-fileupload-header .p-button{padding:.4rem .75rem!important;min-height:34px!important;min-width:34px!important}::ng-deep .p-button.clear-button{min-width:34px!important;height:34px!important}.file-error-message{font-size:.75rem!important;padding:.2rem .5rem!important}.debug-panel{font-size:10px;padding:6px 8px}.debug-item{font-size:10px}.debug-btn{font-size:9px;padding:2px 6px}}\n"] }]
|
|
5490
|
+
}], ctorParameters: () => [], propDecorators: { existingFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFile", required: false }] }], existingFileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFileName", required: false }] }], overrideFileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "overrideFileName", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], accept: [{ type: i0.Input, args: [{ isSignal: true, alias: "accept", required: false }] }], pTooltipOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "pTooltipOverride", required: false }] }], tooltipPositionOverride: [{ type: i0.Input, args: [{ isSignal: true, alias: "tooltipPositionOverride", required: false }] }], maxFileSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxFileSize", required: false }] }], invalidSummary: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidSummary", required: false }] }], invalidDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidDetail", required: false }] }], invalidSizeSummary: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidSizeSummary", required: false }] }], invalidSizeDetail: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalidSizeDetail", required: false }] }], fileUpload: [{ type: i0.ViewChild, args: ['fileUpload', { isSignal: true }] }] } });
|
|
5327
5491
|
|
|
5328
5492
|
function uid() {
|
|
5329
5493
|
return Math.random().toString(36).substring(2, 8);
|
|
@@ -5331,67 +5495,245 @@ function uid() {
|
|
|
5331
5495
|
class JsonValuesDebujComponent {
|
|
5332
5496
|
form = input(null, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
|
|
5333
5497
|
debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
|
|
5498
|
+
debounceTime = input(300, ...(ngDevMode ? [{ debugName: "debounceTime" }] : /* istanbul ignore next */ [])); // ✅ Configurable
|
|
5334
5499
|
debugValue = signal(null, ...(ngDevMode ? [{ debugName: "debugValue" }] : /* istanbul ignore next */ []));
|
|
5500
|
+
serializedValue = signal('', ...(ngDevMode ? [{ debugName: "serializedValue" }] : /* istanbul ignore next */ []));
|
|
5501
|
+
fileInfo = signal(null, ...(ngDevMode ? [{ debugName: "fileInfo" }] : /* istanbul ignore next */ []));
|
|
5502
|
+
isDevModeSignal = computed(() => isDevMode(), ...(ngDevMode ? [{ debugName: "isDevModeSignal" }] : /* istanbul ignore next */ []));
|
|
5503
|
+
isDebugEnabledSignal = computed(() => {
|
|
5504
|
+
const isDev = this.isDevModeSignal();
|
|
5505
|
+
const debug = this.debug();
|
|
5506
|
+
const result = isDev && debug;
|
|
5507
|
+
if (this._lastDebugState !== result) {
|
|
5508
|
+
this._lastDebugState = result;
|
|
5509
|
+
if (result) {
|
|
5510
|
+
console.log(`[JsonDebug:${this.id}] 📝 Logs activados`, {
|
|
5511
|
+
isDevMode: isDev,
|
|
5512
|
+
debug: debug,
|
|
5513
|
+
});
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
return result;
|
|
5517
|
+
}, ...(ngDevMode ? [{ debugName: "isDebugEnabledSignal" }] : /* istanbul ignore next */ []));
|
|
5518
|
+
_lastDebugState = null;
|
|
5519
|
+
_lastVisualState = null;
|
|
5335
5520
|
id = uid();
|
|
5336
5521
|
subscription;
|
|
5337
5522
|
changesCount = 0;
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5523
|
+
isInitialized = false;
|
|
5524
|
+
lastLogTime = 0;
|
|
5525
|
+
shouldShowVisual = computed(() => {
|
|
5526
|
+
const show = this.isDevModeSignal();
|
|
5527
|
+
if (this._lastVisualState !== show) {
|
|
5528
|
+
this._lastVisualState = show;
|
|
5529
|
+
if (this.shouldShowLogs()) {
|
|
5530
|
+
console.log(`[JsonDebug:${this.id}] 👁️ Visual: ${show}`, {
|
|
5531
|
+
isDevMode: isDevMode(),
|
|
5532
|
+
reason: show ? 'Modo desarrollo activo' : 'Modo producción',
|
|
5533
|
+
});
|
|
5534
|
+
}
|
|
5535
|
+
}
|
|
5536
|
+
return show;
|
|
5537
|
+
}, ...(ngDevMode ? [{ debugName: "shouldShowVisual" }] : /* istanbul ignore next */ []));
|
|
5538
|
+
shouldShowLogs = computed(() => {
|
|
5539
|
+
const isDev = this.isDevModeSignal();
|
|
5540
|
+
const debug = this.debug();
|
|
5541
|
+
const show = isDev && debug;
|
|
5542
|
+
if (this._lastDebugState !== show) {
|
|
5543
|
+
this._lastDebugState = show;
|
|
5544
|
+
if (show) {
|
|
5545
|
+
console.log(`[JsonDebug:${this.id}] 📝 Logs activados`, {
|
|
5546
|
+
isDevMode: isDev,
|
|
5547
|
+
debug: debug,
|
|
5548
|
+
});
|
|
5549
|
+
}
|
|
5550
|
+
}
|
|
5551
|
+
return show;
|
|
5552
|
+
}, ...(ngDevMode ? [{ debugName: "shouldShowLogs" }] : /* istanbul ignore next */ []));
|
|
5348
5553
|
constructor() {
|
|
5349
|
-
if (this.shouldShowLogs) {
|
|
5350
|
-
console.log(
|
|
5554
|
+
if (this.shouldShowLogs()) {
|
|
5555
|
+
console.log(`[JsonDebug:${this.id}] 🏗️ Constructor`, {
|
|
5556
|
+
id: this.id,
|
|
5557
|
+
isDevMode: isDevMode(),
|
|
5558
|
+
debug: this.debug(),
|
|
5559
|
+
debounceTime: this.debounceTime(),
|
|
5560
|
+
});
|
|
5351
5561
|
}
|
|
5352
5562
|
effect((onCleanup) => {
|
|
5353
5563
|
const form = this.form();
|
|
5354
|
-
if (!form)
|
|
5564
|
+
if (!form) {
|
|
5565
|
+
if (this.shouldShowLogs() && this.isInitialized) {
|
|
5566
|
+
console.log(`[JsonDebug:${this.id}] ⚠️ Formulario eliminado`);
|
|
5567
|
+
}
|
|
5568
|
+
if (this.subscription) {
|
|
5569
|
+
this.subscription.unsubscribe();
|
|
5570
|
+
this.subscription = undefined;
|
|
5571
|
+
}
|
|
5572
|
+
this.isInitialized = false;
|
|
5355
5573
|
return;
|
|
5356
|
-
|
|
5357
|
-
|
|
5574
|
+
}
|
|
5575
|
+
if (this.isInitialized && this.subscription) {
|
|
5576
|
+
if (this.shouldShowLogs()) {
|
|
5577
|
+
console.log(`[JsonDebug:${this.id}] 🔄 Formulario ya inicializado, saltando...`);
|
|
5578
|
+
}
|
|
5579
|
+
return;
|
|
5580
|
+
}
|
|
5581
|
+
if (this.shouldShowLogs()) {
|
|
5582
|
+
console.log(`[JsonDebug:${this.id}] 🔄 Effect iniciado`, {
|
|
5358
5583
|
controls: Object.keys(form.controls).length,
|
|
5584
|
+
timestamp: new Date().toISOString(),
|
|
5585
|
+
debounceTime: this.debounceTime(),
|
|
5359
5586
|
});
|
|
5360
5587
|
}
|
|
5361
|
-
this.subscription
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5588
|
+
if (this.subscription) {
|
|
5589
|
+
this.subscription.unsubscribe();
|
|
5590
|
+
this.subscription = undefined;
|
|
5591
|
+
}
|
|
5592
|
+
const rawValue = form.getRawValue();
|
|
5593
|
+
this.debugValue.set(rawValue);
|
|
5594
|
+
this.updateSerializedValue(rawValue);
|
|
5595
|
+
this.updateFileInfo(rawValue);
|
|
5596
|
+
if (this.shouldShowLogs()) {
|
|
5597
|
+
console.log(`[JsonDebug:${this.id}] 📊 Procesamiento inicial`, {
|
|
5598
|
+
hasFile: !!rawValue?.file,
|
|
5599
|
+
fileType: rawValue?.file ? typeof rawValue.file : 'N/A',
|
|
5600
|
+
serializedLength: this.serializedValue().length,
|
|
5601
|
+
});
|
|
5602
|
+
}
|
|
5603
|
+
// ✅ Aplicar debounce y distinctUntilChanged para reducir logs
|
|
5604
|
+
this.subscription = form.valueChanges
|
|
5605
|
+
.pipe(debounceTime(this.debounceTime()), // ✅ Esperar 300ms antes de emitir
|
|
5606
|
+
distinctUntilChanged((prev, curr) => {
|
|
5607
|
+
// ✅ Comparar si los valores son iguales
|
|
5608
|
+
return JSON.stringify(prev) === JSON.stringify(curr);
|
|
5609
|
+
}))
|
|
5610
|
+
.subscribe(() => {
|
|
5611
|
+
const startTime = performance.now();
|
|
5612
|
+
const rawValue = form.getRawValue();
|
|
5613
|
+
this.debugValue.set(rawValue);
|
|
5614
|
+
this.updateSerializedValue(rawValue);
|
|
5615
|
+
this.updateFileInfo(rawValue);
|
|
5616
|
+
this.changesCount++;
|
|
5617
|
+
const endTime = performance.now();
|
|
5618
|
+
if (this.shouldShowLogs()) {
|
|
5619
|
+
const elapsed = endTime - startTime;
|
|
5620
|
+
console.log(`[JsonDebug:${this.id}] 🔄 Cambio #${this.changesCount}`, {
|
|
5621
|
+
tiempoMs: elapsed.toFixed(2),
|
|
5622
|
+
hasFile: !!rawValue?.file,
|
|
5623
|
+
serializedLength: this.serializedValue().length,
|
|
5624
|
+
warning: elapsed > 10 ? '⚠️ Cambio lento' : '✅ OK',
|
|
5369
5625
|
});
|
|
5626
|
+
if (this.changesCount % 10 === 0) {
|
|
5627
|
+
console.log(`[JsonDebug:${this.id}] 📊 Estadísticas`, {
|
|
5628
|
+
cambios: this.changesCount,
|
|
5629
|
+
promedioMs: (elapsed / this.changesCount).toFixed(2),
|
|
5630
|
+
ultimoMs: elapsed.toFixed(2),
|
|
5631
|
+
});
|
|
5632
|
+
}
|
|
5370
5633
|
}
|
|
5371
5634
|
});
|
|
5635
|
+
this.isInitialized = true;
|
|
5372
5636
|
onCleanup(() => {
|
|
5373
|
-
this.
|
|
5637
|
+
if (this.shouldShowLogs()) {
|
|
5638
|
+
console.log(`[JsonDebug:${this.id}] 🧹 Limpieza effect`, {
|
|
5639
|
+
cambiosProcesados: this.changesCount,
|
|
5640
|
+
isInitialized: this.isInitialized,
|
|
5641
|
+
});
|
|
5642
|
+
}
|
|
5374
5643
|
});
|
|
5375
5644
|
});
|
|
5376
5645
|
}
|
|
5377
|
-
|
|
5378
|
-
if (
|
|
5379
|
-
|
|
5646
|
+
updateSerializedValue(value) {
|
|
5647
|
+
if (!value) {
|
|
5648
|
+
this.serializedValue.set('');
|
|
5649
|
+
return;
|
|
5380
5650
|
}
|
|
5381
|
-
|
|
5382
|
-
|
|
5383
|
-
|
|
5651
|
+
try {
|
|
5652
|
+
const copy = { ...value };
|
|
5653
|
+
if (copy.file && typeof copy.file === 'object') {
|
|
5654
|
+
const fileProps = {};
|
|
5655
|
+
if ('name' in copy.file && copy.file.name !== undefined) {
|
|
5656
|
+
fileProps.name = copy.file.name;
|
|
5657
|
+
}
|
|
5658
|
+
if ('size' in copy.file && copy.file.size !== undefined) {
|
|
5659
|
+
fileProps.size = copy.file.size;
|
|
5660
|
+
}
|
|
5661
|
+
if ('type' in copy.file && copy.file.type !== undefined) {
|
|
5662
|
+
fileProps.type = copy.file.type;
|
|
5663
|
+
}
|
|
5664
|
+
if ('lastModified' in copy.file &&
|
|
5665
|
+
copy.file.lastModified !== undefined) {
|
|
5666
|
+
fileProps.lastModified = copy.file.lastModified;
|
|
5667
|
+
}
|
|
5668
|
+
const otherProps = Object.keys(copy.file).filter((key) => !['name', 'size', 'type', 'lastModified'].includes(key));
|
|
5669
|
+
otherProps.forEach((key) => {
|
|
5670
|
+
fileProps[key] = copy.file[key];
|
|
5671
|
+
});
|
|
5672
|
+
copy.file = fileProps;
|
|
5673
|
+
}
|
|
5674
|
+
this.serializedValue.set(JSON.stringify(copy, null, 2));
|
|
5675
|
+
}
|
|
5676
|
+
catch (error) {
|
|
5677
|
+
this.serializedValue.set('Error al serializar: ' + error);
|
|
5678
|
+
if (this.shouldShowLogs()) {
|
|
5679
|
+
console.error('[JsonDebug] Error serializing value:', error);
|
|
5680
|
+
}
|
|
5681
|
+
}
|
|
5682
|
+
}
|
|
5683
|
+
updateFileInfo(value) {
|
|
5684
|
+
if (!value || !value.file) {
|
|
5685
|
+
this.fileInfo.set(null);
|
|
5686
|
+
return;
|
|
5687
|
+
}
|
|
5688
|
+
const file = value.file;
|
|
5689
|
+
const info = {
|
|
5690
|
+
exists: true,
|
|
5691
|
+
isFile: file instanceof File,
|
|
5692
|
+
isBlob: file instanceof Blob,
|
|
5693
|
+
hasName: 'name' in file,
|
|
5694
|
+
hasSize: 'size' in file,
|
|
5695
|
+
hasType: 'type' in file,
|
|
5696
|
+
hasLastModified: 'lastModified' in file,
|
|
5697
|
+
};
|
|
5698
|
+
if ('name' in file && file.name !== undefined) {
|
|
5699
|
+
info.name = file.name;
|
|
5700
|
+
}
|
|
5701
|
+
if ('size' in file && file.size !== undefined) {
|
|
5702
|
+
info.size = file.size;
|
|
5703
|
+
}
|
|
5704
|
+
if ('type' in file && file.type !== undefined) {
|
|
5705
|
+
info.mimeType = file.type;
|
|
5706
|
+
}
|
|
5707
|
+
if ('lastModified' in file && file.lastModified !== undefined) {
|
|
5708
|
+
info.lastModified = file.lastModified;
|
|
5709
|
+
}
|
|
5710
|
+
this.fileInfo.set(info);
|
|
5711
|
+
}
|
|
5712
|
+
hasFile() {
|
|
5713
|
+
return !!this.fileInfo();
|
|
5714
|
+
}
|
|
5715
|
+
getFileInfo() {
|
|
5716
|
+
return this.fileInfo();
|
|
5717
|
+
}
|
|
5718
|
+
ngOnDestroy() {
|
|
5719
|
+
if (this.shouldShowLogs()) {
|
|
5720
|
+
console.log(`[JsonDebug:${this.id}] 💀 Destroy`, {
|
|
5384
5721
|
totalChanges: this.changesCount,
|
|
5722
|
+
tiempoVida: `${(performance.now() / 1000).toFixed(2)}s`,
|
|
5385
5723
|
});
|
|
5386
5724
|
}
|
|
5725
|
+
if (this.subscription) {
|
|
5726
|
+
this.subscription.unsubscribe();
|
|
5727
|
+
this.subscription = undefined;
|
|
5728
|
+
}
|
|
5387
5729
|
}
|
|
5388
5730
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonValuesDebujComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5389
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JsonValuesDebujComponent, isStandalone: true, selector: "app-json-values-debuj", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (
|
|
5731
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: JsonValuesDebujComponent, isStandalone: true, selector: "app-json-values-debuj", inputs: { form: { classPropertyName: "form", publicName: "form", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, debounceTime: { classPropertyName: "debounceTime", publicName: "debounceTime", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<!-- json-values-debuj.component.html -->\r\n@if (shouldShowVisual()) {\r\n <div class=\"json-debug-container\">\r\n <div class=\"json-debug-header\">\r\n <span class=\"json-debug-id\">\uD83D\uDD0D Debug #{{ id }}</span>\r\n <span class=\"json-debug-info\">\r\n @if (debugValue()) {\r\n <span class=\"badge badge-success\">\u2713 Formulario activo</span>\r\n } @else {\r\n <span class=\"badge badge-danger\">\u2717 Sin formulario</span>\r\n }\r\n </span>\r\n </div>\r\n\r\n @if (debugValue()) {\r\n <div class=\"json-debug-content\">\r\n <!-- Informaci\u00F3n del archivo (si existe) -->\r\n @if (hasFile()) {\r\n <div class=\"file-info-box\">\r\n <div class=\"file-info-header\">\uD83D\uDCC1 Archivo seleccionado</div>\r\n <div class=\"file-info-grid\">\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Nombre:</span>\r\n <span class=\"value\">{{ getFileInfo()?.name || \"N/A\" }}</span>\r\n </div>\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Tama\u00F1o:</span>\r\n <span class=\"value\"\r\n >{{ getFileInfo()?.size | number }} bytes</span\r\n >\r\n </div>\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Tipo:</span>\r\n <span class=\"value\">{{\r\n getFileInfo()?.mimeType || \"N/A\"\r\n }}</span>\r\n </div>\r\n @if (getFileInfo()?.lastModified) {\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Modificado:</span>\r\n <span class=\"value\">{{\r\n getFileInfo()?.lastModified | date: \"medium\"\r\n }}</span>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- JSON -->\r\n <div class=\"json-display\">\r\n <pre class=\"json-colored\">{{ serializedValue() }}</pre>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".json-debug-container{margin:10px 0;border:1px solid #dee2e6;border-radius:8px;background-color:#f8f9fa;font-family:Consolas,Monaco,Courier New,monospace;font-size:13px;box-shadow:0 2px 8px #0000001a}.json-debug-header{display:flex;justify-content:space-between;align-items:center;padding:12px 20px;background:linear-gradient(135deg,#667eea,#764ba2);border-radius:8px 8px 0 0;color:#fff}.json-debug-id{font-weight:700;font-size:14px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.2)}.badge{padding:4px 14px;border-radius:20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.badge-success{background-color:#10b981;color:#fff;box-shadow:0 2px 4px #10b9814d}.badge-danger{background-color:#ef4444;color:#fff;box-shadow:0 2px 4px #ef44444d}.json-debug-content{padding:16px 20px}.file-info-box{background:linear-gradient(135deg,#f0f9ff,#e0f2fe);border:1px solid #bae6fd;border-radius:6px;padding:12px 16px;margin-bottom:16px}.file-info-header{font-weight:600;color:#0369a1;font-size:13px;margin-bottom:8px}.file-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:6px}.file-info-item{padding:4px 8px;background-color:#fff;border-radius:4px;border:1px solid #e5e7eb}.file-info-item .label{font-weight:600;color:#6b7280;margin-right:8px;font-size:12px}.file-info-item .value{color:#0369a1;font-family:Consolas,Monaco,Courier New,monospace;font-size:12px}.json-display{margin-top:0}.json-colored{margin:0;padding:16px 20px;background-color:#1e1e1e;border-radius:6px;overflow:auto;font-size:13px;line-height:1.7;color:#d4d4d4;min-height:50px;max-height:500px;font-family:Consolas,Monaco,Courier New,monospace;white-space:pre}.json-colored::-webkit-scrollbar{width:10px;height:10px}.json-colored::-webkit-scrollbar-track{background:#2d2d2d;border-radius:4px}.json-colored::-webkit-scrollbar-thumb{background:#666;border-radius:4px}.json-colored::-webkit-scrollbar-thumb:hover{background:#888}@media(max-width:768px){.json-debug-header{flex-direction:column;gap:8px;padding:10px 16px}.json-debug-content{padding:12px 16px}.file-info-grid{grid-template-columns:1fr}.json-colored{font-size:11px;padding:12px 14px;max-height:300px}}\n"], dependencies: [{ kind: "pipe", type: DatePipe, name: "date" }, { kind: "pipe", type: DecimalPipe, name: "number" }], encapsulation: i0.ViewEncapsulation.None });
|
|
5390
5732
|
}
|
|
5391
5733
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonValuesDebujComponent, decorators: [{
|
|
5392
5734
|
type: Component,
|
|
5393
|
-
args: [{ selector: 'app-json-values-debuj', standalone: true, imports: [
|
|
5394
|
-
}], ctorParameters: () => [], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
|
|
5735
|
+
args: [{ selector: 'app-json-values-debuj', standalone: true, imports: [DatePipe, DecimalPipe], encapsulation: ViewEncapsulation.None, template: "<!-- json-values-debuj.component.html -->\r\n@if (shouldShowVisual()) {\r\n <div class=\"json-debug-container\">\r\n <div class=\"json-debug-header\">\r\n <span class=\"json-debug-id\">\uD83D\uDD0D Debug #{{ id }}</span>\r\n <span class=\"json-debug-info\">\r\n @if (debugValue()) {\r\n <span class=\"badge badge-success\">\u2713 Formulario activo</span>\r\n } @else {\r\n <span class=\"badge badge-danger\">\u2717 Sin formulario</span>\r\n }\r\n </span>\r\n </div>\r\n\r\n @if (debugValue()) {\r\n <div class=\"json-debug-content\">\r\n <!-- Informaci\u00F3n del archivo (si existe) -->\r\n @if (hasFile()) {\r\n <div class=\"file-info-box\">\r\n <div class=\"file-info-header\">\uD83D\uDCC1 Archivo seleccionado</div>\r\n <div class=\"file-info-grid\">\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Nombre:</span>\r\n <span class=\"value\">{{ getFileInfo()?.name || \"N/A\" }}</span>\r\n </div>\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Tama\u00F1o:</span>\r\n <span class=\"value\"\r\n >{{ getFileInfo()?.size | number }} bytes</span\r\n >\r\n </div>\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Tipo:</span>\r\n <span class=\"value\">{{\r\n getFileInfo()?.mimeType || \"N/A\"\r\n }}</span>\r\n </div>\r\n @if (getFileInfo()?.lastModified) {\r\n <div class=\"file-info-item\">\r\n <span class=\"label\">Modificado:</span>\r\n <span class=\"value\">{{\r\n getFileInfo()?.lastModified | date: \"medium\"\r\n }}</span>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- JSON -->\r\n <div class=\"json-display\">\r\n <pre class=\"json-colored\">{{ serializedValue() }}</pre>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n}\r\n", styles: [".json-debug-container{margin:10px 0;border:1px solid #dee2e6;border-radius:8px;background-color:#f8f9fa;font-family:Consolas,Monaco,Courier New,monospace;font-size:13px;box-shadow:0 2px 8px #0000001a}.json-debug-header{display:flex;justify-content:space-between;align-items:center;padding:12px 20px;background:linear-gradient(135deg,#667eea,#764ba2);border-radius:8px 8px 0 0;color:#fff}.json-debug-id{font-weight:700;font-size:14px;color:#fff;text-shadow:0 1px 2px rgba(0,0,0,.2)}.badge{padding:4px 14px;border-radius:20px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px}.badge-success{background-color:#10b981;color:#fff;box-shadow:0 2px 4px #10b9814d}.badge-danger{background-color:#ef4444;color:#fff;box-shadow:0 2px 4px #ef44444d}.json-debug-content{padding:16px 20px}.file-info-box{background:linear-gradient(135deg,#f0f9ff,#e0f2fe);border:1px solid #bae6fd;border-radius:6px;padding:12px 16px;margin-bottom:16px}.file-info-header{font-weight:600;color:#0369a1;font-size:13px;margin-bottom:8px}.file-info-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:6px}.file-info-item{padding:4px 8px;background-color:#fff;border-radius:4px;border:1px solid #e5e7eb}.file-info-item .label{font-weight:600;color:#6b7280;margin-right:8px;font-size:12px}.file-info-item .value{color:#0369a1;font-family:Consolas,Monaco,Courier New,monospace;font-size:12px}.json-display{margin-top:0}.json-colored{margin:0;padding:16px 20px;background-color:#1e1e1e;border-radius:6px;overflow:auto;font-size:13px;line-height:1.7;color:#d4d4d4;min-height:50px;max-height:500px;font-family:Consolas,Monaco,Courier New,monospace;white-space:pre}.json-colored::-webkit-scrollbar{width:10px;height:10px}.json-colored::-webkit-scrollbar-track{background:#2d2d2d;border-radius:4px}.json-colored::-webkit-scrollbar-thumb{background:#666;border-radius:4px}.json-colored::-webkit-scrollbar-thumb:hover{background:#888}@media(max-width:768px){.json-debug-header{flex-direction:column;gap:8px;padding:10px 16px}.json-debug-content{padding:12px 16px}.file-info-grid{grid-template-columns:1fr}.json-colored{font-size:11px;padding:12px 14px;max-height:300px}}\n"] }]
|
|
5736
|
+
}], ctorParameters: () => [], propDecorators: { form: [{ type: i0.Input, args: [{ isSignal: true, alias: "form", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], debounceTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceTime", required: false }] }] } });
|
|
5395
5737
|
|
|
5396
5738
|
class IcoLabel {
|
|
5397
5739
|
ico = input.required(...(ngDevMode ? [{ debugName: "ico" }] : /* istanbul ignore next */ []));
|
|
@@ -8272,11 +8614,122 @@ class JoinByPipe {
|
|
|
8272
8614
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JoinByPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
8273
8615
|
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: JoinByPipe, isStandalone: true, name: "joinBy" });
|
|
8274
8616
|
}
|
|
8275
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JoinByPipe, decorators: [{
|
|
8617
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JoinByPipe, decorators: [{
|
|
8618
|
+
type: Pipe,
|
|
8619
|
+
args: [{
|
|
8620
|
+
name: 'joinBy',
|
|
8621
|
+
standalone: true,
|
|
8622
|
+
}]
|
|
8623
|
+
}] });
|
|
8624
|
+
|
|
8625
|
+
class JsonHighlightPipe {
|
|
8626
|
+
/**
|
|
8627
|
+
* Stringifica el JSON manteniendo arrays en una línea para una vista más compacta
|
|
8628
|
+
*/
|
|
8629
|
+
compactStringify(obj, indent = 0) {
|
|
8630
|
+
const spaces = ' '.repeat(indent);
|
|
8631
|
+
if (obj === null) {
|
|
8632
|
+
return 'null';
|
|
8633
|
+
}
|
|
8634
|
+
if (typeof obj !== 'object') {
|
|
8635
|
+
if (typeof obj === 'string') {
|
|
8636
|
+
return JSON.stringify(obj);
|
|
8637
|
+
}
|
|
8638
|
+
return String(obj);
|
|
8639
|
+
}
|
|
8640
|
+
if (obj instanceof Date) {
|
|
8641
|
+
return JSON.stringify(obj.toISOString());
|
|
8642
|
+
}
|
|
8643
|
+
if (Array.isArray(obj)) {
|
|
8644
|
+
// Mantener arrays en una línea
|
|
8645
|
+
const items = obj.map((item) => {
|
|
8646
|
+
if (item instanceof Date) {
|
|
8647
|
+
return JSON.stringify(item.toISOString());
|
|
8648
|
+
}
|
|
8649
|
+
if (typeof item === 'object' && item !== null) {
|
|
8650
|
+
return this.compactStringify(item, indent);
|
|
8651
|
+
}
|
|
8652
|
+
return typeof item === 'string'
|
|
8653
|
+
? JSON.stringify(item)
|
|
8654
|
+
: item === null
|
|
8655
|
+
? 'null'
|
|
8656
|
+
: String(item);
|
|
8657
|
+
});
|
|
8658
|
+
return `[${items.join(', ')}]`;
|
|
8659
|
+
}
|
|
8660
|
+
// Para objetos, mantener la indentación
|
|
8661
|
+
const keys = Object.keys(obj);
|
|
8662
|
+
if (keys.length === 0) {
|
|
8663
|
+
return '{}';
|
|
8664
|
+
}
|
|
8665
|
+
const innerIndent = indent + 2;
|
|
8666
|
+
const innerSpaces = ' '.repeat(innerIndent);
|
|
8667
|
+
const lines = keys.map((key) => {
|
|
8668
|
+
const value = obj[key];
|
|
8669
|
+
const stringified = value instanceof Date
|
|
8670
|
+
? JSON.stringify(value.toISOString())
|
|
8671
|
+
: typeof value === 'object' && value !== null
|
|
8672
|
+
? this.compactStringify(value, innerIndent)
|
|
8673
|
+
: typeof value === 'string'
|
|
8674
|
+
? JSON.stringify(value)
|
|
8675
|
+
: value === null
|
|
8676
|
+
? 'null'
|
|
8677
|
+
: String(value);
|
|
8678
|
+
return `${innerSpaces}"${key}": ${stringified}`;
|
|
8679
|
+
});
|
|
8680
|
+
return `{\n${lines.join(',\n')}\n${spaces}}`;
|
|
8681
|
+
}
|
|
8682
|
+
instanceId = Math.random().toString(36).slice(2, 8);
|
|
8683
|
+
executions = 0;
|
|
8684
|
+
transform(value) {
|
|
8685
|
+
if (isDevMode()) {
|
|
8686
|
+
this.executions++;
|
|
8687
|
+
if (this.executions % 10 === 0) {
|
|
8688
|
+
console.log(`[jsonHighlight:${this.instanceId}] ${this.executions}`);
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
if (!value) {
|
|
8692
|
+
return '';
|
|
8693
|
+
}
|
|
8694
|
+
const json = this.compactStringify(value);
|
|
8695
|
+
// Detecta fechas ISO: "YYYY-MM-DDTHH:MM:SS.mmmZ"
|
|
8696
|
+
const ISO_DATE_RE = /^"(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2}\.\d{3}Z)"$/;
|
|
8697
|
+
// Aplica estilo y colores para diferenciar claves, valores, booleanos, números, etc.
|
|
8698
|
+
return json
|
|
8699
|
+
.replace(/&/g, '&')
|
|
8700
|
+
.replace(/</g, '<')
|
|
8701
|
+
.replace(/>/g, '>')
|
|
8702
|
+
.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?)/g, (match) => {
|
|
8703
|
+
// Si es una clave (tiene dos puntos al final)
|
|
8704
|
+
if (/:$/.test(match)) {
|
|
8705
|
+
return `<span class="json-key">${match}</span>`;
|
|
8706
|
+
}
|
|
8707
|
+
// Detectar fechas ISO (YYYY-MM-DDTHH:MM:SS.mmmZ)
|
|
8708
|
+
const isoMatch = match.match(ISO_DATE_RE);
|
|
8709
|
+
if (isoMatch) {
|
|
8710
|
+
return `<span class="json-string json-date">"<span class="json-date-part">${isoMatch[1]}</span><span class="json-date-sep">T</span><span class="json-date-time">${isoMatch[2]}</span>"</span>`;
|
|
8711
|
+
}
|
|
8712
|
+
// Si es un string (no tiene dos puntos)
|
|
8713
|
+
return `<span class="json-string">${match}</span>`;
|
|
8714
|
+
})
|
|
8715
|
+
.replace(/\b(true|false|null)\b/g, (match) => {
|
|
8716
|
+
if (match === 'null') {
|
|
8717
|
+
return '<span class="json-null">null</span>';
|
|
8718
|
+
}
|
|
8719
|
+
return `<span class="json-boolean">${match}</span>`;
|
|
8720
|
+
})
|
|
8721
|
+
.replace(/\b(\d+\.?\d*)\b/g, '<span class="json-number">$1</span>')
|
|
8722
|
+
.replace(/[{}\[\]]/g, '<span class="json-bracket">$&</span>')
|
|
8723
|
+
.replace(/,/g, '<span class="json-comma">$&</span>')
|
|
8724
|
+
.replace(/:/g, '<span class="json-colon">$&</span>');
|
|
8725
|
+
}
|
|
8726
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonHighlightPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
|
|
8727
|
+
static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: JsonHighlightPipe, isStandalone: true, name: "jsonHighlight" });
|
|
8728
|
+
}
|
|
8729
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonHighlightPipe, decorators: [{
|
|
8276
8730
|
type: Pipe,
|
|
8277
8731
|
args: [{
|
|
8278
|
-
name: '
|
|
8279
|
-
standalone: true,
|
|
8732
|
+
name: 'jsonHighlight',
|
|
8280
8733
|
}]
|
|
8281
8734
|
}] });
|
|
8282
8735
|
|
|
@@ -8513,26 +8966,246 @@ class DsxdateShort {
|
|
|
8513
8966
|
useExisting: forwardRef(() => DsxdateShort),
|
|
8514
8967
|
multi: true,
|
|
8515
8968
|
},
|
|
8516
|
-
], ngImport: i0, template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "
|
|
8969
|
+
], ngImport: i0, template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "directive", type: i1$8.InputMaskDirective, selector: "[pInputMask]", inputs: ["pInputMaskPT", "pInputMaskUnstyled", "pInputMask", "slotChar", "autoClear", "characterPattern", "keepBuffer"], outputs: ["onComplete", "onUnmaskedChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] });
|
|
8517
8970
|
}
|
|
8518
8971
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxdateShort, decorators: [{
|
|
8519
8972
|
type: Component,
|
|
8520
8973
|
args: [{ selector: 'dsxdate-short', imports: [
|
|
8974
|
+
AppMessageErrorComponent,
|
|
8521
8975
|
CommonModule,
|
|
8522
|
-
FloatLabel,
|
|
8523
8976
|
DatePicker,
|
|
8524
|
-
|
|
8525
|
-
|
|
8977
|
+
FloatLabel,
|
|
8978
|
+
InputMaskModule,
|
|
8526
8979
|
JsonPipe,
|
|
8980
|
+
ReactiveFormsModule,
|
|
8527
8981
|
], providers: [
|
|
8528
8982
|
{
|
|
8529
8983
|
provide: NG_VALUE_ACCESSOR,
|
|
8530
8984
|
useExisting: forwardRef(() => DsxdateShort),
|
|
8531
8985
|
multi: true,
|
|
8532
8986
|
},
|
|
8533
|
-
], template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
|
|
8987
|
+
], template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
|
|
8534
8988
|
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], showOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnFocus", required: false }] }], showButtonBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtonBar", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
|
|
8535
8989
|
|
|
8990
|
+
class DsxdateTime {
|
|
8991
|
+
injector = inject(Injector);
|
|
8992
|
+
// ✅ Exponer isDevMode como propiedad pública
|
|
8993
|
+
isDevMode = isDevMode();
|
|
8994
|
+
// === INPUTS CONFIGURABLES ===
|
|
8995
|
+
label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
8996
|
+
placeholder = input('dd/mm/yyyy 00:00', ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
8997
|
+
dateFormat = input('dd/mm/yy', ...(ngDevMode ? [{ debugName: "dateFormat" }] : /* istanbul ignore next */ []));
|
|
8998
|
+
showIcon = input(true, ...(ngDevMode ? [{ debugName: "showIcon" }] : /* istanbul ignore next */ []));
|
|
8999
|
+
showOnFocus = input(false, ...(ngDevMode ? [{ debugName: "showOnFocus" }] : /* istanbul ignore next */ []));
|
|
9000
|
+
showButtonBar = input(true, ...(ngDevMode ? [{ debugName: "showButtonBar" }] : /* istanbul ignore next */ []));
|
|
9001
|
+
showTime = input(true, ...(ngDevMode ? [{ debugName: "showTime" }] : /* istanbul ignore next */ []));
|
|
9002
|
+
hourFormat = input('24', ...(ngDevMode ? [{ debugName: "hourFormat" }] : /* istanbul ignore next */ []));
|
|
9003
|
+
debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
|
|
9004
|
+
// === STATE ===
|
|
9005
|
+
debugEnabled = signal(false, ...(ngDevMode ? [{ debugName: "debugEnabled" }] : /* istanbul ignore next */ []));
|
|
9006
|
+
// === OBTENER EL CONTROL PADRE ===
|
|
9007
|
+
ngControl = computed(() => {
|
|
9008
|
+
try {
|
|
9009
|
+
return this.injector.get(NgControl);
|
|
9010
|
+
}
|
|
9011
|
+
catch {
|
|
9012
|
+
return null;
|
|
9013
|
+
}
|
|
9014
|
+
}, ...(ngDevMode ? [{ debugName: "ngControl" }] : /* istanbul ignore next */ []));
|
|
9015
|
+
// === CONTROL PADRE PARA ERRORES ===
|
|
9016
|
+
parentControl = computed(() => {
|
|
9017
|
+
const control = this.ngControl();
|
|
9018
|
+
return control?.control || null;
|
|
9019
|
+
}, ...(ngDevMode ? [{ debugName: "parentControl" }] : /* istanbul ignore next */ []));
|
|
9020
|
+
// === CONTROL INTERNO (para el template) ===
|
|
9021
|
+
fechaControl = new FormControl(null);
|
|
9022
|
+
// === CONTROL VALUE ACCESSOR ===
|
|
9023
|
+
onChange = () => { };
|
|
9024
|
+
onTouched = () => { };
|
|
9025
|
+
constructor() {
|
|
9026
|
+
// Debug effect
|
|
9027
|
+
effect(() => {
|
|
9028
|
+
const isDebug = this.debug() && this.isDevMode;
|
|
9029
|
+
this.debugEnabled.set(isDebug);
|
|
9030
|
+
if (isDebug) {
|
|
9031
|
+
console.log('[dsxdate-datetime] === DEBUG MODE ACTIVADO ===');
|
|
9032
|
+
console.log('[dsxdate-datetime] Configuración:', {
|
|
9033
|
+
label: this.label(),
|
|
9034
|
+
placeholder: this.placeholder(),
|
|
9035
|
+
dateFormat: this.dateFormat(),
|
|
9036
|
+
showIcon: this.showIcon(),
|
|
9037
|
+
showOnFocus: this.showOnFocus(),
|
|
9038
|
+
showButtonBar: this.showButtonBar(),
|
|
9039
|
+
showTime: this.showTime(),
|
|
9040
|
+
hourFormat: this.hourFormat(),
|
|
9041
|
+
});
|
|
9042
|
+
console.log('[dsxdate-datetime] Control padre:', this.parentControl());
|
|
9043
|
+
}
|
|
9044
|
+
});
|
|
9045
|
+
// ✅ SINCRONIZAR VALIDADORES DEL PADRE AL CONTROL INTERNO
|
|
9046
|
+
effect(() => {
|
|
9047
|
+
const parentCtrl = this.parentControl();
|
|
9048
|
+
if (parentCtrl) {
|
|
9049
|
+
// Copiar validadores del padre al control interno
|
|
9050
|
+
const validators = parentCtrl.validator;
|
|
9051
|
+
const asyncValidators = parentCtrl.asyncValidator;
|
|
9052
|
+
if (this.debugEnabled()) {
|
|
9053
|
+
console.log('[dsxdate-datetime] 🔄 Sincronizando validadores del padre');
|
|
9054
|
+
console.log('[dsxdate-datetime] Validadores del padre:', validators);
|
|
9055
|
+
}
|
|
9056
|
+
this.fechaControl.setValidators(validators ? [validators] : []);
|
|
9057
|
+
this.fechaControl.setAsyncValidators(asyncValidators ? [asyncValidators] : []);
|
|
9058
|
+
this.fechaControl.updateValueAndValidity({ emitEvent: false });
|
|
9059
|
+
if (this.debugEnabled()) {
|
|
9060
|
+
console.log('[dsxdate-datetime] ✅ Validadores sincronizados');
|
|
9061
|
+
console.log('[dsxdate-datetime] Estado del control interno:', {
|
|
9062
|
+
valid: this.fechaControl.valid,
|
|
9063
|
+
invalid: this.fechaControl.invalid,
|
|
9064
|
+
errors: this.fechaControl.errors,
|
|
9065
|
+
});
|
|
9066
|
+
}
|
|
9067
|
+
}
|
|
9068
|
+
});
|
|
9069
|
+
// Monitorear cambios del control padre
|
|
9070
|
+
effect(() => {
|
|
9071
|
+
if (this.debugEnabled()) {
|
|
9072
|
+
const parentCtrl = this.parentControl();
|
|
9073
|
+
console.log('[dsxdate-datetime] 📊 Parent Control state:', {
|
|
9074
|
+
value: parentCtrl?.value,
|
|
9075
|
+
status: parentCtrl?.status,
|
|
9076
|
+
errors: parentCtrl?.errors,
|
|
9077
|
+
valid: parentCtrl?.valid,
|
|
9078
|
+
invalid: parentCtrl?.invalid,
|
|
9079
|
+
touched: parentCtrl?.touched,
|
|
9080
|
+
dirty: parentCtrl?.dirty,
|
|
9081
|
+
});
|
|
9082
|
+
}
|
|
9083
|
+
});
|
|
9084
|
+
// Sincronizar cambios del control interno al padre
|
|
9085
|
+
this.fechaControl.valueChanges.subscribe((value) => {
|
|
9086
|
+
if (this.debugEnabled()) {
|
|
9087
|
+
console.log('[dsxdate-datetime] 🔄 valueChanges - Nuevo valor:', value);
|
|
9088
|
+
}
|
|
9089
|
+
this.onChange(value);
|
|
9090
|
+
});
|
|
9091
|
+
// Sincronizar touched
|
|
9092
|
+
this.fechaControl.statusChanges.subscribe(() => {
|
|
9093
|
+
if (this.debugEnabled()) {
|
|
9094
|
+
console.log('[dsxdate-datetime] 📊 statusChanges - Estado:', this.fechaControl.status);
|
|
9095
|
+
}
|
|
9096
|
+
this.onTouched();
|
|
9097
|
+
});
|
|
9098
|
+
}
|
|
9099
|
+
// === MÉTODOS DE DEBUG ===
|
|
9100
|
+
log(message, data) {
|
|
9101
|
+
if (this.debugEnabled() && this.isDevMode) {
|
|
9102
|
+
if (data !== undefined) {
|
|
9103
|
+
console.log(`[dsxdate-datetime] ${message}`, data);
|
|
9104
|
+
}
|
|
9105
|
+
else {
|
|
9106
|
+
console.log(`[dsxdate-datetime] ${message}`);
|
|
9107
|
+
}
|
|
9108
|
+
}
|
|
9109
|
+
}
|
|
9110
|
+
/**
|
|
9111
|
+
* Convierte cualquier fecha (string ISO, Date, moment) a Date válido
|
|
9112
|
+
*/
|
|
9113
|
+
convertirFechaADate(fecha) {
|
|
9114
|
+
if (this.debugEnabled()) {
|
|
9115
|
+
this.log('🔄 convertirFechaADate - entrada:', fecha);
|
|
9116
|
+
}
|
|
9117
|
+
if (!fecha) {
|
|
9118
|
+
this.log('convertirFechaADate - fecha vacía, retornando null');
|
|
9119
|
+
return null;
|
|
9120
|
+
}
|
|
9121
|
+
if (fecha instanceof Date && !isNaN(fecha.getTime())) {
|
|
9122
|
+
this.log('✅ convertirFechaADate - es Date válido:', fecha);
|
|
9123
|
+
return fecha;
|
|
9124
|
+
}
|
|
9125
|
+
if (typeof fecha === 'string') {
|
|
9126
|
+
this.log('📝 convertirFechaADate - procesando string:', fecha);
|
|
9127
|
+
const momentDate = moment(fecha);
|
|
9128
|
+
if (momentDate.isValid()) {
|
|
9129
|
+
const result = momentDate.toDate();
|
|
9130
|
+
this.log('✅ convertirFechaADate - string convertido a Date:', result);
|
|
9131
|
+
return result;
|
|
9132
|
+
}
|
|
9133
|
+
this.log('❌ convertirFechaADate - string inválido para moment');
|
|
9134
|
+
}
|
|
9135
|
+
if (moment.isMoment(fecha)) {
|
|
9136
|
+
this.log('✅ convertirFechaADate - es objeto moment');
|
|
9137
|
+
return fecha.toDate();
|
|
9138
|
+
}
|
|
9139
|
+
const parsed = new Date(fecha);
|
|
9140
|
+
if (!isNaN(parsed.getTime())) {
|
|
9141
|
+
this.log('✅ convertirFechaADate - parseo directo exitoso:', parsed);
|
|
9142
|
+
return parsed;
|
|
9143
|
+
}
|
|
9144
|
+
this.log('❌ convertirFechaADate - no se pudo convertir, retornando null');
|
|
9145
|
+
return null;
|
|
9146
|
+
}
|
|
9147
|
+
// === CONTROL VALUE ACCESSOR IMPLEMENTATION ===
|
|
9148
|
+
writeValue(value) {
|
|
9149
|
+
if (this.debugEnabled()) {
|
|
9150
|
+
this.log('📥 writeValue - Valor recibido:', value);
|
|
9151
|
+
}
|
|
9152
|
+
const fechaConvertida = this.convertirFechaADate(value);
|
|
9153
|
+
if (this.debugEnabled()) {
|
|
9154
|
+
this.log('📥 writeValue - Valor convertido:', fechaConvertida);
|
|
9155
|
+
}
|
|
9156
|
+
this.fechaControl.setValue(fechaConvertida, { emitEvent: false });
|
|
9157
|
+
}
|
|
9158
|
+
registerOnChange(fn) {
|
|
9159
|
+
if (this.debugEnabled()) {
|
|
9160
|
+
this.log('📞 registerOnChange - Callback registrado');
|
|
9161
|
+
}
|
|
9162
|
+
this.onChange = fn;
|
|
9163
|
+
}
|
|
9164
|
+
registerOnTouched(fn) {
|
|
9165
|
+
if (this.debugEnabled()) {
|
|
9166
|
+
this.log('📞 registerOnTouched - Callback registrado');
|
|
9167
|
+
}
|
|
9168
|
+
this.onTouched = fn;
|
|
9169
|
+
}
|
|
9170
|
+
setDisabledState(isDisabled) {
|
|
9171
|
+
if (this.debugEnabled()) {
|
|
9172
|
+
this.log(`🚫 setDisabledState - Deshabilitado: ${isDisabled}`);
|
|
9173
|
+
}
|
|
9174
|
+
if (isDisabled) {
|
|
9175
|
+
this.fechaControl.disable({ emitEvent: false });
|
|
9176
|
+
}
|
|
9177
|
+
else {
|
|
9178
|
+
this.fechaControl.enable({ emitEvent: false });
|
|
9179
|
+
}
|
|
9180
|
+
}
|
|
9181
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxdateTime, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9182
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DsxdateTime, isStandalone: true, selector: "dsxdate-time", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, dateFormat: { classPropertyName: "dateFormat", publicName: "dateFormat", isSignal: true, isRequired: false, transformFunction: null }, showIcon: { classPropertyName: "showIcon", publicName: "showIcon", isSignal: true, isRequired: false, transformFunction: null }, showOnFocus: { classPropertyName: "showOnFocus", publicName: "showOnFocus", isSignal: true, isRequired: false, transformFunction: null }, showButtonBar: { classPropertyName: "showButtonBar", publicName: "showButtonBar", isSignal: true, isRequired: false, transformFunction: null }, showTime: { classPropertyName: "showTime", publicName: "showTime", isSignal: true, isRequired: false, transformFunction: null }, hourFormat: { classPropertyName: "hourFormat", publicName: "hourFormat", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
9183
|
+
{
|
|
9184
|
+
provide: NG_VALUE_ACCESSOR,
|
|
9185
|
+
useExisting: forwardRef(() => DsxdateTime),
|
|
9186
|
+
multi: true,
|
|
9187
|
+
},
|
|
9188
|
+
], ngImport: i0, template: "<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [showTime]=\"showTime()\"\r\n [hourFormat]=\"hourFormat()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999 99:99\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "directive", type: i1$8.InputMaskDirective, selector: "[pInputMask]", inputs: ["pInputMaskPT", "pInputMaskUnstyled", "pInputMask", "slotChar", "autoClear", "characterPattern", "keepBuffer"], outputs: ["onComplete", "onUnmaskedChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] });
|
|
9189
|
+
}
|
|
9190
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxdateTime, decorators: [{
|
|
9191
|
+
type: Component,
|
|
9192
|
+
args: [{ selector: 'dsxdate-time', imports: [
|
|
9193
|
+
AppMessageErrorComponent,
|
|
9194
|
+
CommonModule,
|
|
9195
|
+
DatePicker,
|
|
9196
|
+
FloatLabel,
|
|
9197
|
+
InputMaskModule,
|
|
9198
|
+
JsonPipe,
|
|
9199
|
+
ReactiveFormsModule,
|
|
9200
|
+
], providers: [
|
|
9201
|
+
{
|
|
9202
|
+
provide: NG_VALUE_ACCESSOR,
|
|
9203
|
+
useExisting: forwardRef(() => DsxdateTime),
|
|
9204
|
+
multi: true,
|
|
9205
|
+
},
|
|
9206
|
+
], template: "<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [showTime]=\"showTime()\"\r\n [hourFormat]=\"hourFormat()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999 99:99\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
|
|
9207
|
+
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], showOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnFocus", required: false }] }], showButtonBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtonBar", required: false }] }], showTime: [{ type: i0.Input, args: [{ isSignal: true, alias: "showTime", required: false }] }], hourFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "hourFormat", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
|
|
9208
|
+
|
|
8536
9209
|
// dsx-input-currency.ts
|
|
8537
9210
|
class DsxInputCurrency {
|
|
8538
9211
|
inputNumber;
|
|
@@ -10018,35 +10691,210 @@ class ResultFileService {
|
|
|
10018
10691
|
* FileContents|fileContents, FileDownloadName|fileDownloadName,
|
|
10019
10692
|
* ContentType|contentType, title y message.
|
|
10020
10693
|
*
|
|
10021
|
-
* Si el backend no cumple alguno de estos formatos, la conversión puede fallar
|
|
10022
|
-
* y el cliente recibirá data: null con isSuccess: false.
|
|
10023
|
-
*/
|
|
10024
|
-
/**
|
|
10025
|
-
* Procesa un ServiceResult<FileData> para visualizar o descargar.
|
|
10026
|
-
*/
|
|
10027
|
-
processFileResult(result, visualizar = false) {
|
|
10028
|
-
if (!result.isSuccess || !result.data?.blob) {
|
|
10029
|
-
this._alertaService.alertaHtmlSuccess(result.title, result.message, {
|
|
10030
|
-
icon: 'question',
|
|
10031
|
-
icono: '',
|
|
10032
|
-
showConfirmButton: true,
|
|
10033
|
-
});
|
|
10034
|
-
return;
|
|
10035
|
-
}
|
|
10036
|
-
const { blob, fileName } = result.data;
|
|
10037
|
-
if (visualizar) {
|
|
10038
|
-
this.PdfView(blob);
|
|
10039
|
-
return;
|
|
10040
|
-
}
|
|
10041
|
-
this.descargarBlob(blob, fileName);
|
|
10042
|
-
}
|
|
10043
|
-
/**
|
|
10044
|
-
* Realiza
|
|
10694
|
+
* Si el backend no cumple alguno de estos formatos, la conversión puede fallar
|
|
10695
|
+
* y el cliente recibirá data: null con isSuccess: false.
|
|
10696
|
+
*/
|
|
10697
|
+
/**
|
|
10698
|
+
* Procesa un ServiceResult<FileData> para visualizar o descargar.
|
|
10699
|
+
*/
|
|
10700
|
+
processFileResult(result, visualizar = false) {
|
|
10701
|
+
if (!result.isSuccess || !result.data?.blob) {
|
|
10702
|
+
this._alertaService.alertaHtmlSuccess(result.title, result.message, {
|
|
10703
|
+
icon: 'question',
|
|
10704
|
+
icono: '',
|
|
10705
|
+
showConfirmButton: true,
|
|
10706
|
+
});
|
|
10707
|
+
return;
|
|
10708
|
+
}
|
|
10709
|
+
const { blob, fileName } = result.data;
|
|
10710
|
+
if (visualizar) {
|
|
10711
|
+
this.PdfView(blob);
|
|
10712
|
+
return;
|
|
10713
|
+
}
|
|
10714
|
+
this.descargarBlob(blob, fileName);
|
|
10715
|
+
}
|
|
10716
|
+
/**
|
|
10717
|
+
* Realiza una petición GET para descargar un archivo y normaliza la respuesta a un ServiceResult<FileData>.
|
|
10718
|
+
*
|
|
10719
|
+
* 📋 **Descripción General:**
|
|
10720
|
+
* Este método unifica la forma en que se manejan las descargas de archivos, soportando múltiples
|
|
10721
|
+
* formatos de respuesta del backend y normalizándolos a una estructura consistente.
|
|
10722
|
+
*
|
|
10723
|
+
* 🔄 **Formatos de Respuesta Soportados:**
|
|
10724
|
+
*
|
|
10725
|
+
* 1. **Binario Directo** (Recomendado):
|
|
10726
|
+
* - El backend retorna directamente el archivo binario.
|
|
10727
|
+
* - El nombre del archivo se obtiene del header `Content-Disposition`.
|
|
10728
|
+
* - Mensajes opcionales via headers: `x-title`, `x-message`, `title`, `message`.
|
|
10729
|
+
* - Ejemplo:
|
|
10730
|
+
* ```
|
|
10731
|
+
* Content-Type: application/pdf
|
|
10732
|
+
* Content-Disposition: attachment; filename="documento.pdf"
|
|
10733
|
+
* x-title: Éxito
|
|
10734
|
+
* x-message: Descarga completada exitosamente
|
|
10735
|
+
*
|
|
10736
|
+
* [bytes del archivo]
|
|
10737
|
+
* ```
|
|
10738
|
+
*
|
|
10739
|
+
* 2. **JSON Serializado con ServiceResult**:
|
|
10740
|
+
* - El backend retorna un JSON con estructura ServiceResult<FileContentResult>.
|
|
10741
|
+
* - El archivo viene codificado en Base64 o como array de bytes.
|
|
10742
|
+
* - Soporta campos en PascalCase o camelCase.
|
|
10743
|
+
* - Ejemplo:
|
|
10744
|
+
* ```json
|
|
10745
|
+
* {
|
|
10746
|
+
* "isSuccess": true,
|
|
10747
|
+
* "title": "Éxito",
|
|
10748
|
+
* "message": "Archivo generado correctamente",
|
|
10749
|
+
* "data": {
|
|
10750
|
+
* "fileContents": "base64EncodedString...",
|
|
10751
|
+
* "fileDownloadName": "reporte.xlsx",
|
|
10752
|
+
* "contentType": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
10753
|
+
* }
|
|
10754
|
+
* }
|
|
10755
|
+
* ```
|
|
10756
|
+
*
|
|
10757
|
+
* 3. **Error del Backend**:
|
|
10758
|
+
* - Retorna un ServiceResult con isSuccess: false.
|
|
10759
|
+
* - Se extrae el título y mensaje de error.
|
|
10760
|
+
* - Ejemplo:
|
|
10761
|
+
* ```json
|
|
10762
|
+
* {
|
|
10763
|
+
* "isSuccess": false,
|
|
10764
|
+
* "title": "Error de Validación",
|
|
10765
|
+
* "message": "El ID del documento no existe"
|
|
10766
|
+
* }
|
|
10767
|
+
* ```
|
|
10768
|
+
*
|
|
10769
|
+
* 4. **Error de Red/HTTP**:
|
|
10770
|
+
* - Captura errores de conexión, timeout, etc.
|
|
10771
|
+
* - Retorna un ServiceResult con isSuccess: false y mensaje genérico.
|
|
10772
|
+
*
|
|
10773
|
+
* 🎯 **Método de Uso Recomendado:**
|
|
10774
|
+
* ```typescript
|
|
10775
|
+
* // Opción 1: Descarga automática
|
|
10776
|
+
* this.resultFileService.getDownloadableFile('/api/documentos/123')
|
|
10777
|
+
* .subscribe(result => {
|
|
10778
|
+
* this.resultFileService.processFileResult(result, false);
|
|
10779
|
+
* });
|
|
10780
|
+
*
|
|
10781
|
+
* // Opción 2: Visualización de PDF
|
|
10782
|
+
* this.resultFileService.getDownloadableFile('/api/documentos/123')
|
|
10783
|
+
* .subscribe(result => {
|
|
10784
|
+
* this.resultFileService.processFileResult(result, true);
|
|
10785
|
+
* });
|
|
10786
|
+
*
|
|
10787
|
+
* // Opción 3: Procesamiento personalizado
|
|
10788
|
+
* this.resultFileService.getDownloadableFile('/api/documentos/123')
|
|
10789
|
+
* .subscribe({
|
|
10790
|
+
* next: (result) => {
|
|
10791
|
+
* if (result.isSuccess && result.data?.blob) {
|
|
10792
|
+
* // Procesar el blob manualmente
|
|
10793
|
+
* const { blob, fileName } = result.data;
|
|
10794
|
+
* // Hacer algo con el archivo
|
|
10795
|
+
* } else {
|
|
10796
|
+
* // Manejar error
|
|
10797
|
+
* console.error(result.title, result.message);
|
|
10798
|
+
* }
|
|
10799
|
+
* },
|
|
10800
|
+
* error: (error) => {
|
|
10801
|
+
* console.error('Error de red:', error);
|
|
10802
|
+
* }
|
|
10803
|
+
* });
|
|
10804
|
+
* ```
|
|
10805
|
+
*
|
|
10806
|
+
* 📝 **Parámetros:**
|
|
10807
|
+
* @param url - URL del endpoint de descarga.
|
|
10808
|
+
* @param options - Opciones adicionales para la solicitud.
|
|
10809
|
+
* @param options.params - Parámetros de consulta (Query params). Soporta:
|
|
10810
|
+
* - `string`: valor simple
|
|
10811
|
+
* - `string[]`: múltiples valores para el mismo parámetro
|
|
10812
|
+
* - `Date`: se convierte automáticamente a ISO string
|
|
10813
|
+
* - `number`: se convierte a string
|
|
10814
|
+
* - `HttpParams`: instancia directamente
|
|
10815
|
+
* @param options.headers - Headers HTTP personalizados (HttpHeaders).
|
|
10816
|
+
* @param options.withCredentials - Enviar cookies/credenciales en la solicitud.
|
|
10817
|
+
*
|
|
10818
|
+
* 🔍 **Estructura de Retorno:**
|
|
10819
|
+
* ```typescript
|
|
10820
|
+
* Observable<ServiceResult<FileData>>
|
|
10821
|
+
* ```
|
|
10822
|
+
*
|
|
10823
|
+
* Donde `ServiceResult<FileData>` tiene:
|
|
10824
|
+
* ```typescript
|
|
10825
|
+
* {
|
|
10826
|
+
* data: {
|
|
10827
|
+
* blob: Blob, // El archivo como Blob
|
|
10828
|
+
* fileName: string // Nombre del archivo (con extensión)
|
|
10829
|
+
* } | null,
|
|
10830
|
+
* isSuccess: boolean, // Indica si la operación fue exitosa
|
|
10831
|
+
* title: string, // Título del resultado (éxito o error)
|
|
10832
|
+
* message: string // Mensaje descriptivo
|
|
10833
|
+
* }
|
|
10834
|
+
* ```
|
|
10835
|
+
*
|
|
10836
|
+
* ⚠️ **Notas Importantes:**
|
|
10837
|
+
* - El método siempre retorna un Observable que emite un ServiceResult.
|
|
10838
|
+
* - Nunca emite error en el Observable (los errores se convierten a ServiceResult con isSuccess: false).
|
|
10839
|
+
* - Para descarga automática usar `processFileResult(result, false)`.
|
|
10840
|
+
* - Para visualización de PDF usar `processFileResult(result, true)`.
|
|
10841
|
+
* - El nombre del archivo se determina en este orden de prioridad:
|
|
10842
|
+
* 1. `Content-Disposition` header (si existe)
|
|
10843
|
+
* 2. `fileDownloadName` del JSON (si es respuesta JSON)
|
|
10844
|
+
* 3. `fileName` del JSON (si es respuesta JSON)
|
|
10845
|
+
* 4. Valor por defecto: 'document'
|
|
10846
|
+
*
|
|
10847
|
+
* 💡 **Ejemplos de Uso Adicionales:**
|
|
10848
|
+
*
|
|
10849
|
+
* ```typescript
|
|
10850
|
+
* // Con parámetros de consulta
|
|
10851
|
+
* const options = {
|
|
10852
|
+
* params: {
|
|
10853
|
+
* id: 123,
|
|
10854
|
+
* formato: 'pdf',
|
|
10855
|
+
* fechas: ['2024-01-01', '2024-12-31']
|
|
10856
|
+
* }
|
|
10857
|
+
* };
|
|
10858
|
+
* this.resultFileService.getDownloadableFile('/api/reportes', options)
|
|
10859
|
+
* .subscribe(result => {
|
|
10860
|
+
* this.resultFileService.processFileResult(result);
|
|
10861
|
+
* });
|
|
10862
|
+
*
|
|
10863
|
+
* // Con headers personalizados (ej: autenticación)
|
|
10864
|
+
* const options = {
|
|
10865
|
+
* headers: new HttpHeaders({
|
|
10866
|
+
* 'Authorization': 'Bearer ' + token,
|
|
10867
|
+
* 'X-Custom-Header': 'valor'
|
|
10868
|
+
* })
|
|
10869
|
+
* };
|
|
10870
|
+
* this.resultFileService.getDownloadableFile('/api/documentos', options)
|
|
10871
|
+
* .subscribe(result => {
|
|
10872
|
+
* this.resultFileService.processFileResult(result);
|
|
10873
|
+
* });
|
|
10874
|
+
*
|
|
10875
|
+
* // Con credenciales
|
|
10876
|
+
* const options = {
|
|
10877
|
+
* withCredentials: true
|
|
10878
|
+
* };
|
|
10879
|
+
* this.resultFileService.getDownloadableFile('/api/documentos', options)
|
|
10880
|
+
* .subscribe(result => {
|
|
10881
|
+
* this.resultFileService.processFileResult(result);
|
|
10882
|
+
* });
|
|
10883
|
+
* ```
|
|
10884
|
+
*
|
|
10885
|
+
* 🚀 **Flujo de Trabajo Típico:**
|
|
10886
|
+
* 1. Llamar a `getDownloadableFile(url, options)` con la URL del endpoint.
|
|
10887
|
+
* 2. Suscribirse al Observable.
|
|
10888
|
+
* 3. En el `next`, recibir el `ServiceResult<FileData>`.
|
|
10889
|
+
* 4. Usar `processFileResult(result, visualizar)` para:
|
|
10890
|
+
* - Descargar automáticamente (visualizar: false)
|
|
10891
|
+
* - Visualizar PDF en nueva pestaña (visualizar: true)
|
|
10892
|
+
* 5. Alternativamente, procesar el blob manualmente si se necesita.
|
|
10045
10893
|
*
|
|
10046
|
-
*
|
|
10047
|
-
* -
|
|
10048
|
-
* -
|
|
10049
|
-
*
|
|
10894
|
+
* @see processFileResult - Para descargar o visualizar el archivo obtenido.
|
|
10895
|
+
* @see FileData - Estructura de datos del archivo.
|
|
10896
|
+
* @see BlobRequestOptions - Opciones de configuración para la solicitud.
|
|
10897
|
+
* @see ServiceResult - Estructura de resultado estandarizada.
|
|
10050
10898
|
*/
|
|
10051
10899
|
getDownloadableFile(url, options = {}) {
|
|
10052
10900
|
return this.http
|
|
@@ -10251,25 +11099,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
10251
11099
|
}] });
|
|
10252
11100
|
|
|
10253
11101
|
class SweetAlert2DialogService {
|
|
10254
|
-
/**
|
|
10255
|
-
* Configuración del entorno inyectada (opcional).
|
|
10256
|
-
* Se usa para obtener el tema global de SweetAlert2 desde el environment.
|
|
10257
|
-
*/
|
|
10258
11102
|
environment = inject(ENVIRONMENT, { optional: true });
|
|
10259
|
-
/**
|
|
10260
|
-
* Tema visual por defecto para todas las alertas SweetAlert2.
|
|
10261
|
-
* Se inicializa desde el environment si está configurado, o puede establecerse
|
|
10262
|
-
* manualmente llamando a setDefaultTheme().
|
|
10263
|
-
*
|
|
10264
|
-
* **Prioridad de temas:**
|
|
10265
|
-
* 1. Tema específico pasado en options de cada alerta
|
|
10266
|
-
* 2. Tema establecido con setDefaultTheme()
|
|
10267
|
-
* 3. Tema configurado en environment.sweetAlertTheme
|
|
10268
|
-
* 4. Tema por defecto de SweetAlert2
|
|
10269
|
-
*/
|
|
10270
11103
|
defaultTheme = undefined;
|
|
11104
|
+
NATIVE_ICONS = [
|
|
11105
|
+
'success',
|
|
11106
|
+
'error',
|
|
11107
|
+
'warning',
|
|
11108
|
+
'info',
|
|
11109
|
+
'question',
|
|
11110
|
+
];
|
|
10271
11111
|
constructor() {
|
|
10272
|
-
// Inicializar el tema desde el environment si está disponible
|
|
10273
11112
|
if (this.environment?.sweetAlertTheme) {
|
|
10274
11113
|
const allowed = SWEET_ALERT_THEMES;
|
|
10275
11114
|
const themeValue = this.environment.sweetAlertTheme;
|
|
@@ -10289,59 +11128,102 @@ class SweetAlert2DialogService {
|
|
|
10289
11128
|
img.onerror = (err) => reject(err);
|
|
10290
11129
|
});
|
|
10291
11130
|
}
|
|
10292
|
-
//
|
|
10293
|
-
|
|
10294
|
-
Dialog(firstParam, text, icoOrIcono, options = {}) {
|
|
11131
|
+
// Implementación unificada
|
|
11132
|
+
Dialog(firstParam, text, secondOrThird, thirdOrOptions) {
|
|
10295
11133
|
let title = '';
|
|
10296
|
-
let
|
|
10297
|
-
let
|
|
10298
|
-
|
|
11134
|
+
let messageContent = '';
|
|
11135
|
+
let options = {};
|
|
11136
|
+
let iconFromParam = null;
|
|
11137
|
+
let imageFromOptions = null;
|
|
11138
|
+
// === PROCESAR PARÁMETROS ===
|
|
10299
11139
|
if (typeof firstParam === 'object' && firstParam !== null) {
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
11140
|
+
// Caso 1: ActionMessageConfig
|
|
11141
|
+
const config = firstParam;
|
|
11142
|
+
title = config.title;
|
|
11143
|
+
messageContent = config.message;
|
|
11144
|
+
// Extraer opciones
|
|
11145
|
+
const { title: _title, message: _message, icon: _icon, icono: _icono, html: _html, footer: _footer, ...restOptions } = config;
|
|
11146
|
+
options = { ...restOptions };
|
|
11147
|
+
if (_html)
|
|
11148
|
+
options.html = _html;
|
|
11149
|
+
if (_footer)
|
|
11150
|
+
options.footer = _footer;
|
|
11151
|
+
// ✅ Si hay icono en el config, es el parámetro
|
|
11152
|
+
if (config.icon) {
|
|
11153
|
+
iconFromParam = config.icon;
|
|
11154
|
+
}
|
|
11155
|
+
// ✅ Si hay icono personalizado en el config
|
|
11156
|
+
if (config.icono) {
|
|
11157
|
+
imageFromOptions = config.icono;
|
|
11158
|
+
}
|
|
10305
11159
|
}
|
|
10306
11160
|
else {
|
|
11161
|
+
// Caso 2, 3, 4: Parámetros tradicionales
|
|
10307
11162
|
title = firstParam;
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
'
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
11163
|
+
messageContent = text || '';
|
|
11164
|
+
// Determinar qué tipo de parámetros tenemos
|
|
11165
|
+
if (secondOrThird !== undefined && secondOrThird !== null) {
|
|
11166
|
+
if (typeof secondOrThird === 'string') {
|
|
11167
|
+
// ✅ Es un icono nativo (Caso 4)
|
|
11168
|
+
if (this.NATIVE_ICONS.includes(secondOrThird)) {
|
|
11169
|
+
iconFromParam = secondOrThird;
|
|
11170
|
+
}
|
|
11171
|
+
options = thirdOrOptions || {};
|
|
11172
|
+
}
|
|
11173
|
+
else if (typeof secondOrThird === 'object') {
|
|
11174
|
+
// ✅ Son opciones (Caso 3)
|
|
11175
|
+
options = secondOrThird;
|
|
11176
|
+
// ✅ Si hay icono personalizado en options
|
|
11177
|
+
if (options.icono) {
|
|
11178
|
+
imageFromOptions = options.icono;
|
|
11179
|
+
}
|
|
11180
|
+
}
|
|
10322
11181
|
}
|
|
10323
11182
|
else {
|
|
10324
|
-
|
|
11183
|
+
// Solo título y texto (Caso 2)
|
|
11184
|
+
options = thirdOrOptions || {};
|
|
11185
|
+
if (options.icono) {
|
|
11186
|
+
imageFromOptions = options.icono;
|
|
11187
|
+
}
|
|
10325
11188
|
}
|
|
10326
11189
|
}
|
|
10327
|
-
//
|
|
10328
|
-
|
|
11190
|
+
// ✅ PRIORIDAD: imageFromOptions (imagen) > iconFromParam (icono nativo)
|
|
11191
|
+
let ico = null;
|
|
11192
|
+
let iconoImg = null;
|
|
11193
|
+
// 1. Si hay imagen en options, usarla (máxima prioridad)
|
|
11194
|
+
if (imageFromOptions) {
|
|
11195
|
+
iconoImg = imageFromOptions;
|
|
11196
|
+
}
|
|
11197
|
+
// 2. Si hay icono en el parámetro, usarlo
|
|
11198
|
+
else if (iconFromParam) {
|
|
11199
|
+
ico = iconFromParam;
|
|
11200
|
+
}
|
|
11201
|
+
// === CONFIGURACIÓN DE LA ALERTA ===
|
|
11202
|
+
const { confirmButtonText = 'Aceptar', cancelButtonText = 'Cancelar', showCloseButton = true, showCancelButton = true, imageWidth = 150, imageHeight = 150, theme, ...rest } = options;
|
|
10329
11203
|
const alertTheme = theme !== undefined ? theme : this.defaultTheme;
|
|
11204
|
+
// === CONSTRUIR FOOTER ===
|
|
10330
11205
|
const year = new Date().getFullYear();
|
|
10331
|
-
const
|
|
10332
|
-
const footer = `${
|
|
11206
|
+
const footerText = options.footer ?? this.environment?.sweetAlertFooter ?? 'DEVSOFTXela';
|
|
11207
|
+
const footer = `${footerText} <strong>${year}</strong>`;
|
|
11208
|
+
// === CONSTRUIR CONFIGURACIÓN DE SWEETALERT ===
|
|
10333
11209
|
const alertConfig = {
|
|
10334
11210
|
title,
|
|
10335
|
-
html: text,
|
|
10336
11211
|
footer,
|
|
10337
11212
|
showCloseButton,
|
|
10338
11213
|
showCancelButton,
|
|
10339
11214
|
confirmButtonText,
|
|
10340
11215
|
cancelButtonText,
|
|
10341
|
-
//reverseButtons: true,
|
|
10342
11216
|
...(alertTheme !== undefined && { theme: alertTheme }),
|
|
10343
11217
|
...rest,
|
|
10344
11218
|
};
|
|
11219
|
+
// === PRIORIDAD DEL CONTENIDO HTML ===
|
|
11220
|
+
if (options.html) {
|
|
11221
|
+
alertConfig.html = options.html;
|
|
11222
|
+
}
|
|
11223
|
+
else if (messageContent) {
|
|
11224
|
+
alertConfig.html = messageContent;
|
|
11225
|
+
}
|
|
11226
|
+
// === IMAGEN O ICONO ===
|
|
10345
11227
|
if (iconoImg) {
|
|
10346
11228
|
alertConfig.imageUrl = `/${iconoImg}`;
|
|
10347
11229
|
alertConfig.imageWidth = imageWidth;
|
|
@@ -10365,6 +11247,320 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
10365
11247
|
}]
|
|
10366
11248
|
}], ctorParameters: () => [] });
|
|
10367
11249
|
|
|
11250
|
+
class DsxMessagesService {
|
|
11251
|
+
AlertaService = inject(AlertaService);
|
|
11252
|
+
SweetDialog = inject(SweetAlert2DialogService);
|
|
11253
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxMessagesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
11254
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxMessagesService, providedIn: 'root' });
|
|
11255
|
+
}
|
|
11256
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxMessagesService, decorators: [{
|
|
11257
|
+
type: Injectable,
|
|
11258
|
+
args: [{
|
|
11259
|
+
providedIn: 'root',
|
|
11260
|
+
}]
|
|
11261
|
+
}] });
|
|
11262
|
+
|
|
11263
|
+
/**
|
|
11264
|
+
* Obtiene los datos de un FormGroup excluyendo TODAS las propiedades de tipo File
|
|
11265
|
+
*
|
|
11266
|
+
* @param form - FormGroup del cual extraer los datos
|
|
11267
|
+
* @returns Objeto con los datos (sin archivos) y la lista de archivos encontrados
|
|
11268
|
+
*
|
|
11269
|
+
* @internal
|
|
11270
|
+
*/
|
|
11271
|
+
function getFormDataExcludingFiles(form) {
|
|
11272
|
+
const rawValue = form.getRawValue();
|
|
11273
|
+
const entries = Object.entries(rawValue);
|
|
11274
|
+
const files = [];
|
|
11275
|
+
const data = {};
|
|
11276
|
+
for (const [key, value] of entries) {
|
|
11277
|
+
if (value instanceof File) {
|
|
11278
|
+
files.push(value);
|
|
11279
|
+
}
|
|
11280
|
+
else {
|
|
11281
|
+
data[key] = value;
|
|
11282
|
+
}
|
|
11283
|
+
}
|
|
11284
|
+
return { data: data, files };
|
|
11285
|
+
}
|
|
11286
|
+
/**
|
|
11287
|
+
* Servicio para guardar entidades y subir archivos automáticamente.
|
|
11288
|
+
*
|
|
11289
|
+
* Este servicio centraliza el flujo común de:
|
|
11290
|
+
* 1. Extraer automáticamente el archivo del FormGroup (por tipo `File`)
|
|
11291
|
+
* 2. Guardar la entidad (sin el archivo) en la base de datos
|
|
11292
|
+
* 3. Si el guardado es exitoso y hay archivo, subirlo
|
|
11293
|
+
* 4. Manejar errores de forma consistente
|
|
11294
|
+
* 5. Resetear el formulario automáticamente en caso de éxito
|
|
11295
|
+
*
|
|
11296
|
+
* @example
|
|
11297
|
+
* // En tu componente:
|
|
11298
|
+
*
|
|
11299
|
+
* // Guardar sin archivo
|
|
11300
|
+
* this._saveEntityPostFile.saveOnly(
|
|
11301
|
+
* this.form,
|
|
11302
|
+
* this._baseService.save.bind(this._baseService),
|
|
11303
|
+
* (response) => {
|
|
11304
|
+
* this.formState.emit(response);
|
|
11305
|
+
* }
|
|
11306
|
+
* );
|
|
11307
|
+
*
|
|
11308
|
+
* // Guardar con archivo (se extrae automáticamente)
|
|
11309
|
+
* this._saveEntityPostFile.saveWithFile(
|
|
11310
|
+
* this.form,
|
|
11311
|
+
* this._baseService.save.bind(this._baseService),
|
|
11312
|
+
* this._baseService.AdjuntarSoporte.bind(this._baseService),
|
|
11313
|
+
* (response) => {
|
|
11314
|
+
* this.formState.emit(response);
|
|
11315
|
+
* }
|
|
11316
|
+
* );
|
|
11317
|
+
*/
|
|
11318
|
+
class SaveEntityPostFileService {
|
|
11319
|
+
/** Servicio para mostrar mensajes al usuario */
|
|
11320
|
+
_dsxMessageService = inject(DsxMessagesService);
|
|
11321
|
+
/** Configuración del entorno (para controlar debug) */
|
|
11322
|
+
environment = inject(ENVIRONMENT);
|
|
11323
|
+
/**
|
|
11324
|
+
* Determina si el debug está activo
|
|
11325
|
+
*
|
|
11326
|
+
* @param debug - Solicitud explícita de debug
|
|
11327
|
+
* @returns `true` solo si `debug=true` Y no estamos en producción
|
|
11328
|
+
*
|
|
11329
|
+
* @internal
|
|
11330
|
+
*/
|
|
11331
|
+
shouldLog(debug) {
|
|
11332
|
+
return debug && !this.environment.production;
|
|
11333
|
+
}
|
|
11334
|
+
/**
|
|
11335
|
+
* Guarda una entidad Y sube automáticamente el archivo extraído del FormGroup.
|
|
11336
|
+
*
|
|
11337
|
+
* El servicio extrae automáticamente el archivo del FormGroup (por tipo `File`),
|
|
11338
|
+
* lo excluye del objeto que se envía al backend, y después de guardar
|
|
11339
|
+
* exitosamente, procede a subir el archivo usando el ID generado.
|
|
11340
|
+
*
|
|
11341
|
+
* @param form - FormGroup que contiene los datos del formulario
|
|
11342
|
+
* @param saveMethod - Método de save que recibe los datos (sin archivo)
|
|
11343
|
+
* @param uploadMethod - Método de upload que recibe (id, file) y retorna Observable
|
|
11344
|
+
* @param onSuccess - Callback de éxito (opcional)
|
|
11345
|
+
* @param onError - Callback de error (opcional)
|
|
11346
|
+
* @param resetForm - Función para resetear el formulario (opcional)
|
|
11347
|
+
* @param debug - Activa logs en consola (solo funciona en desarrollo). Por defecto `false`.
|
|
11348
|
+
*
|
|
11349
|
+
* @example
|
|
11350
|
+
* // Uso básico
|
|
11351
|
+
* this._saveEntityPostFile.saveWithFile(
|
|
11352
|
+
* this.form,
|
|
11353
|
+
* this._baseService.save.bind(this._baseService),
|
|
11354
|
+
* this._baseService.AdjuntarSoporte.bind(this._baseService),
|
|
11355
|
+
* (response) => {
|
|
11356
|
+
* this.formState.emit(response);
|
|
11357
|
+
* }
|
|
11358
|
+
* );
|
|
11359
|
+
*
|
|
11360
|
+
* @example
|
|
11361
|
+
* // Con reset personalizado
|
|
11362
|
+
* this._saveEntityPostFile.saveWithFile(
|
|
11363
|
+
* this.form,
|
|
11364
|
+
* this._baseService.save.bind(this._baseService),
|
|
11365
|
+
* this._baseService.AdjuntarSoporte.bind(this._baseService),
|
|
11366
|
+
* (response) => {
|
|
11367
|
+
* this.formState.emit(response);
|
|
11368
|
+
* },
|
|
11369
|
+
* (error) => {
|
|
11370
|
+
* console.error('Error:', error);
|
|
11371
|
+
* },
|
|
11372
|
+
* () => {
|
|
11373
|
+
* this.form.reset();
|
|
11374
|
+
* this.getForm();
|
|
11375
|
+
* this.cleanFileInput();
|
|
11376
|
+
* }
|
|
11377
|
+
* );
|
|
11378
|
+
*/
|
|
11379
|
+
saveWithFile(form, saveMethod, uploadMethod, deleteMethod, onSuccess, onError, resetForm, debug = false) {
|
|
11380
|
+
this.execute(form, saveMethod, uploadMethod, deleteMethod, onSuccess, onError, resetForm, debug);
|
|
11381
|
+
}
|
|
11382
|
+
/**
|
|
11383
|
+
* Ejecuta el flujo completo de guardado y subida de archivo.
|
|
11384
|
+
*
|
|
11385
|
+
* Este método es privado y contiene la lógica central del servicio.
|
|
11386
|
+
*
|
|
11387
|
+
* ## Flujo de ejecución:
|
|
11388
|
+
* 1. Extrae el archivo del FormGroup (por tipo `File`)
|
|
11389
|
+
* 2. Construye el objeto de datos sin el archivo
|
|
11390
|
+
* 3. Ejecuta el `saveMethod` con los datos limpios
|
|
11391
|
+
* 4. Si el save es exitoso y hay archivo, ejecuta el `uploadMethod`
|
|
11392
|
+
* 5. Si todo es exitoso, ejecuta `resetForm` y `onSuccess`
|
|
11393
|
+
* 6. Si hay error en cualquier paso, ejecuta `onError`
|
|
11394
|
+
*
|
|
11395
|
+
* @param form - FormGroup que contiene los datos del formulario
|
|
11396
|
+
* @param saveMethod - Método de save que recibe los datos (sin archivo)
|
|
11397
|
+
* @param uploadMethod - Método de upload que recibe (id, file) (opcional)
|
|
11398
|
+
* @param onSuccess - Callback de éxito (opcional)
|
|
11399
|
+
* @param onError - Callback de error (opcional)
|
|
11400
|
+
* @param resetForm - Función para resetear el formulario (opcional)
|
|
11401
|
+
* @param debug - Activa logs en consola (solo funciona en desarrollo)
|
|
11402
|
+
*
|
|
11403
|
+
* @internal
|
|
11404
|
+
*/
|
|
11405
|
+
execute(form, saveMethod, uploadMethod, deleteMethod, onSuccess, onError, resetForm, debug = false) {
|
|
11406
|
+
const log = this.shouldLog(debug);
|
|
11407
|
+
// 1️⃣ Extraer archivo y datos del FormGroup
|
|
11408
|
+
const { data: saveData, files } = getFormDataExcludingFiles(form);
|
|
11409
|
+
const fileUpload = files.length > 0 ? files[0] : null;
|
|
11410
|
+
if (log) {
|
|
11411
|
+
console.group('📤 [SaveEntityPostFile] Preparando datos');
|
|
11412
|
+
console.log('📄 Datos a guardar (sin archivo):', saveData);
|
|
11413
|
+
console.log('📎 Archivo encontrado:', fileUpload?.name || 'Ninguno');
|
|
11414
|
+
console.groupEnd();
|
|
11415
|
+
}
|
|
11416
|
+
// 2️⃣ Crear el observable del save con los datos sin archivo
|
|
11417
|
+
const save$ = saveMethod(saveData);
|
|
11418
|
+
// 3️⃣ Ejecutar el flujo completo
|
|
11419
|
+
save$.subscribe({
|
|
11420
|
+
next: (saveResult) => {
|
|
11421
|
+
// 📥 Log del save
|
|
11422
|
+
if (log) {
|
|
11423
|
+
console.group('📥 [SaveEntityPostFile] Respuesta del save');
|
|
11424
|
+
console.log('Resultado:', saveResult);
|
|
11425
|
+
console.groupEnd();
|
|
11426
|
+
}
|
|
11427
|
+
// ❌ Si el guardado falla
|
|
11428
|
+
if (!saveResult.isSuccess) {
|
|
11429
|
+
this._dsxMessageService.SweetDialog.Dialog(saveResult.title || 'Error', saveResult.message || 'Error al guardar los datos', 'error', { showConfirmButton: true, showCancelButton: false });
|
|
11430
|
+
onError?.(saveResult);
|
|
11431
|
+
return;
|
|
11432
|
+
}
|
|
11433
|
+
// ✅ Si el guardado es exitoso y hay archivo
|
|
11434
|
+
if (uploadMethod && fileUpload && saveResult.data) {
|
|
11435
|
+
if (log)
|
|
11436
|
+
console.log('📤 [SaveEntityPostFile] Subiendo archivo...');
|
|
11437
|
+
uploadMethod(saveResult.data, fileUpload).subscribe({
|
|
11438
|
+
next: (uploadResult) => {
|
|
11439
|
+
// 📥 Log del upload
|
|
11440
|
+
if (log) {
|
|
11441
|
+
console.group('📥 [SaveEntityPostFile] Respuesta del upload');
|
|
11442
|
+
console.log('Resultado:', uploadResult);
|
|
11443
|
+
console.groupEnd();
|
|
11444
|
+
}
|
|
11445
|
+
if (uploadResult.isSuccess) {
|
|
11446
|
+
// ✅ Éxito: guardado + archivo
|
|
11447
|
+
if (log)
|
|
11448
|
+
console.log('✅ [SaveEntityPostFile] Subida exitosa');
|
|
11449
|
+
resetForm?.();
|
|
11450
|
+
onSuccess?.(uploadResult);
|
|
11451
|
+
}
|
|
11452
|
+
else {
|
|
11453
|
+
// ❌ Error en la subida
|
|
11454
|
+
if (log)
|
|
11455
|
+
console.warn('⚠️ [SaveEntityPostFile] Subida falló');
|
|
11456
|
+
const htmlContent = `
|
|
11457
|
+
<div style="text-align: left; font-family: -apple-system, system-ui, sans-serif;">
|
|
11458
|
+
|
|
11459
|
+
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 12px;">
|
|
11460
|
+
<span style="font-size: 22px;">⚠️</span>
|
|
11461
|
+
<span style="font-size: 17px; font-weight: 600; color: #1a1a1a;">Archivo no subido</span>
|
|
11462
|
+
</div>
|
|
11463
|
+
|
|
11464
|
+
<p style="color: #555; font-size: 14px; line-height: 1.5; margin: 0 0 8px 0;">
|
|
11465
|
+
El registro se guardó, pero el archivo falló.
|
|
11466
|
+
</p>
|
|
11467
|
+
|
|
11468
|
+
<div style="background: #f8f4ec; padding: 6px 12px; border-radius: 4px; margin: 6px 0 14px 0;">
|
|
11469
|
+
<span style="color: #7a6b4a; font-size: 13px;">
|
|
11470
|
+
<strong>Motivo:</strong> ${uploadResult.message}
|
|
11471
|
+
</span>
|
|
11472
|
+
</div>
|
|
11473
|
+
|
|
11474
|
+
<div style="margin-top: 12px; padding-top: 12px; border-top: 1px solid #eee;">
|
|
11475
|
+
<p style="font-weight: 500; color: #1a1a1a; font-size: 13px; margin: 0 0 6px 0;">
|
|
11476
|
+
¿Qué deseas hacer?
|
|
11477
|
+
</p>
|
|
11478
|
+
<div style="font-size: 13px; color: #555; line-height: 1.7;">
|
|
11479
|
+
<div>📁 Guardar sin archivo <span style="color: #999; font-size: 12px;">(subir después)</span></div>
|
|
11480
|
+
<div>🗑️ Eliminar registro <span style="color: #999; font-size: 12px;">(corregir y reintentar)</span></div>
|
|
11481
|
+
</div>
|
|
11482
|
+
</div>
|
|
11483
|
+
|
|
11484
|
+
</div>`;
|
|
11485
|
+
this._dsxMessageService.SweetDialog.Dialog(uploadResult.title || 'Error', '', 'error', {
|
|
11486
|
+
html: htmlContent,
|
|
11487
|
+
showConfirmButton: true,
|
|
11488
|
+
confirmButtonText: 'Guardar',
|
|
11489
|
+
showCancelButton: true,
|
|
11490
|
+
cancelButtonText: 'Eliminar',
|
|
11491
|
+
}).then((isConfirmed) => {
|
|
11492
|
+
if (isConfirmed) {
|
|
11493
|
+
if (log)
|
|
11494
|
+
console.warn('⚠️ [SaveEntityPostFile] Se conservo el registro');
|
|
11495
|
+
resetForm?.();
|
|
11496
|
+
onSuccess?.(saveResult);
|
|
11497
|
+
}
|
|
11498
|
+
else {
|
|
11499
|
+
if (log)
|
|
11500
|
+
console.warn('❌ [SaveEntityPostFile] Se elimino el registro');
|
|
11501
|
+
// 2️⃣ Crear el observable del save con los datos sin archivo
|
|
11502
|
+
const delete$ = deleteMethod(saveResult.data, false);
|
|
11503
|
+
delete$.subscribe({
|
|
11504
|
+
next: (deleteResult) => {
|
|
11505
|
+
// 📥 Log del save
|
|
11506
|
+
if (log) {
|
|
11507
|
+
console.group('📥 [SaveEntityPostFile] Respuesta del delete');
|
|
11508
|
+
console.log('Resultado:', deleteResult);
|
|
11509
|
+
console.groupEnd();
|
|
11510
|
+
}
|
|
11511
|
+
if (deleteResult.isSuccess) {
|
|
11512
|
+
if (log)
|
|
11513
|
+
console.log('✅ [SaveEntityPostFile] Registro eliminado');
|
|
11514
|
+
onSuccess?.(deleteResult);
|
|
11515
|
+
}
|
|
11516
|
+
else {
|
|
11517
|
+
this._dsxMessageService.SweetDialog.Dialog('❌ Error al eliminar', deleteResult.message ||
|
|
11518
|
+
'No se pudo eliminar el registro.', 'error', {
|
|
11519
|
+
showConfirmButton: true,
|
|
11520
|
+
showCancelButton: false,
|
|
11521
|
+
});
|
|
11522
|
+
onError?.(deleteResult);
|
|
11523
|
+
}
|
|
11524
|
+
},
|
|
11525
|
+
error: (err) => console.warn(err),
|
|
11526
|
+
complete: () => '',
|
|
11527
|
+
});
|
|
11528
|
+
}
|
|
11529
|
+
});
|
|
11530
|
+
onError?.(uploadResult);
|
|
11531
|
+
}
|
|
11532
|
+
},
|
|
11533
|
+
error: (uploadError) => {
|
|
11534
|
+
// ❌ Error en la subida (excepción)
|
|
11535
|
+
console.warn('Error no controlado en upload:', uploadError);
|
|
11536
|
+
onError?.(uploadError);
|
|
11537
|
+
},
|
|
11538
|
+
});
|
|
11539
|
+
return;
|
|
11540
|
+
}
|
|
11541
|
+
// ✅ Éxito sin archivo
|
|
11542
|
+
if (log)
|
|
11543
|
+
console.log('✅ [SaveEntityPostFile] Proceso completado (sin archivo)');
|
|
11544
|
+
resetForm?.();
|
|
11545
|
+
onSuccess?.(saveResult);
|
|
11546
|
+
},
|
|
11547
|
+
error: (error) => {
|
|
11548
|
+
// ❌ Error en el save (excepción)
|
|
11549
|
+
console.warn('Error no controlado en save:', error);
|
|
11550
|
+
onError?.(error);
|
|
11551
|
+
},
|
|
11552
|
+
});
|
|
11553
|
+
}
|
|
11554
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SaveEntityPostFileService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
11555
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SaveEntityPostFileService, providedIn: 'root' });
|
|
11556
|
+
}
|
|
11557
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: SaveEntityPostFileService, decorators: [{
|
|
11558
|
+
type: Injectable,
|
|
11559
|
+
args: [{
|
|
11560
|
+
providedIn: 'root',
|
|
11561
|
+
}]
|
|
11562
|
+
}] });
|
|
11563
|
+
|
|
10368
11564
|
class NextToastNotifyService {
|
|
10369
11565
|
// Configuraciones iniciales por defecto (ambos activados como en tus switches)
|
|
10370
11566
|
defaultOptions = {
|
|
@@ -10408,8 +11604,10 @@ class ResultResponseService {
|
|
|
10408
11604
|
else {
|
|
10409
11605
|
const guideHtml = this.buildAssociatedGuideHtml(response?.data);
|
|
10410
11606
|
const mainMessage = this.formatPrimaryAlertMessage(response.message);
|
|
10411
|
-
this._sweetAlert2DialogService.Dialog(response.title,
|
|
10412
|
-
|
|
11607
|
+
this._sweetAlert2DialogService.Dialog(response.title,
|
|
11608
|
+
// Se concatena limpiamente sin alterar la lógica
|
|
11609
|
+
`${mainMessage}${guideHtml}`, {
|
|
11610
|
+
icono: 'icon/sign.png',
|
|
10413
11611
|
showCancelButton: false,
|
|
10414
11612
|
showConfirmButton: true,
|
|
10415
11613
|
});
|
|
@@ -10455,10 +11653,407 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
10455
11653
|
}]
|
|
10456
11654
|
}] });
|
|
10457
11655
|
|
|
11656
|
+
/**
|
|
11657
|
+
* Servicio especializado para extraer y procesar archivos (Blob) desde respuestas del backend.
|
|
11658
|
+
*
|
|
11659
|
+
* 📋 **Propósito:**
|
|
11660
|
+
* Este servicio actúa como un puente entre la respuesta del backend (normalizada como ServiceResult<FileData>)
|
|
11661
|
+
* y el consumidor final, extrayendo el archivo (Blob) y su nombre de forma segura y validada.
|
|
11662
|
+
*
|
|
11663
|
+
* 🎯 **Cuándo usar este servicio:**
|
|
11664
|
+
* - Cuando necesitas obtener el Blob y el nombre de archivo de un Observable<ServiceResult<FileData>>
|
|
11665
|
+
* - Cuando quieres validar que el archivo existe y no está vacío antes de procesarlo
|
|
11666
|
+
* - Cuando necesitas asegurar que el nombre del archivo tenga la extensión correcta
|
|
11667
|
+
* - Cuando quieres un manejo de errores detallado y estructurado
|
|
11668
|
+
*
|
|
11669
|
+
* 🔄 **Flujo de Trabajo:**
|
|
11670
|
+
* 1. El backend retorna un ServiceResult<FileData> (normalizado por getDownloadableFile)
|
|
11671
|
+
* 2. Este servicio extrae el Blob y el fileName
|
|
11672
|
+
* 3. Valida que el archivo exista y tenga contenido
|
|
11673
|
+
* 4. Asegura que el nombre tenga la extensión correcta según el MIME type
|
|
11674
|
+
* 5. Retorna un objeto con { blob, fileName } o un error estructurado
|
|
11675
|
+
*
|
|
11676
|
+
* 🔗 **Relación con otros servicios:**
|
|
11677
|
+
* - **ResultFileService**: Proporciona el Observable<ServiceResult<FileData>> de entrada
|
|
11678
|
+
* - **AlertaService**: Para mostrar mensajes al usuario
|
|
11679
|
+
*
|
|
11680
|
+
* @see ResultFileService - Servicio que realiza la petición HTTP
|
|
11681
|
+
* @see FileData - Estructura de datos del archivo
|
|
11682
|
+
* @see ServiceResult - Estructura de resultado estandarizada
|
|
11683
|
+
* @see Blob - API nativa de Blob de JavaScript
|
|
11684
|
+
*/
|
|
11685
|
+
class ResultFileReturnBlobNameService {
|
|
11686
|
+
/** Configuración del entorno (para controlar debug) */
|
|
11687
|
+
environment = inject(ENVIRONMENT);
|
|
11688
|
+
/**
|
|
11689
|
+
* Extrae el Blob y el nombre de archivo de un Observable<ServiceResult<FileData>>,
|
|
11690
|
+
* realizando validaciones exhaustivas y manejo de errores detallado.
|
|
11691
|
+
*
|
|
11692
|
+
* 📝 **Descripción:**
|
|
11693
|
+
* Este método toma el Observable resultante de `getDownloadableFile` y extrae
|
|
11694
|
+
* el archivo (Blob) y su nombre, validando que todo sea correcto antes de
|
|
11695
|
+
* entregar el resultado al consumidor.
|
|
11696
|
+
*
|
|
11697
|
+
* 🔍 **Validaciones Realizadas:**
|
|
11698
|
+
* 1. ✅ Resultado nulo o indefinido → Error: "El resultado es nulo o indefinido"
|
|
11699
|
+
* 2. ✅ isSuccess = false → Error con title y message del backend
|
|
11700
|
+
* 3. ✅ data es null → Error: "El resultado no contiene datos"
|
|
11701
|
+
* 4. ✅ blob es null → Error: "El blob del archivo no está presente"
|
|
11702
|
+
* 5. ✅ blob.size = 0 → Error: "El archivo descargado está vacío (0 bytes)"
|
|
11703
|
+
* 6. ✅ Error de conexión → Error: "No se pudo obtener el archivo del servidor"
|
|
11704
|
+
*
|
|
11705
|
+
* 📁 **Gestión de Extensiones:**
|
|
11706
|
+
* El método automáticamente asegura que el nombre del archivo tenga la extensión
|
|
11707
|
+
* correcta basada en el MIME type. Si el nombre ya tiene extensión, la mantiene.
|
|
11708
|
+
*
|
|
11709
|
+
* 💡 **Ejemplos de Uso:**
|
|
11710
|
+
*
|
|
11711
|
+
* ```typescript
|
|
11712
|
+
* // EJEMPLO 1: Uso básico con descarga automática
|
|
11713
|
+
* const fileObservable = this.resultFileService.getDownloadableFile('/api/documentos/123');
|
|
11714
|
+
*
|
|
11715
|
+
* this.resultFileReturnBlobNameService.extractFileDataFromServiceResultEnhanced(fileObservable)
|
|
11716
|
+
* .subscribe({
|
|
11717
|
+
* next: ({ blob, fileName }) => {
|
|
11718
|
+
* // Descargar el archivo automáticamente
|
|
11719
|
+
* this.resultFileService.descargarBlob(blob, fileName);
|
|
11720
|
+
* },
|
|
11721
|
+
* error: (error) => {
|
|
11722
|
+
* // Error estructurado
|
|
11723
|
+
* this.alertaService.mostrarError(error.title, error.message);
|
|
11724
|
+
* }
|
|
11725
|
+
* });
|
|
11726
|
+
*
|
|
11727
|
+
* // EJEMPLO 2: Visualizar PDF
|
|
11728
|
+
* const fileObservable = this.resultFileService.getDownloadableFile('/api/documentos/123');
|
|
11729
|
+
*
|
|
11730
|
+
* this.resultFileReturnBlobNameService.extractFileDataFromServiceResultEnhanced(fileObservable)
|
|
11731
|
+
* .subscribe({
|
|
11732
|
+
* next: ({ blob, fileName }) => {
|
|
11733
|
+
* // Crear URL para visualizar
|
|
11734
|
+
* const url = URL.createObjectURL(blob);
|
|
11735
|
+
* window.open(url, '_blank');
|
|
11736
|
+
* },
|
|
11737
|
+
* error: (error) => {
|
|
11738
|
+
* console.error('Error al visualizar:', error);
|
|
11739
|
+
* }
|
|
11740
|
+
* });
|
|
11741
|
+
*
|
|
11742
|
+
* // EJEMPLO 3: Procesamiento personalizado con validación
|
|
11743
|
+
* const fileObservable = this.resultFileService.getDownloadableFile('/api/reportes', {
|
|
11744
|
+
* params: { formato: 'excel' }
|
|
11745
|
+
* });
|
|
11746
|
+
*
|
|
11747
|
+
* this.resultFileReturnBlobNameService.extractFileDataFromServiceResultEnhanced(
|
|
11748
|
+
* fileObservable,
|
|
11749
|
+
* 'reporte_generado' // fallbackFileName
|
|
11750
|
+
* ).subscribe({
|
|
11751
|
+
* next: ({ blob, fileName }) => {
|
|
11752
|
+
* // Procesar el blob según necesidades
|
|
11753
|
+
* if (fileName.endsWith('.xlsx')) {
|
|
11754
|
+
* this.procesarExcel(blob);
|
|
11755
|
+
* } else {
|
|
11756
|
+
* this.descargarArchivo(blob, fileName);
|
|
11757
|
+
* }
|
|
11758
|
+
* },
|
|
11759
|
+
* error: (error) => {
|
|
11760
|
+
* // Manejo detallado de errores
|
|
11761
|
+
* if (error.title === 'Archivo vacío') {
|
|
11762
|
+
* this.alertaService.advertencia('El archivo está vacío');
|
|
11763
|
+
* } else if (error.title === 'Error de conexión') {
|
|
11764
|
+
* this.alertaService.error('Error de red', 'Verifica tu conexión');
|
|
11765
|
+
* } else {
|
|
11766
|
+
* this.alertaService.error(error.title, error.message);
|
|
11767
|
+
* }
|
|
11768
|
+
* }
|
|
11769
|
+
* });
|
|
11770
|
+
*
|
|
11771
|
+
* // EJEMPLO 4: Uso con loading state
|
|
11772
|
+
* cargando = false;
|
|
11773
|
+
*
|
|
11774
|
+
* descargarDocumento(id: number): void {
|
|
11775
|
+
* this.cargando = true;
|
|
11776
|
+
* const fileObservable = this.resultFileService.getDownloadableFile(`/api/documentos/${id}`);
|
|
11777
|
+
*
|
|
11778
|
+
* this.resultFileReturnBlobNameService.extractFileDataFromServiceResultEnhanced(
|
|
11779
|
+
* fileObservable,
|
|
11780
|
+
* `documento_${id}`
|
|
11781
|
+
* ).subscribe({
|
|
11782
|
+
* next: ({ blob, fileName }) => {
|
|
11783
|
+
* this.cargando = false;
|
|
11784
|
+
* this.resultFileService.descargarBlob(blob, fileName);
|
|
11785
|
+
* this.alertaService.exito('Descarga completada');
|
|
11786
|
+
* },
|
|
11787
|
+
* error: (error) => {
|
|
11788
|
+
* this.cargando = false;
|
|
11789
|
+
* this.alertaService.error('Error', error.message);
|
|
11790
|
+
* }
|
|
11791
|
+
* });
|
|
11792
|
+
* }
|
|
11793
|
+
*
|
|
11794
|
+
* // EJEMPLO 5: Combinación con otros operadores RxJS
|
|
11795
|
+
* const fileObservable = this.resultFileService.getDownloadableFile('/api/documentos/123');
|
|
11796
|
+
*
|
|
11797
|
+
* this.resultFileReturnBlobNameService.extractFileDataFromServiceResultEnhanced(fileObservable)
|
|
11798
|
+
* .pipe(
|
|
11799
|
+
* tap(({ blob, fileName }) => {
|
|
11800
|
+
* console.log(`Archivo: ${fileName}, Tamaño: ${blob.size} bytes`);
|
|
11801
|
+
* }),
|
|
11802
|
+
* delay(1000), // Simular procesamiento
|
|
11803
|
+
* map(({ blob, fileName }) => ({
|
|
11804
|
+
* blob,
|
|
11805
|
+
* fileName,
|
|
11806
|
+
* tipo: this.obtenerTipoArchivo(fileName)
|
|
11807
|
+
* }))
|
|
11808
|
+
* )
|
|
11809
|
+
* .subscribe(({ blob, fileName, tipo }) => {
|
|
11810
|
+
* console.log(`Tipo de archivo: ${tipo}`);
|
|
11811
|
+
* });
|
|
11812
|
+
* ```
|
|
11813
|
+
*
|
|
11814
|
+
* @param fileObservable - Observable que emite un ServiceResult<FileData>
|
|
11815
|
+
* @param fallbackFileName - Nombre alternativo si no se puede determinar el nombre (por defecto: 'archivo sin nombre')
|
|
11816
|
+
*
|
|
11817
|
+
* @returns Observable que emite un objeto con:
|
|
11818
|
+
* - blob: Blob - El archivo extraído
|
|
11819
|
+
* - fileName: string - Nombre del archivo con extensión
|
|
11820
|
+
* - headers?: HttpHeaders - Headers de la respuesta (opcional, actualmente no implementado)
|
|
11821
|
+
*
|
|
11822
|
+
* @throws {Object} En caso de error, emite un objeto con:
|
|
11823
|
+
* - success: false
|
|
11824
|
+
* - title: string - Título del error
|
|
11825
|
+
* - message: string - Mensaje descriptivo
|
|
11826
|
+
* - error?: any - Error original (en caso de error de conexión)
|
|
11827
|
+
*
|
|
11828
|
+
* @see ResultFileService.getDownloadableFile - Método que genera el Observable de entrada
|
|
11829
|
+
* @see ResultFileService.descargarBlob - Para descargar el archivo extraído
|
|
11830
|
+
* @see ResultFileService.PdfView - Para visualizar PDFs
|
|
11831
|
+
*/
|
|
11832
|
+
extractFileDataFromServiceResultEnhanced(fileObservable, fallbackFileName = 'archivo sin nombre') {
|
|
11833
|
+
return new Observable((observer) => {
|
|
11834
|
+
fileObservable.subscribe({
|
|
11835
|
+
next: (result) => {
|
|
11836
|
+
// Validación completa del resultado
|
|
11837
|
+
if (!result) {
|
|
11838
|
+
observer.error({
|
|
11839
|
+
success: false,
|
|
11840
|
+
title: 'Resultado nulo',
|
|
11841
|
+
message: 'El resultado es nulo o indefinido',
|
|
11842
|
+
});
|
|
11843
|
+
return;
|
|
11844
|
+
}
|
|
11845
|
+
if (!result.isSuccess) {
|
|
11846
|
+
observer.error({
|
|
11847
|
+
success: false,
|
|
11848
|
+
title: result.title || 'Error en la operación',
|
|
11849
|
+
message: result.message || 'El servidor reportó un error',
|
|
11850
|
+
});
|
|
11851
|
+
return;
|
|
11852
|
+
}
|
|
11853
|
+
if (!result.data) {
|
|
11854
|
+
observer.error({
|
|
11855
|
+
success: false,
|
|
11856
|
+
title: 'Datos no disponibles',
|
|
11857
|
+
message: 'El resultado no contiene datos',
|
|
11858
|
+
});
|
|
11859
|
+
return;
|
|
11860
|
+
}
|
|
11861
|
+
const blob = result.data.blob;
|
|
11862
|
+
if (!blob) {
|
|
11863
|
+
observer.error({
|
|
11864
|
+
success: false,
|
|
11865
|
+
title: 'Archivo no disponible',
|
|
11866
|
+
message: 'El blob del archivo no está presente',
|
|
11867
|
+
});
|
|
11868
|
+
return;
|
|
11869
|
+
}
|
|
11870
|
+
// Validar que el blob tenga tamaño
|
|
11871
|
+
if (blob.size === 0) {
|
|
11872
|
+
observer.error({
|
|
11873
|
+
success: false,
|
|
11874
|
+
title: 'Archivo vacío',
|
|
11875
|
+
message: 'El archivo descargado está vacío (0 bytes)',
|
|
11876
|
+
});
|
|
11877
|
+
return;
|
|
11878
|
+
}
|
|
11879
|
+
// Procesar nombre de archivo
|
|
11880
|
+
let fileName = result.data.fileName || fallbackFileName;
|
|
11881
|
+
fileName = this.ensureFileNameWithExtension(fileName, blob.type);
|
|
11882
|
+
// Log para debugging
|
|
11883
|
+
this.logIfNotProduction('✅ Archivo extraído exitosamente:', {
|
|
11884
|
+
tamaño: this.formatFileSize(blob.size),
|
|
11885
|
+
tipo: blob.type,
|
|
11886
|
+
nombre: fileName,
|
|
11887
|
+
});
|
|
11888
|
+
observer.next({
|
|
11889
|
+
blob: blob,
|
|
11890
|
+
fileName: fileName,
|
|
11891
|
+
});
|
|
11892
|
+
observer.complete();
|
|
11893
|
+
},
|
|
11894
|
+
error: (err) => {
|
|
11895
|
+
this.logIfNotProduction('❌ Error extrayendo archivo:', err);
|
|
11896
|
+
observer.error({
|
|
11897
|
+
success: false,
|
|
11898
|
+
title: 'Error de conexión',
|
|
11899
|
+
message: 'No se pudo obtener el archivo del servidor',
|
|
11900
|
+
error: err,
|
|
11901
|
+
});
|
|
11902
|
+
},
|
|
11903
|
+
});
|
|
11904
|
+
});
|
|
11905
|
+
}
|
|
11906
|
+
/**
|
|
11907
|
+
* Asegura que el nombre de archivo tenga una extensión válida.
|
|
11908
|
+
*
|
|
11909
|
+
* 📋 **Funcionamiento:**
|
|
11910
|
+
* 1. Si el nombre ya tiene una extensión (contiene un punto), lo retorna sin cambios.
|
|
11911
|
+
* 2. Si no tiene extensión, intenta obtenerla del MIME type.
|
|
11912
|
+
* 3. Si se puede determinar la extensión, la agrega al nombre.
|
|
11913
|
+
* 4. Si no se puede determinar, retorna el nombre sin cambios.
|
|
11914
|
+
*
|
|
11915
|
+
* 💡 **Ejemplos:**
|
|
11916
|
+
* - `ensureFileNameWithExtension('documento', 'application/pdf')` → `'documento.pdf'`
|
|
11917
|
+
* - `ensureFileNameWithExtension('foto', 'image/jpeg')` → `'foto.jpg'`
|
|
11918
|
+
* - `ensureFileNameWithExtension('archivo.txt', 'text/plain')` → `'archivo.txt'` (ya tiene extensión)
|
|
11919
|
+
* - `ensureFileNameWithExtension('reporte', 'application/unknown')` → `'reporte'`
|
|
11920
|
+
*
|
|
11921
|
+
* @param fileName - Nombre del archivo (puede o no tener extensión)
|
|
11922
|
+
* @param mimeType - Tipo MIME del archivo
|
|
11923
|
+
* @returns Nombre del archivo con extensión (si fue posible determinarla)
|
|
11924
|
+
*/
|
|
11925
|
+
ensureFileNameWithExtension(fileName, mimeType) {
|
|
11926
|
+
// Si ya tiene extensión, retornarlo
|
|
11927
|
+
if (fileName.includes('.')) {
|
|
11928
|
+
return fileName;
|
|
11929
|
+
}
|
|
11930
|
+
// Obtener extensión del MIME type
|
|
11931
|
+
const extension = this.getExtensionFromMimeType(mimeType);
|
|
11932
|
+
return extension ? `${fileName}.${extension}` : fileName;
|
|
11933
|
+
}
|
|
11934
|
+
/**
|
|
11935
|
+
* Obtiene la extensión del archivo basado en el MIME type.
|
|
11936
|
+
*
|
|
11937
|
+
* 📁 **Mapa de MIME Types Soportados:**
|
|
11938
|
+
*
|
|
11939
|
+
* | MIME Type | Extensión |
|
|
11940
|
+
* |-----------|-----------|
|
|
11941
|
+
* | application/pdf | pdf |
|
|
11942
|
+
* | application/msword | doc |
|
|
11943
|
+
* | application/vnd.openxmlformats-officedocument.wordprocessingml.document | docx |
|
|
11944
|
+
* | application/vnd.ms-excel | xls |
|
|
11945
|
+
* | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet | xlsx |
|
|
11946
|
+
* | application/vnd.ms-powerpoint | ppt |
|
|
11947
|
+
* | application/vnd.openxmlformats-officedocument.presentationml.presentation | pptx |
|
|
11948
|
+
* | image/jpeg | jpg |
|
|
11949
|
+
* | image/png | png |
|
|
11950
|
+
* | image/gif | gif |
|
|
11951
|
+
* | text/plain | txt |
|
|
11952
|
+
* | application/json | json |
|
|
11953
|
+
* | application/zip | zip |
|
|
11954
|
+
* | application/octet-stream | bin |
|
|
11955
|
+
*
|
|
11956
|
+
* 🔄 **Comportamiento:**
|
|
11957
|
+
* 1. Busca el MIME type en el mapa predefinido.
|
|
11958
|
+
* 2. Si lo encuentra, retorna la extensión correspondiente.
|
|
11959
|
+
* 3. Si no lo encuentra, intenta extraer el subtipo del MIME (ej: 'pdf' de 'application/pdf').
|
|
11960
|
+
* 4. Si el subtipo es 'octet-stream' o no se puede determinar, retorna 'file'.
|
|
11961
|
+
*
|
|
11962
|
+
* @param mimeType - Tipo MIME del archivo
|
|
11963
|
+
* @returns Extensión del archivo (sin el punto) o 'file' si no se puede determinar
|
|
11964
|
+
*/
|
|
11965
|
+
getExtensionFromMimeType(mimeType) {
|
|
11966
|
+
const mimeMap = {
|
|
11967
|
+
'application/pdf': 'pdf',
|
|
11968
|
+
'application/msword': 'doc',
|
|
11969
|
+
'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'docx',
|
|
11970
|
+
'application/vnd.ms-excel': 'xls',
|
|
11971
|
+
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
|
11972
|
+
'application/vnd.ms-powerpoint': 'ppt',
|
|
11973
|
+
'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'pptx',
|
|
11974
|
+
'image/jpeg': 'jpg',
|
|
11975
|
+
'image/png': 'png',
|
|
11976
|
+
'image/gif': 'gif',
|
|
11977
|
+
'text/plain': 'txt',
|
|
11978
|
+
'application/json': 'json',
|
|
11979
|
+
'application/zip': 'zip',
|
|
11980
|
+
'application/octet-stream': 'bin',
|
|
11981
|
+
};
|
|
11982
|
+
// Buscar extensión en el mapa
|
|
11983
|
+
const extension = mimeMap[mimeType];
|
|
11984
|
+
if (extension) {
|
|
11985
|
+
return extension;
|
|
11986
|
+
}
|
|
11987
|
+
// Si no está en el mapa, intentar extraer del MIME type
|
|
11988
|
+
const mimeParts = mimeType.split('/');
|
|
11989
|
+
if (mimeParts.length === 2) {
|
|
11990
|
+
const type = mimeParts[1];
|
|
11991
|
+
if (type !== 'octet-stream') {
|
|
11992
|
+
return type;
|
|
11993
|
+
}
|
|
11994
|
+
}
|
|
11995
|
+
// Si no se puede determinar, devolver 'file'
|
|
11996
|
+
return 'file';
|
|
11997
|
+
}
|
|
11998
|
+
/**
|
|
11999
|
+
* Formatea el tamaño del archivo para logging.
|
|
12000
|
+
*
|
|
12001
|
+
* 📊 **Formato de Salida:**
|
|
12002
|
+
* - Bytes: `"123 B"`
|
|
12003
|
+
* - Kilobytes: `"1.5 KB"`
|
|
12004
|
+
* - Megabytes: `"2.3 MB"`
|
|
12005
|
+
* - Gigabytes: `"1.2 GB"`
|
|
12006
|
+
*
|
|
12007
|
+
* 💡 **Ejemplos:**
|
|
12008
|
+
* - `formatFileSize(0)` → `"0 B"`
|
|
12009
|
+
* - `formatFileSize(1024)` → `"1.00 KB"`
|
|
12010
|
+
* - `formatFileSize(1536)` → `"1.50 KB"`
|
|
12011
|
+
* - `formatFileSize(1048576)` → `"1.00 MB"`
|
|
12012
|
+
*
|
|
12013
|
+
* @param bytes - Tamaño en bytes
|
|
12014
|
+
* @returns String formateado del tamaño
|
|
12015
|
+
*/
|
|
12016
|
+
formatFileSize(bytes) {
|
|
12017
|
+
if (bytes === 0)
|
|
12018
|
+
return '0 B';
|
|
12019
|
+
const k = 1024;
|
|
12020
|
+
const sizes = ['B', 'KB', 'MB', 'GB'];
|
|
12021
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
12022
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
12023
|
+
}
|
|
12024
|
+
/**
|
|
12025
|
+
* Registra mensajes de log solo en entornos de desarrollo.
|
|
12026
|
+
*
|
|
12027
|
+
* 🛠️ **Comportamiento:**
|
|
12028
|
+
* - En desarrollo (production = false): Muestra los logs en consola
|
|
12029
|
+
* - En producción (production = true): No muestra nada
|
|
12030
|
+
*
|
|
12031
|
+
* 📝 **Uso Interno:**
|
|
12032
|
+
* Se utiliza para depuración sin afectar el rendimiento en producción.
|
|
12033
|
+
*
|
|
12034
|
+
* @param args - Argumentos para mostrar en el log
|
|
12035
|
+
*/
|
|
12036
|
+
logIfNotProduction(...args) {
|
|
12037
|
+
if (!this.environment.production) {
|
|
12038
|
+
console.log('[ResultFileService]', ...args);
|
|
12039
|
+
}
|
|
12040
|
+
}
|
|
12041
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResultFileReturnBlobNameService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
12042
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResultFileReturnBlobNameService, providedIn: 'root' });
|
|
12043
|
+
}
|
|
12044
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ResultFileReturnBlobNameService, decorators: [{
|
|
12045
|
+
type: Injectable,
|
|
12046
|
+
args: [{
|
|
12047
|
+
providedIn: 'root',
|
|
12048
|
+
}]
|
|
12049
|
+
}] });
|
|
12050
|
+
|
|
10458
12051
|
class HelpersService {
|
|
10459
12052
|
Array = inject(ArrayHelpersService);
|
|
12053
|
+
ServiceEntityPostFile = inject(SaveEntityPostFileService);
|
|
10460
12054
|
ServiceResultFile = inject(ResultFileService);
|
|
10461
12055
|
ServiceResultResponse = inject(ResultResponseService);
|
|
12056
|
+
ServiceResultFileReturnBlobName = inject(ResultFileReturnBlobNameService);
|
|
10462
12057
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HelpersService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
10463
12058
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: HelpersService, providedIn: 'root' });
|
|
10464
12059
|
}
|
|
@@ -10916,19 +12511,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
|
|
|
10916
12511
|
}]
|
|
10917
12512
|
}] });
|
|
10918
12513
|
|
|
10919
|
-
class DsxMessagesService {
|
|
10920
|
-
AlertaService = inject(AlertaService);
|
|
10921
|
-
SweetDialog = inject(SweetAlert2DialogService);
|
|
10922
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxMessagesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
10923
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxMessagesService, providedIn: 'root' });
|
|
10924
|
-
}
|
|
10925
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxMessagesService, decorators: [{
|
|
10926
|
-
type: Injectable,
|
|
10927
|
-
args: [{
|
|
10928
|
-
providedIn: 'root',
|
|
10929
|
-
}]
|
|
10930
|
-
}] });
|
|
10931
|
-
|
|
10932
12514
|
// form-preview.service.ts
|
|
10933
12515
|
class FormPreviewService {
|
|
10934
12516
|
environment;
|
|
@@ -12175,5 +13757,5 @@ function sorensenDiceValidator(dataSource, key, umbral = 0.7) {
|
|
|
12175
13757
|
* Generated bundle index. Do not edit.
|
|
12176
13758
|
*/
|
|
12177
13759
|
|
|
12178
|
-
export { ACTION_TYPES, AlertaService, AppMessageErrorComponent, AppMessageHelpComponent, ArrowNavigationDirective, AuthorizeService, AutoScrollHeightDirective, BaseCRUDService, CACHE_KEYS, CacheService, CssV2Component, DSX_PALETTE, DateIndicator, DocxPreviewComponent, DsxAddToolsModule, DsxAutocomplete, DsxButtonComponent, DsxEnableDisable, DsxInputCurrency, DsxMessagesService, DsxSelect, DsxStatusToggle, DsxdateShort, DteService, ENVIRONMENT, EndpointService, ErrorHandlerService, FileComponent, FormPreviewService, GTQFormatter, HeaderDsx, HelpersService, HttpHelpersService, INITIAL_PARAMETERS, IcoLabel, IconDsxComponent, JoinByPipe, JsonHighlightPipe, JsonValuesDebujComponent, JsonViewerComponent, KpicardComponent, LoadingComponent, LoadingLottieComponent, LogoDsxComponent, MasterDetailChangeService, NavbarDsxComponent, NetworkStatusComponent, OnlyRangoPatternDirective, ParameterValuesService, PdfPreviewComponent, PrimeNgModule, QrGenerator, ResultFileService, SWEET_ALERT_THEMES, ScreenInspector, SecurityService, SelectAllOnFocusDirective, SpinnerLoadingService, SweetAlert2DialogService, TablePreviewService, TemplateHighlight, TokenPurposeLogin, TruncatePipe, UtilityAddService, asyncExistsValidator, atLeastOneFieldRequiredValidator, chainControlGroups, createCurrencyFormatter, createInitialCache, createTypedCacheProvider, cuiValidator, dateMinMaxValidator, dateRangeValidator, dateRangeValidatorFromTo, developmentEnvironment, getActionMessageConfig, getZeroBasedRolIndex, guardTokenPurposeGuard, httpAuthorizeInterceptor, minimumAgeValidator, nitValidator, productionEnvironment, provideEnvironment, sorensenDiceValidator, templateStructureValidator, templateVariablesValidator, validateEnvironmentConfig };
|
|
13760
|
+
export { ACTION_TYPES, AlertaService, AppMessageErrorComponent, AppMessageHelpComponent, ArrowNavigationDirective, AuthorizeService, AutoScrollHeightDirective, BaseCRUDService, CACHE_KEYS, CacheService, CssV2Component, DSX_PALETTE, DateIndicator, DocxPreviewComponent, DsxAddToolsModule, DsxAutocomplete, DsxButtonComponent, DsxEnableDisable, DsxInputCurrency, DsxMessagesService, DsxSelect, DsxStatusToggle, DsxdateShort, DsxdateTime, DteService, ENVIRONMENT, EndpointService, ErrorHandlerService, FileComponent, FormPreviewService, GTQFormatter, HeaderDsx, HelpersService, HttpHelpersService, INITIAL_PARAMETERS, IcoLabel, IconDsxComponent, JoinByPipe, JsonHighlightPipe, JsonValuesDebujComponent, JsonViewerComponent, KpicardComponent, LoadingComponent, LoadingLottieComponent, LogoDsxComponent, MasterDetailChangeService, NavbarDsxComponent, NetworkStatusComponent, OnlyRangoPatternDirective, ParameterValuesService, PdfPreviewComponent, PrimeNgModule, QrGenerator, ResultFileService, SWEET_ALERT_THEMES, ScreenInspector, SecurityService, SelectAllOnFocusDirective, SpinnerLoadingService, SweetAlert2DialogService, TablePreviewService, TemplateHighlight, TokenPurposeLogin, TruncatePipe, UtilityAddService, asyncExistsValidator, atLeastOneFieldRequiredValidator, chainControlGroups, createCurrencyFormatter, createInitialCache, createTypedCacheProvider, cuiValidator, dateMinMaxValidator, dateRangeValidator, dateRangeValidatorFromTo, developmentEnvironment, getActionMessageConfig, getZeroBasedRolIndex, guardTokenPurposeGuard, httpAuthorizeInterceptor, minimumAgeValidator, nitValidator, productionEnvironment, provideEnvironment, sorensenDiceValidator, templateStructureValidator, templateVariablesValidator, validateEnvironmentConfig };
|
|
12179
13761
|
//# sourceMappingURL=ngx-dsxlibrary.mjs.map
|