ngx-dsxlibrary 2.21.75 → 2.21.76

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.
@@ -52,6 +52,7 @@ import { AvatarGroupModule } from 'primeng/avatargroup';
52
52
  import { BadgeModule } from 'primeng/badge';
53
53
  import { CardModule } from 'primeng/card';
54
54
  import { CheckboxModule } from 'primeng/checkbox';
55
+ import { ChipModule } from 'primeng/chip';
55
56
  import { ContextMenuModule } from 'primeng/contextmenu';
56
57
  import { DataViewModule } from 'primeng/dataview';
57
58
  import { DatePickerModule, DatePicker } from 'primeng/datepicker';
@@ -1887,11 +1888,11 @@ class AppMessageHelpComponent {
1887
1888
  return null;
1888
1889
  }
1889
1890
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AppMessageHelpComponent, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Component });
1890
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AppMessageHelpComponent, isStandalone: true, selector: "dsx-message-help", inputs: { control: "control", currentIdControl: "currentIdControl", helperFunction: "helperFunction", dataSource: "dataSource", key: "key", umbral: "umbral", debugMode: "debugMode", idFieldName: "idFieldName", maxSugerencias: "maxSugerencias" }, ngImport: i0, template: "<div class=\"dsx-help-slot\" [class.is-visible]=\"isHelpVisible()\">\r\n @if (isHelpVisible()) {\r\n <div\r\n class=\"dsx-help-message\"\r\n [class.is-warning]=\"helperFunction === 'similarity'\"\r\n >\r\n @if (helperFunction === \"similarity\") {\r\n <icon-dsx name=\"emergency_home\"></icon-dsx>\r\n } @else {\r\n <icon-dsx name=\"chat_info\"></icon-dsx>\r\n }\r\n <span>{{ getHelpMessage() }}</span>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%}.dsx-help-slot{max-height:0;margin-top:0;overflow:hidden;opacity:0;transition:max-height .22s ease,margin-top .22s ease,opacity .18s ease}.dsx-help-slot.is-visible{max-height:4rem;margin-top:.3rem;opacity:1}.dsx-help-message{--dsx-help-color: #067647;--dsx-help-bg: #ecfdf3;color:var(--dsx-help-color);font-family:Roboto,Montserrat,sans-serif;font-size:clamp(.75rem,.72rem + .12vw,.8125rem);font-weight:400;line-height:1.35;letter-spacing:.01em;display:flex;align-items:center;gap:.35rem;position:relative;z-index:1;padding:.2rem .45rem;border-left:3px solid var(--dsx-help-color);border-radius:0 6px 6px 0;background:var(--dsx-help-bg);box-shadow:0 1px 2px #00000014;width:100%;max-width:100%;white-space:normal;word-break:break-word;transform:translateY(-2px);transition:transform .2s ease;box-sizing:border-box}.dsx-help-message.is-warning{--dsx-help-color: #b54708;--dsx-help-bg: #fffaeb}.dsx-help-slot.is-visible .dsx-help-message{transform:translateY(0)}.dsx-help-icon{flex-shrink:0;font-size:.78rem;line-height:1;color:var(--dsx-help-color)}.dsx-help-message svg{width:1rem;height:1rem;flex-shrink:0;display:inline-block;vertical-align:middle;transition:color .2s ease,fill .2s ease,stroke .2s ease}.dsx-help-message svg,.dsx-help-message svg path{fill:var(--dsx-help-color);stroke:var(--dsx-help-color)}.dsx-help-message span{flex:1;word-break:break-word}@media(max-width:640px){.dsx-help-slot.is-visible{max-height:5rem}.dsx-help-message{width:100%;max-width:100%}}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:3;pointer-events:none}:host-context(.p-floatlabel) .dsx-help-slot{max-height:none;margin-top:.25rem;overflow:visible;opacity:0;transform:translateY(-3px);transition:opacity .16s ease,transform .18s ease}:host-context(.p-floatlabel) .dsx-help-slot.is-visible{opacity:1;transform:translateY(0)}:host-context(.p-floatlabel) .dsx-help-message{width:100%;max-width:100%;box-sizing:border-box}\n"], dependencies: [{ kind: "component", type: IconDsxComponent, selector: "icon-dsx", inputs: ["name", "style", "debug"] }] });
1891
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: AppMessageHelpComponent, isStandalone: true, selector: "dsx-message-help", inputs: { control: "control", currentIdControl: "currentIdControl", helperFunction: "helperFunction", dataSource: "dataSource", key: "key", umbral: "umbral", debugMode: "debugMode", idFieldName: "idFieldName", maxSugerencias: "maxSugerencias" }, ngImport: i0, template: "<div class=\"dsx-help-slot\" [class.is-visible]=\"isHelpVisible()\">\r\n @if (isHelpVisible()) {\r\n <div\r\n class=\"dsx-help-message\"\r\n [class.is-warning]=\"helperFunction === 'similarity'\"\r\n >\r\n <span>{{ getHelpMessage() }}</span>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%}.dsx-help-slot{max-height:0;margin-top:0;overflow:hidden;opacity:0;transition:max-height .22s ease,margin-top .22s ease,opacity .18s ease}.dsx-help-slot.is-visible{max-height:4rem;margin-top:.15rem;opacity:1}.dsx-help-message{--dsx-help-color: #059669;color:var(--dsx-help-color);font-family:Roboto,Montserrat,sans-serif;font-size:clamp(.7rem,.68rem + .08vw,.75rem);font-weight:400;line-height:1.4;letter-spacing:.01em;padding:.1rem 0;border:none;background:transparent;box-shadow:none;width:100%;max-width:100%;white-space:normal;word-break:break-word;box-sizing:border-box}.dsx-help-message.is-warning{--dsx-help-color: #d97706}.dsx-help-message span{display:block;word-break:break-word;color:var(--dsx-help-color)}.dsx-help-slot.is-visible .dsx-help-message{animation:fadeInHelp .2s ease}@keyframes fadeInHelp{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:translateY(0)}}@media(max-width:640px){.dsx-help-slot.is-visible{max-height:5rem}.dsx-help-message{font-size:clamp(.6rem,.55rem + .15vw,.65rem);padding:.05rem 0}}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:3;pointer-events:none}:host-context(.p-floatlabel) .dsx-help-slot{max-height:none;margin-top:.1rem;overflow:visible;opacity:0;transform:translateY(-2px);transition:opacity .16s ease,transform .18s ease}:host-context(.p-floatlabel) .dsx-help-slot.is-visible{opacity:1;transform:translateY(0)}:host-context(.p-floatlabel) .dsx-help-message{width:100%;max-width:100%;padding:.05rem 0;box-sizing:border-box}\n"] });
1891
1892
  }
1892
1893
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: AppMessageHelpComponent, decorators: [{
1893
1894
  type: Component,
1894
- args: [{ selector: 'dsx-message-help', imports: [IconDsxComponent], template: "<div class=\"dsx-help-slot\" [class.is-visible]=\"isHelpVisible()\">\r\n @if (isHelpVisible()) {\r\n <div\r\n class=\"dsx-help-message\"\r\n [class.is-warning]=\"helperFunction === 'similarity'\"\r\n >\r\n @if (helperFunction === \"similarity\") {\r\n <icon-dsx name=\"emergency_home\"></icon-dsx>\r\n } @else {\r\n <icon-dsx name=\"chat_info\"></icon-dsx>\r\n }\r\n <span>{{ getHelpMessage() }}</span>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%}.dsx-help-slot{max-height:0;margin-top:0;overflow:hidden;opacity:0;transition:max-height .22s ease,margin-top .22s ease,opacity .18s ease}.dsx-help-slot.is-visible{max-height:4rem;margin-top:.3rem;opacity:1}.dsx-help-message{--dsx-help-color: #067647;--dsx-help-bg: #ecfdf3;color:var(--dsx-help-color);font-family:Roboto,Montserrat,sans-serif;font-size:clamp(.75rem,.72rem + .12vw,.8125rem);font-weight:400;line-height:1.35;letter-spacing:.01em;display:flex;align-items:center;gap:.35rem;position:relative;z-index:1;padding:.2rem .45rem;border-left:3px solid var(--dsx-help-color);border-radius:0 6px 6px 0;background:var(--dsx-help-bg);box-shadow:0 1px 2px #00000014;width:100%;max-width:100%;white-space:normal;word-break:break-word;transform:translateY(-2px);transition:transform .2s ease;box-sizing:border-box}.dsx-help-message.is-warning{--dsx-help-color: #b54708;--dsx-help-bg: #fffaeb}.dsx-help-slot.is-visible .dsx-help-message{transform:translateY(0)}.dsx-help-icon{flex-shrink:0;font-size:.78rem;line-height:1;color:var(--dsx-help-color)}.dsx-help-message svg{width:1rem;height:1rem;flex-shrink:0;display:inline-block;vertical-align:middle;transition:color .2s ease,fill .2s ease,stroke .2s ease}.dsx-help-message svg,.dsx-help-message svg path{fill:var(--dsx-help-color);stroke:var(--dsx-help-color)}.dsx-help-message span{flex:1;word-break:break-word}@media(max-width:640px){.dsx-help-slot.is-visible{max-height:5rem}.dsx-help-message{width:100%;max-width:100%}}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:3;pointer-events:none}:host-context(.p-floatlabel) .dsx-help-slot{max-height:none;margin-top:.25rem;overflow:visible;opacity:0;transform:translateY(-3px);transition:opacity .16s ease,transform .18s ease}:host-context(.p-floatlabel) .dsx-help-slot.is-visible{opacity:1;transform:translateY(0)}:host-context(.p-floatlabel) .dsx-help-message{width:100%;max-width:100%;box-sizing:border-box}\n"] }]
1895
+ args: [{ selector: 'dsx-message-help', imports: [IconDsxComponent], template: "<div class=\"dsx-help-slot\" [class.is-visible]=\"isHelpVisible()\">\r\n @if (isHelpVisible()) {\r\n <div\r\n class=\"dsx-help-message\"\r\n [class.is-warning]=\"helperFunction === 'similarity'\"\r\n >\r\n <span>{{ getHelpMessage() }}</span>\r\n </div>\r\n }\r\n</div>\r\n", styles: [":host{display:block;width:100%}.dsx-help-slot{max-height:0;margin-top:0;overflow:hidden;opacity:0;transition:max-height .22s ease,margin-top .22s ease,opacity .18s ease}.dsx-help-slot.is-visible{max-height:4rem;margin-top:.15rem;opacity:1}.dsx-help-message{--dsx-help-color: #059669;color:var(--dsx-help-color);font-family:Roboto,Montserrat,sans-serif;font-size:clamp(.7rem,.68rem + .08vw,.75rem);font-weight:400;line-height:1.4;letter-spacing:.01em;padding:.1rem 0;border:none;background:transparent;box-shadow:none;width:100%;max-width:100%;white-space:normal;word-break:break-word;box-sizing:border-box}.dsx-help-message.is-warning{--dsx-help-color: #d97706}.dsx-help-message span{display:block;word-break:break-word;color:var(--dsx-help-color)}.dsx-help-slot.is-visible .dsx-help-message{animation:fadeInHelp .2s ease}@keyframes fadeInHelp{0%{opacity:0;transform:translateY(-2px)}to{opacity:1;transform:translateY(0)}}@media(max-width:640px){.dsx-help-slot.is-visible{max-height:5rem}.dsx-help-message{font-size:clamp(.6rem,.55rem + .15vw,.65rem);padding:.05rem 0}}:host-context(.p-floatlabel){position:absolute;top:100%;left:0;width:100%;z-index:3;pointer-events:none}:host-context(.p-floatlabel) .dsx-help-slot{max-height:none;margin-top:.1rem;overflow:visible;opacity:0;transform:translateY(-2px);transition:opacity .16s ease,transform .18s ease}:host-context(.p-floatlabel) .dsx-help-slot.is-visible{opacity:1;transform:translateY(0)}:host-context(.p-floatlabel) .dsx-help-message{width:100%;max-width:100%;padding:.05rem 0;box-sizing:border-box}\n"] }]
1895
1896
  }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { control: [{
1896
1897
  type: Input
1897
1898
  }], currentIdControl: [{
@@ -5077,6 +5078,435 @@ const ACTION_CONFIG = {
5077
5078
  },
5078
5079
  };
5079
5080
 
5081
+ // dsx-calendar-view.component.ts - VERSIÓN CON isDevMode()
5082
+ class DsxCalendarView {
5083
+ // ============================================
5084
+ // INPUTS
5085
+ // ============================================
5086
+ // Two-way binding
5087
+ startDate = model(null, ...(ngDevMode ? [{ debugName: "startDate" }] : /* istanbul ignore next */ []));
5088
+ endDate = model(null, ...(ngDevMode ? [{ debugName: "endDate" }] : /* istanbul ignore next */ []));
5089
+ excludeSundays = model(false, ...(ngDevMode ? [{ debugName: "excludeSundays" }] : /* istanbul ignore next */ []));
5090
+ // Configuración
5091
+ holidays = input([], ...(ngDevMode ? [{ debugName: "holidays" }] : /* istanbul ignore next */ []));
5092
+ compactMode = input(true, ...(ngDevMode ? [{ debugName: "compactMode" }] : /* istanbul ignore next */ []));
5093
+ showKPI = input(true, ...(ngDevMode ? [{ debugName: "showKPI" }] : /* istanbul ignore next */ []));
5094
+ debugMode = input(false, ...(ngDevMode ? [{ debugName: "debugMode" }] : /* istanbul ignore next */ []));
5095
+ isEdit = input(false, ...(ngDevMode ? [{ debugName: "isEdit" }] : /* istanbul ignore next */ []));
5096
+ showControls = input(true, ...(ngDevMode ? [{ debugName: "showControls" }] : /* istanbul ignore next */ []));
5097
+ // Output
5098
+ rangeChanged = output();
5099
+ // ============================================
5100
+ // STATE
5101
+ // ============================================
5102
+ startDateStr = signal('', ...(ngDevMode ? [{ debugName: "startDateStr" }] : /* istanbul ignore next */ []));
5103
+ endDateStr = signal('', ...(ngDevMode ? [{ debugName: "endDateStr" }] : /* istanbul ignore next */ []));
5104
+ // ✅ Exponer isDevMode como propiedad pública para el template si es necesario
5105
+ isDevMode = isDevMode();
5106
+ // ============================================
5107
+ // COMPUTED
5108
+ // ============================================
5109
+ shouldShowControls = computed(() => {
5110
+ return this.isEdit() || this.showControls();
5111
+ }, ...(ngDevMode ? [{ debugName: "shouldShowControls" }] : /* istanbul ignore next */ []));
5112
+ months = computed(() => {
5113
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5114
+ const start = this.startDateStr();
5115
+ const end = this.endDateStr();
5116
+ if (!start || !end) {
5117
+ this.logPerformance('months', perfStart);
5118
+ return [];
5119
+ }
5120
+ const zonaHoraria = 'America/Guatemala';
5121
+ const startDate = moment(start).tz(zonaHoraria).toDate();
5122
+ const endDate = moment(end).tz(zonaHoraria).toDate();
5123
+ if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
5124
+ this.logDebug('Fechas inválidas:', { start, end });
5125
+ this.logPerformance('months', perfStart);
5126
+ return [];
5127
+ }
5128
+ this.logDebug('fecha inicio: ', startDate);
5129
+ this.logDebug('fecha final: ', endDate);
5130
+ const startMonth = new Date(startDate.getFullYear(), startDate.getMonth(), 1);
5131
+ const endMonth = new Date(endDate.getFullYear(), endDate.getMonth(), 1);
5132
+ const monthsToShow = [];
5133
+ monthsToShow.push(this.generateMonth(startMonth, startDate, endDate));
5134
+ if (startMonth.getTime() !== endMonth.getTime()) {
5135
+ monthsToShow.push(this.generateMonth(endMonth, startDate, endDate));
5136
+ }
5137
+ this.logPerformance('months', perfStart);
5138
+ return monthsToShow;
5139
+ }, ...(ngDevMode ? [{ debugName: "months" }] : /* istanbul ignore next */ []));
5140
+ formattedHolidays = computed(() => {
5141
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5142
+ const holidays = this.kpi()?.holidaysList || [];
5143
+ const result = holidays.map((h) => ({
5144
+ date: this.normalizeDateToString(h.dateHoliday),
5145
+ label: h.labelHoliday,
5146
+ }));
5147
+ this.logPerformance('formattedHolidays', perfStart);
5148
+ return result;
5149
+ }, ...(ngDevMode ? [{ debugName: "formattedHolidays" }] : /* istanbul ignore next */ []));
5150
+ // ============================================
5151
+ // KPI: Estadísticas del rango (CORREGIDO)
5152
+ // ============================================
5153
+ kpi = computed(() => {
5154
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5155
+ const start = this.startDateStr();
5156
+ const end = this.endDateStr();
5157
+ if (!start || !end) {
5158
+ this.logPerformance('kpi', perfStart);
5159
+ return null;
5160
+ }
5161
+ const startDate = new Date(start);
5162
+ const endDate = new Date(end);
5163
+ if (isNaN(startDate.getTime()) || isNaN(endDate.getTime())) {
5164
+ this.logPerformance('kpi', perfStart);
5165
+ return null;
5166
+ }
5167
+ const holidays = this.holidays();
5168
+ const excludeSun = this.excludeSundays();
5169
+ // 1. Calcular días totales
5170
+ const totalDays = this.getDaysBetween(startDate, endDate);
5171
+ // 2. Calcular domingos en el rango
5172
+ let sundays = 0;
5173
+ const current = new Date(startDate);
5174
+ while (current <= endDate) {
5175
+ if (current.getDay() === 0) {
5176
+ sundays++;
5177
+ }
5178
+ current.setDate(current.getDate() + 1);
5179
+ }
5180
+ // 3. 🔥 Calcular feriados en el rango
5181
+ const holidaysInRange = holidays.filter((h) => {
5182
+ const hStr = this.normalizeDateToString(h.dateHoliday);
5183
+ return hStr >= start && hStr <= end;
5184
+ });
5185
+ // 4. 🔥 Días hábiles = total - domingos - feriados
5186
+ let businessDays = totalDays;
5187
+ if (excludeSun) {
5188
+ businessDays = totalDays - sundays;
5189
+ }
5190
+ // 🔥 CORRECCIÓN: Restar feriados independientemente de excludeSun
5191
+ businessDays = businessDays - holidaysInRange.length;
5192
+ const kpiResult = {
5193
+ totalDays,
5194
+ businessDays,
5195
+ holidays: holidaysInRange.length,
5196
+ sundays: excludeSun ? sundays : 0,
5197
+ holidaysList: holidaysInRange,
5198
+ startDate: start,
5199
+ endDate: end,
5200
+ };
5201
+ this.logDebug('KPI calculado:', kpiResult);
5202
+ this.logPerformance('kpi', perfStart);
5203
+ return kpiResult;
5204
+ }, ...(ngDevMode ? [{ debugName: "kpi" }] : /* istanbul ignore next */ []));
5205
+ // ============================================
5206
+ // EFFECTS
5207
+ // ============================================
5208
+ constructor() {
5209
+ effect(() => {
5210
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5211
+ const value = this.startDate();
5212
+ const formatted = this.formatInputDate(value);
5213
+ this.startDateStr.set(formatted);
5214
+ this.logDebug('startDate actualizado:', { value, formatted });
5215
+ this.emitRangeChange();
5216
+ this.logPerformance('startDate effect', perfStart);
5217
+ });
5218
+ effect(() => {
5219
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5220
+ const value = this.endDate();
5221
+ const formatted = this.formatInputDate(value);
5222
+ this.endDateStr.set(formatted);
5223
+ this.logDebug('endDate actualizado:', { value, formatted });
5224
+ this.emitRangeChange();
5225
+ this.logPerformance('endDate effect', perfStart);
5226
+ });
5227
+ effect(() => {
5228
+ this.logDebug('excludeSundays actualizado:', this.excludeSundays());
5229
+ });
5230
+ }
5231
+ // ============================================
5232
+ // PRIVATE METHODS
5233
+ // ============================================
5234
+ normalizeDateToString(date) {
5235
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5236
+ let result = '';
5237
+ if (!date) {
5238
+ this.logPerformance('normalizeDateToString', perfStart);
5239
+ return result;
5240
+ }
5241
+ if (date instanceof Date) {
5242
+ if (isNaN(date.getTime())) {
5243
+ this.logPerformance('normalizeDateToString', perfStart);
5244
+ return result;
5245
+ }
5246
+ result = this.formatDate(date);
5247
+ }
5248
+ if (typeof date === 'string') {
5249
+ if (/^\d{4}-\d{2}-\d{2}$/.test(date)) {
5250
+ result = date;
5251
+ }
5252
+ else {
5253
+ const parsed = new Date(date);
5254
+ if (!isNaN(parsed.getTime())) {
5255
+ result = this.formatDate(parsed);
5256
+ }
5257
+ }
5258
+ }
5259
+ this.logPerformance('normalizeDateToString', perfStart);
5260
+ return result;
5261
+ }
5262
+ formatInputDate(value) {
5263
+ if (!value)
5264
+ return '';
5265
+ if (value instanceof Date) {
5266
+ if (isNaN(value.getTime()))
5267
+ return '';
5268
+ return this.formatDate(value);
5269
+ }
5270
+ if (typeof value === 'string') {
5271
+ if (/^\d{4}-\d{2}-\d{2}$/.test(value)) {
5272
+ return value;
5273
+ }
5274
+ const date = new Date(value);
5275
+ if (!isNaN(date.getTime())) {
5276
+ return this.formatDate(date);
5277
+ }
5278
+ return '';
5279
+ }
5280
+ return '';
5281
+ }
5282
+ emitRangeChange() {
5283
+ const start = this.startDateStr();
5284
+ const end = this.endDateStr();
5285
+ if (start && end) {
5286
+ this.logDebug('Emitiendo rangeChanged:', { start, end });
5287
+ this.rangeChanged.emit({ start, end });
5288
+ }
5289
+ }
5290
+ // ============================================
5291
+ // PUBLIC METHODS (Template)
5292
+ // ============================================
5293
+ onStartDateChange(event) {
5294
+ if (!this.isEdit())
5295
+ return;
5296
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5297
+ const value = event.target.value;
5298
+ this.startDate.set(value);
5299
+ this.logDebug('onStartDateChange:', value);
5300
+ this.logPerformance('onStartDateChange', perfStart);
5301
+ }
5302
+ onEndDateChange(event) {
5303
+ if (!this.isEdit())
5304
+ return;
5305
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5306
+ const value = event.target.value;
5307
+ this.endDate.set(value);
5308
+ this.logDebug('onEndDateChange:', value);
5309
+ this.logPerformance('onEndDateChange', perfStart);
5310
+ }
5311
+ onToggleSundays(event) {
5312
+ if (!this.isEdit())
5313
+ return;
5314
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5315
+ const checked = event.target.checked;
5316
+ this.excludeSundays.set(checked);
5317
+ this.logDebug('onToggleSundays:', checked);
5318
+ this.logPerformance('onToggleSundays', perfStart);
5319
+ }
5320
+ // ============================================
5321
+ // GENERATE MONTH
5322
+ // ============================================
5323
+ generateMonth(monthDate, rangeStart, rangeEnd) {
5324
+ const perfStart = this.shouldLog() ? performance.now() : 0;
5325
+ const year = monthDate.getFullYear();
5326
+ const month = monthDate.getMonth();
5327
+ const firstDay = new Date(year, month, 1);
5328
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
5329
+ const startDayOfWeek = firstDay.getDay();
5330
+ const days = [];
5331
+ const holidays = this.holidays();
5332
+ const totalRangeDays = this.getDaysBetween(rangeStart, rangeEnd);
5333
+ // Días anteriores
5334
+ const prevMonthLastDay = new Date(year, month, 0).getDate();
5335
+ for (let i = startDayOfWeek - 1; i >= 0; i--) {
5336
+ const d = new Date(year, month, -i);
5337
+ days.push({
5338
+ date: d,
5339
+ day: prevMonthLastDay - i,
5340
+ isCurrentMonth: false,
5341
+ isSunday: d.getDay() === 0,
5342
+ isInRange: false,
5343
+ isStart: false,
5344
+ isEnd: false,
5345
+ isHoliday: false,
5346
+ disabled: false,
5347
+ });
5348
+ }
5349
+ // Días del mes actual
5350
+ for (let i = 1; i <= daysInMonth; i++) {
5351
+ const d = new Date(year, month, i);
5352
+ const dateStr = this.formatDate(d);
5353
+ const holiday = holidays.find((h) => {
5354
+ const hStr = this.normalizeDateToString(h.dateHoliday);
5355
+ return hStr === dateStr;
5356
+ });
5357
+ const isSunday = d.getDay() === 0;
5358
+ const isInRange = this.isDateInRange(d, rangeStart, rangeEnd);
5359
+ const isStart = this.isSameDay(d, rangeStart);
5360
+ const isEnd = this.isSameDay(d, rangeEnd);
5361
+ const disabled = this.excludeSundays() && isSunday;
5362
+ let rangePosition = 0;
5363
+ if (isInRange && !disabled) {
5364
+ const dayDiff = this.getDaysBetween(rangeStart, d) - 1;
5365
+ rangePosition = totalRangeDays > 1 ? dayDiff / (totalRangeDays - 1) : 0;
5366
+ }
5367
+ days.push({
5368
+ date: d,
5369
+ day: i,
5370
+ isCurrentMonth: true,
5371
+ isSunday,
5372
+ isInRange,
5373
+ isStart,
5374
+ isEnd,
5375
+ isHoliday: !!holiday,
5376
+ holidayLabel: holiday?.labelHoliday,
5377
+ disabled,
5378
+ rangePosition,
5379
+ });
5380
+ }
5381
+ // Días siguientes
5382
+ const totalCells = Math.ceil((startDayOfWeek + daysInMonth) / 7) * 7;
5383
+ const remaining = totalCells - days.length;
5384
+ for (let i = 1; i <= remaining; i++) {
5385
+ const d = new Date(year, month + 1, i);
5386
+ days.push({
5387
+ date: d,
5388
+ day: i,
5389
+ isCurrentMonth: false,
5390
+ isSunday: d.getDay() === 0,
5391
+ isInRange: false,
5392
+ isStart: false,
5393
+ isEnd: false,
5394
+ isHoliday: false,
5395
+ disabled: false,
5396
+ });
5397
+ }
5398
+ // Recalcular para días del mes actual
5399
+ for (const day of days) {
5400
+ if (day.isCurrentMonth) {
5401
+ const d = day.date;
5402
+ day.isInRange = this.isDateInRange(d, rangeStart, rangeEnd);
5403
+ day.isStart = this.isSameDay(d, rangeStart);
5404
+ day.isEnd = this.isSameDay(d, rangeEnd);
5405
+ day.disabled = this.excludeSundays() && day.isSunday;
5406
+ if (day.isInRange && !day.disabled) {
5407
+ const dayDiff = this.getDaysBetween(rangeStart, d) - 1;
5408
+ day.rangePosition =
5409
+ totalRangeDays > 1 ? dayDiff / (totalRangeDays - 1) : 0;
5410
+ }
5411
+ }
5412
+ }
5413
+ const monthNames = [
5414
+ 'Enero',
5415
+ 'Febrero',
5416
+ 'Marzo',
5417
+ 'Abril',
5418
+ 'Mayo',
5419
+ 'Junio',
5420
+ 'Julio',
5421
+ 'Agosto',
5422
+ 'Septiembre',
5423
+ 'Octubre',
5424
+ 'Noviembre',
5425
+ 'Diciembre',
5426
+ ];
5427
+ this.logPerformance('generateMonth', perfStart);
5428
+ return {
5429
+ label: `${monthNames[month]} ${year}`,
5430
+ days,
5431
+ };
5432
+ }
5433
+ // ============================================
5434
+ // HELPERS
5435
+ // ============================================
5436
+ getDaysBetween(start, end) {
5437
+ const s = new Date(start.getFullYear(), start.getMonth(), start.getDate());
5438
+ const e = new Date(end.getFullYear(), end.getMonth(), end.getDate());
5439
+ const diffTime = Math.abs(e.getTime() - s.getTime());
5440
+ return Math.ceil(diffTime / (1000 * 60 * 60 * 24)) + 1;
5441
+ }
5442
+ isDateInRange(date, start, end) {
5443
+ const d = new Date(date.getFullYear(), date.getMonth(), date.getDate());
5444
+ const s = new Date(start.getFullYear(), start.getMonth(), start.getDate());
5445
+ const e = new Date(end.getFullYear(), end.getMonth(), end.getDate());
5446
+ return d >= s && d <= e;
5447
+ }
5448
+ isSameDay(d1, d2) {
5449
+ if (!d2)
5450
+ return false;
5451
+ return (d1.getFullYear() === d2.getFullYear() &&
5452
+ d1.getMonth() === d2.getMonth() &&
5453
+ d1.getDate() === d2.getDate());
5454
+ }
5455
+ formatDate(date) {
5456
+ const y = date.getFullYear();
5457
+ const m = String(date.getMonth() + 1).padStart(2, '0');
5458
+ const d = String(date.getDate()).padStart(2, '0');
5459
+ return `${y}-${m}-${d}`;
5460
+ }
5461
+ // ============================================
5462
+ // LOGGING - SIMPLE Y SEGURO
5463
+ // ============================================
5464
+ /**
5465
+ * Verifica si debemos mostrar logs
5466
+ * ✅ Usa isDevMode() de Angular - simple y confiable
5467
+ */
5468
+ shouldLog() {
5469
+ return isDevMode() && this.debugMode();
5470
+ }
5471
+ /**
5472
+ * Log de depuración - Solo visible en desarrollo con debugMode=true
5473
+ * 🔥 NUNCA se ejecuta en producción
5474
+ */
5475
+ logDebug(...args) {
5476
+ if (this.shouldLog()) {
5477
+ console.log('[DsxCalendarView]', ...args);
5478
+ }
5479
+ }
5480
+ /**
5481
+ * Log de rendimiento - Mide tiempos de ejecución
5482
+ * 🔥 NUNCA se ejecuta en producción
5483
+ */
5484
+ logPerformance(method, startTime) {
5485
+ if (this.shouldLog() && startTime > 0) {
5486
+ const elapsed = (performance.now() - startTime).toFixed(2);
5487
+ if (Number(elapsed) > 0.1) {
5488
+ console.log(`[DsxCalendarView] ⚡ ${method} - ${elapsed}ms`);
5489
+ }
5490
+ }
5491
+ }
5492
+ // ============================================
5493
+ // TEMPLATE HELPERS
5494
+ // ============================================
5495
+ chunk(arr, size) {
5496
+ const result = [];
5497
+ for (let i = 0; i < arr.length; i += size) {
5498
+ result.push(arr.slice(i, i + size));
5499
+ }
5500
+ return result;
5501
+ }
5502
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxCalendarView, deps: [], target: i0.ɵɵFactoryTarget.Component });
5503
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.17", type: DsxCalendarView, isStandalone: true, selector: "dsx-calendar-view", inputs: { startDate: { classPropertyName: "startDate", publicName: "startDate", isSignal: true, isRequired: false, transformFunction: null }, endDate: { classPropertyName: "endDate", publicName: "endDate", isSignal: true, isRequired: false, transformFunction: null }, excludeSundays: { classPropertyName: "excludeSundays", publicName: "excludeSundays", isSignal: true, isRequired: false, transformFunction: null }, holidays: { classPropertyName: "holidays", publicName: "holidays", isSignal: true, isRequired: false, transformFunction: null }, compactMode: { classPropertyName: "compactMode", publicName: "compactMode", isSignal: true, isRequired: false, transformFunction: null }, showKPI: { classPropertyName: "showKPI", publicName: "showKPI", isSignal: true, isRequired: false, transformFunction: null }, debugMode: { classPropertyName: "debugMode", publicName: "debugMode", isSignal: true, isRequired: false, transformFunction: null }, isEdit: { classPropertyName: "isEdit", publicName: "isEdit", isSignal: true, isRequired: false, transformFunction: null }, showControls: { classPropertyName: "showControls", publicName: "showControls", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { startDate: "startDateChange", endDate: "endDateChange", excludeSundays: "excludeSundaysChange", rangeChanged: "rangeChanged" }, ngImport: i0, template: "<!-- dsx-calendar-view.html - ACTUALIZADO CON showControls -->\r\n\r\n<div class=\"calendar-range\" [class.compact]=\"compactMode()\">\r\n <!-- ==========================================\r\n CONTROLES - Ocultables con showControls\r\n ========================================== -->\r\n @if (shouldShowControls()) {\r\n <div class=\"controls\">\r\n <div class=\"control-group\">\r\n <label>Fecha Inicio</label>\r\n <input\r\n type=\"date\"\r\n [value]=\"startDateStr()\"\r\n (input)=\"onStartDateChange($event)\"\r\n [disabled]=\"!isEdit()\"\r\n />\r\n </div>\r\n <div class=\"control-group\">\r\n <label>Fecha Fin</label>\r\n <input\r\n type=\"date\"\r\n [value]=\"endDateStr()\"\r\n (input)=\"onEndDateChange($event)\"\r\n [disabled]=\"!isEdit()\"\r\n />\r\n </div>\r\n <div class=\"control-group checkbox\">\r\n <label>\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"excludeSundays()\"\r\n (change)=\"onToggleSundays($event)\"\r\n [disabled]=\"!isEdit()\"\r\n />\r\n Excluir Domingos\r\n </label>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- ==========================================\r\n KPIs\r\n ========================================== -->\r\n @if (showKPI() && kpi()) {\r\n <div class=\"kpi-container\">\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-regular fa-calendar-days\"></i>\r\n D\u00EDas calendario\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.totalDays }}</span>\r\n </div>\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-regular fa-circle-check\"></i>\r\n D\u00EDas h\u00E1biles\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.businessDays }}</span>\r\n </div>\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-solid fa-not-equal\"></i>\r\n Feriados\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.holidays }}</span>\r\n </div>\r\n @if (excludeSundays()) {\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-solid fa-calendar-day\"></i>\r\n Domingos\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.sundays }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- ==========================================\r\n CALENDARIO\r\n ========================================== -->\r\n <div class=\"months-grid\">\r\n @for (month of months(); track month.label) {\r\n <div class=\"month-card\">\r\n <div class=\"month-title\">{{ month.label }}</div>\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n @for (dia of ['Do','Lu','Ma','Mi','Ju','Vi','Sa']; track dia) {\r\n <th>{{ dia }}</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (row of chunk(month.days, 7); track $index) {\r\n <tr>\r\n @for (day of row; track day.date) {\r\n <td\r\n [class.other-month]=\"!day.isCurrentMonth\"\r\n [class.sunday]=\"day.isSunday && !day.disabled && day.isCurrentMonth\"\r\n [class.disabled-sunday]=\"day.disabled\"\r\n [class.in-range]=\"day.isInRange && day.isCurrentMonth\"\r\n [class.start-date]=\"day.isStart && day.isCurrentMonth\"\r\n [class.end-date]=\"day.isEnd && day.isCurrentMonth\"\r\n [class.holiday]=\"day.isHoliday && day.isCurrentMonth\"\r\n [class.same-day]=\"day.isStart && day.isEnd\"\r\n [style.--range-position]=\"day.rangePosition\"\r\n [title]=\"day.isHoliday ? day.holidayLabel : ''\"\r\n >\r\n <span class=\"day-number\">{{ day.day }}</span>\r\n @if (day.isHoliday && day.isCurrentMonth) {\r\n <span class=\"holiday-label\">{{ day.holidayLabel }}</span>\r\n }\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- ==========================================\r\n FERIADOS DETALLE\r\n ========================================== -->\r\n @if (kpi()?.holidays && formattedHolidays().length) {\r\n <div class=\"holidays-detail\">\r\n <div class=\"holidays-detail-title\">\r\n <i class=\"fa-solid fa-calendar-day\"></i>\r\n Feriados en el per\u00EDodo\r\n </div>\r\n <div class=\"holidays-detail-list\">\r\n @for (h of formattedHolidays(); track h.date) {\r\n <span class=\"holiday-detail-tag\">\r\n <i class=\"fa-regular fa-calendar\"></i>\r\n {{ h.date }} - {{ h.label }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".calendar-range{font-family:system-ui,-apple-system,sans-serif;max-width:100%;padding:8px}.calendar-range.compact{max-width:750px;margin:0 auto}.controls{display:flex;flex-wrap:wrap;gap:16px 24px;margin-bottom:12px;padding:10px 16px;background:#f8f9fa;border-radius:6px;border:1px solid #e9ecef;align-items:center;justify-content:center}.control-group{display:flex;align-items:center;gap:8px}.control-group label{font-size:.8rem;font-weight:600;color:#495057;white-space:nowrap}.control-group input[type=date]{padding:4px 8px;border:1px solid #ced4da;border-radius:4px;font-size:.8rem;background:#fff;min-width:130px}.control-group.checkbox{margin-left:0;padding-left:8px;border-left:1px solid #dee2e6}.control-group.checkbox label{font-weight:400;font-size:.75rem;cursor:pointer;display:flex;align-items:center;gap:4px}.kpi-container{display:flex;flex-wrap:wrap;gap:20px 32px;padding:10px 20px;margin-bottom:12px;background:linear-gradient(135deg,#f8f9fa,#e9ecef);border-radius:8px;border:1px solid #dee2e6;justify-content:center;align-items:center}.kpi-item{display:flex;align-items:center;gap:6px;font-size:.85rem}.kpi-label{color:#6c757d;font-weight:500}.kpi-value{font-weight:700;color:#212529;font-size:1.1rem;min-width:24px;text-align:center;background:#fff;padding:0 8px;border-radius:4px}.kpi-holidays{display:none}.months-grid{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.month-card{flex:1 1 auto;min-width:230px;max-width:300px;background:#fff;border:1px solid #dee2e6;border-radius:6px;padding:6px 4px 4px}.month-title{text-align:center;font-size:.85rem;font-weight:600;color:#212529;margin-bottom:4px;padding:2px 0}.calendar-table{width:100%;border-collapse:collapse;font-size:.7rem;table-layout:fixed}.calendar-table th{text-align:center;font-weight:600;color:#6c757d;padding:2px 0;font-size:.65rem}.calendar-table td{text-align:center;padding:2px 0;border-radius:3px;height:28px;vertical-align:middle;font-size:.7rem;transition:background .1s;position:relative}.other-month{color:#ced4da;background:transparent}.sunday{background:#ffe0e0}.disabled-sunday{background:#f1f3f5!important;color:#adb5bd!important;cursor:default}.in-range{background:#c4eeff}.start-date{background:#28a745!important;color:#fff!important;border-radius:3px 0 0 3px;font-weight:700}.end-date{background:#d17015!important;color:#fff!important;border-radius:0 3px 3px 0;font-weight:700}.same-day{background:#28a745!important;color:#fff!important;border-radius:3px}.day-number{display:block;font-size:.75rem;line-height:1.2}.holiday:not(.other-month){background:#e8f0fe!important;border:2px solid #1a5a8c!important;border-radius:4px!important;position:relative;z-index:1}.holiday .day-number{font-weight:700!important;color:#1a3a5c!important}.holiday-label{display:block;font-size:.45rem;line-height:1.2;color:#1a3a5c;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:95%;margin:0 auto;background:#1a5a8c1a;border-radius:2px;padding:0 2px;font-weight:600}.holiday:hover .holiday-label{white-space:normal;overflow:visible;position:absolute;bottom:100%;left:50%;transform:translate(-50%);background:#1a3a5c;color:#fff;padding:4px 10px;border-radius:4px;font-size:.6rem;z-index:10;box-shadow:0 2px 8px #0003;max-width:180px;white-space:nowrap}.holidays-detail{margin-top:12px;padding:10px 16px;background:#f0f4f8;border:1px solid #1a5a8c;border-radius:8px;border-left:4px solid #1a5a8c}.holidays-detail-title{font-size:.8rem;font-weight:600;color:#1a3a5c;margin-bottom:6px;display:flex;align-items:center;gap:6px}.holidays-detail-title i{color:#1a5a8c}.holidays-detail-list{display:flex;flex-wrap:wrap;gap:6px 12px}.holiday-detail-tag{font-size:.7rem;color:#1a3a5c;background:#e8f0fe;padding:2px 12px;border-radius:12px;border:1px solid #b3cfe5;display:inline-flex;align-items:center;gap:4px;white-space:nowrap}.holiday-detail-tag i{font-size:.6rem;color:#1a5a8c}.control-group input:disabled,.control-group input[disabled]{background:#f1f3f5!important;color:#6c757d!important;cursor:not-allowed;opacity:.7}.control-group input[type=checkbox]:disabled{cursor:not-allowed;opacity:.6}.control-group.checkbox label:has(input:disabled){color:#6c757d;cursor:not-allowed}@media(max-width:600px){.month-card{min-width:180px;max-width:100%}.controls{gap:10px;padding:8px 12px;flex-direction:column;align-items:stretch}.control-group{justify-content:space-between}.control-group input[type=date]{min-width:100px;flex:1}.control-group.checkbox{border-left:none;padding-left:0;justify-content:center}.kpi-container{gap:10px 16px;padding:8px 12px;justify-content:center}.kpi-item{font-size:.75rem}.kpi-value{font-size:.9rem;min-width:20px;padding:0 4px}.holidays-detail{padding:8px 12px}.holiday-detail-tag{font-size:.6rem;padding:2px 8px}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FormsModule }] });
5504
+ }
5505
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxCalendarView, decorators: [{
5506
+ type: Component,
5507
+ args: [{ selector: 'dsx-calendar-view', standalone: true, imports: [CommonModule, FormsModule], template: "<!-- dsx-calendar-view.html - ACTUALIZADO CON showControls -->\r\n\r\n<div class=\"calendar-range\" [class.compact]=\"compactMode()\">\r\n <!-- ==========================================\r\n CONTROLES - Ocultables con showControls\r\n ========================================== -->\r\n @if (shouldShowControls()) {\r\n <div class=\"controls\">\r\n <div class=\"control-group\">\r\n <label>Fecha Inicio</label>\r\n <input\r\n type=\"date\"\r\n [value]=\"startDateStr()\"\r\n (input)=\"onStartDateChange($event)\"\r\n [disabled]=\"!isEdit()\"\r\n />\r\n </div>\r\n <div class=\"control-group\">\r\n <label>Fecha Fin</label>\r\n <input\r\n type=\"date\"\r\n [value]=\"endDateStr()\"\r\n (input)=\"onEndDateChange($event)\"\r\n [disabled]=\"!isEdit()\"\r\n />\r\n </div>\r\n <div class=\"control-group checkbox\">\r\n <label>\r\n <input\r\n type=\"checkbox\"\r\n [checked]=\"excludeSundays()\"\r\n (change)=\"onToggleSundays($event)\"\r\n [disabled]=\"!isEdit()\"\r\n />\r\n Excluir Domingos\r\n </label>\r\n </div>\r\n </div>\r\n }\r\n\r\n <!-- ==========================================\r\n KPIs\r\n ========================================== -->\r\n @if (showKPI() && kpi()) {\r\n <div class=\"kpi-container\">\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-regular fa-calendar-days\"></i>\r\n D\u00EDas calendario\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.totalDays }}</span>\r\n </div>\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-regular fa-circle-check\"></i>\r\n D\u00EDas h\u00E1biles\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.businessDays }}</span>\r\n </div>\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-solid fa-not-equal\"></i>\r\n Feriados\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.holidays }}</span>\r\n </div>\r\n @if (excludeSundays()) {\r\n <div class=\"kpi-item\">\r\n <span class=\"kpi-label\">\r\n <i class=\"fa-solid fa-calendar-day\"></i>\r\n Domingos\r\n </span>\r\n <span class=\"kpi-value\">{{ kpi()?.sundays }}</span>\r\n </div>\r\n }\r\n </div>\r\n }\r\n\r\n <!-- ==========================================\r\n CALENDARIO\r\n ========================================== -->\r\n <div class=\"months-grid\">\r\n @for (month of months(); track month.label) {\r\n <div class=\"month-card\">\r\n <div class=\"month-title\">{{ month.label }}</div>\r\n <table class=\"calendar-table\">\r\n <thead>\r\n <tr>\r\n @for (dia of ['Do','Lu','Ma','Mi','Ju','Vi','Sa']; track dia) {\r\n <th>{{ dia }}</th>\r\n }\r\n </tr>\r\n </thead>\r\n <tbody>\r\n @for (row of chunk(month.days, 7); track $index) {\r\n <tr>\r\n @for (day of row; track day.date) {\r\n <td\r\n [class.other-month]=\"!day.isCurrentMonth\"\r\n [class.sunday]=\"day.isSunday && !day.disabled && day.isCurrentMonth\"\r\n [class.disabled-sunday]=\"day.disabled\"\r\n [class.in-range]=\"day.isInRange && day.isCurrentMonth\"\r\n [class.start-date]=\"day.isStart && day.isCurrentMonth\"\r\n [class.end-date]=\"day.isEnd && day.isCurrentMonth\"\r\n [class.holiday]=\"day.isHoliday && day.isCurrentMonth\"\r\n [class.same-day]=\"day.isStart && day.isEnd\"\r\n [style.--range-position]=\"day.rangePosition\"\r\n [title]=\"day.isHoliday ? day.holidayLabel : ''\"\r\n >\r\n <span class=\"day-number\">{{ day.day }}</span>\r\n @if (day.isHoliday && day.isCurrentMonth) {\r\n <span class=\"holiday-label\">{{ day.holidayLabel }}</span>\r\n }\r\n </td>\r\n }\r\n </tr>\r\n }\r\n </tbody>\r\n </table>\r\n </div>\r\n }\r\n </div>\r\n\r\n <!-- ==========================================\r\n FERIADOS DETALLE\r\n ========================================== -->\r\n @if (kpi()?.holidays && formattedHolidays().length) {\r\n <div class=\"holidays-detail\">\r\n <div class=\"holidays-detail-title\">\r\n <i class=\"fa-solid fa-calendar-day\"></i>\r\n Feriados en el per\u00EDodo\r\n </div>\r\n <div class=\"holidays-detail-list\">\r\n @for (h of formattedHolidays(); track h.date) {\r\n <span class=\"holiday-detail-tag\">\r\n <i class=\"fa-regular fa-calendar\"></i>\r\n {{ h.date }} - {{ h.label }}\r\n </span>\r\n }\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n", styles: [".calendar-range{font-family:system-ui,-apple-system,sans-serif;max-width:100%;padding:8px}.calendar-range.compact{max-width:750px;margin:0 auto}.controls{display:flex;flex-wrap:wrap;gap:16px 24px;margin-bottom:12px;padding:10px 16px;background:#f8f9fa;border-radius:6px;border:1px solid #e9ecef;align-items:center;justify-content:center}.control-group{display:flex;align-items:center;gap:8px}.control-group label{font-size:.8rem;font-weight:600;color:#495057;white-space:nowrap}.control-group input[type=date]{padding:4px 8px;border:1px solid #ced4da;border-radius:4px;font-size:.8rem;background:#fff;min-width:130px}.control-group.checkbox{margin-left:0;padding-left:8px;border-left:1px solid #dee2e6}.control-group.checkbox label{font-weight:400;font-size:.75rem;cursor:pointer;display:flex;align-items:center;gap:4px}.kpi-container{display:flex;flex-wrap:wrap;gap:20px 32px;padding:10px 20px;margin-bottom:12px;background:linear-gradient(135deg,#f8f9fa,#e9ecef);border-radius:8px;border:1px solid #dee2e6;justify-content:center;align-items:center}.kpi-item{display:flex;align-items:center;gap:6px;font-size:.85rem}.kpi-label{color:#6c757d;font-weight:500}.kpi-value{font-weight:700;color:#212529;font-size:1.1rem;min-width:24px;text-align:center;background:#fff;padding:0 8px;border-radius:4px}.kpi-holidays{display:none}.months-grid{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.month-card{flex:1 1 auto;min-width:230px;max-width:300px;background:#fff;border:1px solid #dee2e6;border-radius:6px;padding:6px 4px 4px}.month-title{text-align:center;font-size:.85rem;font-weight:600;color:#212529;margin-bottom:4px;padding:2px 0}.calendar-table{width:100%;border-collapse:collapse;font-size:.7rem;table-layout:fixed}.calendar-table th{text-align:center;font-weight:600;color:#6c757d;padding:2px 0;font-size:.65rem}.calendar-table td{text-align:center;padding:2px 0;border-radius:3px;height:28px;vertical-align:middle;font-size:.7rem;transition:background .1s;position:relative}.other-month{color:#ced4da;background:transparent}.sunday{background:#ffe0e0}.disabled-sunday{background:#f1f3f5!important;color:#adb5bd!important;cursor:default}.in-range{background:#c4eeff}.start-date{background:#28a745!important;color:#fff!important;border-radius:3px 0 0 3px;font-weight:700}.end-date{background:#d17015!important;color:#fff!important;border-radius:0 3px 3px 0;font-weight:700}.same-day{background:#28a745!important;color:#fff!important;border-radius:3px}.day-number{display:block;font-size:.75rem;line-height:1.2}.holiday:not(.other-month){background:#e8f0fe!important;border:2px solid #1a5a8c!important;border-radius:4px!important;position:relative;z-index:1}.holiday .day-number{font-weight:700!important;color:#1a3a5c!important}.holiday-label{display:block;font-size:.45rem;line-height:1.2;color:#1a3a5c;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:95%;margin:0 auto;background:#1a5a8c1a;border-radius:2px;padding:0 2px;font-weight:600}.holiday:hover .holiday-label{white-space:normal;overflow:visible;position:absolute;bottom:100%;left:50%;transform:translate(-50%);background:#1a3a5c;color:#fff;padding:4px 10px;border-radius:4px;font-size:.6rem;z-index:10;box-shadow:0 2px 8px #0003;max-width:180px;white-space:nowrap}.holidays-detail{margin-top:12px;padding:10px 16px;background:#f0f4f8;border:1px solid #1a5a8c;border-radius:8px;border-left:4px solid #1a5a8c}.holidays-detail-title{font-size:.8rem;font-weight:600;color:#1a3a5c;margin-bottom:6px;display:flex;align-items:center;gap:6px}.holidays-detail-title i{color:#1a5a8c}.holidays-detail-list{display:flex;flex-wrap:wrap;gap:6px 12px}.holiday-detail-tag{font-size:.7rem;color:#1a3a5c;background:#e8f0fe;padding:2px 12px;border-radius:12px;border:1px solid #b3cfe5;display:inline-flex;align-items:center;gap:4px;white-space:nowrap}.holiday-detail-tag i{font-size:.6rem;color:#1a5a8c}.control-group input:disabled,.control-group input[disabled]{background:#f1f3f5!important;color:#6c757d!important;cursor:not-allowed;opacity:.7}.control-group input[type=checkbox]:disabled{cursor:not-allowed;opacity:.6}.control-group.checkbox label:has(input:disabled){color:#6c757d;cursor:not-allowed}@media(max-width:600px){.month-card{min-width:180px;max-width:100%}.controls{gap:10px;padding:8px 12px;flex-direction:column;align-items:stretch}.control-group{justify-content:space-between}.control-group input[type=date]{min-width:100px;flex:1}.control-group.checkbox{border-left:none;padding-left:0;justify-content:center}.kpi-container{gap:10px 16px;padding:8px 12px;justify-content:center}.kpi-item{font-size:.75rem}.kpi-value{font-size:.9rem;min-width:20px;padding:0 4px}.holidays-detail{padding:8px 12px}.holiday-detail-tag{font-size:.6rem;padding:2px 8px}}\n"] }]
5508
+ }], ctorParameters: () => [], propDecorators: { startDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "startDate", required: false }] }, { type: i0.Output, args: ["startDateChange"] }], endDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "endDate", required: false }] }, { type: i0.Output, args: ["endDateChange"] }], excludeSundays: [{ type: i0.Input, args: [{ isSignal: true, alias: "excludeSundays", required: false }] }, { type: i0.Output, args: ["excludeSundaysChange"] }], holidays: [{ type: i0.Input, args: [{ isSignal: true, alias: "holidays", required: false }] }], compactMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "compactMode", required: false }] }], showKPI: [{ type: i0.Input, args: [{ isSignal: true, alias: "showKPI", required: false }] }], debugMode: [{ type: i0.Input, args: [{ isSignal: true, alias: "debugMode", required: false }] }], isEdit: [{ type: i0.Input, args: [{ isSignal: true, alias: "isEdit", required: false }] }], showControls: [{ type: i0.Input, args: [{ isSignal: true, alias: "showControls", required: false }] }], rangeChanged: [{ type: i0.Output, args: ["rangeChanged"] }] } });
5509
+
5080
5510
  class FileComponent {
5081
5511
  // Inputs
5082
5512
  existingFile = input(null, ...(ngDevMode ? [{ debugName: "existingFile" }] : /* istanbul ignore next */ []));
@@ -8551,6 +8981,7 @@ const PRIME_NG_MODULES = [
8551
8981
  ButtonModule,
8552
8982
  CardModule,
8553
8983
  CheckboxModule,
8984
+ ChipModule,
8554
8985
  ContextMenuModule,
8555
8986
  DataViewModule,
8556
8987
  DatePickerModule,
@@ -8610,6 +9041,7 @@ class PrimeNgModule {
8610
9041
  ButtonModule,
8611
9042
  CardModule,
8612
9043
  CheckboxModule,
9044
+ ChipModule,
8613
9045
  ContextMenuModule,
8614
9046
  DataViewModule,
8615
9047
  DatePickerModule,
@@ -8666,6 +9098,7 @@ class PrimeNgModule {
8666
9098
  ButtonModule,
8667
9099
  CardModule,
8668
9100
  CheckboxModule,
9101
+ ChipModule,
8669
9102
  ContextMenuModule,
8670
9103
  DataViewModule,
8671
9104
  DatePickerModule,
@@ -9124,7 +9557,7 @@ class DsxdateShort {
9124
9557
  useExisting: forwardRef(() => DsxdateShort),
9125
9558
  multi: true,
9126
9559
  },
9127
- ], ngImport: i0, template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "directive", type: i1$7.InputMaskDirective, selector: "[pInputMask]", inputs: ["pInputMaskPT", "pInputMaskUnstyled", "pInputMask", "slotChar", "autoClear", "characterPattern", "keepBuffer"], outputs: ["onComplete", "onUnmaskedChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] });
9560
+ ], ngImport: i0, template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n size=\"small\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<dsx-message-help [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n", styles: [""], dependencies: [{ kind: "component", type: AppMessageErrorComponent, selector: "dsx-message-error", inputs: ["control", "form", "debugMode"] }, { kind: "ngmodule", type: CommonModule }, { kind: "component", type: DatePicker, selector: "p-datePicker, p-datepicker, p-date-picker", inputs: ["iconDisplay", "styleClass", "inputStyle", "inputId", "inputStyleClass", "placeholder", "ariaLabelledBy", "ariaLabel", "iconAriaLabel", "dateFormat", "multipleSeparator", "rangeSeparator", "inline", "showOtherMonths", "selectOtherMonths", "showIcon", "icon", "readonlyInput", "shortYearCutoff", "hourFormat", "timeOnly", "stepHour", "stepMinute", "stepSecond", "showSeconds", "showOnFocus", "showWeek", "startWeekFromFirstDayOfYear", "showClear", "dataType", "selectionMode", "maxDateCount", "showButtonBar", "todayButtonStyleClass", "clearButtonStyleClass", "autofocus", "autoZIndex", "baseZIndex", "panelStyleClass", "panelStyle", "keepInvalid", "hideOnDateTimeSelect", "touchUI", "timeSeparator", "focusTrap", "showTransitionOptions", "hideTransitionOptions", "tabindex", "minDate", "maxDate", "disabledDates", "disabledDays", "showTime", "responsiveOptions", "numberOfMonths", "firstDayOfWeek", "view", "defaultDate", "appendTo", "motionOptions"], outputs: ["onFocus", "onBlur", "onClose", "onSelect", "onClear", "onInput", "onTodayClick", "onClearClick", "onMonthChange", "onYearChange", "onClickOutside", "onShow"] }, { kind: "component", type: FloatLabel, selector: "p-floatlabel, p-floatLabel, p-float-label", inputs: ["variant"] }, { kind: "ngmodule", type: InputMaskModule }, { kind: "directive", type: i1$7.InputMaskDirective, selector: "[pInputMask]", inputs: ["pInputMaskPT", "pInputMaskUnstyled", "pInputMask", "slotChar", "autoClear", "characterPattern", "keepBuffer"], outputs: ["onComplete", "onUnmaskedChange"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "component", type: AppMessageHelpComponent, selector: "dsx-message-help", inputs: ["control", "currentIdControl", "helperFunction", "dataSource", "key", "umbral", "debugMode", "idFieldName", "maxSugerencias"] }, { kind: "pipe", type: i1$1.JsonPipe, name: "json" }] });
9128
9561
  }
9129
9562
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxdateShort, decorators: [{
9130
9563
  type: Component,
@@ -9136,13 +9569,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
9136
9569
  InputMaskModule,
9137
9570
  JsonPipe,
9138
9571
  ReactiveFormsModule,
9572
+ AppMessageHelpComponent,
9139
9573
  ], providers: [
9140
9574
  {
9141
9575
  provide: NG_VALUE_ACCESSOR,
9142
9576
  useExisting: forwardRef(() => DsxdateShort),
9143
9577
  multi: true,
9144
9578
  },
9145
- ], template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
9579
+ ], template: "<!-- dsxdate-short.html -->\r\n<p-floatLabel variant=\"on\">\r\n <p-datepicker\r\n fluid\r\n appendTo=\"body\"\r\n size=\"small\"\r\n [formControl]=\"fechaControl\"\r\n [fluid]=\"true\"\r\n [showIcon]=\"showIcon()\"\r\n [showOnFocus]=\"showOnFocus()\"\r\n [showButtonBar]=\"showButtonBar()\"\r\n [dateFormat]=\"dateFormat()\"\r\n [placeholder]=\"placeholder()\"\r\n pInputMask=\"99/99/9999\"\r\n />\r\n <label>{{ label() }}</label>\r\n</p-floatLabel>\r\n\r\n<!-- \u2705 SIN CONDICI\u00D3N - IGUAL QUE EL ORIGINAL -->\r\n<dsx-message-error [control]=\"fechaControl\" />\r\n\r\n<dsx-message-help [control]=\"fechaControl\" />\r\n\r\n<!-- Debug info -->\r\n@if (debugEnabled() && isDevMode) {\r\n<div\r\n class=\"debug-info\"\r\n style=\"\r\n font-size: 12px;\r\n color: #666;\r\n margin-top: 4px;\r\n border-top: 1px dashed #ccc;\r\n padding-top: 4px;\r\n \"\r\n>\r\n <small>\r\n <strong>Debug:</strong>\r\n Value: {{ fechaControl.value | json }} | Valid: {{ fechaControl.valid }} |\r\n Invalid: {{ fechaControl.invalid }} | Errors: {{ fechaControl.errors | json\r\n }} | Dirty: {{ fechaControl.dirty }} | Touched: {{ fechaControl.touched }} |\r\n Status: {{ fechaControl.status }}\r\n </small>\r\n</div>\r\n}\r\n" }]
9146
9580
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], dateFormat: [{ type: i0.Input, args: [{ isSignal: true, alias: "dateFormat", required: false }] }], showIcon: [{ type: i0.Input, args: [{ isSignal: true, alias: "showIcon", required: false }] }], showOnFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "showOnFocus", required: false }] }], showButtonBar: [{ type: i0.Input, args: [{ isSignal: true, alias: "showButtonBar", required: false }] }], debug: [{ type: i0.Input, args: [{ isSignal: true, alias: "debug", required: false }] }] } });
9147
9581
 
9148
9582
  class DsxdateTime {
@@ -10488,7 +10922,7 @@ class DsxSelect {
10488
10922
  useExisting: forwardRef(() => DsxSelect),
10489
10923
  multi: true,
10490
10924
  },
10491
- ], 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 [virtualScroll]=\"virtualScroll()\"\r\n [virtualScrollItemSize]=\"virtualScrollItemSize()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n (onFocus)=\"onFocus()\"\r\n (onHide)=\"onHide()\"\r\n >\r\n <!-- Item template -->\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\" class=\"text-gray-500!\"></icon-dsx>\r\n } @if (showSecondaryField()) {\r\n <div class=\"flex flex-col\">\r\n <span class=\"font-semibold text-sky-800\">\r\n {{ item[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-xs text-gray-400\">\r\n {{ item[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ item[resolvedOptionLabel()] }} }\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Selected item template -->\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"selectedIcon()\" class=\"text-green-600!\"></icon-dsx>\r\n } @if (showSecondaryField() && selectedOptions) {\r\n <div class=\"flex gap-x-2\">\r\n <span class=\"font-semibold text-sky-700\">\r\n {{ selectedOptions[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-gray-400\">\r\n {{ selectedOptions[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ 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<!-- Mensaje de error condicional -->\r\n@if (showError() && errorControl()) {\r\n<dsx-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: "dsx-message-error", inputs: ["control", "form", "debugMode"] }] });
10925
+ ], 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 size=\"small\"\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 [virtualScroll]=\"virtualScroll()\"\r\n [virtualScrollItemSize]=\"virtualScrollItemSize()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n (onFocus)=\"onFocus()\"\r\n (onHide)=\"onHide()\"\r\n >\r\n <!-- Item template -->\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\" class=\"text-gray-500!\"></icon-dsx>\r\n } @if (showSecondaryField()) {\r\n <div class=\"flex flex-col\">\r\n <span class=\"font-semibold text-sky-800\">\r\n {{ item[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-xs text-gray-400\">\r\n {{ item[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ item[resolvedOptionLabel()] }} }\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Selected item template -->\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"selectedIcon()\" class=\"text-green-600!\"></icon-dsx>\r\n } @if (showSecondaryField() && selectedOptions) {\r\n <div class=\"flex gap-x-2\">\r\n <span class=\"font-semibold text-sky-700\">\r\n {{ selectedOptions[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-gray-400\">\r\n {{ selectedOptions[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ 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<!-- Mensaje de error condicional -->\r\n@if (showError() && errorControl()) {\r\n<dsx-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: "dsx-message-error", inputs: ["control", "form", "debugMode"] }] });
10492
10926
  }
10493
10927
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DsxSelect, decorators: [{
10494
10928
  type: Component,
@@ -10504,7 +10938,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
10504
10938
  useExisting: forwardRef(() => DsxSelect),
10505
10939
  multi: true,
10506
10940
  },
10507
- ], 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 [virtualScroll]=\"virtualScroll()\"\r\n [virtualScrollItemSize]=\"virtualScrollItemSize()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n (onFocus)=\"onFocus()\"\r\n (onHide)=\"onHide()\"\r\n >\r\n <!-- Item template -->\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\" class=\"text-gray-500!\"></icon-dsx>\r\n } @if (showSecondaryField()) {\r\n <div class=\"flex flex-col\">\r\n <span class=\"font-semibold text-sky-800\">\r\n {{ item[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-xs text-gray-400\">\r\n {{ item[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ item[resolvedOptionLabel()] }} }\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Selected item template -->\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"selectedIcon()\" class=\"text-green-600!\"></icon-dsx>\r\n } @if (showSecondaryField() && selectedOptions) {\r\n <div class=\"flex gap-x-2\">\r\n <span class=\"font-semibold text-sky-700\">\r\n {{ selectedOptions[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-gray-400\">\r\n {{ selectedOptions[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ 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<!-- Mensaje de error condicional -->\r\n@if (showError() && errorControl()) {\r\n<dsx-message-error [control]=\"errorControl()\" />\r\n}\r\n", styles: ["icon-dsx{font-size:1.5em}\n"] }]
10941
+ ], template: "<!-- dsx-select.html -->\r\n<p-floatLabel [variant]=\"variant()\">\r\n <p-select\r\n fluid\r\n appendTo=\"body\"\r\n size=\"small\"\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 [virtualScroll]=\"virtualScroll()\"\r\n [virtualScrollItemSize]=\"virtualScrollItemSize()\"\r\n [(ngModel)]=\"value\"\r\n (ngModelChange)=\"onValueChange($event)\"\r\n (onFocus)=\"onFocus()\"\r\n (onHide)=\"onHide()\"\r\n >\r\n <!-- Item template -->\r\n <ng-template let-item #item>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"itemIcon()\" class=\"text-gray-500!\"></icon-dsx>\r\n } @if (showSecondaryField()) {\r\n <div class=\"flex flex-col\">\r\n <span class=\"font-semibold text-sky-800\">\r\n {{ item[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-xs text-gray-400\">\r\n {{ item[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ item[resolvedOptionLabel()] }} }\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Selected item template -->\r\n <ng-template #selectedItem let-selectedOptions>\r\n <div class=\"flex align-items-center gap-1\">\r\n @if (useIcon()) {\r\n <icon-dsx [name]=\"selectedIcon()\" class=\"text-green-600!\"></icon-dsx>\r\n } @if (showSecondaryField() && selectedOptions) {\r\n <div class=\"flex gap-x-2\">\r\n <span class=\"font-semibold text-sky-700\">\r\n {{ selectedOptions[resolvedOptionLabel()] }}\r\n </span>\r\n <span class=\"text-gray-400\">\r\n {{ selectedOptions[secondaryField()!] }}\r\n </span>\r\n </div>\r\n } @else { {{ 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<!-- Mensaje de error condicional -->\r\n@if (showError() && errorControl()) {\r\n<dsx-message-error [control]=\"errorControl()\" />\r\n}\r\n", styles: ["icon-dsx{font-size:1.5em}\n"] }]
10508
10942
  }], 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 }] }], secondaryField: [{ type: i0.Input, args: [{ isSignal: true, alias: "secondaryField", required: false }] }], virtualScroll: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScroll", required: false }] }], virtualScrollItemSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "virtualScrollItemSize", 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"] }] } });
10509
10943
 
10510
10944
  /**
@@ -12672,9 +13106,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImpo
12672
13106
  // form-preview.service.ts
12673
13107
  class FormPreviewService {
12674
13108
  environment;
13109
+ isDev;
12675
13110
  constructor() {
12676
- // Inyectar el token en el constructor
12677
13111
  this.environment = inject(ENVIRONMENT);
13112
+ this.isDev = !this.environment.production;
12678
13113
  }
12679
13114
  /**
12680
13115
  * Construye el HTML para el preview de un objeto
@@ -12685,17 +13120,30 @@ class FormPreviewService {
12685
13120
  *
12686
13121
  * @example
12687
13122
  * ```typescript
13123
+ * // Ejemplo básico
12688
13124
  * const data = {
12689
- * usuario: {
12690
- * nombre: 'Juan Pérez',
12691
- * email: 'juan@email.com'
12692
- * },
13125
+ * empleadoId: 2,
13126
+ * fechaIngreso: '2026-04-07',
12693
13127
  * estado: 1
12694
13128
  * };
12695
13129
  *
13130
+ * const empleados = [
13131
+ * { id: 1, nombreCompleto: 'Juan Pérez' },
13132
+ * { id: 2, nombreCompleto: 'María García' }
13133
+ * ];
13134
+ *
12696
13135
  * const fields = [
12697
- * { key: 'usuario.nombre', label: 'Nombre' },
12698
- * { key: 'usuario.email', label: 'Email' },
13136
+ * {
13137
+ * key: 'empleadoId',
13138
+ * label: 'Empleado',
13139
+ * referenceMap: empleados,
13140
+ * referenceLabel: 'nombreCompleto'
13141
+ * },
13142
+ * {
13143
+ * key: 'fechaIngreso',
13144
+ * label: 'Fecha de Ingreso',
13145
+ * format: 'dateLong' // "07 de abril de 2026"
13146
+ * },
12699
13147
  * {
12700
13148
  * key: 'estado',
12701
13149
  * label: 'Estado',
@@ -12703,30 +13151,33 @@ class FormPreviewService {
12703
13151
  * }
12704
13152
  * ];
12705
13153
  *
12706
- * const html = this.formPreviewService.buildObjectSummaryHtml(data, fields, {
12707
- * title: 'Datos del Usuario',
12708
- * showEmpty: false,
12709
- * compact: true,
12710
- * debug: false
12711
- * });
13154
+ * const html = this.formPreviewService.buildObjectSummaryHtml(
13155
+ * data,
13156
+ * fields,
13157
+ * {
13158
+ * title: 'Datos del Usuario',
13159
+ * showEmpty: false,
13160
+ * compact: true,
13161
+ * debug: true // Solo en desarrollo
13162
+ * }
13163
+ * );
12712
13164
  * ```
12713
13165
  */
12714
13166
  buildObjectSummaryHtml(data, fields, options) {
12715
13167
  const debug = options?.debug ?? false;
12716
- const isDev = !this.environment.production;
12717
- // Solo ejecutar logs si estamos en desarrollo y debug está activado
12718
- if (debug && isDev) {
13168
+ // 🔍 Debug solo en desarrollo
13169
+ if (debug && this.isDev) {
12719
13170
  this.debugInputData(data, fields, options);
12720
13171
  }
12721
- // Logs de error siempre se muestran en desarrollo si debug está activado
13172
+ // Validaciones
12722
13173
  if (!data) {
12723
- if (debug && isDev) {
13174
+ if (debug && this.isDev) {
12724
13175
  console.error('❌ FormPreview: data es null o undefined');
12725
13176
  }
12726
13177
  return '';
12727
13178
  }
12728
13179
  if (!fields || fields.length === 0) {
12729
- if (debug && isDev) {
13180
+ if (debug && this.isDev) {
12730
13181
  console.error('❌ FormPreview: fields está vacío');
12731
13182
  }
12732
13183
  return '';
@@ -12742,11 +13193,13 @@ class FormPreviewService {
12742
13193
  if (field.referenceMap && value !== undefined && value !== null) {
12743
13194
  const originalValue = value;
12744
13195
  value = this.resolveReferenceValue(value, field);
12745
- // Logs de depuración solo en desarrollo
12746
- if (debug && isDev) {
13196
+ // 🔍 Debug solo en desarrollo
13197
+ if (debug && this.isDev) {
12747
13198
  console.log(`🔍 Resolviendo referencia para "${field.key}":`, {
12748
13199
  original: originalValue,
12749
13200
  resuelto: value,
13201
+ referenciaId: field.referenceId || field.key || 'id',
13202
+ referenciaLabel: field.referenceLabel || 'nombre',
12750
13203
  tipoMapa: Array.isArray(field.referenceMap)
12751
13204
  ? 'Array'
12752
13205
  : field.referenceMap instanceof Map
@@ -12757,9 +13210,10 @@ class FormPreviewService {
12757
13210
  });
12758
13211
  }
12759
13212
  }
13213
+ // Aplicar formato
12760
13214
  const valueText = field.format
12761
- ? field.format(value)
12762
- : this.formatValue(value);
13215
+ ? this.formatValue(value, field.format)
13216
+ : this.formatDefault(value);
12763
13217
  if (value === undefined) {
12764
13218
  propiedadesNoEncontradas.push(field.key);
12765
13219
  }
@@ -12770,8 +13224,8 @@ class FormPreviewService {
12770
13224
  value: valueText || '—',
12771
13225
  });
12772
13226
  }
12773
- // Logs de depuración solo en desarrollo
12774
- if (debug && isDev) {
13227
+ // 🔍 Debug solo en desarrollo
13228
+ if (debug && this.isDev) {
12775
13229
  if (propiedadesNoEncontradas.length > 0) {
12776
13230
  console.warn('⚠️ FormPreview: Propiedades no encontradas:', propiedadesNoEncontradas);
12777
13231
  console.warn('📋 Propiedades disponibles en data:', Object.keys(data));
@@ -12783,19 +13237,29 @@ class FormPreviewService {
12783
13237
  console.log('📋 Fields:', fields);
12784
13238
  }
12785
13239
  else {
12786
- console.log('✅ FormPreview: HTML generado correctamente', { cells });
13240
+ console.log('✅ FormPreview: HTML generado correctamente', {
13241
+ totalCeldas: cells.length,
13242
+ campos: cells.map((c) => c.label),
13243
+ });
12787
13244
  }
12788
13245
  }
12789
13246
  if (!cells.length)
12790
13247
  return '';
12791
13248
  return this.generateHtml(title, cells, compact);
12792
13249
  }
13250
+ /**
13251
+ * Resuelve el valor de referencia
13252
+ * Soporta: arrays, maps, objetos literales y funciones
13253
+ */
12793
13254
  resolveReferenceValue(value, field) {
12794
13255
  if (Array.isArray(value)) {
12795
13256
  return value.map((item) => this.resolveSingleReference(item, field));
12796
13257
  }
12797
13258
  return this.resolveSingleReference(value, field);
12798
13259
  }
13260
+ /**
13261
+ * Resuelve un valor individual de referencia
13262
+ */
12799
13263
  resolveSingleReference(value, field) {
12800
13264
  if (!value)
12801
13265
  return value;
@@ -12812,8 +13276,31 @@ class FormPreviewService {
12812
13276
  referencedItem = map.get(value);
12813
13277
  }
12814
13278
  else if (Array.isArray(map)) {
12815
- const idKey = field.referenceId || 'id';
12816
- referencedItem = map.find((item) => item[idKey] === value);
13279
+ // Si no se especifica referenceId, usar el key del campo
13280
+ const idKey = field.referenceId || field.key || 'id';
13281
+ const labelKey = field.referenceLabel || 'nombre';
13282
+ const found = map.find((item) => {
13283
+ if (item[idKey] !== undefined) {
13284
+ return item[idKey] === value;
13285
+ }
13286
+ if (item.id !== undefined) {
13287
+ return item.id === value;
13288
+ }
13289
+ return false;
13290
+ });
13291
+ if (found) {
13292
+ if (found[labelKey] !== undefined && found[labelKey] !== null) {
13293
+ return found[labelKey];
13294
+ }
13295
+ // Fallback: buscar otras propiedades comunes
13296
+ for (const prop of ['label', 'nombre', 'name', 'text', 'value']) {
13297
+ if (found[prop] !== undefined && found[prop] !== null) {
13298
+ return found[prop];
13299
+ }
13300
+ }
13301
+ return found;
13302
+ }
13303
+ return value;
12817
13304
  }
12818
13305
  else if (typeof map === 'object') {
12819
13306
  referencedItem = map[String(value)];
@@ -12825,12 +13312,23 @@ class FormPreviewService {
12825
13312
  referencedItem[labelProp] !== null) {
12826
13313
  return referencedItem[labelProp];
12827
13314
  }
13315
+ for (const prop of ['label', 'nombre', 'name', 'text', 'value']) {
13316
+ if (referencedItem[prop] !== undefined &&
13317
+ referencedItem[prop] !== null) {
13318
+ return referencedItem[prop];
13319
+ }
13320
+ }
12828
13321
  return referencedItem;
12829
13322
  }
12830
13323
  return referencedItem;
12831
13324
  }
12832
13325
  return value;
12833
13326
  }
13327
+ /**
13328
+ * Obtiene un valor anidado usando notación punto
13329
+ * @example
13330
+ * getNestedValue({ user: { name: 'Juan' } }, 'user.name') // 'Juan'
13331
+ */
12834
13332
  getNestedValue(obj, path) {
12835
13333
  if (!obj || !path)
12836
13334
  return undefined;
@@ -12843,7 +13341,29 @@ class FormPreviewService {
12843
13341
  }
12844
13342
  return result;
12845
13343
  }
12846
- formatValue(value) {
13344
+ /**
13345
+ * Formatea un valor usando el formato especificado
13346
+ * @param value - Valor a formatear
13347
+ * @param format - Formato a aplicar (string predefinido o función personalizada)
13348
+ * @returns String formateado
13349
+ */
13350
+ formatValue(value, format) {
13351
+ if (value === null || value === undefined)
13352
+ return '';
13353
+ // Si es función personalizada, ejecutarla
13354
+ if (typeof format === 'function') {
13355
+ return format(value);
13356
+ }
13357
+ // Si es string, verificar si es un formato de fecha
13358
+ if (typeof format === 'string') {
13359
+ return this.formatDate(value, format);
13360
+ }
13361
+ return this.formatDefault(value);
13362
+ }
13363
+ /**
13364
+ * Formato por defecto para cualquier valor
13365
+ */
13366
+ formatDefault(value) {
12847
13367
  if (value === null || value === undefined)
12848
13368
  return '';
12849
13369
  if (value instanceof Date)
@@ -12859,6 +13379,68 @@ class FormPreviewService {
12859
13379
  }
12860
13380
  return String(value);
12861
13381
  }
13382
+ /**
13383
+ * 🗓️ FORMATOS DE FECHA DISPONIBLES
13384
+ *
13385
+ * Para agregar un nuevo formato:
13386
+ * 1. Agrega el tipo en DateFormat
13387
+ * 2. Agrega el case en el switch del método formatDate
13388
+ * 3. Define las opciones de Intl.DateTimeFormatOptions
13389
+ *
13390
+ * @example
13391
+ * // Agregar 'dateShort' para mostrar "07-abr-2026"
13392
+ * // 1. type DateFormat = 'date' | 'dateLong' | 'dateTime' | 'dateShort'
13393
+ * // 2. case 'dateShort':
13394
+ * // options.day = '2-digit';
13395
+ * // options.month = 'short';
13396
+ * // options.year = 'numeric';
13397
+ * // break;
13398
+ */
13399
+ formatDate(value, format) {
13400
+ if (!value)
13401
+ return '';
13402
+ const date = value instanceof Date ? value : new Date(value);
13403
+ if (isNaN(date.getTime())) {
13404
+ // Si no es una fecha válida, devolver el valor original
13405
+ return String(value);
13406
+ }
13407
+ const options = {};
13408
+ switch (format) {
13409
+ case 'date':
13410
+ // "07/04/2026"
13411
+ options.day = '2-digit';
13412
+ options.month = '2-digit';
13413
+ options.year = 'numeric';
13414
+ break;
13415
+ case 'dateLong':
13416
+ // "martes 07 de abril de 2026"
13417
+ options.weekday = 'long';
13418
+ options.day = '2-digit';
13419
+ options.month = 'long';
13420
+ options.year = 'numeric';
13421
+ break;
13422
+ case 'dateTime':
13423
+ // "07/04/2026 14:30"
13424
+ options.day = '2-digit';
13425
+ options.month = '2-digit';
13426
+ options.year = 'numeric';
13427
+ options.hour = '2-digit';
13428
+ options.minute = '2-digit';
13429
+ break;
13430
+ default:
13431
+ return this.formatDefault(value);
13432
+ }
13433
+ try {
13434
+ return date.toLocaleDateString('es-ES', options);
13435
+ }
13436
+ catch (error) {
13437
+ // Si hay error al formatear, devolver el valor original
13438
+ return String(value);
13439
+ }
13440
+ }
13441
+ /**
13442
+ * Formatea un valor individual (para objetos y arrays)
13443
+ */
12862
13444
  formatSingleValue(value) {
12863
13445
  if (!value || typeof value !== 'object')
12864
13446
  return String(value);
@@ -12890,6 +13472,9 @@ class FormPreviewService {
12890
13472
  return '[Objeto]';
12891
13473
  }
12892
13474
  }
13475
+ /**
13476
+ * Genera el HTML del preview
13477
+ */
12893
13478
  generateHtml(title, cells, compact) {
12894
13479
  if (compact) {
12895
13480
  return `
@@ -12940,15 +13525,20 @@ class FormPreviewService {
12940
13525
  </div>
12941
13526
  `;
12942
13527
  }
13528
+ /**
13529
+ * 🔍 Debug: Muestra información detallada para depuración
13530
+ * ⚠️ SOLO se ejecuta en entorno de desarrollo
13531
+ */
12943
13532
  debugInputData(data, fields, options) {
12944
- const isDev = !this.environment.production;
12945
- if (!isDev)
13533
+ // ⚠️ Seguridad: Si es producción, NO ejecutar nada
13534
+ if (!this.isDev)
12946
13535
  return;
12947
13536
  console.group('🔍 FormPreview Debug');
12948
13537
  console.log('📊 Data:', data);
12949
13538
  console.log('📋 Propiedades disponibles:', data ? Object.keys(data) : 'data es null');
12950
13539
  console.log('🎯 Fields solicitados:', fields);
12951
13540
  console.log('⚙️ Options:', options);
13541
+ console.log('🌍 Entorno:', this.environment.production ? 'Producción' : 'Desarrollo');
12952
13542
  if (data) {
12953
13543
  for (const field of fields) {
12954
13544
  const value = this.getNestedValue(data, field.key);
@@ -12962,11 +13552,18 @@ class FormPreviewService {
12962
13552
  }
12963
13553
  else {
12964
13554
  console.log(`✅ Campo "${field.key}" existe:`, value);
13555
+ // Mostrar información del formato si existe
13556
+ if (field.format) {
13557
+ console.log(` 📝 Formato: ${typeof field.format === 'function' ? 'Función personalizada' : field.format}`);
13558
+ }
12965
13559
  }
12966
13560
  }
12967
13561
  }
12968
13562
  console.groupEnd();
12969
13563
  }
13564
+ /**
13565
+ * Encuentra propiedades similares para sugerencias en debug
13566
+ */
12970
13567
  findSimilarProperties(search, properties) {
12971
13568
  const searchLower = search.toLowerCase();
12972
13569
  return properties
@@ -12976,6 +13573,9 @@ class FormPreviewService {
12976
13573
  })
12977
13574
  .slice(0, 3);
12978
13575
  }
13576
+ /**
13577
+ * Escapa caracteres HTML para evitar inyección
13578
+ */
12979
13579
  escapeHtml(str) {
12980
13580
  if (!str)
12981
13581
  return '';
@@ -13375,6 +13975,54 @@ function hasStorage(key) {
13375
13975
  }
13376
13976
  }
13377
13977
 
13978
+ /**
13979
+ * Calcula la fecha sumando días hábiles (Lunes a Sábado, Domingos no cuentan)
13980
+ * Los feriados en el rango no consumen días
13981
+ *
13982
+ * @param fechaInicio - Fecha desde donde empezar
13983
+ * @param cantidadDias - Días hábiles a sumar
13984
+ * @param feriados - Lista de feriados (opcional)
13985
+ * @returns Fecha calculada o null si fechaInicio es null
13986
+ *
13987
+ * @example
13988
+ * // Sin feriados
13989
+ * calcularFechaFin(new Date('2026-07-07'), 15) // 2026-07-25
13990
+ *
13991
+ * @example
13992
+ * // Con feriados
13993
+ * calcularFechaFin(
13994
+ * new Date('2026-07-07'),
13995
+ * 15,
13996
+ * [{ date: '2026-07-14' }, { date: '2026-07-21' }]
13997
+ * ) // 2026-07-27
13998
+ */
13999
+ function dsxCalcularRangoFecha(fechaInicio, cantidadDias, feriados) {
14000
+ // Validación básica
14001
+ if (!fechaInicio)
14002
+ return null;
14003
+ if (cantidadDias === 0)
14004
+ return new Date(fechaInicio);
14005
+ // Crear Set de feriados con moment para comparación directa
14006
+ const feriadosSet = new Set(feriados?.map((f) => moment(f.dateHoliday).format('YYYY-MM-DD')));
14007
+ // Usar moment para la fecha
14008
+ const fecha = moment(fechaInicio).startOf('day');
14009
+ let dias = cantidadDias;
14010
+ while (dias > 0) {
14011
+ fecha.add(1, 'day');
14012
+ const diaSemana = fecha.day(); // 0=domingo
14013
+ const fechaStr = fecha.format('YYYY-MM-DD');
14014
+ // Días que NO consumen días (se saltan):
14015
+ // 1. Domingos
14016
+ // 2. Feriados
14017
+ if (diaSemana === 0 || feriadosSet.has(fechaStr)) {
14018
+ continue;
14019
+ }
14020
+ // Sábado = medio día, resto = día completo
14021
+ dias -= diaSemana === 6 ? 0.5 : 1;
14022
+ }
14023
+ return fecha.toDate();
14024
+ }
14025
+
13378
14026
  function chainControlGroups(form, groups) {
13379
14027
  groups.forEach((group) => {
13380
14028
  chainControls(form, group);
@@ -14008,5 +14656,5 @@ function sorensenDiceValidator(dataSource, key, umbral = 0.7) {
14008
14656
  * Generated bundle index. Do not edit.
14009
14657
  */
14010
14658
 
14011
- export { ACTION_TYPES, AlertaService, AppMessageErrorComponent, AppMessageHelpComponent, ArrowNavigationDirective, AuthorizeService, AutoScrollHeightDirective, BaseCRUDService, CACHE_KEYS, CacheService, CssV2Component, DSX_PALETTE, DateIndicator, DocxPreviewComponent, DsxAddToolsModule, DsxAutocomplete, DsxButtonComponent, DsxEnableDisable, DsxInputCurrency, DsxMessagesService, DsxSelect, DsxStatusToggle, DsxdateShort, DsxdateTime, DteService, ENVIRONMENT, EndpointService, ErrorHandlerService, FileComponent, FormPreviewService, GTQFormatter, HeaderDsx, HelpersService, HttpHelpersService, INITIAL_PARAMETERS, IcoLabel, IconDsxComponent, JoinByPipe, JsonHighlightPipe, JsonValuesDebujComponent, JsonViewerComponent, KpicardComponent, LoadingComponent, LoadingLottieComponent, LogoDsxComponent, MasterDetailChangeService, NavbarDsxComponent, NetworkStatusComponent, OnlyRangoPatternDirective, ParameterValuesService, PdfPreviewComponent, PrimeNgModule, QrGenerator, ResultFileService, SWEET_ALERT_THEMES, ScreenInspector, SecurityService, SelectAllOnFocusDirective, SpinnerLoadingService, SweetAlert2DialogService, TablePreviewService, TemplateHighlight, TokenPurposeLogin, TruncatePipe, UtilityAddService, asyncExistsValidator, atLeastOneFieldRequiredValidator, chainControlGroups, createCurrencyFormatter, createInitialCache, createTypedCacheProvider, cuiValidator, dateMinMaxValidator, dateRangeValidator, dateRangeValidatorFromTo, developmentEnvironment, getActionMessageConfig, getStorage, getZeroBasedRolIndex, guardTokenPurposeGuard, hasStorage, httpAuthorizeInterceptor, minimumAgeValidator, nitValidator, productionEnvironment, provideEnvironment, removeStorage, setStorage, setStorageObject, sorensenDiceValidator, templateStructureValidator, templateVariablesValidator, validateEnvironmentConfig };
14659
+ export { ACTION_TYPES, AlertaService, AppMessageErrorComponent, AppMessageHelpComponent, ArrowNavigationDirective, AuthorizeService, AutoScrollHeightDirective, BaseCRUDService, CACHE_KEYS, CacheService, CssV2Component, DSX_PALETTE, DateIndicator, DocxPreviewComponent, DsxAddToolsModule, DsxAutocomplete, DsxButtonComponent, DsxCalendarView, DsxEnableDisable, DsxInputCurrency, DsxMessagesService, DsxSelect, DsxStatusToggle, DsxdateShort, DsxdateTime, DteService, ENVIRONMENT, EndpointService, ErrorHandlerService, FileComponent, FormPreviewService, GTQFormatter, HeaderDsx, HelpersService, HttpHelpersService, INITIAL_PARAMETERS, IcoLabel, IconDsxComponent, JoinByPipe, JsonHighlightPipe, JsonValuesDebujComponent, JsonViewerComponent, KpicardComponent, LoadingComponent, LoadingLottieComponent, LogoDsxComponent, MasterDetailChangeService, NavbarDsxComponent, NetworkStatusComponent, OnlyRangoPatternDirective, ParameterValuesService, PdfPreviewComponent, PrimeNgModule, QrGenerator, ResultFileService, SWEET_ALERT_THEMES, ScreenInspector, SecurityService, SelectAllOnFocusDirective, SpinnerLoadingService, SweetAlert2DialogService, TablePreviewService, TemplateHighlight, TokenPurposeLogin, TruncatePipe, UtilityAddService, asyncExistsValidator, atLeastOneFieldRequiredValidator, chainControlGroups, createCurrencyFormatter, createInitialCache, createTypedCacheProvider, cuiValidator, dateMinMaxValidator, dateRangeValidator, dateRangeValidatorFromTo, developmentEnvironment, dsxCalcularRangoFecha, getActionMessageConfig, getStorage, getZeroBasedRolIndex, guardTokenPurposeGuard, hasStorage, httpAuthorizeInterceptor, minimumAgeValidator, nitValidator, productionEnvironment, provideEnvironment, removeStorage, setStorage, setStorageObject, sorensenDiceValidator, templateStructureValidator, templateVariablesValidator, validateEnvironmentConfig };
14012
14660
  //# sourceMappingURL=ngx-dsxlibrary.mjs.map