ngx-dsxlibrary 2.21.67 → 2.21.68

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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, forwardRef, Component, EventEmitter, effect, Output, viewChild, signal, Input, model, inject, computed, Injectable, isDevMode, InjectionToken, output, HostBinding, ChangeDetectorRef, untracked, Pipe, ViewEncapsulation, HostListener, Directive, NgZone, ElementRef, NgModule, Injector } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
- import { FormsModule, NG_VALUE_ACCESSOR, FormControl, NG_VALIDATORS, FormBuilder, Validators, ReactiveFormsModule, NgControl, FormGroup } from '@angular/forms';
4
+ import { FormsModule, NG_VALUE_ACCESSOR, AbstractControl, FormControl, NG_VALIDATORS, FormBuilder, Validators, ReactiveFormsModule, NgControl, FormGroup } from '@angular/forms';
5
5
  import * as i2 from 'primeng/togglebutton';
6
6
  import { ToggleButtonModule } from 'primeng/togglebutton';
7
7
  import * as i1$1 from '@angular/common';
@@ -1079,7 +1079,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
1079
1079
  args: [{ selector: 'icon-dsx', imports: [AsyncPipe], template: "<span class=\"dsx-icon\" [innerHTML]=\"svg$ | async\"></span>\r\n", styles: [":host{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;width:1em;height:1em;font-size:24px;line-height:1;flex-shrink:0}.dsx-icon{display:inline-flex;align-items:center;justify-content:center;width:100%;height:100%}:host ::ng-deep .dsx-icon svg{width:100%;height:100%;fill:currentColor;shape-rendering:geometricPrecision;stroke:currentColor;stroke-width:4px;stroke-linejoin:round;stroke-linecap:round;overflow:visible;transition:fill .2s ease,stroke .2s ease}\n"] }]
1080
1080
  }], ctorParameters: () => [{ type: IconDsxService }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], style: [{ type: i0.Input, args: [{ isSignal: true, alias: "style", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
1081
1081
 
1082
- // app-message-error.component.ts - Versión corregida
1083
1082
  class AppMessageErrorComponent {
1084
1083
  elementRef;
1085
1084
  renderer;
@@ -1128,7 +1127,6 @@ class AppMessageErrorComponent {
1128
1127
  this.renderer = renderer;
1129
1128
  }
1130
1129
  ngAfterViewInit() {
1131
- // Intentar encontrar el floatLabel padre
1132
1130
  this.floatLabelElement = this.elementRef.nativeElement.closest('.p-floatlabel');
1133
1131
  if (this.floatLabelElement) {
1134
1132
  this.previousInlineMarginBottom =
@@ -1142,7 +1140,6 @@ class AppMessageErrorComponent {
1142
1140
  console.log('[ERROR-COMPONENT] No hay FloatLabel, modo estático');
1143
1141
  }
1144
1142
  }
1145
- // Observador de visibilidad
1146
1143
  if (typeof window !== 'undefined' && 'IntersectionObserver' in window) {
1147
1144
  this.observer = new IntersectionObserver((entries) => {
1148
1145
  entries.forEach((entry) => {
@@ -1191,7 +1188,6 @@ class AppMessageErrorComponent {
1191
1188
  if (this.observer) {
1192
1189
  this.observer.disconnect();
1193
1190
  }
1194
- // Restaurar margen solo si existe floatLabelElement
1195
1191
  if (this.floatLabelElement) {
1196
1192
  if (this.previousInlineMarginBottom) {
1197
1193
  this.renderer.setStyle(this.floatLabelElement, 'margin-bottom', this.previousInlineMarginBottom);
@@ -1202,11 +1198,7 @@ class AppMessageErrorComponent {
1202
1198
  }
1203
1199
  }
1204
1200
  syncFloatLabelSpacing() {
1205
- // Si no hay floatLabel, no hacemos nada (el CSS maneja el espaciado)
1206
- if (!this.floatLabelElement) {
1207
- return;
1208
- }
1209
- if (!this.isElementAttachedAndVisible)
1201
+ if (!this.floatLabelElement || !this.isElementAttachedAndVisible)
1210
1202
  return;
1211
1203
  const isVisible = this.isControlErrorVisible();
1212
1204
  const desiredSpace = this.getFloatLabelErrorSpace();
@@ -1228,11 +1220,9 @@ class AppMessageErrorComponent {
1228
1220
  this.renderer.removeStyle(this.floatLabelElement, 'margin-bottom');
1229
1221
  }
1230
1222
  isControlErrorVisible() {
1231
- // ✅ La condición base es la misma para ambos contextos
1232
1223
  if (!this.control || !this.control.invalid) {
1233
1224
  return false;
1234
1225
  }
1235
- // Solo mostrar si el usuario ha interactuado
1236
1226
  return !!(this.control.touched || this.control.dirty);
1237
1227
  }
1238
1228
  getFloatLabelErrorSpace() {
@@ -1258,11 +1248,11 @@ class AppMessageErrorComponent {
1258
1248
  return num % 1 === 0 ? String(num) : parseFloat(num.toFixed(2)).toString();
1259
1249
  }
1260
1250
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AppMessageErrorComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
1261
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AppMessageErrorComponent, isStandalone: true, selector: "app-message-error", inputs: { control: "control", form: "form", debugMode: "debugMode" }, ngImport: i0, template: "<!-- app-message-error.component.html -->\r\n<div class=\"dsx-error-slot\" [class.is-visible]=\"isControlErrorVisible()\">\r\n <div class=\"dsx-error-message\">\r\n <div class=\"dsx-error-message-content\">\r\n <!-- \uD83C\uDFAF INYECTA ESTA L\u00CDNEA TEMPORAL DE DEPURACI\u00D3N AQU\u00CD -->\r\n <!-- <span\r\n style=\"\r\n color: blue;\r\n font-size: 10px;\r\n display: block;\r\n word-break: break-all;\r\n \"\r\n >\r\n DEBUG ERRORES: {{ control?.errors | json }}\r\n </span> -->\r\n\r\n <icon-dsx name=\"warning\"></icon-dsx>\r\n\r\n @if (control?.errors?.[\"required\"]) {\r\n El campo <strong>es requerido.</strong>\r\n } @else if (control?.errors?.[\"invalidNIT\"]) {\r\n <strong>{{ control?.errors?.[\"invalidNIT\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidCUI\"]) {\r\n <strong>{{ control?.errors?.[\"invalidCUI\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDateRange\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDateRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"dateNotRange\"]) {\r\n <strong>{{ control?.errors?.[\"dateNotRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDate\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDate\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minimumAge\"]) {\r\n <strong>{{ control?.errors?.[\"minimumAge\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minlength\"]) {\r\n Debe tener al menos\r\n <strong>{{ control?.errors?.[\"minlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"maxlength\"]) {\r\n Debe tener como m\u00E1ximo\r\n <strong>{{ control?.errors?.[\"maxlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"min\"]) {\r\n El valor m\u00EDnimo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"min\"]?.min) }}</strong>\r\n } @else if (control?.errors?.[\"max\"]) {\r\n El valor m\u00E1ximo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"max\"]?.max) }}</strong>\r\n } @else if (control?.errors?.[\"email\"]) {\r\n Debe ser una direcci\u00F3n de correo v\u00E1lida.\r\n } @else if (control?.errors?.[\"pattern\"]) {\r\n El campo no tiene el formato requerido.\r\n } @else if (control?.errors?.[\"alreadyValueExists\"]) {\r\n <strong>{{ control?.errors?.[\"alreadyValueExists\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidTemplateVariables\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateVariables\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"invalidTemplateStructure\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateStructure\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"similitudDetectada\"]) {\r\n <!-- \uD83C\uDFAF Estructuraci\u00F3n del mensaje con el nombre aislado en negrita -->\r\n Ya existe un registro muy similar en el sistema:\r\n <strong\r\n >\"{{ control?.errors?.[\"similitudDetectada\"]?.sugerencia }}\"</strong\r\n >.\r\n } @else {\r\n Existe un error a\u00FAn no identificado.\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- Mensaje para formulario en general (Cambiado form.touched por form.dirty para evitar falsos positivos al clicar el fondo) -->\r\n@if (form?.invalid && form?.dirty) {\r\n <div class=\"mt-2 mb-2\">\r\n @if (this.form?.errors?.[\"atLeastOneRequired\"]) {\r\n <p-tag severity=\"danger\" [rounded]=\"true\">\r\n {{ form?.getError(\"atLeastOneRequired\")?.message }}\r\n </p-tag>\r\n }\r\n </div>\r\n}\r\n", styles: [".dsx-error-slot{display:block;max-height:0;overflow:hidden;opacity:0;transform:translateY(-2px);transition:opacity .15s ease,transform .15s ease}.dsx-error-slot.is-visible{max-height:80px;opacity:1;transform:translateY(0)}.dsx-error-message{display:block;width:100%}.dsx-error-message-content{display:flex;align-items:center;gap:.5rem;width:100%;box-sizing:border-box;padding:.4rem .7rem;font-size:.85rem;background:var(--dsx-error-bg, #fff2f0)!important;color:var(--dsx-error-color, #b42318)!important;border-left:3px solid var(--dsx-error-color, #b42318)!important;border-radius:.375rem;box-shadow:0 1px 2px #0000000f;line-height:1.4}.dsx-error-icon{flex:0 0 auto;line-height:1;display:inline-flex;align-items:center;justify-content:center}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:var(--dsx-error-color, #ff5a4e)!important;fill:var(--dsx-error-color, #e45036)!important}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:10;pointer-events:none}:host-context(.p-floatlabel) .dsx-error-slot{margin-top:.1rem}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.1rem}:host-context(.p-floatlabel) .dsx-error-message-content{padding:.35rem .7rem .35rem .5rem}:host-context(:not(.p-floatlabel)){display:block;width:100%}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.2rem;margin-bottom:0}:host-context(:not(.p-floatlabel)) .dsx-error-message-content{padding:.35rem .7rem}@media(max-width:640px){.dsx-error-message-content{padding:.3rem .6rem;font-size:.8rem;gap:.4rem}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.05rem}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.15rem}}@media(prefers-color-scheme:dark){.dsx-error-message-content{background:#2d1a1a;border-left-color:#ef4444;color:#fca5a5}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:#ef4444!important;fill:#ef4444!important}}\n"], dependencies: [{ kind: "ngmodule", type: TagModule }, { kind: "component", type: i1$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: IconDsxComponent, selector: "icon-dsx", inputs: ["name", "style", "debug"] }] });
1251
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AppMessageErrorComponent, isStandalone: true, selector: "app-message-error", inputs: { control: "control", form: "form", debugMode: "debugMode" }, ngImport: i0, template: "<div class=\"dsx-error-slot\" [class.is-visible]=\"isControlErrorVisible()\">\r\n <div class=\"dsx-error-message\">\r\n <div class=\"dsx-error-message-content\">\r\n <icon-dsx name=\"warning\"></icon-dsx>\r\n\r\n @if (control?.errors?.[\"required\"]) {\r\n El campo <strong>es requerido.</strong>\r\n } @else if (control?.errors?.[\"invalidNIT\"]) {\r\n <strong>{{ control?.errors?.[\"invalidNIT\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidCUI\"]) {\r\n <strong>{{ control?.errors?.[\"invalidCUI\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDateRange\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDateRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"dateNotRange\"]) {\r\n <strong>{{ control?.errors?.[\"dateNotRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDate\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDate\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minimumAge\"]) {\r\n <strong>{{ control?.errors?.[\"minimumAge\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minlength\"]) {\r\n Debe tener al menos\r\n <strong>{{ control?.errors?.[\"minlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"maxlength\"]) {\r\n Debe tener como m\u00E1ximo\r\n <strong>{{ control?.errors?.[\"maxlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"min\"]) {\r\n El valor m\u00EDnimo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"min\"]?.min) }}</strong>\r\n } @else if (control?.errors?.[\"max\"]) {\r\n El valor m\u00E1ximo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"max\"]?.max) }}</strong>\r\n } @else if (control?.errors?.[\"email\"]) {\r\n Debe ser una direcci\u00F3n de correo v\u00E1lida.\r\n } @else if (control?.errors?.[\"pattern\"]) {\r\n El campo no tiene el formato requerido.\r\n } @else if (control?.errors?.[\"alreadyValueExists\"]) {\r\n <strong>{{ control?.errors?.[\"alreadyValueExists\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidTemplateVariables\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateVariables\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"invalidTemplateStructure\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateStructure\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"similitudDetectada\"]) {\r\n Ya existe un registro muy similar en el sistema:\r\n <strong\r\n >\"{{ control?.errors?.[\"similitudDetectada\"]?.sugerencia }}\"</strong\r\n >.\r\n } @else {\r\n Existe un error a\u00FAn no identificado.\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- Mensaje para formulario en general -->\r\n@if (form?.invalid && form?.dirty) {\r\n <div class=\"mt-2 mb-2\">\r\n @if (this.form?.errors?.[\"atLeastOneRequired\"]) {\r\n <p-tag severity=\"danger\" [rounded]=\"true\">\r\n {{ form?.getError(\"atLeastOneRequired\")?.message }}\r\n </p-tag>\r\n }\r\n </div>\r\n}\r\n", styles: [".dsx-error-slot{display:block;max-height:0;overflow:hidden;opacity:0;transform:translateY(-4px) scale(.98);transition:max-height .3s cubic-bezier(.4,0,.2,1),opacity .25s cubic-bezier(.4,0,.2,1),transform .25s cubic-bezier(.4,0,.2,1),margin .25s cubic-bezier(.4,0,.2,1)}.dsx-error-slot.is-visible{max-height:60px;opacity:1;transform:translateY(0) scale(1)}.dsx-error-message{display:block;width:100%}.dsx-error-message-content{display:flex;align-items:center;gap:.4rem;width:100%;box-sizing:border-box;padding:.25rem .6rem;font-size:.82rem;background:var(--dsx-error-bg, #fff2f0)!important;color:var(--dsx-error-color, #b42318)!important;border-left:3px solid var(--dsx-error-color, #b42318)!important;border-radius:.3rem;box-shadow:0 1px 2px #0000000f;line-height:1.3;animation:errorSlideIn .25s cubic-bezier(.4,0,.2,1)}.dsx-error-icon{flex:0 0 auto;line-height:1;display:inline-flex;align-items:center;justify-content:center}.dsx-error-icon icon-dsx{transform:scale(.85);transition:transform .2s ease}.dsx-error-slot.is-visible .dsx-error-icon icon-dsx{transform:scale(1)}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:var(--dsx-error-color, #ff5a4e)!important;fill:var(--dsx-error-color, #e45036)!important}@keyframes errorSlideIn{0%{opacity:0;transform:translateY(-6px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes errorPulse{0%{box-shadow:0 1px 2px #0000000f}50%{box-shadow:0 2px 8px #b4231826}to{box-shadow:0 1px 2px #0000000f}}.dsx-error-slot.is-visible .dsx-error-message-content{animation:errorSlideIn .25s cubic-bezier(.4,0,.2,1),errorPulse .4s ease .15s}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:10;pointer-events:none}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.08rem}:host-context(.p-floatlabel) .dsx-error-message-content{padding:.2rem .6rem .2rem .4rem}:host-context(:not(.p-floatlabel)){display:block;width:100%}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.15rem;margin-bottom:0}:host-context(:not(.p-floatlabel)) .dsx-error-message-content{padding:.2rem .6rem}@media(max-width:640px){.dsx-error-message-content{padding:.15rem .5rem;font-size:.75rem;gap:.3rem}.dsx-error-icon icon-dsx{transform:scale(.8)}.dsx-error-slot{transition:max-height .25s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1),transform .2s cubic-bezier(.4,0,.2,1)}@keyframes errorSlideIn{0%{opacity:0;transform:translateY(-4px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.05rem}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.1rem}}@media(prefers-color-scheme:dark){.dsx-error-message-content{background:#2d1a1a;border-left-color:#ef4444;color:#fca5a5}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:#ef4444!important;fill:#ef4444!important}@keyframes errorPulse{0%{box-shadow:0 1px 2px #0000004d}50%{box-shadow:0 2px 8px #ef444433}to{box-shadow:0 1px 2px #0000004d}}}\n"], dependencies: [{ kind: "ngmodule", type: TagModule }, { kind: "component", type: i1$2.Tag, selector: "p-tag", inputs: ["styleClass", "severity", "value", "icon", "rounded"] }, { kind: "component", type: IconDsxComponent, selector: "icon-dsx", inputs: ["name", "style", "debug"] }] });
1262
1252
  }
1263
1253
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AppMessageErrorComponent, decorators: [{
1264
1254
  type: Component,
1265
- args: [{ selector: 'app-message-error', standalone: true, imports: [TagModule, IconDsxComponent], template: "<!-- app-message-error.component.html -->\r\n<div class=\"dsx-error-slot\" [class.is-visible]=\"isControlErrorVisible()\">\r\n <div class=\"dsx-error-message\">\r\n <div class=\"dsx-error-message-content\">\r\n <!-- \uD83C\uDFAF INYECTA ESTA L\u00CDNEA TEMPORAL DE DEPURACI\u00D3N AQU\u00CD -->\r\n <!-- <span\r\n style=\"\r\n color: blue;\r\n font-size: 10px;\r\n display: block;\r\n word-break: break-all;\r\n \"\r\n >\r\n DEBUG ERRORES: {{ control?.errors | json }}\r\n </span> -->\r\n\r\n <icon-dsx name=\"warning\"></icon-dsx>\r\n\r\n @if (control?.errors?.[\"required\"]) {\r\n El campo <strong>es requerido.</strong>\r\n } @else if (control?.errors?.[\"invalidNIT\"]) {\r\n <strong>{{ control?.errors?.[\"invalidNIT\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidCUI\"]) {\r\n <strong>{{ control?.errors?.[\"invalidCUI\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDateRange\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDateRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"dateNotRange\"]) {\r\n <strong>{{ control?.errors?.[\"dateNotRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDate\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDate\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minimumAge\"]) {\r\n <strong>{{ control?.errors?.[\"minimumAge\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minlength\"]) {\r\n Debe tener al menos\r\n <strong>{{ control?.errors?.[\"minlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"maxlength\"]) {\r\n Debe tener como m\u00E1ximo\r\n <strong>{{ control?.errors?.[\"maxlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"min\"]) {\r\n El valor m\u00EDnimo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"min\"]?.min) }}</strong>\r\n } @else if (control?.errors?.[\"max\"]) {\r\n El valor m\u00E1ximo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"max\"]?.max) }}</strong>\r\n } @else if (control?.errors?.[\"email\"]) {\r\n Debe ser una direcci\u00F3n de correo v\u00E1lida.\r\n } @else if (control?.errors?.[\"pattern\"]) {\r\n El campo no tiene el formato requerido.\r\n } @else if (control?.errors?.[\"alreadyValueExists\"]) {\r\n <strong>{{ control?.errors?.[\"alreadyValueExists\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidTemplateVariables\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateVariables\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"invalidTemplateStructure\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateStructure\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"similitudDetectada\"]) {\r\n <!-- \uD83C\uDFAF Estructuraci\u00F3n del mensaje con el nombre aislado en negrita -->\r\n Ya existe un registro muy similar en el sistema:\r\n <strong\r\n >\"{{ control?.errors?.[\"similitudDetectada\"]?.sugerencia }}\"</strong\r\n >.\r\n } @else {\r\n Existe un error a\u00FAn no identificado.\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- Mensaje para formulario en general (Cambiado form.touched por form.dirty para evitar falsos positivos al clicar el fondo) -->\r\n@if (form?.invalid && form?.dirty) {\r\n <div class=\"mt-2 mb-2\">\r\n @if (this.form?.errors?.[\"atLeastOneRequired\"]) {\r\n <p-tag severity=\"danger\" [rounded]=\"true\">\r\n {{ form?.getError(\"atLeastOneRequired\")?.message }}\r\n </p-tag>\r\n }\r\n </div>\r\n}\r\n", styles: [".dsx-error-slot{display:block;max-height:0;overflow:hidden;opacity:0;transform:translateY(-2px);transition:opacity .15s ease,transform .15s ease}.dsx-error-slot.is-visible{max-height:80px;opacity:1;transform:translateY(0)}.dsx-error-message{display:block;width:100%}.dsx-error-message-content{display:flex;align-items:center;gap:.5rem;width:100%;box-sizing:border-box;padding:.4rem .7rem;font-size:.85rem;background:var(--dsx-error-bg, #fff2f0)!important;color:var(--dsx-error-color, #b42318)!important;border-left:3px solid var(--dsx-error-color, #b42318)!important;border-radius:.375rem;box-shadow:0 1px 2px #0000000f;line-height:1.4}.dsx-error-icon{flex:0 0 auto;line-height:1;display:inline-flex;align-items:center;justify-content:center}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:var(--dsx-error-color, #ff5a4e)!important;fill:var(--dsx-error-color, #e45036)!important}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:10;pointer-events:none}:host-context(.p-floatlabel) .dsx-error-slot{margin-top:.1rem}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.1rem}:host-context(.p-floatlabel) .dsx-error-message-content{padding:.35rem .7rem .35rem .5rem}:host-context(:not(.p-floatlabel)){display:block;width:100%}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.2rem;margin-bottom:0}:host-context(:not(.p-floatlabel)) .dsx-error-message-content{padding:.35rem .7rem}@media(max-width:640px){.dsx-error-message-content{padding:.3rem .6rem;font-size:.8rem;gap:.4rem}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.05rem}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.15rem}}@media(prefers-color-scheme:dark){.dsx-error-message-content{background:#2d1a1a;border-left-color:#ef4444;color:#fca5a5}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:#ef4444!important;fill:#ef4444!important}}\n"] }]
1255
+ args: [{ selector: 'app-message-error', standalone: true, imports: [TagModule, IconDsxComponent], template: "<div class=\"dsx-error-slot\" [class.is-visible]=\"isControlErrorVisible()\">\r\n <div class=\"dsx-error-message\">\r\n <div class=\"dsx-error-message-content\">\r\n <icon-dsx name=\"warning\"></icon-dsx>\r\n\r\n @if (control?.errors?.[\"required\"]) {\r\n El campo <strong>es requerido.</strong>\r\n } @else if (control?.errors?.[\"invalidNIT\"]) {\r\n <strong>{{ control?.errors?.[\"invalidNIT\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidCUI\"]) {\r\n <strong>{{ control?.errors?.[\"invalidCUI\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDateRange\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDateRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"dateNotRange\"]) {\r\n <strong>{{ control?.errors?.[\"dateNotRange\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidDate\"]) {\r\n <strong>{{ control?.errors?.[\"invalidDate\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minimumAge\"]) {\r\n <strong>{{ control?.errors?.[\"minimumAge\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"minlength\"]) {\r\n Debe tener al menos\r\n <strong>{{ control?.errors?.[\"minlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"maxlength\"]) {\r\n Debe tener como m\u00E1ximo\r\n <strong>{{ control?.errors?.[\"maxlength\"]?.requiredLength }}</strong>\r\n caracteres.\r\n } @else if (control?.errors?.[\"min\"]) {\r\n El valor m\u00EDnimo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"min\"]?.min) }}</strong>\r\n } @else if (control?.errors?.[\"max\"]) {\r\n El valor m\u00E1ximo permitido es\r\n <strong>{{ formatNumber(control?.errors?.[\"max\"]?.max) }}</strong>\r\n } @else if (control?.errors?.[\"email\"]) {\r\n Debe ser una direcci\u00F3n de correo v\u00E1lida.\r\n } @else if (control?.errors?.[\"pattern\"]) {\r\n El campo no tiene el formato requerido.\r\n } @else if (control?.errors?.[\"alreadyValueExists\"]) {\r\n <strong>{{ control?.errors?.[\"alreadyValueExists\"]?.message }}</strong>\r\n } @else if (control?.errors?.[\"invalidTemplateVariables\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateVariables\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateVariables\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"invalidTemplateStructure\"]) {\r\n <div class=\"dsx-template-error\">\r\n <strong>\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.message }}\r\n </strong>\r\n @if (control?.errors?.[\"invalidTemplateStructure\"]?.details?.length) {\r\n <span class=\"dsx-template-error-detail\">\r\n {{ control?.errors?.[\"invalidTemplateStructure\"]?.details?.[0] }}\r\n </span>\r\n }\r\n </div>\r\n } @else if (control?.errors?.[\"similitudDetectada\"]) {\r\n Ya existe un registro muy similar en el sistema:\r\n <strong\r\n >\"{{ control?.errors?.[\"similitudDetectada\"]?.sugerencia }}\"</strong\r\n >.\r\n } @else {\r\n Existe un error a\u00FAn no identificado.\r\n }\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<!-- Mensaje para formulario en general -->\r\n@if (form?.invalid && form?.dirty) {\r\n <div class=\"mt-2 mb-2\">\r\n @if (this.form?.errors?.[\"atLeastOneRequired\"]) {\r\n <p-tag severity=\"danger\" [rounded]=\"true\">\r\n {{ form?.getError(\"atLeastOneRequired\")?.message }}\r\n </p-tag>\r\n }\r\n </div>\r\n}\r\n", styles: [".dsx-error-slot{display:block;max-height:0;overflow:hidden;opacity:0;transform:translateY(-4px) scale(.98);transition:max-height .3s cubic-bezier(.4,0,.2,1),opacity .25s cubic-bezier(.4,0,.2,1),transform .25s cubic-bezier(.4,0,.2,1),margin .25s cubic-bezier(.4,0,.2,1)}.dsx-error-slot.is-visible{max-height:60px;opacity:1;transform:translateY(0) scale(1)}.dsx-error-message{display:block;width:100%}.dsx-error-message-content{display:flex;align-items:center;gap:.4rem;width:100%;box-sizing:border-box;padding:.25rem .6rem;font-size:.82rem;background:var(--dsx-error-bg, #fff2f0)!important;color:var(--dsx-error-color, #b42318)!important;border-left:3px solid var(--dsx-error-color, #b42318)!important;border-radius:.3rem;box-shadow:0 1px 2px #0000000f;line-height:1.3;animation:errorSlideIn .25s cubic-bezier(.4,0,.2,1)}.dsx-error-icon{flex:0 0 auto;line-height:1;display:inline-flex;align-items:center;justify-content:center}.dsx-error-icon icon-dsx{transform:scale(.85);transition:transform .2s ease}.dsx-error-slot.is-visible .dsx-error-icon icon-dsx{transform:scale(1)}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:var(--dsx-error-color, #ff5a4e)!important;fill:var(--dsx-error-color, #e45036)!important}@keyframes errorSlideIn{0%{opacity:0;transform:translateY(-6px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}@keyframes errorPulse{0%{box-shadow:0 1px 2px #0000000f}50%{box-shadow:0 2px 8px #b4231826}to{box-shadow:0 1px 2px #0000000f}}.dsx-error-slot.is-visible .dsx-error-message-content{animation:errorSlideIn .25s cubic-bezier(.4,0,.2,1),errorPulse .4s ease .15s}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:10;pointer-events:none}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.08rem}:host-context(.p-floatlabel) .dsx-error-message-content{padding:.2rem .6rem .2rem .4rem}:host-context(:not(.p-floatlabel)){display:block;width:100%}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.15rem;margin-bottom:0}:host-context(:not(.p-floatlabel)) .dsx-error-message-content{padding:.2rem .6rem}@media(max-width:640px){.dsx-error-message-content{padding:.15rem .5rem;font-size:.75rem;gap:.3rem}.dsx-error-icon icon-dsx{transform:scale(.8)}.dsx-error-slot{transition:max-height .25s cubic-bezier(.4,0,.2,1),opacity .2s cubic-bezier(.4,0,.2,1),transform .2s cubic-bezier(.4,0,.2,1)}@keyframes errorSlideIn{0%{opacity:0;transform:translateY(-4px) scale(.97)}to{opacity:1;transform:translateY(0) scale(1)}}:host-context(.p-floatlabel) .dsx-error-slot.is-visible{margin-top:.05rem}:host-context(:not(.p-floatlabel)) .dsx-error-slot.is-visible{margin-top:.1rem}}@media(prefers-color-scheme:dark){.dsx-error-message-content{background:#2d1a1a;border-left-color:#ef4444;color:#fca5a5}.dsx-error-message-content icon-dsx,.dsx-error-message-content icon-dsx svg,.dsx-error-message-content icon-dsx svg path{color:#ef4444!important;fill:#ef4444!important}@keyframes errorPulse{0%{box-shadow:0 1px 2px #0000004d}50%{box-shadow:0 2px 8px #ef444433}to{box-shadow:0 1px 2px #0000004d}}}\n"] }]
1266
1256
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { control: [{
1267
1257
  type: Input
1268
1258
  }], form: [{
@@ -2671,11 +2661,18 @@ class UtilityAddService {
2671
2661
  const resolvedOptions = Array.isArray(groupValidatorsOrOptions)
2672
2662
  ? options
2673
2663
  : (groupValidatorsOrOptions ?? options);
2664
+ // IMPORTANTE: Cambiamos el tipado a AbstractControl para permitir meter FormArrays o FormGroups
2674
2665
  const formGroup = {};
2675
- // Crear controles de formulario individuales
2666
+ // Crear controles de formulario individuales o inyectar estructuras abstractas
2676
2667
  for (const key in config) {
2677
2668
  if (Object.prototype.hasOwnProperty.call(config, key)) {
2678
2669
  const fieldConfig = config[key];
2670
+ // ¡EL CAMBIO MÁGICO!: Si ya pasaste un FormArray o FormGroup pre-construido, lo asignamos directo
2671
+ if (fieldConfig instanceof AbstractControl) {
2672
+ formGroup[key] = fieldConfig;
2673
+ continue; // Saltamos a la siguiente iteración
2674
+ }
2675
+ // Tu lógica intacta original para los FormControl estándar
2679
2676
  const { value, validators = [], asyncValidators = [], } = fieldConfig;
2680
2677
  const hasAssignedDisabled = Object.prototype.hasOwnProperty.call(fieldConfig, 'disabled');
2681
2678
  const disabled = hasAssignedDisabled
@@ -4771,13 +4768,9 @@ class DsxAutocomplete {
4771
4768
  // Inputs usando Señales
4772
4769
  datasource = input.required(...(ngDevMode ? [{ debugName: "datasource" }] : /* istanbul ignore next */ []));
4773
4770
  optionLabel = input.required(...(ngDevMode ? [{ debugName: "optionLabel" }] : /* istanbul ignore next */ []));
4774
- // Label del input (ej: 'Requisitos')
4775
4771
  label = input('Seleccionar', ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
4776
- //Delay por defecto
4777
4772
  delay = input(100, ...(ngDevMode ? [{ debugName: "delay" }] : /* istanbul ignore next */ []));
4778
- // OBLIGATORIO para la creación automática: nombre de la propiedad ID (ej: 'requisitoId')
4779
4773
  idKey = input.required(...(ngDevMode ? [{ debugName: "idKey" }] : /* istanbul ignore next */ []));
4780
- // Opcional para PrimeNG (por defecto toma el valor de idKey para identificar duplicados)
4781
4774
  dataKey = input('', ...(ngDevMode ? [{ debugName: "dataKey" }] : /* istanbul ignore next */ []));
4782
4775
  permitirCrear = input(false, ...(ngDevMode ? [{ debugName: "permitirCrear" }] : /* istanbul ignore next */ []));
4783
4776
  debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
@@ -4788,20 +4781,30 @@ class DsxAutocomplete {
4788
4781
  onChange = () => { };
4789
4782
  onTouched = () => { };
4790
4783
  disabled = false;
4784
+ lastLogTime = 0;
4785
+ timeoutLimpieza = null;
4786
+ logDebug(message, data) {
4787
+ if (!this.debug())
4788
+ return;
4789
+ const now = Date.now();
4790
+ if (now - this.lastLogTime < 50)
4791
+ return;
4792
+ this.lastLogTime = now;
4793
+ console.log(`[DsxAutocomplete] ${message}`, data || '');
4794
+ }
4791
4795
  searchRequisitos(event) {
4792
4796
  const inicio = performance.now();
4793
4797
  const label = this.optionLabel();
4794
- // FLUJO A: El usuario hizo clic en el botón dropdown (Texto vacío)
4798
+ // FLUJO A: Dropdown (sin texto)
4795
4799
  if (!event.query || event.query.trim() === '') {
4796
- // Cargamos TODO el universo de datos para que el usuario pueda navegar libremente
4797
4800
  const todoElUniverso = this.datasource();
4798
4801
  this.dataFiltrada.set(todoElUniverso);
4799
- if (this.debug()) {
4800
- console.log('[DsxAutocomplete] Modo dropdown activo: Mostrando lista completa de registros:', todoElUniverso.length);
4801
- }
4802
+ this.logDebug('Modo dropdown activo', {
4803
+ registros: todoElUniverso.length,
4804
+ });
4802
4805
  return;
4803
4806
  }
4804
- // FLUJO B: El usuario está escribiendo (Búsqueda inteligente con límite de 10)
4807
+ // FLUJO B: Búsqueda
4805
4808
  const palabrasBuscadas = event.query
4806
4809
  .toLowerCase()
4807
4810
  .trim()
@@ -4812,25 +4815,26 @@ class DsxAutocomplete {
4812
4815
  const textoRegistro = String(item[label] || '').toLowerCase();
4813
4816
  return palabrasBuscadas.every((palabra) => textoRegistro.includes(palabra));
4814
4817
  })
4815
- .slice(0, 10); // <-- El límite se aplica estrictamente al escribir
4818
+ .slice(0, 10);
4816
4819
  this.dataFiltrada.set(filtrados);
4817
- if (this.debug()) {
4818
- console.log(`[DsxAutocomplete] Búsqueda inteligente para "${event.query}" tardó ${(performance.now() - inicio).toFixed(2)}ms. Encontrados:`, filtrados.length);
4819
- }
4820
+ this.logDebug(`Búsqueda "${event.query}" (${(performance.now() - inicio).toFixed(2)}ms)`, { encontrados: filtrados.length });
4820
4821
  }
4821
4822
  evaluarYAgregar(event) {
4822
4823
  const valorInput = event.target.value?.trim();
4823
- if (!valorInput)
4824
+ if (!valorInput) {
4825
+ this.dataFiltrada.set([]);
4824
4826
  return;
4827
+ }
4825
4828
  const sugerencias = this.dataFiltrada();
4826
4829
  const actuales = this.value || [];
4827
- const campoId = this.idKey(); // Obtenemos de forma segura el nombre del ID stringificado
4830
+ const campoId = this.idKey();
4828
4831
  if (sugerencias.length > 0) {
4829
4832
  const primeraMatch = sugerencias[0];
4830
- // Comparamos identidades usando el campoId dinámico
4831
4833
  const yaExiste = actuales.some((item) => item[campoId] === primeraMatch[campoId]);
4832
- if (!yaExiste)
4834
+ if (!yaExiste) {
4833
4835
  this.actualizarFormulario([...actuales, primeraMatch]);
4836
+ this.logDebug('✅ Elemento agregado');
4837
+ }
4834
4838
  }
4835
4839
  else if (this.permitirCrear()) {
4836
4840
  let nuevo;
@@ -4838,7 +4842,6 @@ class DsxAutocomplete {
4838
4842
  nuevo = this.factoryNuevoRegistro()(valorInput);
4839
4843
  }
4840
4844
  else {
4841
- // Al estructurar el objeto usando [campoId], garantizamos que viaje con el nombre correcto de tu entidad
4842
4845
  nuevo = {
4843
4846
  [campoId]: 0,
4844
4847
  [this.optionLabel()]: valorInput.toUpperCase(),
@@ -4846,19 +4849,47 @@ class DsxAutocomplete {
4846
4849
  };
4847
4850
  }
4848
4851
  this.actualizarFormulario([...actuales, nuevo]);
4852
+ this.logDebug('🆕 Nuevo elemento creado', valorInput);
4849
4853
  }
4850
4854
  event.target.value = '';
4851
4855
  this.dataFiltrada.set([]);
4852
4856
  }
4857
+ onHide() {
4858
+ this.logDebug('Dropdown ocultado - Limpiando estado');
4859
+ // Limpiar timeout anterior si existe
4860
+ if (this.timeoutLimpieza) {
4861
+ clearTimeout(this.timeoutLimpieza);
4862
+ this.timeoutLimpieza = null;
4863
+ }
4864
+ // Limpiar después de un pequeño delay
4865
+ this.timeoutLimpieza = setTimeout(() => {
4866
+ this.dataFiltrada.set([]);
4867
+ this.timeoutLimpieza = null;
4868
+ }, 100);
4869
+ }
4870
+ onShow() {
4871
+ this.logDebug('Dropdown mostrado');
4872
+ // Limpiar timeout pendiente
4873
+ if (this.timeoutLimpieza) {
4874
+ clearTimeout(this.timeoutLimpieza);
4875
+ this.timeoutLimpieza = null;
4876
+ }
4877
+ }
4853
4878
  actualizarFormulario(nuevaLista) {
4854
4879
  this.value = nuevaLista;
4855
4880
  this.onChange(this.value);
4856
- if (this.debug())
4857
- console.log('[DsxAutocomplete] Estado del Formulario Actualizado con Objetos:', this.value);
4881
+ this.logDebug('Formulario actualizado', {
4882
+ total: nuevaLista.length,
4883
+ });
4858
4884
  }
4859
4885
  // --- ControlValueAccessor ---
4860
4886
  writeValue(value) {
4861
4887
  this.value = value || [];
4888
+ this.dataFiltrada.set([]);
4889
+ if (this.timeoutLimpieza) {
4890
+ clearTimeout(this.timeoutLimpieza);
4891
+ this.timeoutLimpieza = null;
4892
+ }
4862
4893
  }
4863
4894
  registerOnChange(fn) {
4864
4895
  this.onChange = fn;
@@ -4876,7 +4907,7 @@ class DsxAutocomplete {
4876
4907
  useExisting: forwardRef(() => DsxAutocomplete),
4877
4908
  multi: true,
4878
4909
  },
4879
- ], ngImport: i0, template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n multiple\r\n [dropdown]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onChange(value)\"\r\n (onBlur)=\"onTouched()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n >\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n", styles: [""], dependencies: [{ kind: "component", type: AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }] });
4910
+ ], ngImport: i0, template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n multiple\r\n [dropdown]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onChange(value)\"\r\n (onBlur)=\"onTouched()\"\r\n (onShow)=\"onShow()\"\r\n (onHide)=\"onHide()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n placeholder=\"Ingresa un texto....\"\r\n >\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n", styles: [""], dependencies: [{ kind: "component", type: AutoComplete, selector: "p-autoComplete, p-autocomplete, p-auto-complete", inputs: ["minLength", "minQueryLength", "delay", "panelStyle", "styleClass", "panelStyleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "readonly", "scrollHeight", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "autoHighlight", "forceSelection", "type", "autoZIndex", "baseZIndex", "ariaLabel", "dropdownAriaLabel", "ariaLabelledBy", "dropdownIcon", "unique", "group", "completeOnFocus", "showClear", "dropdown", "showEmptyMessage", "dropdownMode", "multiple", "addOnTab", "tabindex", "dataKey", "emptyMessage", "showTransitionOptions", "hideTransitionOptions", "autofocus", "autocomplete", "optionGroupChildren", "optionGroupLabel", "overlayOptions", "suggestions", "optionLabel", "optionValue", "id", "searchMessage", "emptySelectionMessage", "selectionMessage", "autoOptionFocus", "selectOnFocus", "searchLocale", "optionDisabled", "focusOnHover", "typeahead", "addOnBlur", "separator", "appendTo", "motionOptions"], outputs: ["completeMethod", "onSelect", "onUnselect", "onAdd", "onFocus", "onBlur", "onDropdownClick", "onClear", "onInputKeydown", "onKeyUp", "onShow", "onHide", "onLazyLoad"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }] });
4880
4911
  }
4881
4912
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxAutocomplete, decorators: [{
4882
4913
  type: Component,
@@ -4886,13 +4917,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
4886
4917
  useExisting: forwardRef(() => DsxAutocomplete),
4887
4918
  multi: true,
4888
4919
  },
4889
- ], template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n multiple\r\n [dropdown]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onChange(value)\"\r\n (onBlur)=\"onTouched()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n >\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n" }]
4920
+ ], template: "<p-floatlabel variant=\"on\">\r\n <p-autoComplete\r\n fluid\r\n multiple\r\n [dropdown]=\"true\"\r\n [suggestions]=\"dataFiltrada()\"\r\n (completeMethod)=\"searchRequisitos($event)\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onChange(value)\"\r\n (onBlur)=\"onTouched()\"\r\n (onShow)=\"onShow()\"\r\n (onHide)=\"onHide()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"optionLabel()\"\r\n [dataKey]=\"dataKey() || idKey()\"\r\n (keyup.enter)=\"evaluarYAgregar($event)\"\r\n [delay]=\"delay()\"\r\n placeholder=\"Ingresa un texto....\"\r\n >\r\n </p-autoComplete>\r\n <label>{{ label() }}</label>\r\n</p-floatlabel>\r\n" }]
4890
4921
  }], propDecorators: { datasource: [{ type: i0.Input, args: [{ isSignal: true, alias: "datasource", required: true }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], delay: [{ type: i0.Input, args: [{ isSignal: true, alias: "delay", required: false }] }], idKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "idKey", required: true }] }], dataKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataKey", required: false }] }], permitirCrear: [{ type: i0.Input, args: [{ isSignal: true, alias: "permitirCrear", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], factoryNuevoRegistro: [{ type: i0.Input, args: [{ isSignal: true, alias: "factoryNuevoRegistro", required: false }] }] } });
4891
4922
 
4892
4923
  class FileComponent {
4893
4924
  // Inputs
4894
4925
  existingFile = input(null, ...(ngDevMode ? [{ debugName: "existingFile" }] : /* istanbul ignore next */ []));
4895
4926
  existingFileName = input(null, ...(ngDevMode ? [{ debugName: "existingFileName" }] : /* istanbul ignore next */ []));
4927
+ overrideFileName = input(null, ...(ngDevMode ? [{ debugName: "overrideFileName" }] : /* istanbul ignore next */ []));
4896
4928
  debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
4897
4929
  required = input(true, ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
4898
4930
  accept = input('.xls,.xlsx', ...(ngDevMode ? [{ debugName: "accept" }] : /* istanbul ignore next */ []));
@@ -5101,7 +5133,7 @@ class FileComponent {
5101
5133
  console.log(`[FileComponent] ${method}`, data || '');
5102
5134
  }
5103
5135
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FileComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
5104
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FileComponent, isStandalone: true, selector: "dsx-file-upload", inputs: { existingFile: { classPropertyName: "existingFile", publicName: "existingFile", isSignal: true, isRequired: false, transformFunction: null }, existingFileName: { classPropertyName: "existingFileName", publicName: "existingFileName", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, pTooltipOverride: { classPropertyName: "pTooltipOverride", publicName: "pTooltipOverride", isSignal: true, isRequired: false, transformFunction: null }, tooltipPositionOverride: { classPropertyName: "tooltipPositionOverride", publicName: "tooltipPositionOverride", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, invalidSummary: { classPropertyName: "invalidSummary", publicName: "invalidSummary", isSignal: true, isRequired: false, transformFunction: null }, invalidDetail: { classPropertyName: "invalidDetail", publicName: "invalidDetail", isSignal: true, isRequired: false, transformFunction: null }, invalidSizeSummary: { classPropertyName: "invalidSizeSummary", publicName: "invalidSizeSummary", isSignal: true, isRequired: false, transformFunction: null }, invalidSizeDetail: { classPropertyName: "invalidSizeDetail", publicName: "invalidSizeDetail", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
5136
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: FileComponent, isStandalone: true, selector: "dsx-file-upload", inputs: { existingFile: { classPropertyName: "existingFile", publicName: "existingFile", isSignal: true, isRequired: false, transformFunction: null }, existingFileName: { classPropertyName: "existingFileName", publicName: "existingFileName", isSignal: true, isRequired: false, transformFunction: null }, overrideFileName: { classPropertyName: "overrideFileName", publicName: "overrideFileName", isSignal: true, isRequired: false, transformFunction: null }, debug: { classPropertyName: "debug", publicName: "debug", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, accept: { classPropertyName: "accept", publicName: "accept", isSignal: true, isRequired: false, transformFunction: null }, pTooltipOverride: { classPropertyName: "pTooltipOverride", publicName: "pTooltipOverride", isSignal: true, isRequired: false, transformFunction: null }, tooltipPositionOverride: { classPropertyName: "tooltipPositionOverride", publicName: "tooltipPositionOverride", isSignal: true, isRequired: false, transformFunction: null }, maxFileSize: { classPropertyName: "maxFileSize", publicName: "maxFileSize", isSignal: true, isRequired: false, transformFunction: null }, invalidSummary: { classPropertyName: "invalidSummary", publicName: "invalidSummary", isSignal: true, isRequired: false, transformFunction: null }, invalidDetail: { classPropertyName: "invalidDetail", publicName: "invalidDetail", isSignal: true, isRequired: false, transformFunction: null }, invalidSizeSummary: { classPropertyName: "invalidSizeSummary", publicName: "invalidSizeSummary", isSignal: true, isRequired: false, transformFunction: null }, invalidSizeDetail: { classPropertyName: "invalidSizeDetail", publicName: "invalidSizeDetail", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
5105
5137
  {
5106
5138
  provide: NG_VALUE_ACCESSOR,
5107
5139
  useExisting: forwardRef(() => FileComponent),
@@ -5112,7 +5144,7 @@ class FileComponent {
5112
5144
  useExisting: forwardRef(() => FileComponent),
5113
5145
  multi: true,
5114
5146
  },
5115
- ], 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\"> \uD83D\uDCC4 {{ existingFileName() }} </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n #fileUpload\r\n mode=\"basic\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"pTooltipOverride()\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n />\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"], dependencies: [{ kind: "component", type: FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }] });
5147
+ ], viewQueries: [{ propertyName: "fileUpload", first: true, predicate: ["fileUpload"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\">\r\n \uD83D\uDCC4 {{ overrideFileName() ?? existingFileName() }}\r\n </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n #fileUpload\r\n mode=\"basic\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"pTooltipOverride()\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n />\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"], dependencies: [{ kind: "component", type: FileUpload, selector: "p-fileupload, p-fileUpload", inputs: ["name", "url", "method", "multiple", "accept", "disabled", "auto", "withCredentials", "maxFileSize", "invalidFileSizeMessageSummary", "invalidFileSizeMessageDetail", "invalidFileTypeMessageSummary", "invalidFileTypeMessageDetail", "invalidFileLimitMessageDetail", "invalidFileLimitMessageSummary", "style", "styleClass", "previewWidth", "chooseLabel", "uploadLabel", "cancelLabel", "chooseIcon", "uploadIcon", "cancelIcon", "showUploadButton", "showCancelButton", "mode", "headers", "customUpload", "fileLimit", "uploadStyleClass", "cancelStyleClass", "removeStyleClass", "chooseStyleClass", "chooseButtonProps", "uploadButtonProps", "cancelButtonProps", "files"], outputs: ["onBeforeUpload", "onSend", "onUpload", "onError", "onClear", "onRemove", "onSelect", "onProgress", "uploadHandler", "onImageError", "onRemoveUploadedFile"] }, { kind: "directive", type: Tooltip, selector: "[pTooltip]", inputs: ["tooltipPosition", "tooltipEvent", "positionStyle", "tooltipStyleClass", "tooltipZIndex", "escape", "showDelay", "hideDelay", "life", "positionTop", "positionLeft", "autoHide", "fitContent", "hideOnEscape", "showOnEllipsis", "pTooltip", "tooltipDisabled", "tooltipOptions", "appendTo", "ptTooltip", "pTooltipPT", "pTooltipUnstyled"] }, { kind: "component", type: Button, selector: "p-button", inputs: ["hostName", "type", "badge", "disabled", "raised", "rounded", "text", "plain", "outlined", "link", "tabindex", "size", "variant", "style", "styleClass", "badgeClass", "badgeSeverity", "ariaLabel", "autofocus", "iconPos", "icon", "label", "loading", "loadingIcon", "severity", "buttonProps", "fluid"], outputs: ["onClick", "onFocus", "onBlur"] }] });
5116
5148
  }
5117
5149
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: FileComponent, decorators: [{
5118
5150
  type: Component,
@@ -5127,8 +5159,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
5127
5159
  useExisting: forwardRef(() => FileComponent),
5128
5160
  multi: true,
5129
5161
  },
5130
- ], template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\"> \uD83D\uDCC4 {{ existingFileName() }} </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n #fileUpload\r\n mode=\"basic\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"pTooltipOverride()\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n />\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"] }]
5131
- }], ctorParameters: () => [], propDecorators: { existingFile: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFile", required: false }] }], existingFileName: [{ type: i0.Input, args: [{ isSignal: true, alias: "existingFileName", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", 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 }] }] } });
5162
+ ], template: "@if (showExistingFile() && !existingFile()) {\r\n <div class=\"flex items-center gap-2\">\r\n <span class=\"file-name\">\r\n \uD83D\uDCC4 {{ overrideFileName() ?? existingFileName() }}\r\n </span>\r\n\r\n <p-button\r\n icon=\"pi pi-cloud-upload\"\r\n [rounded]=\"true\"\r\n severity=\"info\"\r\n (click)=\"startReplace()\"\r\n [disabled]=\"isReplaceButtonDisabled()\"\r\n pTooltip=\"Reemplazar archivo\"\r\n tooltipPosition=\"top\"\r\n />\r\n </div>\r\n} @else {\r\n <div class=\"flex items-center gap-2\">\r\n <p-fileUpload\r\n #fileUpload\r\n mode=\"basic\"\r\n [accept]=\"accept()\"\r\n [maxFileSize]=\"maxFileSize() * 1024 * 1024\"\r\n [invalidFileTypeMessageSummary]=\"invalidSummary()\"\r\n [invalidFileTypeMessageDetail]=\"invalidDetail()\"\r\n [invalidFileSizeMessageSummary]=\"invalidSizeSummary()\"\r\n [invalidFileSizeMessageDetail]=\"invalidSizeDetail()\"\r\n (onSelect)=\"onSelect($event)\"\r\n [disabled]=\"!isFileUploadEnabled()\"\r\n [pTooltip]=\"pTooltipOverride()\"\r\n [tooltipPosition]=\"tooltipPositionOverride()\"\r\n />\r\n\r\n @if (isReplacing() || existingFile()) {\r\n <p-button\r\n icon=\"pi pi-times\"\r\n [rounded]=\"true\"\r\n severity=\"secondary\"\r\n (click)=\"cancelReplace()\"\r\n [disabled]=\"disabled()\"\r\n pTooltip=\"Cancelar reemplazo\"\r\n tooltipPosition=\"top\"\r\n />\r\n }\r\n </div>\r\n}\r\n", styles: [".file-name{display:inline-block;font-size:clamp(.85rem,1vw + .5rem,1.4rem);font-weight:500;color:#2c3e50;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}\n"] }]
5163
+ }], 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 }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", 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 }] }] } });
5132
5164
 
5133
5165
  class JsonHighlightPipe {
5134
5166
  /**
@@ -8112,7 +8144,7 @@ class DsxSelect {
8112
8144
  optionValue = input(undefined, ...(ngDevMode ? [{ debugName: "optionValue" }] : /* istanbul ignore next */ []));
8113
8145
  // Iconos
8114
8146
  useIcon = input(true, ...(ngDevMode ? [{ debugName: "useIcon" }] : /* istanbul ignore next */ []));
8115
- itemIcon = input('label', ...(ngDevMode ? [{ debugName: "itemIcon" }] : /* istanbul ignore next */ []));
8147
+ itemIcon = input('keyboard_arrow_right', ...(ngDevMode ? [{ debugName: "itemIcon" }] : /* istanbul ignore next */ []));
8116
8148
  selectedIcon = input('done_outline', ...(ngDevMode ? [{ debugName: "selectedIcon" }] : /* istanbul ignore next */ []));
8117
8149
  // Debug solo consola
8118
8150
  debug = input(false, ...(ngDevMode ? [{ debugName: "debug" }] : /* istanbul ignore next */ []));
@@ -8277,7 +8309,7 @@ class DsxSelect {
8277
8309
  useExisting: forwardRef(() => DsxSelect),
8278
8310
  multi: true,
8279
8311
  },
8280
- ], ngImport: i0, template: "<!-- dsx-select.html -->\r\n<p-floatLabel [variant]=\"variant()\">\r\n <p-select\r\n fluid\r\n [options]=\"dataList()\"\r\n [filter]=\"true\"\r\n [filterPlaceholder]=\"filterPlaceholder()\"\r\n [showClear]=\"showClear()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"resolvedOptionLabel()\"\r\n [optionValue]=\"resolvedOptionValue()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n >\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\" class=\"text-cyan-500! mr-1\"></icon-dsx>\r\n } {{ item[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx\r\n [name]=\"selectedIcon()\"\r\n class=\"text-green-600! mr-1\"\r\n ></icon-dsx>\r\n } {{ selectedOptions?.[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n </p-select>\r\n <label>{{ resolvedLabel() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 Mensaje de error condicional (dentro del floatLabel) -->\r\n@if (showError() && errorControl()) {\r\n<app-message-error [control]=\"errorControl()\" />\r\n}\r\n", styles: ["icon-dsx{font-size:1.5em}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: IconDsxComponent, selector: "icon-dsx", inputs: ["name", "style", "debug"] }, { kind: "component", type: AppMessageErrorComponent, selector: "app-message-error", inputs: ["control", "form", "debugMode"] }] });
8312
+ ], ngImport: i0, template: "<!-- dsx-select.html -->\r\n<p-floatLabel [variant]=\"variant()\">\r\n <p-select\r\n fluid\r\n appendTo=\"body\"\r\n [options]=\"dataList()\"\r\n [filter]=\"true\"\r\n [filterPlaceholder]=\"filterPlaceholder()\"\r\n [showClear]=\"showClear()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"resolvedOptionLabel()\"\r\n [optionValue]=\"resolvedOptionValue()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n >\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\"></icon-dsx>\r\n } {{ item[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx\r\n [name]=\"selectedIcon()\"\r\n class=\"text-green-600! mr-1\"\r\n ></icon-dsx>\r\n } {{ selectedOptions?.[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n </p-select>\r\n <label>{{ resolvedLabel() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 Mensaje de error condicional (dentro del floatLabel) -->\r\n@if (showError() && errorControl()) {\r\n<app-message-error [control]=\"errorControl()\" />\r\n}\r\n", styles: ["icon-dsx{font-size:1.5em}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Select, selector: "p-select", inputs: ["id", "scrollHeight", "filter", "panelStyle", "styleClass", "panelStyleClass", "readonly", "editable", "tabindex", "placeholder", "loadingIcon", "filterPlaceholder", "filterLocale", "inputId", "dataKey", "filterBy", "filterFields", "autofocus", "resetFilterOnHide", "checkmark", "dropdownIcon", "loading", "optionLabel", "optionValue", "optionDisabled", "optionGroupLabel", "optionGroupChildren", "group", "showClear", "emptyFilterMessage", "emptyMessage", "lazy", "virtualScroll", "virtualScrollItemSize", "virtualScrollOptions", "overlayOptions", "ariaFilterLabel", "ariaLabel", "ariaLabelledBy", "filterMatchMode", "tooltip", "tooltipPosition", "tooltipPositionStyle", "tooltipStyleClass", "focusOnHover", "selectOnFocus", "autoOptionFocus", "autofocusFilter", "filterValue", "options", "appendTo", "motionOptions"], outputs: ["onChange", "onFilter", "onFocus", "onBlur", "onClick", "onShow", "onHide", "onClear", "onLazyLoad"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "component", type: IconDsxComponent, selector: "icon-dsx", inputs: ["name", "style", "debug"] }, { kind: "component", type: AppMessageErrorComponent, selector: "app-message-error", inputs: ["control", "form", "debugMode"] }] });
8281
8313
  }
8282
8314
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxSelect, decorators: [{
8283
8315
  type: Component,
@@ -8293,7 +8325,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
8293
8325
  useExisting: forwardRef(() => DsxSelect),
8294
8326
  multi: true,
8295
8327
  },
8296
- ], template: "<!-- dsx-select.html -->\r\n<p-floatLabel [variant]=\"variant()\">\r\n <p-select\r\n fluid\r\n [options]=\"dataList()\"\r\n [filter]=\"true\"\r\n [filterPlaceholder]=\"filterPlaceholder()\"\r\n [showClear]=\"showClear()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"resolvedOptionLabel()\"\r\n [optionValue]=\"resolvedOptionValue()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n >\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\" class=\"text-cyan-500! mr-1\"></icon-dsx>\r\n } {{ item[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx\r\n [name]=\"selectedIcon()\"\r\n class=\"text-green-600! mr-1\"\r\n ></icon-dsx>\r\n } {{ selectedOptions?.[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n </p-select>\r\n <label>{{ resolvedLabel() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 Mensaje de error condicional (dentro del floatLabel) -->\r\n@if (showError() && errorControl()) {\r\n<app-message-error [control]=\"errorControl()\" />\r\n}\r\n", styles: ["icon-dsx{font-size:1.5em}\n"] }]
8328
+ ], template: "<!-- dsx-select.html -->\r\n<p-floatLabel [variant]=\"variant()\">\r\n <p-select\r\n fluid\r\n appendTo=\"body\"\r\n [options]=\"dataList()\"\r\n [filter]=\"true\"\r\n [filterPlaceholder]=\"filterPlaceholder()\"\r\n [showClear]=\"showClear()\"\r\n [disabled]=\"disabled\"\r\n [optionLabel]=\"resolvedOptionLabel()\"\r\n [optionValue]=\"resolvedOptionValue()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n >\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\"></icon-dsx>\r\n } {{ item[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center\">\r\n @if (useIcon()) {\r\n <icon-dsx\r\n [name]=\"selectedIcon()\"\r\n class=\"text-green-600! mr-1\"\r\n ></icon-dsx>\r\n } {{ selectedOptions?.[resolvedOptionLabel()] }}\r\n </div>\r\n </ng-template>\r\n </p-select>\r\n <label>{{ resolvedLabel() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 Mensaje de error condicional (dentro del floatLabel) -->\r\n@if (showError() && errorControl()) {\r\n<app-message-error [control]=\"errorControl()\" />\r\n}\r\n", styles: ["icon-dsx{font-size:1.5em}\n"] }]
8297
8329
  }], ctorParameters: () => [], propDecorators: { dataList: [{ type: i0.Input, args: [{ isSignal: true, alias: "dataList", required: true }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], filterPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "filterPlaceholder", required: false }] }], showClear: [{ type: i0.Input, args: [{ isSignal: true, alias: "showClear", required: false }] }], optionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionLabel", required: false }] }], optionValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionValue", required: false }] }], useIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "useIcon", required: false }] }], itemIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemIcon", required: false }] }], selectedIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedIcon", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }], showError: [{ type: i0.Input, args: [{ isSignal: true, alias: "showError", required: false }] }], selectedValue: [{ type: i0.Input, args: [{ isSignal: true, alias: "selectedValue", required: false }] }, { type: i0.Output, args: ["selectedValueChange"] }] } });
8298
8330
 
8299
8331
  /**
@@ -9551,15 +9583,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
9551
9583
 
9552
9584
  // form-preview.service.ts
9553
9585
  class FormPreviewService {
9554
- /**
9555
- * Genera resumen HTML desde un objeto plano
9556
- * @param data - Objeto con los datos (ej: form.value)
9557
- * @param fields - Array de campos a mostrar
9558
- * @param options - Opciones adicionales
9559
- */
9560
9586
  buildObjectSummaryHtml(data, fields, options) {
9561
9587
  const debug = options?.debug ?? false;
9562
- // Modo desarrollo: mostrar información de depuración
9563
9588
  if (debug) {
9564
9589
  this.debugInputData(data, fields, options);
9565
9590
  }
@@ -9579,16 +9604,31 @@ class FormPreviewService {
9579
9604
  const cells = [];
9580
9605
  const propiedadesNoEncontradas = [];
9581
9606
  for (const field of fields) {
9582
- // Obtener valor
9583
- const value = this.getNestedValue(data, field.key);
9607
+ let value = this.getNestedValue(data, field.key);
9608
+ // Resolver referencia si existe
9609
+ if (field.referenceMap && value !== undefined && value !== null) {
9610
+ const originalValue = value;
9611
+ value = this.resolveReferenceValue(value, field);
9612
+ if (debug) {
9613
+ console.log(`🔍 Resolviendo referencia para "${field.key}":`, {
9614
+ original: originalValue,
9615
+ resuelto: value,
9616
+ tipoMapa: Array.isArray(field.referenceMap)
9617
+ ? 'Array'
9618
+ : field.referenceMap instanceof Map
9619
+ ? 'Map'
9620
+ : typeof field.referenceMap === 'function'
9621
+ ? 'Función'
9622
+ : 'Objeto',
9623
+ });
9624
+ }
9625
+ }
9584
9626
  const valueText = field.format
9585
9627
  ? field.format(value)
9586
9628
  : this.formatValue(value);
9587
- // Registrar si no se encontró la propiedad
9588
9629
  if (value === undefined) {
9589
9630
  propiedadesNoEncontradas.push(field.key);
9590
9631
  }
9591
- // Filtrar vacíos
9592
9632
  if (!showEmpty && !valueText)
9593
9633
  continue;
9594
9634
  cells.push({
@@ -9596,7 +9636,6 @@ class FormPreviewService {
9596
9636
  value: valueText || '—',
9597
9637
  });
9598
9638
  }
9599
- // Modo desarrollo: mostrar advertencias
9600
9639
  if (debug) {
9601
9640
  if (propiedadesNoEncontradas.length > 0) {
9602
9641
  console.warn('⚠️ FormPreview: Propiedades no encontradas:', propiedadesNoEncontradas);
@@ -9616,6 +9655,56 @@ class FormPreviewService {
9616
9655
  return '';
9617
9656
  return this.generateHtml(title, cells, compact);
9618
9657
  }
9658
+ resolveReferenceValue(value, field) {
9659
+ // Si es array (multi-select)
9660
+ if (Array.isArray(value)) {
9661
+ return value.map((item) => this.resolveSingleReference(item, field));
9662
+ }
9663
+ return this.resolveSingleReference(value, field);
9664
+ }
9665
+ resolveSingleReference(value, field) {
9666
+ if (!value)
9667
+ return value;
9668
+ // Si ya es objeto, devolverlo
9669
+ if (typeof value === 'object' && value !== null) {
9670
+ return value;
9671
+ }
9672
+ const map = field.referenceMap;
9673
+ if (!map)
9674
+ return value;
9675
+ let referencedItem = null;
9676
+ // Si es función
9677
+ if (typeof map === 'function') {
9678
+ referencedItem = map(value);
9679
+ }
9680
+ // Si es Map
9681
+ else if (map instanceof Map) {
9682
+ referencedItem = map.get(value);
9683
+ }
9684
+ // Si es Array
9685
+ else if (Array.isArray(map)) {
9686
+ const idKey = field.referenceId || 'id';
9687
+ referencedItem = map.find((item) => item[idKey] === value);
9688
+ }
9689
+ // Si es objeto literal
9690
+ else if (typeof map === 'object') {
9691
+ referencedItem = map[String(value)];
9692
+ }
9693
+ if (referencedItem) {
9694
+ const labelProp = field.referenceLabel || 'nombre';
9695
+ if (typeof referencedItem === 'object' && referencedItem !== null) {
9696
+ // Buscar la propiedad en el objeto
9697
+ if (referencedItem[labelProp] !== undefined &&
9698
+ referencedItem[labelProp] !== null) {
9699
+ return referencedItem[labelProp];
9700
+ }
9701
+ // Si no encuentra la propiedad, devolver el objeto completo
9702
+ return referencedItem;
9703
+ }
9704
+ return referencedItem;
9705
+ }
9706
+ return value;
9707
+ }
9619
9708
  getNestedValue(obj, path) {
9620
9709
  if (!obj || !path)
9621
9710
  return undefined;
@@ -9635,24 +9724,47 @@ class FormPreviewService {
9635
9724
  if (value instanceof Date)
9636
9725
  return value.toLocaleDateString();
9637
9726
  if (typeof value === 'object') {
9638
- // Para selects, mostrar propiedades comunes
9639
- if (value.label)
9640
- return String(value.label);
9641
- if (value.nombre)
9642
- return String(value.nombre);
9643
- if (value.name)
9644
- return String(value.name);
9645
- if (value.value)
9646
- return String(value.value);
9647
- try {
9648
- return JSON.stringify(value);
9649
- }
9650
- catch {
9651
- return String(value);
9727
+ if (Array.isArray(value)) {
9728
+ return value
9729
+ .map((item) => this.formatSingleValue(item))
9730
+ .filter((v) => v)
9731
+ .join(', ');
9652
9732
  }
9733
+ return this.formatSingleValue(value);
9653
9734
  }
9654
9735
  return String(value);
9655
9736
  }
9737
+ formatSingleValue(value) {
9738
+ if (!value || typeof value !== 'object')
9739
+ return String(value);
9740
+ const props = [
9741
+ 'label',
9742
+ 'nombre',
9743
+ 'name',
9744
+ 'empresaNombre',
9745
+ 'descripcion',
9746
+ 'description',
9747
+ 'text',
9748
+ 'value',
9749
+ ];
9750
+ for (const prop of props) {
9751
+ if (value[prop] !== undefined &&
9752
+ value[prop] !== null &&
9753
+ value[prop] !== '') {
9754
+ return String(value[prop]);
9755
+ }
9756
+ }
9757
+ if (value.id !== undefined && value.id !== null) {
9758
+ return `ID: ${value.id}`;
9759
+ }
9760
+ try {
9761
+ const json = JSON.stringify(value);
9762
+ return json.length > 50 ? json.substring(0, 50) + '...' : json;
9763
+ }
9764
+ catch {
9765
+ return '[Objeto]';
9766
+ }
9767
+ }
9656
9768
  generateHtml(title, cells, compact) {
9657
9769
  if (compact) {
9658
9770
  return `
@@ -9679,7 +9791,6 @@ class FormPreviewService {
9679
9791
  </div>
9680
9792
  `;
9681
9793
  }
9682
- // Versión no compacta (con más padding)
9683
9794
  return `
9684
9795
  <div style="margin: 12px 0; border: 1px solid #dee2e6; border-radius: 8px; overflow: hidden; background: #ffffff;">
9685
9796
  <div style="padding: 12px; background: #f8f9fa; border-bottom: 1px solid #dee2e6; font-weight: 600; color: #495057;">
@@ -9710,14 +9821,12 @@ class FormPreviewService {
9710
9821
  console.log('📋 Propiedades disponibles:', data ? Object.keys(data) : 'data es null');
9711
9822
  console.log('🎯 Fields solicitados:', fields);
9712
9823
  console.log('⚙️ Options:', options);
9713
- // Verificar cada campo
9714
9824
  if (data) {
9715
9825
  for (const field of fields) {
9716
9826
  const value = this.getNestedValue(data, field.key);
9717
9827
  const existe = value !== undefined;
9718
9828
  if (!existe) {
9719
9829
  console.warn(`❌ Campo "${field.key}" NO existe en data`);
9720
- // Sugerir propiedades similares
9721
9830
  const similares = this.findSimilarProperties(field.key, Object.keys(data));
9722
9831
  if (similares.length > 0) {
9723
9832
  console.warn(`💡 Quizás quisiste decir: ${similares.join(', ')}`);
@@ -10376,7 +10485,7 @@ function buildAlreadyExistsMessage(control, options) {
10376
10485
  * Validador asíncrono que verifica si un valor ya existe mediante una llamada a un servicio.
10377
10486
  * Útil para validar unicidad de campos como nombres de usuario, correos electrónicos, etc.
10378
10487
  *
10379
- * Aplica `debounceTime(400)` y `distinctUntilChanged()` para evitar llamadas innecesarias
10488
+ * Aplica `debounceTime(800)` y `distinctUntilChanged()` para evitar llamadas innecesarias
10380
10489
  * al servicio mientras el usuario escribe.
10381
10490
  *
10382
10491
  * @template T Tipo del valor del control a validar.
@@ -10384,39 +10493,93 @@ function buildAlreadyExistsMessage(control, options) {
10384
10493
  * el identificador del registro actual. Debe retornar un `Observable<boolean>`
10385
10494
  * emitiendo `true` si el valor ya existe (en otro registro), `false` en caso contrario.
10386
10495
  * @param options Opciones del validador:
10387
- * - `currentId`: Id opcional del registro en edición para excluirlo en backend.
10496
+ * - `currentId`: Id fijo del registro en edición para excluirlo en backend.
10497
+ * ⚠️ Útil cuando el ID no cambia durante la vida del validador.
10498
+ * - `getCurrentId`: Función que obtiene el ID en tiempo real.
10499
+ * ✅ **RECOMENDADO** cuando el ID puede cambiar (ej: signals, componentes reactivos).
10500
+ * Si se proporciona, tiene prioridad sobre `currentId`.
10388
10501
  * - `fieldName`: Nombre del campo para construir el mensaje dinámico
10389
10502
  * (si se omite, se intenta inferir del control).
10390
10503
  * - `errorMessage`: Mensaje personalizado (si se omite y se resuelve nombre
10391
10504
  * de campo, se usa `El valor {campo} ya existe`).
10505
+ * - `debug`: Si es `true`, muestra logs en consola para depuración.
10392
10506
  * @returns Un `AsyncValidatorFn` que emite `null` si el valor no existe o el control está vacío,
10393
10507
  * o un objeto `ValidationErrors` con la clave fija `alreadyValueExists` si el valor ya existe.
10394
10508
  *
10395
10509
  * @example
10396
10510
  * ```ts
10397
- * // Creación
10511
+ * // EJEMPLO 1: Creación (ID fijo o sin ID)
10398
10512
  * email: ['', [], asyncExistsValidator(
10399
10513
  * (value, id) => this.userService.emailExists({ value, id: Number(id) })
10400
10514
  * )]
10401
10515
  *
10402
- * // Edición (excluyendo el registro actual por id)
10516
+ * // EJEMPLO 2: Edición con ID fijo (cuando el ID no cambia)
10403
10517
  * email: ['', [], asyncExistsValidator(
10404
10518
  * (value, id) => this.userService.emailExists(value, id),
10405
- * { currentId: this.id, fieldName: 'email' }
10519
+ * { currentId: this.userId, fieldName: 'email' }
10520
+ * )]
10521
+ *
10522
+ * // EJEMPLO 3: Edición con ID dinámico (RECOMENDADO con signals)
10523
+ * email: ['', [], asyncExistsValidator(
10524
+ * (value, id) => this.userService.emailExists(value, id),
10525
+ * {
10526
+ * getCurrentId: () => this.id(), // ✅ Obtiene el ID en tiempo real
10527
+ * fieldName: 'email',
10528
+ * debug: true // Para depuración
10529
+ * }
10530
+ * )]
10531
+ *
10532
+ * // EJEMPLO 4: Con mensaje personalizado
10533
+ * codigo: ['', [], asyncExistsValidator(
10534
+ * (value, id) => this.productService.codigoExists(value, id),
10535
+ * {
10536
+ * getCurrentId: () => this.productId(),
10537
+ * errorMessage: 'El código de producto ya está registrado',
10538
+ * fieldName: 'código de producto'
10539
+ * }
10406
10540
  * )]
10407
10541
  * ```
10408
10542
  */
10409
10543
  function asyncExistsValidator(serviceMethod, options = {}) {
10410
- const { currentId = null, fieldName, errorMessage } = options;
10544
+ const { currentId = null, fieldName, errorMessage, debug = false, getCurrentId, } = options;
10411
10545
  const errorKey = 'alreadyValueExists';
10412
10546
  return (control) => {
10413
- if (!control.value)
10547
+ if (!control.value) {
10548
+ if (debug)
10549
+ console.log('[AsyncExistsValidator] Control value is empty, returning null');
10414
10550
  return of(null);
10551
+ }
10552
+ // Obtener el ID: prioridad a getCurrentId, luego currentId, luego 0
10553
+ const resolvedId = getCurrentId ? getCurrentId() : currentId;
10554
+ const finalId = resolvedId ?? 0;
10555
+ if (debug) {
10556
+ console.log('[AsyncExistsValidator] Starting validation for:', {
10557
+ value: control.value,
10558
+ currentId: finalId,
10559
+ fieldName,
10560
+ errorMessage,
10561
+ debug,
10562
+ usingGetCurrentId: !!getCurrentId,
10563
+ });
10564
+ }
10415
10565
  return of(control.value).pipe(debounceTime$1(800), distinctUntilChanged$1(), switchMap$1((value) => {
10416
- const resolvedId = currentId ?? 0;
10417
- return serviceMethod(value, resolvedId);
10566
+ if (debug) {
10567
+ console.log('[AsyncExistsValidator] Calling service method with:', {
10568
+ value: value,
10569
+ resolvedId: finalId,
10570
+ });
10571
+ }
10572
+ return serviceMethod(value, finalId);
10418
10573
  }), map((exists) => {
10574
+ if (debug) {
10575
+ console.log('[AsyncExistsValidator] Service response:', {
10576
+ exists,
10577
+ value: control.value,
10578
+ });
10579
+ }
10419
10580
  if (exists) {
10581
+ if (debug)
10582
+ console.log('[AsyncExistsValidator] Validation failed - value already exists');
10420
10583
  return {
10421
10584
  [errorKey]: {
10422
10585
  message: buildAlreadyExistsMessage(control, {
@@ -10427,8 +10590,15 @@ function asyncExistsValidator(serviceMethod, options = {}) {
10427
10590
  },
10428
10591
  };
10429
10592
  }
10593
+ if (debug)
10594
+ console.log('[AsyncExistsValidator] Validation passed - value is available');
10430
10595
  return null;
10431
- }), catchError(() => of(null)));
10596
+ }), catchError((error) => {
10597
+ if (debug) {
10598
+ console.error('[AsyncExistsValidator] Error occurred:', error);
10599
+ }
10600
+ return of(null);
10601
+ }));
10432
10602
  };
10433
10603
  }
10434
10604
  function templateVariablesValidator(allowedKeys) {