ngx-dsxlibrary 2.21.72 → 2.21.74

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.
@@ -5,7 +5,7 @@ import { FormsModule, NG_VALUE_ACCESSOR, AbstractControl, FormControl, NG_VALIDA
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, JsonPipe, DatePipe, DecimalPipe, Location } from '@angular/common';
8
+ import { CommonModule, AsyncPipe, JsonPipe, DecimalPipe, DatePipe, 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';
@@ -4301,25 +4301,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
4301
4301
  * | details | Azul índigo | Abrir detalle de registro |
4302
4302
  */
4303
4303
  const DSX_PALETTE = {
4304
- danger: '#EF5350',
4305
- warning: '#FF7043',
4304
+ brown: '#8D6E63',
4306
4305
  caution: '#FFB300',
4307
- success: '#0ba055',
4308
- primary: '#0066cc',
4309
- info: '#26C6DA',
4310
- neutral: '#90A4AE',
4311
- restore: '#B0BEC5',
4312
- return: '#11438d',
4313
- view: '#00838F',
4306
+ cyan: '#00BCD4',
4307
+ danger: '#EF5350',
4308
+ details: '#3949AB',
4309
+ download: '#e6763a',
4314
4310
  excel: '#217346',
4315
- download: '#E65100',
4316
4311
  import: '#546E7A',
4317
- details: '#3949AB',
4312
+ info: '#26C6DA',
4318
4313
  magenta: '#D81B60',
4319
- cyan: '#00BCD4',
4314
+ neutral: '#90A4AE',
4320
4315
  olive: '#808000',
4321
- brown: '#8D6E63',
4316
+ primary: '#0066cc',
4317
+ restore: '#B0BEC5',
4318
+ return: '#11438d',
4322
4319
  slate: '#607D8B',
4320
+ success: '#0ba055',
4321
+ upload: '#5C6BC0', // 👈 Agregado: Índigo para contrastar limpiamente con download (naranja)
4322
+ view: '#00838F',
4323
+ warning: '#FF7043',
4323
4324
  };
4324
4325
  /** Tipos que necesitan un `[id]` mayor a 0 para poder mostrarse. */
4325
4326
  const TYPES_REQUIRING_ID = [
@@ -4932,7 +4933,7 @@ const ACTION_CONFIG = {
4932
4933
  details: {
4933
4934
  label: 'Detalles',
4934
4935
  icon: 'list',
4935
- primeIcon: 'fa-solid fa-list-ol',
4936
+ primeIcon: 'fa-solid fa-list-check',
4936
4937
  colorToken: 'details',
4937
4938
  tooltip: 'Detalle de registro',
4938
4939
  },
@@ -4960,7 +4961,7 @@ const ACTION_CONFIG = {
4960
4961
  download: {
4961
4962
  label: 'Descargar',
4962
4963
  icon: 'download',
4963
- primeIcon: 'pi pi-download',
4964
+ primeIcon: 'fa-solid fa-cloud-arrow-down',
4964
4965
  colorToken: 'download',
4965
4966
  tooltip: 'Descargar archivo',
4966
4967
  },
@@ -5001,12 +5002,8 @@ class FileComponent {
5001
5002
  // --- Para el manejo de errores del formulario y debug ---
5002
5003
  _control = null;
5003
5004
  // 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
5005
+ errorControl = computed(() => this._control, ...(ngDevMode ? [{ debugName: "errorControl" }] : /* istanbul ignore next */ []));
5008
5006
  controlErrors = signal(null, ...(ngDevMode ? [{ debugName: "controlErrors" }] : /* istanbul ignore next */ []));
5009
- // Estado del control
5010
5007
  controlValid = signal(true, ...(ngDevMode ? [{ debugName: "controlValid" }] : /* istanbul ignore next */ []));
5011
5008
  controlTouched = signal(false, ...(ngDevMode ? [{ debugName: "controlTouched" }] : /* istanbul ignore next */ []));
5012
5009
  controlDirty = signal(false, ...(ngDevMode ? [{ debugName: "controlDirty" }] : /* istanbul ignore next */ []));
@@ -5022,33 +5019,44 @@ class FileComponent {
5022
5019
  return false;
5023
5020
  return this._control.errors?.['required'] ?? false;
5024
5021
  }
5025
- // Computed properties
5022
+ // ============================================
5023
+ // COMPUTED PROPERTIES - LIMPIOS Y CLAROS
5024
+ // ============================================
5025
+ // ✅ Hay archivo guardado en BD (fuente: existingFileName)
5026
5026
  hasExistingFile = computed(() => {
5027
5027
  if (!this.isViewActive())
5028
5028
  return false;
5029
- const hasFile = !!this.existingFileName() && !this.isReplacing() && !this.existingFile();
5030
- if (this.debug() && this.isViewActive())
5031
- this.log('hasExistingFile', { hasFile });
5029
+ const hasFile = !!this.existingFileName() && !this.isReplacing();
5030
+ if (this.debug() && this.isViewActive()) {
5031
+ this.log('hasExistingFile', {
5032
+ hasFile,
5033
+ existingFileName: this.existingFileName(),
5034
+ isReplacing: this.isReplacing(),
5035
+ });
5036
+ }
5032
5037
  return hasFile;
5033
5038
  }, ...(ngDevMode ? [{ debugName: "hasExistingFile" }] : /* istanbul ignore next */ []));
5039
+ // ✅ Mostrar estado de edición (archivo guardado SIN archivo seleccionado)
5034
5040
  showExistingFile = computed(() => {
5035
5041
  if (!this.isViewActive())
5036
5042
  return false;
5037
5043
  const show = this.hasExistingFile() && !this.isReplacing();
5038
- if (this.debug() && this.isViewActive())
5039
- this.log('showExistingFile', { show });
5044
+ if (this.debug() && this.isViewActive()) {
5045
+ this.log('showExistingFile', {
5046
+ show,
5047
+ hasExistingFile: this.hasExistingFile(),
5048
+ isReplacing: this.isReplacing(),
5049
+ });
5050
+ }
5040
5051
  return show;
5041
5052
  }, ...(ngDevMode ? [{ debugName: "showExistingFile" }] : /* istanbul ignore next */ []));
5053
+ // ✅ El upload está habilitado solo si el padre no lo deshabilita
5042
5054
  isFileUploadEnabled = computed(() => {
5043
5055
  if (!this.isViewActive())
5044
5056
  return false;
5045
- if (this.disabled())
5046
- return false;
5047
- const enabled = !this.hasExistingFile() || this.isReplacing();
5048
- if (this.debug() && this.isViewActive())
5049
- this.log('isFileUploadEnabled', { enabled });
5050
- return enabled;
5057
+ return !this.disabled();
5051
5058
  }, ...(ngDevMode ? [{ debugName: "isFileUploadEnabled" }] : /* istanbul ignore next */ []));
5059
+ // ✅ Botón de reemplazo deshabilitado
5052
5060
  isReplaceButtonDisabled = computed(() => {
5053
5061
  if (!this.isViewActive())
5054
5062
  return true;
@@ -5058,6 +5066,7 @@ class FileComponent {
5058
5066
  return true;
5059
5067
  return false;
5060
5068
  }, ...(ngDevMode ? [{ debugName: "isReplaceButtonDisabled" }] : /* istanbul ignore next */ []));
5069
+ // ✅ Tipo de archivo permitido para mostrar
5061
5070
  allowedFileTypeLabel = computed(() => {
5062
5071
  if (!this.isViewActive())
5063
5072
  return 'archivo';
@@ -5112,11 +5121,11 @@ class FileComponent {
5112
5121
  this.isViewActive.set(true);
5113
5122
  if (this.debug()) {
5114
5123
  console.log('[FileComponent] ✅ View activated');
5115
- console.log('[FileComponent] 📋 Configuración actual:', {
5124
+ console.log('[FileComponent] 📋 Configuración:', {
5116
5125
  accept: this.accept(),
5117
5126
  maxFileSize: this.maxFileSize(),
5118
5127
  debug: this.debug(),
5119
- isDevMode: this.isDevMode,
5128
+ existingFileName: this.existingFileName(),
5120
5129
  });
5121
5130
  }
5122
5131
  });
@@ -5124,183 +5133,101 @@ class FileComponent {
5124
5133
  ngOnDestroy() {
5125
5134
  this.isViewActive.set(false);
5126
5135
  if (this.debug())
5127
- console.log('[FileComponent] 🗑️ Component destroyed');
5136
+ console.log('[FileComponent] 🗑️ Destroyed');
5128
5137
  }
5129
- // --- Método para validar archivo manualmente ---
5138
+ // --- Validación de archivo ---
5130
5139
  validateFile(file) {
5131
5140
  if (this.debug()) {
5132
- this.log('🔍 validateFile - Iniciando validación manual', {
5141
+ this.log('🔍 validateFile', {
5133
5142
  fileName: file.name,
5134
5143
  fileSize: file.size,
5135
- fileType: file.type,
5136
5144
  accept: this.accept(),
5137
5145
  maxSize: this.maxFileSize(),
5138
5146
  });
5139
5147
  }
5140
- // Validar tipo de archivo
5141
5148
  const acceptStr = this.accept();
5142
5149
  if (acceptStr) {
5143
5150
  const acceptedExtensions = acceptStr
5144
5151
  .split(',')
5145
5152
  .map((ext) => ext.trim().toLowerCase());
5146
5153
  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
5154
  if (!acceptedExtensions.includes(fileExtension)) {
5155
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 };
5156
+ return { valid: false, error: `${this.invalidSummary()} ${detail}` };
5162
5157
  }
5163
5158
  }
5164
- // Validar tamaño
5165
5159
  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
5160
  if (file.size > maxSizeBytes) {
5174
5161
  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 };
5162
+ return { valid: false, error: `${this.invalidSizeSummary()} ${detail}` };
5179
5163
  }
5180
5164
  if (this.debug())
5181
- this.log('✅ validateFile - Archivo válido');
5165
+ this.log('✅ validateFile - Válido');
5182
5166
  return { valid: true };
5183
5167
  }
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
- }
5207
- }
5208
5168
  // --- Métodos públicos ---
5209
5169
  onSelect(event) {
5210
- const startTime = performance.now();
5211
5170
  if (this.debug()) {
5212
- this.log('📤 onSelect - Evento recibido', {
5213
- files: event.files,
5214
- currentFiles: event.currentFiles,
5215
- isFileUploadEnabled: this.isFileUploadEnabled(),
5171
+ this.log('📤 onSelect', {
5172
+ isReplacing: this.isReplacing(),
5173
+ hasExistingFile: this.hasExistingFile(),
5174
+ disabled: this.disabled(),
5216
5175
  });
5217
5176
  }
5218
- if (!this.isViewActive()) {
5219
- if (this.debug())
5220
- this.log('⚠️ Componente no activo, ignorando selección');
5221
- return;
5222
- }
5223
- if (!this.isFileUploadEnabled()) {
5224
- console.warn('[FileComponent] ⚠️ File upload is disabled');
5177
+ if (!this.isViewActive() || this.disabled()) {
5178
+ if (this.disabled())
5179
+ console.warn('[FileComponent] ⚠️ Disabled');
5225
5180
  this.fileUpload()?.clear();
5226
5181
  return;
5227
5182
  }
5228
5183
  const file = event.files?.[0] ?? null;
5229
5184
  if (!file) {
5230
5185
  if (this.debug())
5231
- this.log('⚠️ No se seleccionó ningún archivo');
5186
+ this.log('⚠️ No file selected');
5232
5187
  return;
5233
5188
  }
5234
5189
  if (this.debug()) {
5235
- this.log('📄 Archivo seleccionado', {
5190
+ this.log('📄 File selected', {
5236
5191
  name: file.name,
5237
- size: file.size,
5192
+ size: this.formatFileSize(file.size),
5238
5193
  type: file.type,
5239
- sizeFormatted: this.formatFileSize(file.size),
5240
5194
  });
5241
5195
  }
5242
- // Validar archivo manualmente
5243
5196
  const validation = this.validateFile(file);
5244
5197
  if (!validation.valid) {
5245
- // Archivo inválido - mostrar error y limpiar
5246
5198
  this.errorMessage.set(validation.error || 'Archivo inválido');
5247
5199
  this.fileUpload()?.clear();
5248
5200
  this.value = null;
5249
5201
  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
5202
  setTimeout(() => {
5257
5203
  this.errorMessage.set(null);
5258
5204
  if (this.debug())
5259
- this.log('⏰ Mensaje de error auto-ocultado después de 5s');
5205
+ this.log('⏰ Error auto-ocultado');
5260
5206
  }, 5000);
5261
- // Actualizar estado del control
5262
5207
  this.updateControlState();
5263
5208
  return;
5264
5209
  }
5265
- // Archivo válido
5210
+ // Archivo válido
5266
5211
  this.errorMessage.set(null);
5267
5212
  this.value = file;
5268
5213
  this.onChange(file);
5269
5214
  this.markAsTouched();
5270
- if (this.isReplacing() && file) {
5215
+ // Salir del modo reemplazo si estaba activo
5216
+ if (this.isReplacing()) {
5271
5217
  this.isReplacing.set(false);
5272
5218
  if (this.debug())
5273
- this.log('🔄 Reemplazo completado');
5219
+ this.log('🔄 Reemplazo completado', { file: file.name });
5274
5220
  }
5275
5221
  if (this.control) {
5276
5222
  this.control.updateValueAndValidity();
5277
5223
  }
5278
- // Actualizar estado del control
5279
5224
  this.updateControlState();
5280
- const endTime = performance.now();
5281
- if (this.debug()) {
5282
- this.log(`⏱️ onSelect completado en ${(endTime - startTime).toFixed(2)}ms`);
5283
- }
5284
- }
5285
- clear() {
5286
5225
  if (this.debug())
5287
- this.log('🧹 clear() - Limpiando componente');
5288
- if (!this.isViewActive())
5289
- return;
5290
- this.fileUpload()?.clear();
5291
- this.value = null;
5292
- this.errorMessage.set(null);
5293
- this.onChange(null);
5294
- this.markAsTouched();
5295
- if (this.control)
5296
- this.control.updateValueAndValidity();
5297
- this.updateControlState();
5298
- if (this.debug())
5299
- this.log('✅ Limpieza completada');
5226
+ this.log('⏱️ onSelect completado');
5300
5227
  }
5301
- clearFile() {
5228
+ clear() {
5302
5229
  if (this.debug())
5303
- this.log('🧹 clearFile() - Limpiando archivo seleccionado');
5230
+ this.log('🧹 clearFile()');
5304
5231
  if (!this.isViewActive())
5305
5232
  return;
5306
5233
  this.fileUpload()?.clear();
@@ -5314,46 +5241,23 @@ class FileComponent {
5314
5241
  if (this.debug())
5315
5242
  this.log('✅ Archivo limpiado');
5316
5243
  }
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
- }
5326
- this.value = value;
5327
- }
5328
- registerOnChange(fn) {
5329
- if (this.debug())
5330
- this.log('📝 registerOnChange');
5331
- this.onChange = fn;
5332
- }
5333
- registerOnTouched(fn) {
5334
- if (this.debug())
5335
- this.log('📝 registerOnTouched');
5336
- this.onTouched = fn;
5337
- }
5338
- setDisabledState(isDisabled) {
5339
- if (!this.isViewActive())
5340
- return;
5244
+ startReplace() {
5341
5245
  if (this.debug()) {
5342
- this.log('🔒 setDisabledState', {
5343
- isDisabled,
5344
- previousState: this.disabled(),
5246
+ this.log('🔄 startReplace', {
5247
+ hasExistingFile: this.hasExistingFile(),
5248
+ existingFileName: this.existingFileName(),
5345
5249
  });
5346
5250
  }
5347
- this.disabled.set(isDisabled);
5348
- }
5349
- startReplace() {
5350
- if (this.debug())
5351
- this.log('🔄 startReplace - Iniciando reemplazo');
5352
5251
  if (!this.isViewActive())
5353
5252
  return;
5354
5253
  if (this.disabled()) {
5355
5254
  if (this.debug())
5356
- this.log('⛔ Reemplazo cancelado - componente deshabilitado');
5255
+ this.log('⛔ Reemplazo cancelado - disabled');
5256
+ return;
5257
+ }
5258
+ if (!this.hasExistingFile()) {
5259
+ if (this.debug())
5260
+ this.log('⛔ Reemplazo cancelado - no hay archivo existente');
5357
5261
  return;
5358
5262
  }
5359
5263
  this.fileUpload()?.clear();
@@ -5366,7 +5270,7 @@ class FileComponent {
5366
5270
  }
5367
5271
  cancelReplace() {
5368
5272
  if (this.debug())
5369
- this.log('❌ cancelReplace - Cancelando reemplazo');
5273
+ this.log('❌ cancelReplace');
5370
5274
  if (!this.isViewActive())
5371
5275
  return;
5372
5276
  this.isReplacing.set(false);
@@ -5380,29 +5284,87 @@ class FileComponent {
5380
5284
  }
5381
5285
  choose(event, chooseCallback) {
5382
5286
  if (this.debug())
5383
- this.log('🔍 choose - Abriendo selector de archivos');
5287
+ this.log('🔍 choose - Abriendo selector');
5384
5288
  chooseCallback();
5385
5289
  }
5290
+ // --- ControlValueAccessor ---
5291
+ writeValue(value) {
5292
+ if (!this.isViewActive())
5293
+ return;
5294
+ if (this.debug())
5295
+ this.log('✍️ writeValue', { value: value?.name || null });
5296
+ this.value = value;
5297
+ }
5298
+ registerOnChange(fn) {
5299
+ if (this.debug())
5300
+ this.log('📝 registerOnChange');
5301
+ this.onChange = fn;
5302
+ }
5303
+ registerOnTouched(fn) {
5304
+ if (this.debug())
5305
+ this.log('📝 registerOnTouched');
5306
+ this.onTouched = fn;
5307
+ }
5308
+ setDisabledState(isDisabled) {
5309
+ if (!this.isViewActive())
5310
+ return;
5311
+ if (this.debug())
5312
+ this.log('🔒 setDisabledState', { isDisabled });
5313
+ this.disabled.set(isDisabled);
5314
+ }
5315
+ // --- Helpers ---
5386
5316
  formatFileSize(bytes) {
5387
5317
  if (!bytes)
5388
5318
  return '';
5389
5319
  const sizes = ['B', 'KB', 'MB', 'GB'];
5390
5320
  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]}`;
5321
+ return `${(bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0)} ${sizes[i]}`;
5322
+ }
5323
+ showError() {
5324
+ return !!(this._control && this._control.invalid && this._control.touched);
5325
+ }
5326
+ updateControlState() {
5327
+ if (this._control) {
5328
+ this.controlValid.set(this._control.valid);
5329
+ this.controlTouched.set(this._control.touched);
5330
+ this.controlDirty.set(this._control.dirty);
5331
+ this.controlErrors.set(this._control.errors);
5332
+ }
5333
+ }
5334
+ // ✅ Método para diagnóstico (solo debug)
5335
+ diagnoseState() {
5336
+ if (this.debug()) {
5337
+ console.log('🔍 === DIAGNÓSTICO ===');
5338
+ console.log('isReplacing:', this.isReplacing());
5339
+ console.log('existingFileName:', this.existingFileName());
5340
+ console.log('hasExistingFile:', this.hasExistingFile());
5341
+ console.log('showExistingFile:', this.showExistingFile());
5342
+ console.log('isFileUploadEnabled:', this.isFileUploadEnabled());
5343
+ console.log('isReplaceButtonDisabled:', this.isReplaceButtonDisabled());
5344
+ console.log('value:', this.value);
5345
+ console.log('disabled:', this.disabled());
5346
+ console.log('isRequired:', this.isRequired());
5347
+ console.log('🔍 === FIN ===');
5348
+ }
5349
+ }
5350
+ forceReload() {
5351
+ if (this.debug()) {
5352
+ console.log('[FileComponent] 🔄 Force reload');
5353
+ if (this._control) {
5354
+ this._control.updateValueAndValidity();
5355
+ this.updateControlState();
5356
+ }
5357
+ this.cdr.detectChanges();
5358
+ }
5393
5359
  }
5394
5360
  // --- Validación ---
5395
5361
  validate(control) {
5396
- const startTime = performance.now();
5397
5362
  this._control = control;
5398
5363
  this.control = control;
5399
- if (!this.isViewActive()) {
5400
- if (this.debug())
5401
- this.log('⚠️ validate - Componente no activo');
5364
+ if (!this.isViewActive())
5402
5365
  return null;
5403
- }
5404
5366
  if (this.debug()) {
5405
- this.log('🔍 validate - Ejecutando validación', {
5367
+ this.log('🔍 validate', {
5406
5368
  isRequired: this.isRequired(),
5407
5369
  hasExistingFile: this.hasExistingFile(),
5408
5370
  isReplacing: this.isReplacing(),
@@ -5411,39 +5373,18 @@ class FileComponent {
5411
5373
  errorMessage: this.errorMessage(),
5412
5374
  });
5413
5375
  }
5414
- // Actualizar estado del control
5415
5376
  this.updateControlState();
5416
- // Si hay un mensaje de error, consideramos que la validación falla
5417
5377
  if (this.errorMessage()) {
5418
- if (this.debug())
5419
- this.log('❌ validate - Error: Hay un mensaje de error activo');
5420
5378
  return { invalidFile: true };
5421
5379
  }
5422
5380
  if (!this.isRequired()) {
5423
- if (this.debug())
5424
- this.log('✅ validate - No requerido, pasa validación');
5425
5381
  return null;
5426
5382
  }
5427
- if (this.hasExistingFile()) {
5428
- if (this.debug())
5429
- this.log('✅ validate - Archivo existente, pasa validación');
5383
+ // Pasa si hay archivo guardado O hay archivo seleccionado
5384
+ if (this.hasExistingFile() || this.value) {
5430
5385
  return null;
5431
5386
  }
5432
- if (this.isReplacing() && !this.value) {
5433
- if (this.debug())
5434
- this.log('❌ validate - Error: Reemplazando pero sin archivo');
5435
- return { required: true };
5436
- }
5437
- if (!this.hasExistingFile() && !this.value) {
5438
- if (this.debug())
5439
- this.log('❌ validate - Error: Sin archivo seleccionado');
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
- }
5446
- return null;
5387
+ return { required: true };
5447
5388
  }
5448
5389
  // --- Privados ---
5449
5390
  onChange = (_) => { };
@@ -5453,7 +5394,7 @@ class FileComponent {
5453
5394
  this.touched = true;
5454
5395
  this.onTouched();
5455
5396
  if (this.debug())
5456
- this.log('👆 markAsTouched - Componente marcado como touched');
5397
+ this.log('👆 markAsTouched');
5457
5398
  }
5458
5399
  }
5459
5400
  log(method, data) {
@@ -5471,7 +5412,7 @@ class FileComponent {
5471
5412
  useExisting: forwardRef(() => FileComponent),
5472
5413
  multi: true,
5473
5414
  },
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" }] });
5415
+ ], 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)=\"clear()\"\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 <!-- Bot\u00F3n para cancelar remplazo -->\r\n @if (isReplacing()) {\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 <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 <!-- === ESTADOS ADICIONALES PARA DEBUG === -->\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA hasExistingFile: {{ hasExistingFile() ? \"\u2705\" : \"\u274C\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA existingFileName: {{ existingFileName() || \"null\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA isFileUploadEnabled: {{ isFileUploadEnabled() ? \"\u2705\" : \"\u274C\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA showExistingFile: {{ showExistingFile() ? \"\u2705\" : \"\u274C\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA value: {{ value ? value.name : \"null\" }}\r\n </span>\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)=\"clear()\" class=\"debug-btn\" type=\"button\">\r\n \uD83E\uDDF9 Limpiar\r\n </button>\r\n <button (click)=\"diagnoseState()\" class=\"debug-btn\" type=\"button\">\r\n \uD83D\uDCCA Diagn\u00F3stico\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-weight:500;color:#1d8678;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:#22994a}.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" }] });
5475
5416
  }
5476
5417
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FileComponent, decorators: [{
5477
5418
  type: Component,
@@ -5486,7 +5427,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
5486
5427
  useExisting: forwardRef(() => FileComponent),
5487
5428
  multi: true,
5488
5429
  },
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"] }]
5430
+ ], 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)=\"clear()\"\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 <!-- Bot\u00F3n para cancelar remplazo -->\r\n @if (isReplacing()) {\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 <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 <!-- === ESTADOS ADICIONALES PARA DEBUG === -->\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA hasExistingFile: {{ hasExistingFile() ? \"\u2705\" : \"\u274C\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA existingFileName: {{ existingFileName() || \"null\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA isFileUploadEnabled: {{ isFileUploadEnabled() ? \"\u2705\" : \"\u274C\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA showExistingFile: {{ showExistingFile() ? \"\u2705\" : \"\u274C\" }}\r\n </span>\r\n <span\r\n class=\"debug-item\"\r\n style=\"background: #f0f0f0; padding: 0 6px; border-radius: 4px\"\r\n >\r\n \uD83D\uDCCA value: {{ value ? value.name : \"null\" }}\r\n </span>\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)=\"clear()\" class=\"debug-btn\" type=\"button\">\r\n \uD83E\uDDF9 Limpiar\r\n </button>\r\n <button (click)=\"diagnoseState()\" class=\"debug-btn\" type=\"button\">\r\n \uD83D\uDCCA Diagn\u00F3stico\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-weight:500;color:#1d8678;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:#22994a}.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
5431
  }], 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 }] }] } });
5491
5432
 
5492
5433
  function uid() {
@@ -5495,10 +5436,11 @@ function uid() {
5495
5436
  class JsonValuesDebujComponent {
5496
5437
  form = input(null, ...(ngDevMode ? [{ debugName: "form" }] : /* istanbul ignore next */ []));
5497
5438
  debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
5498
- debounceTime = input(300, ...(ngDevMode ? [{ debugName: "debounceTime" }] : /* istanbul ignore next */ [])); // ✅ Configurable
5439
+ debounceTime = input(300, ...(ngDevMode ? [{ debugName: "debounceTime" }] : /* istanbul ignore next */ []));
5499
5440
  debugValue = signal(null, ...(ngDevMode ? [{ debugName: "debugValue" }] : /* istanbul ignore next */ []));
5500
5441
  serializedValue = signal('', ...(ngDevMode ? [{ debugName: "serializedValue" }] : /* istanbul ignore next */ []));
5501
5442
  fileInfo = signal(null, ...(ngDevMode ? [{ debugName: "fileInfo" }] : /* istanbul ignore next */ []));
5443
+ filesSummary = signal('', ...(ngDevMode ? [{ debugName: "filesSummary" }] : /* istanbul ignore next */ []));
5502
5444
  isDevModeSignal = computed(() => isDevMode(), ...(ngDevMode ? [{ debugName: "isDevModeSignal" }] : /* istanbul ignore next */ []));
5503
5445
  isDebugEnabledSignal = computed(() => {
5504
5446
  const isDev = this.isDevModeSignal();
@@ -5521,7 +5463,6 @@ class JsonValuesDebujComponent {
5521
5463
  subscription;
5522
5464
  changesCount = 0;
5523
5465
  isInitialized = false;
5524
- lastLogTime = 0;
5525
5466
  shouldShowVisual = computed(() => {
5526
5467
  const show = this.isDevModeSignal();
5527
5468
  if (this._lastVisualState !== show) {
@@ -5594,17 +5535,17 @@ class JsonValuesDebujComponent {
5594
5535
  this.updateSerializedValue(rawValue);
5595
5536
  this.updateFileInfo(rawValue);
5596
5537
  if (this.shouldShowLogs()) {
5538
+ const filesFound = this.findFilesInObject(rawValue);
5597
5539
  console.log(`[JsonDebug:${this.id}] 📊 Procesamiento inicial`, {
5598
5540
  hasFile: !!rawValue?.file,
5599
5541
  fileType: rawValue?.file ? typeof rawValue.file : 'N/A',
5600
5542
  serializedLength: this.serializedValue().length,
5543
+ filesFound: filesFound.length,
5544
+ filesSummary: filesFound.map((f) => f.path).join(', '),
5601
5545
  });
5602
5546
  }
5603
- // ✅ Aplicar debounce y distinctUntilChanged para reducir logs
5604
5547
  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
5548
+ .pipe(debounceTime(this.debounceTime()), distinctUntilChanged((prev, curr) => {
5608
5549
  return JSON.stringify(prev) === JSON.stringify(curr);
5609
5550
  }))
5610
5551
  .subscribe(() => {
@@ -5617,10 +5558,15 @@ class JsonValuesDebujComponent {
5617
5558
  const endTime = performance.now();
5618
5559
  if (this.shouldShowLogs()) {
5619
5560
  const elapsed = endTime - startTime;
5561
+ const filesFound = this.findFilesInObject(rawValue);
5620
5562
  console.log(`[JsonDebug:${this.id}] 🔄 Cambio #${this.changesCount}`, {
5621
5563
  tiempoMs: elapsed.toFixed(2),
5622
5564
  hasFile: !!rawValue?.file,
5623
5565
  serializedLength: this.serializedValue().length,
5566
+ filesFound: filesFound.length,
5567
+ filesSummary: filesFound
5568
+ .map((f) => `${f.path} (${f.file?.name || 'sin nombre'})`)
5569
+ .join(', '),
5624
5570
  warning: elapsed > 10 ? '⚠️ Cambio lento' : '✅ OK',
5625
5571
  });
5626
5572
  if (this.changesCount % 10 === 0) {
@@ -5643,34 +5589,51 @@ class JsonValuesDebujComponent {
5643
5589
  });
5644
5590
  });
5645
5591
  }
5592
+ // ✅ Método recursivo para encontrar archivos en toda la estructura
5593
+ findFilesInObject(obj, path = 'root') {
5594
+ const files = [];
5595
+ if (!obj || typeof obj !== 'object') {
5596
+ return files;
5597
+ }
5598
+ // Verificar si el objeto actual tiene un archivo
5599
+ if (obj.file && (obj.file instanceof File || obj.file instanceof Blob)) {
5600
+ files.push({
5601
+ path: path,
5602
+ file: obj.file,
5603
+ name: obj.file.name || 'sin nombre',
5604
+ size: obj.file.size || 0,
5605
+ type: obj.file.type || 'desconocido',
5606
+ });
5607
+ }
5608
+ // Si es un array, recorrer sus elementos
5609
+ if (Array.isArray(obj)) {
5610
+ obj.forEach((item, index) => {
5611
+ const childFiles = this.findFilesInObject(item, `${path}[${index}]`);
5612
+ files.push(...childFiles);
5613
+ });
5614
+ }
5615
+ // Si es un objeto, recorrer sus propiedades
5616
+ else if (typeof obj === 'object') {
5617
+ Object.keys(obj).forEach((key) => {
5618
+ // Saltar la propiedad 'file' si ya la procesamos y es un File
5619
+ if (key === 'file' &&
5620
+ (obj.file instanceof File || obj.file instanceof Blob)) {
5621
+ return;
5622
+ }
5623
+ const childFiles = this.findFilesInObject(obj[key], `${path}.${key}`);
5624
+ files.push(...childFiles);
5625
+ });
5626
+ }
5627
+ return files;
5628
+ }
5646
5629
  updateSerializedValue(value) {
5647
5630
  if (!value) {
5648
5631
  this.serializedValue.set('');
5649
5632
  return;
5650
5633
  }
5651
5634
  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
- }
5635
+ // Clonar y procesar archivos en toda la estructura
5636
+ const copy = this.cloneWithFileProcessing(value);
5674
5637
  this.serializedValue.set(JSON.stringify(copy, null, 2));
5675
5638
  }
5676
5639
  catch (error) {
@@ -5680,41 +5643,104 @@ class JsonValuesDebujComponent {
5680
5643
  }
5681
5644
  }
5682
5645
  }
5646
+ // ✅ Clonar objeto procesando archivos recursivamente
5647
+ cloneWithFileProcessing(obj) {
5648
+ if (!obj || typeof obj !== 'object') {
5649
+ return obj;
5650
+ }
5651
+ // Si es un File o Blob, extraer sus propiedades
5652
+ if (obj instanceof File || obj instanceof Blob) {
5653
+ const fileProps = {
5654
+ __type: obj instanceof File ? 'File' : 'Blob',
5655
+ };
5656
+ if ('name' in obj && obj.name !== undefined) {
5657
+ fileProps.name = obj.name;
5658
+ }
5659
+ if ('size' in obj && obj.size !== undefined) {
5660
+ fileProps.size = obj.size;
5661
+ }
5662
+ if ('type' in obj && obj.type !== undefined) {
5663
+ fileProps.type = obj.type;
5664
+ }
5665
+ if ('lastModified' in obj && obj.lastModified !== undefined) {
5666
+ fileProps.lastModified = obj.lastModified;
5667
+ }
5668
+ return fileProps;
5669
+ }
5670
+ // Si es un array, procesar cada elemento
5671
+ if (Array.isArray(obj)) {
5672
+ return obj.map((item) => this.cloneWithFileProcessing(item));
5673
+ }
5674
+ // Si es un objeto, procesar cada propiedad
5675
+ const result = {};
5676
+ Object.keys(obj).forEach((key) => {
5677
+ // Si la propiedad es 'file' y es un File/Blob, procesarla
5678
+ if (key === 'file' &&
5679
+ (obj[key] instanceof File || obj[key] instanceof Blob)) {
5680
+ result[key] = this.cloneWithFileProcessing(obj[key]);
5681
+ }
5682
+ else {
5683
+ result[key] = this.cloneWithFileProcessing(obj[key]);
5684
+ }
5685
+ });
5686
+ return result;
5687
+ }
5683
5688
  updateFileInfo(value) {
5684
- if (!value || !value.file) {
5689
+ if (!value) {
5685
5690
  this.fileInfo.set(null);
5691
+ this.filesSummary.set('');
5686
5692
  return;
5687
5693
  }
5688
- const file = value.file;
5694
+ // Buscar todos los archivos en la estructura
5695
+ const files = this.findFilesInObject(value);
5696
+ if (files.length === 0) {
5697
+ this.fileInfo.set({
5698
+ exists: false,
5699
+ isEmpty: true,
5700
+ message: 'No hay archivos seleccionados',
5701
+ count: 0,
5702
+ });
5703
+ this.filesSummary.set('📭 Sin archivos');
5704
+ return;
5705
+ }
5706
+ // ✅ Resumir información de archivos
5707
+ const summary = files
5708
+ .map((f) => {
5709
+ const name = f.file?.name || 'sin nombre';
5710
+ const size = f.file?.size || 0;
5711
+ const sizeStr = size > 0 ? `${(size / 1024).toFixed(1)} KB` : '0 KB';
5712
+ return `${name} (${sizeStr}) en ${f.path}`;
5713
+ })
5714
+ .join('; ');
5715
+ this.filesSummary.set(`📁 ${files.length} archivo(s): ${summary}`);
5716
+ // ✅ Información detallada
5689
5717
  const info = {
5690
5718
  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,
5719
+ count: files.length,
5720
+ files: files.map((f) => ({
5721
+ path: f.path,
5722
+ name: f.name,
5723
+ size: f.size,
5724
+ type: f.type,
5725
+ })),
5726
+ summary: summary,
5697
5727
  };
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
5728
  this.fileInfo.set(info);
5711
5729
  }
5712
- hasFile() {
5713
- return !!this.fileInfo();
5730
+ hasFileProperty() {
5731
+ const value = this.debugValue();
5732
+ if (!value)
5733
+ return false;
5734
+ // ✅ Buscar archivos en toda la estructura
5735
+ const files = this.findFilesInObject(value);
5736
+ return files.length > 0;
5714
5737
  }
5715
5738
  getFileInfo() {
5716
5739
  return this.fileInfo();
5717
5740
  }
5741
+ getFilesSummary() {
5742
+ return this.filesSummary();
5743
+ }
5718
5744
  ngOnDestroy() {
5719
5745
  if (this.shouldShowLogs()) {
5720
5746
  console.log(`[JsonDebug:${this.id}] 💀 Destroy`, {
@@ -5728,11 +5754,11 @@ class JsonValuesDebujComponent {
5728
5754
  }
5729
5755
  }
5730
5756
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonValuesDebujComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
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 });
5757
+ 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 <!-- \u2705 Informaci\u00F3n de archivos encontrados en toda la estructura -->\r\n @if (hasFileProperty()) {\r\n <div class=\"file-info-box\">\r\n <div class=\"file-info-header\">\r\n \uD83D\uDCC1 Archivos encontrados ({{ getFileInfo()?.count || 0 }})\r\n </div>\r\n <div class=\"file-info-grid\">\r\n @for (file of getFileInfo()?.files || []; track file.path) {\r\n <div class=\"file-info-item file-item\">\r\n <div class=\"file-path\">\uD83D\uDCC4 {{ file.path }}</div>\r\n <div class=\"file-details\">\r\n <span class=\"label\">Nombre:</span>\r\n <span class=\"value\">{{ file.name }}</span>\r\n </div>\r\n <div class=\"file-details\">\r\n <span class=\"label\">Tama\u00F1o:</span>\r\n <span class=\"value\"\r\n >{{ file.size / 1024 | number: \"1.1-1\" }} KB</span\r\n >\r\n </div>\r\n <div class=\"file-details\">\r\n <span class=\"label\">Tipo:</span>\r\n <span class=\"value\">{{ file.type || \"Desconocido\" }}</span>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"file-info-box file-empty\">\r\n <div class=\"file-info-header\">\uD83D\uDCED Sin archivos seleccionados</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: DecimalPipe, name: "number" }], encapsulation: i0.ViewEncapsulation.None });
5732
5758
  }
5733
5759
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JsonValuesDebujComponent, decorators: [{
5734
5760
  type: Component,
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"] }]
5761
+ 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 <!-- \u2705 Informaci\u00F3n de archivos encontrados en toda la estructura -->\r\n @if (hasFileProperty()) {\r\n <div class=\"file-info-box\">\r\n <div class=\"file-info-header\">\r\n \uD83D\uDCC1 Archivos encontrados ({{ getFileInfo()?.count || 0 }})\r\n </div>\r\n <div class=\"file-info-grid\">\r\n @for (file of getFileInfo()?.files || []; track file.path) {\r\n <div class=\"file-info-item file-item\">\r\n <div class=\"file-path\">\uD83D\uDCC4 {{ file.path }}</div>\r\n <div class=\"file-details\">\r\n <span class=\"label\">Nombre:</span>\r\n <span class=\"value\">{{ file.name }}</span>\r\n </div>\r\n <div class=\"file-details\">\r\n <span class=\"label\">Tama\u00F1o:</span>\r\n <span class=\"value\"\r\n >{{ file.size / 1024 | number: \"1.1-1\" }} KB</span\r\n >\r\n </div>\r\n <div class=\"file-details\">\r\n <span class=\"label\">Tipo:</span>\r\n <span class=\"value\">{{ file.type || \"Desconocido\" }}</span>\r\n </div>\r\n </div>\r\n }\r\n </div>\r\n </div>\r\n } @else {\r\n <div class=\"file-info-box file-empty\">\r\n <div class=\"file-info-header\">\uD83D\uDCED Sin archivos seleccionados</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
5762
  }], 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 }] }] } });
5737
5763
 
5738
5764
  class IcoLabel {
@@ -12575,7 +12601,7 @@ class FormPreviewService {
12575
12601
  }
12576
12602
  const showEmpty = options?.showEmpty ?? false;
12577
12603
  const compact = options?.compact ?? true;
12578
- const title = options?.title || '📋 Datos a guardar';
12604
+ const title = options?.title || '📋 Datos del registro';
12579
12605
  const cells = [];
12580
12606
  const propiedadesNoEncontradas = [];
12581
12607
  for (const field of fields) {
@@ -12845,7 +12871,7 @@ class TablePreviewService {
12845
12871
  buildSingleRowSummaryHtml(row, columns, options) {
12846
12872
  const debug = !this.isProduction && (options?.debug ?? false);
12847
12873
  const showRowId = options?.showRowId ?? false;
12848
- const title = options?.title || '🗑️ Datos a eliminar';
12874
+ const title = options?.title || '📋 Datos del registro';
12849
12875
  const icon = options?.icon || '';
12850
12876
  if (debug) {
12851
12877
  this.debugInputData(row, columns, options);