@jvsoft/utils 0.0.13-alpha.6 → 1.0.0-alpha.4

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.
@@ -14,14 +14,6 @@ import * as i0 from '@angular/core';
14
14
  import { Pipe } from '@angular/core';
15
15
  import * as i1 from '@angular/platform-browser';
16
16
 
17
- /**
18
- * Realiza una fusión profunda entre dos objetos, sin modificar el original.
19
- * Si el valor en target no es un objeto, lo reemplaza directamente.
20
- *
21
- * @param source - Objeto fuente que se clonará y fusionará.
22
- * @param target - Objeto destino cuyos valores se fusionarán.
23
- * @returns Un nuevo objeto con la fusión profunda.
24
- */
25
17
  function deepMerge(source, target) {
26
18
  // Crea un clon profundo sin usar JSON.parse(JSON.stringify)
27
19
  let cloneSource = deepClone(source);
@@ -43,12 +35,7 @@ function deepMerge(source, target) {
43
35
  }
44
36
  return cloneSource; // Retorna el clon y no modifica el original
45
37
  }
46
- /**
47
- * Realiza una clonación profunda de un objeto, manejando funciones, fechas y arrays.
48
- *
49
- * @param obj - Objeto a clonar.
50
- * @returns Una copia profunda del objeto.
51
- */
38
+ // Función de clonación profunda que maneja funciones, fechas y otros tipos especiales
52
39
  function deepClone(obj) {
53
40
  if (obj === null || typeof obj !== 'object') {
54
41
  return obj; // Devuelve el valor si no es un objeto
@@ -74,7 +61,6 @@ function deepClone(obj) {
74
61
  /**
75
62
  * Busca un elemento en un array o jerarquía de objetos según un campo y valor especificado.
76
63
  *
77
- * @param datosFn - Objeto con los parámetros de búsqueda: items, campo, valor y opcionalmente campoHijo.
78
64
  * @returns El elemento encontrado o undefined si no existe.
79
65
  */
80
66
  function buscarPorCampo(datosFn) {
@@ -101,33 +87,13 @@ function buscarPorCampo(datosFn) {
101
87
  // Si no se encuentra nada, retorna undefined
102
88
  return undefined;
103
89
  }
104
- /**
105
- * Suma los valores de las propiedades especificadas de un objeto.
106
- *
107
- * @param item - Objeto con las propiedades a sumar.
108
- * @param campos - Array de nombres de las propiedades a sumar.
109
- * @returns La suma de los valores de las propiedades.
110
- */
111
90
  function sumarPropiedades(item, campos) {
112
91
  const datosSumar = campos.map(campo => (item[campo] * 1));
113
92
  return datosSumar.reduce((a, b) => a + b, 0);
114
93
  }
115
- /**
116
- * Verifica si el valor proporcionado es un número válido.
117
- *
118
- * @param value - Valor a verificar.
119
- * @returns true si es un número, false en caso contrario.
120
- */
121
94
  function esNumero(value) {
122
95
  return !isNaN(Number(value));
123
96
  }
124
- /**
125
- * Suma los valores de las propiedades especificadas en un array de objetos.
126
- *
127
- * @param arrayObjetos - Array de objetos a procesar.
128
- * @param campos - Array de nombres de las propiedades a sumar.
129
- * @returns Objeto con la suma de cada propiedad.
130
- */
131
97
  function sumarObjetos(arrayObjetos, campos) {
132
98
  return arrayObjetos.reduce((accumulator, item) => {
133
99
  campos.forEach(campo => {
@@ -139,22 +105,9 @@ function sumarObjetos(arrayObjetos, campos) {
139
105
  return accumulator;
140
106
  }, {});
141
107
  }
142
- /**
143
- * Obtiene los valores únicos de un array.
144
- *
145
- * @param array - Array de valores.
146
- * @returns Array con los valores únicos.
147
- */
148
108
  function getUniqueValues(array) {
149
109
  return array.filter((currentValue, index, arr) => (arr.indexOf(currentValue) === index));
150
110
  }
151
- /**
152
- * Obtiene los objetos únicos de un array según una propiedad específica.
153
- *
154
- * @param objetos - Array de objetos.
155
- * @param campo - Nombre de la propiedad para determinar unicidad.
156
- * @returns Array de objetos únicos por la propiedad.
157
- */
158
111
  function getUniqueValuesByProperty(objetos, campo) {
159
112
  const objetosUnicos = {};
160
113
  objetos.forEach(objeto => {
@@ -170,14 +123,6 @@ function getUniqueValuesByProperty(objetos, campo) {
170
123
  // Convertir el objeto de claves únicas de vuelta a una lista de objetos
171
124
  return Object.values(objetosUnicos);
172
125
  }
173
- /**
174
- * Ordena un array de valores numéricos o alfabéticos.
175
- *
176
- * @param array - Array a ordenar.
177
- * @param numeros - Si es true, ordena como números.
178
- * @param sentido - 'ASC' para ascendente, 'DESC' para descendente.
179
- * @returns Array ordenado.
180
- */
181
126
  function ordenarArray(array, numeros = false, sentido = 'ASC') {
182
127
  if (numeros) {
183
128
  if (sentido != 'ASC') {
@@ -187,24 +132,9 @@ function ordenarArray(array, numeros = false, sentido = 'ASC') {
187
132
  }
188
133
  return array.sort((a, b) => (a > b) ? 1 : ((b > a) ? -1 : 0));
189
134
  }
190
- /**
191
- * Ordena un array de objetos por una propiedad específica.
192
- *
193
- * @param objData - Array de objetos a ordenar.
194
- * @param propiedad - Propiedad por la que se ordena.
195
- * @param numeros - (Obsoleto) Si es true, ordena como números.
196
- * @returns Array ordenado.
197
- */
198
135
  function ordenarPorPropiedad(objData, propiedad, /**@deprecated*/ numeros = false) {
199
136
  return ordenarPorPropiedades(objData, { propiedades: [propiedad], direcciones: ['asc'] });
200
137
  }
201
- /**
202
- * Ordena un array de objetos por varias propiedades y direcciones.
203
- *
204
- * @param arr - Array de objetos a ordenar.
205
- * @param options - Opciones con propiedades y direcciones de orden.
206
- * @returns Array ordenado.
207
- */
208
138
  function ordenarPorPropiedades(arr, options) {
209
139
  const { propiedades, direcciones = [] } = options;
210
140
  const orden = direcciones.map(d => d === 'desc' ? -1 : 1);
@@ -224,13 +154,6 @@ function ordenarPorPropiedades(arr, options) {
224
154
  }, 0);
225
155
  });
226
156
  }
227
- /**
228
- * Agrupa los elementos de un array según una clave o función de clave.
229
- *
230
- * @param array - Array de objetos a agrupar.
231
- * @param key - Propiedad o función para agrupar.
232
- * @returns Objeto con los grupos por clave.
233
- */
234
157
  function groupBy(array, key) {
235
158
  const keyFn = key instanceof Function ? key : (obj) => obj[key];
236
159
  return array.reduce((objectsByKeyValue, obj) => {
@@ -239,13 +162,6 @@ function groupBy(array, key) {
239
162
  return objectsByKeyValue;
240
163
  }, {});
241
164
  }
242
- /**
243
- * Agrupa y anida los elementos de un array según una lista de propiedades.
244
- *
245
- * @param arr - Array de objetos a agrupar.
246
- * @param properties - Propiedades para agrupar de forma anidada.
247
- * @returns Objeto anidado por los grupos de propiedades.
248
- */
249
165
  function nestGroupsBy(arr, properties) {
250
166
  const fnGroupBy = (conversions, property2) => {
251
167
  return conversions.reduce((acc, obj) => {
@@ -319,13 +235,6 @@ function eliminarColumnaPorIndex(data, columnIndex) {
319
235
  return newRow;
320
236
  });
321
237
  }
322
- /**
323
- * Elimina elementos duplicados de un array de objetos según claves específicas.
324
- *
325
- * @param array - Array de objetos.
326
- * @param claves - Claves para determinar unicidad. Si no se especifica, compara todo el objeto.
327
- * @returns Array sin duplicados.
328
- */
329
238
  function eliminarDuplicados(array, claves) {
330
239
  const unicos = new Map();
331
240
  for (const item of array) {
@@ -338,14 +247,6 @@ function eliminarDuplicados(array, claves) {
338
247
  }
339
248
  return Array.from(unicos.values());
340
249
  }
341
- /**
342
- * Elimina elementos de un array origen que estén presentes en otro array, según claves específicas.
343
- *
344
- * @param origen - Array original.
345
- * @param elementosAEliminar - Elementos a eliminar del array origen.
346
- * @param claves - Claves para comparar los objetos. Si no se especifica, compara todo el objeto.
347
- * @returns Array filtrado sin los elementos eliminados.
348
- */
349
250
  function eliminarElementos(origen, elementosAEliminar, claves) {
350
251
  const clavesSet = new Set();
351
252
  for (const item of elementosAEliminar) {
@@ -362,152 +263,227 @@ function eliminarElementos(origen, elementosAEliminar, claves) {
362
263
  });
363
264
  }
364
265
 
365
- /**
366
- * Devuelve un valor de visualización para un elemento buscado.
367
- * Compatible con listas simples, objetos y campos múltiples.
368
- */
369
266
  function mostrarValorEnBusqueda(campos, idxSel) {
370
- const buscarEnLista = (lista) => {
371
- if (!lista)
372
- return null;
373
- if (campos.campoId === '*object*' || campos.campoId === '*objeto*') {
374
- return lista.find((x) => JSON.stringify(x).trim() === JSON.stringify(idxSel).trim());
375
- }
376
- return lista.find((x) => x[campos.campoId] === idxSel);
377
- };
378
267
  const impDataMostrar = () => {
379
- let vD = buscarEnLista(campos.lista) || (campos.opcExtra && buscarEnLista(campos.opcExtra));
380
- if (!vD)
381
- return '';
382
- if (Array.isArray(campos.campoValue)) {
383
- return campos.campoValue.map((vCampo) => vD[vCampo] ?? '').join(' - ').trim();
268
+ let vD;
269
+ if (campos.campoId == '*object*' || campos.campoId == '*objeto*') {
270
+ console.log(campos);
271
+ vD = campos.lista.find((x) => JSON.stringify(x).trim() == JSON.stringify(idxSel).trim());
272
+ console.log(vD);
273
+ }
274
+ else {
275
+ vD = campos.lista.find((x) => x[campos.campoId] == idxSel);
276
+ }
277
+ if (!vD && campos.opcExtra) {
278
+ console.log('eval ', campos.opcExtra);
279
+ if (campos.campoId == '*object*' || campos.campoId == '*objeto*') {
280
+ console.log(campos);
281
+ vD = campos.opcExtra.find((x) => JSON.stringify(x).trim() == JSON.stringify(idxSel).trim());
282
+ console.log(vD);
283
+ }
284
+ else {
285
+ vD = campos.opcExtra.find((x) => x[campos.campoId] == idxSel);
286
+ }
287
+ }
288
+ if (vD) {
289
+ let txtFinal = '';
290
+ if (Array.isArray(campos.campoValue)) {
291
+ campos.campoValue.forEach((vCampo, idx) => {
292
+ txtFinal += (vD[vCampo] ?? '');
293
+ if (idx < campos.campoValue.length - 1) {
294
+ txtFinal += ' - ';
295
+ }
296
+ });
297
+ }
298
+ else {
299
+ txtFinal = vD[campos.campoValue] ?? '';
300
+ }
301
+ return txtFinal.trim();
302
+ }
303
+ else {
304
+ console.log('ASSSSS ----- SSSS ');
384
305
  }
385
- return (vD[campos.campoValue] ?? '').trim();
306
+ return '';
386
307
  };
387
308
  if (esNumero(idxSel)) {
388
- if ((idxSel > 0 && campos.lista?.length) || (idxSel && typeof idxSel === 'object')) {
309
+ if (idxSel > 0 && campos.lista?.length > 0) {
310
+ return impDataMostrar();
311
+ }
312
+ else if (idxSel && typeof idxSel == 'object') {
389
313
  return impDataMostrar();
390
314
  }
391
315
  }
392
- else if (campos.lista?.length) {
393
- return impDataMostrar();
316
+ else {
317
+ if (campos.lista?.length > 0) {
318
+ return impDataMostrar();
319
+ }
394
320
  }
395
321
  return '';
396
322
  }
397
- /**
398
- * Filtra datos locales de un array basado en un valor de búsqueda.
399
- */
400
- function filtrarDatosLocal(data, value, campoBuscar) {
401
- if (!value)
402
- return data;
403
- const normalizar = (val) => val?.toString()?.toLowerCase() ?? '';
404
- const esNum = !isNaN(Number(value));
405
- return data.filter(item => {
406
- const campos = Array.isArray(campoBuscar) ? campoBuscar : [campoBuscar];
407
- return campos.some(campo => {
408
- const campoVal = item[campo];
409
- if (campoVal == null)
410
- return false;
411
- return esNum
412
- ? campoVal.toString().includes(value.toString())
413
- : normalizar(campoVal).includes(normalizar(value));
414
- });
415
- });
416
- }
417
- /**
418
- * Vincula un FormControl a datos locales para autocompletado.
419
- */
420
- function changeSelectData(objThis, { formControl, data, campoBuscar, variableResultado }) {
421
- objThis.filtrados[variableResultado] = formControl.valueChanges.pipe(untilDestroyed(objThis)).pipe(startWith(''), map(value => data ? filtrarDatosLocal(data, value, campoBuscar) : []));
323
+ function changeSelectData(objThis, dataFiltro) {
324
+ objThis['filtrados'][dataFiltro.variableResultado] = dataFiltro.formControl.valueChanges.pipe(untilDestroyed(objThis)).pipe(startWith(''), map(value => {
325
+ const varN = dataFiltro.data;
326
+ if (varN) {
327
+ if (value) {
328
+ return varN.map(x => x).filter(dat => {
329
+ if (Array.isArray(dataFiltro.campoBuscar)) {
330
+ let encontrado = false;
331
+ for (const vCampo of dataFiltro.campoBuscar) {
332
+ // console.log(vCampo, value, dat[vCampo]);
333
+ if (isNaN(Number(value))) {
334
+ // NO ES NUMERO
335
+ if (value && dat[vCampo] && dat[vCampo].toLowerCase().includes(value?.toString().toLowerCase())) {
336
+ encontrado = true;
337
+ break;
338
+ }
339
+ }
340
+ else {
341
+ if (value && dat[vCampo] && dat[vCampo].toString().includes(value?.toString())) {
342
+ encontrado = true;
343
+ break;
344
+ }
345
+ }
346
+ }
347
+ return encontrado;
348
+ }
349
+ else {
350
+ if (isNaN(Number(value))) {
351
+ return dat[dataFiltro.campoBuscar].toLowerCase().includes(value?.toString().toLowerCase());
352
+ }
353
+ else {
354
+ return dat[dataFiltro.campoBuscar].toString().includes(value?.toString());
355
+ }
356
+ }
357
+ });
358
+ }
359
+ return varN;
360
+ }
361
+ return false;
362
+ }));
422
363
  }
423
- /**
424
- * Vincula un FormControl a datos locales obtenidos de dataServidor o dataServidorSuscripcion.
425
- */
426
364
  function changeSelect(control, formControl, tipo, campoBuscar, campoFiltro = null) {
427
- const filtro = campoFiltro ?? tipo;
428
- control.filtrados[filtro] = formControl.valueChanges.pipe(untilDestroyed(control)).pipe(startWith(''), map(value => {
365
+ // console.log(formControl);
366
+ // const formGroup = formControl.parent.controls;
367
+ // console.warn( Object.keys(formGroup).find(name => formControl === formGroup[name]) || null );
368
+ if (!campoFiltro) {
369
+ campoFiltro = tipo;
370
+ }
371
+ control['filtrados'][campoFiltro ?? '__'] = formControl.valueChanges.pipe(untilDestroyed(control)).pipe(startWith(''), map(value => {
372
+ // console.warn(value);
429
373
  const partes = tipo.split('.');
430
- const varN = control.dataServidor?.[partes[0]]?.[partes[1]] ??
431
- control.dataServidor?.[tipo] ??
432
- control.dataServidorSuscripcion?.[tipo]?.getValue();
433
- return varN ? filtrarDatosLocal(varN, value, campoBuscar) : [];
374
+ let varN;
375
+ if (control['dataServidor']) {
376
+ varN = (partes.length > 1) ? control['dataServidor'][partes[0]][partes[1]] : control['dataServidor'][tipo];
377
+ }
378
+ else if (control['dataServidorSuscripcion']) {
379
+ varN = control['dataServidorSuscripcion'][tipo].getValue();
380
+ }
381
+ if (varN) {
382
+ if (value) {
383
+ return varN.map((x) => x).filter((dat) => {
384
+ if (Array.isArray(campoBuscar)) {
385
+ let encontrado = false;
386
+ for (const vCampo of campoBuscar) {
387
+ // console.log(vCampo, value, dat[vCampo]);
388
+ if (isNaN(Number(value))) {
389
+ // NO ES NUMERO
390
+ if (value && dat[vCampo] && dat[vCampo].toLowerCase().includes(value?.toString().toLowerCase())) {
391
+ encontrado = true;
392
+ break;
393
+ }
394
+ }
395
+ else {
396
+ if (value && dat[vCampo] && dat[vCampo].toString().includes(value?.toString())) {
397
+ encontrado = true;
398
+ break;
399
+ }
400
+ }
401
+ }
402
+ return encontrado;
403
+ }
404
+ else {
405
+ if (isNaN(Number(value))) {
406
+ return dat[campoBuscar].toLowerCase().includes(value?.toString().toLowerCase());
407
+ }
408
+ else {
409
+ return dat[campoBuscar].toString().includes(value?.toString());
410
+ }
411
+ }
412
+ });
413
+ }
414
+ return varN;
415
+ }
416
+ return false;
434
417
  }));
435
418
  }
436
- /**
437
- * Función genérica para vincular un FormControl con datos desde API o Promise.
438
- * Preparada para migrar a signals en el futuro.
439
- */
440
- function changeSelectReformateado(config) {
441
- const { objThis, tipoReq, formControl, queryService, campoId, minLength = 3, dataExtra = {}, dataExtraVariable = [], anonimo = false, variableResultado = tipoReq, } = config;
442
- formControl.valueChanges.pipe(debounceTime(500), tap(() => {
443
- objThis.filtrados[variableResultado + 'tmp'] = isObservable(objThis.filtrados[variableResultado])
444
- ? []
445
- : objThis.filtrados[variableResultado] || [];
446
- if (objThis.filtrados[variableResultado] !== objThis.filtrados[variableResultado + 'tmp']) {
447
- objThis.filtrados[variableResultado] = [];
419
+ function changeSelectDataApi(objThis, dataFiltro) {
420
+ if (!dataFiltro.variableResultado) {
421
+ dataFiltro.variableResultado = dataFiltro.tipoReq;
422
+ }
423
+ const idFiltrado = dataFiltro.variableResultado;
424
+ dataFiltro.formControl.valueChanges.pipe(debounceTime(500), tap(() => {
425
+ objThis.filtrados[dataFiltro.variableResultado + 'tmp'] = isObservable(objThis.filtrados[idFiltrado]) ? [] : objThis.filtrados[idFiltrado] || [];
426
+ if (objThis.filtrados[idFiltrado] !== objThis.filtrados[idFiltrado + 'tmp']) {
427
+ objThis.filtrados[idFiltrado] = [];
448
428
  }
449
429
  objThis.isLoading = true;
450
430
  }), switchMap(value => {
451
- if (campoId) {
452
- const existe = objThis.filtrados[variableResultado + 'tmp']
453
- .findIndex((item) => item[campoId] === value);
454
- if (existe >= 0) {
455
- return of({ [tipoReq]: objThis.filtrados[variableResultado + 'tmp'] });
431
+ if (dataFiltro.campoId) {
432
+ const busquedaActual2 = objThis.filtrados[idFiltrado + 'tmp'].findIndex((item) => item[dataFiltro.campoId ?? '--'] === value);
433
+ if (busquedaActual2 >= 0) {
434
+ return of({ [dataFiltro.tipoReq]: objThis.filtrados[idFiltrado + 'tmp'] });
456
435
  }
457
436
  }
458
- if (!value || value.length < minLength) {
459
- objThis.isLoading = false;
460
- return of({ [tipoReq]: [] });
461
- }
462
- const extraVars = Object.fromEntries(dataExtraVariable.map(v => [v.campo, v.ctrlValue.value]));
463
- const query = queryService.getDataMethod('GET', tipoReq, { ...dataExtra, ...extraVars, txtBuscar: value }, anonimo);
464
- if (esPromise(query)) {
465
- return query.then((data) => ({ [tipoReq]: data[tipoReq] ?? [] }))
466
- .finally(() => { objThis.isLoading = false; });
467
- }
468
- return query.pipe(finalize(() => { objThis.isLoading = false; }));
437
+ return !value || value.length < (dataFiltro.minLength ?? 3) ? [] : (dataFiltro.queryService.getDataMethod('GET', dataFiltro.tipoReq, {
438
+ ...(dataFiltro.dataExtra ?? {}),
439
+ ...(dataFiltro.dataExtraVariable ? Object.fromEntries(dataFiltro.dataExtraVariable.map((objData) => [objData.campo, objData.ctrlValue.value])) : {}),
440
+ txtBuscar: value,
441
+ }, dataFiltro.anonimo).pipe(finalize(() => objThis.isLoading = false)));
469
442
  })).subscribe((data) => {
470
- objThis.filtrados[variableResultado] = data[tipoReq] ?? [];
471
- });
472
- }
473
- /**
474
- * Alias para compatibilidad.
475
- */
476
- function changeSelectDataApi(objThis, dataFiltro) {
477
- return changeSelectReformateado({
478
- objThis,
479
- tipoReq: dataFiltro.tipoReq,
480
- formControl: dataFiltro.formControl,
481
- queryService: dataFiltro.queryService,
482
- campoId: dataFiltro.campoId,
483
- minLength: dataFiltro.minLength,
484
- dataExtra: dataFiltro.dataExtra,
485
- dataExtraVariable: dataFiltro.dataExtraVariable,
486
- anonimo: dataFiltro.anonimo,
487
- variableResultado: dataFiltro.variableResultado,
443
+ objThis.filtrados[idFiltrado] = data[dataFiltro.tipoReq] ?? [];
488
444
  });
489
445
  }
490
- /**
491
- * Alias para compatibilidad.
492
- */
493
446
  function changeSelectApi(control, queryService, formControl, tipo, dataExtra = {}, dataExtraVariable = null, minLength = 1, anonimo = false) {
494
- return changeSelectReformateado({
495
- objThis: control,
496
- tipoReq: tipo,
497
- formControl,
498
- queryService,
499
- minLength,
500
- dataExtra,
501
- dataExtraVariable: dataExtraVariable ?? [],
502
- anonimo,
447
+ formControl.valueChanges.pipe(debounceTime(500), tap((value) => {
448
+ control['filtrados'][tipo + 'tmp'] = isObservable(control['filtrados'][tipo]) ? [] : control['filtrados'][tipo];
449
+ if (control['filtrados'][tipo] != control['filtrados'][tipo + 'tmp']) {
450
+ control['filtrados'][tipo] = [];
451
+ }
452
+ control['isLoading'] = true;
453
+ }), switchMap(value => {
454
+ const formGroup = formControl.parent?.controls;
455
+ const nombreControl = Object.keys(formGroup).find(name => formControl === formGroup[name]) || null;
456
+ if (nombreControl && control['filtrados'][tipo + 'tmp'] && control['filtrados'][tipo + 'tmp'].length > 0) {
457
+ const busquedaActual = control['filtrados'][tipo + 'tmp'].findIndex((item) => item[nombreControl] == value);
458
+ if (busquedaActual >= 0) {
459
+ const vRet = {};
460
+ vRet[tipo] = control['filtrados'][tipo + 'tmp'];
461
+ control['isLoading'] = false;
462
+ return of(vRet);
463
+ }
464
+ }
465
+ if (!value || value.length < minLength) {
466
+ return [];
467
+ }
468
+ const dataExtraVariableData = {};
469
+ if (dataExtraVariable) {
470
+ // @ts-ignore
471
+ for (const objData of dataExtraVariable) {
472
+ dataExtraVariableData[objData.campo] = objData.ctrlValue.value;
473
+ }
474
+ }
475
+ return queryService.getDataMethod('GET', tipo, { ...dataExtra, ...dataExtraVariableData, ...{ txtBuscar: value } }, anonimo).pipe(finalize(() => {
476
+ control['isLoading'] = false;
477
+ }));
478
+ })).subscribe((data) => {
479
+ if (data[tipo] == undefined) {
480
+ control['filtrados'][tipo] = [];
481
+ }
482
+ else {
483
+ control['filtrados'][tipo] = data[tipo];
484
+ }
503
485
  });
504
486
  }
505
- /**
506
- * Comprueba si un valor es una Promesa.
507
- */
508
- function esPromise(valor) {
509
- return !!valor && typeof valor.then === 'function';
510
- }
511
487
 
512
488
  function seleccionarTextoInput(event) {
513
489
  event.target.select();
@@ -519,65 +495,6 @@ function b64Encode(val) {
519
495
  function b64Decode(val) {
520
496
  return Buffer.from(val, 'base64').toString('binary');
521
497
  }
522
- /**
523
- * Codificar string a Base64 (UTF-8 seguro)
524
- */
525
- function encodeBase64String(str) {
526
- const encoder = new TextEncoder();
527
- const bytes = encoder.encode(str);
528
- let binary = '';
529
- bytes.forEach(b => binary += String.fromCharCode(b));
530
- return btoa(binary);
531
- }
532
- /**
533
- * Decodificar Base64 a string (UTF-8 seguro)
534
- */
535
- function decodeBase64String(b64) {
536
- const binary = atob(b64);
537
- const bytes = new Uint8Array(binary.length);
538
- for (let i = 0; i < binary.length; i++) {
539
- bytes[i] = binary.charCodeAt(i);
540
- }
541
- const decoder = new TextDecoder();
542
- return decoder.decode(bytes);
543
- }
544
- /**
545
- * Codificar un objeto a base64 (UTF-8 seguro)
546
- */
547
- function encodeBase64Object(obj) {
548
- return encodeBase64String(JSON.stringify(obj));
549
- }
550
- /**
551
- * Decodificar un base64 y obtener el objeto original
552
- */
553
- function decodeBase64Object(b64) {
554
- return JSON.parse(decodeBase64String(b64));
555
- }
556
- /**
557
- * Codificar archivo a Base64 (retorna solo el contenido, sin "data:...")
558
- */
559
- function encodeBase64File(file) {
560
- return new Promise((resolve, reject) => {
561
- const reader = new FileReader();
562
- reader.onload = () => {
563
- const result = reader.result;
564
- resolve(result.split(',')[1]); // quita el prefijo "data:...;base64,"
565
- };
566
- reader.onerror = reject;
567
- reader.readAsDataURL(file);
568
- });
569
- }
570
- /**
571
- * Decodificar Base64 a Blob (para reconstruir archivos en Angular)
572
- */
573
- function decodeBase64ToBlob(b64, mimeType = 'application/octet-stream') {
574
- const byteChars = atob(b64);
575
- const byteNumbers = new Array(byteChars.length);
576
- for (let i = 0; i < byteChars.length; i++) {
577
- byteNumbers[i] = byteChars.charCodeAt(i);
578
- }
579
- return new Blob([new Uint8Array(byteNumbers)], { type: mimeType });
580
- }
581
498
 
582
499
  function getBrowserName() {
583
500
  const agent = window.navigator.userAgent.toLowerCase();
@@ -642,6 +559,147 @@ function formatearFechaCadena(fecha) {
642
559
  return new Date(year, month, day);
643
560
  }
644
561
 
562
+ /**
563
+ * Configuración global para las utilidades de logging
564
+ */
565
+ let isProductionMode = false;
566
+ /**
567
+ * Configura el modo de producción para las utilidades de logging
568
+ * @param production - true si está en modo producción, false para desarrollo
569
+ *
570
+ * @example
571
+ * ```typescript
572
+ * import { setProductionMode } from '@jvsoft/utils';
573
+ * import { environment } from './environments/environment';
574
+ *
575
+ * setProductionMode(environment.production);
576
+ * ```
577
+ */
578
+ function setProductionMode(production) {
579
+ isProductionMode = production;
580
+ }
581
+ /**
582
+ * Obtiene el estado actual del modo de producción
583
+ * @returns true si está en modo producción, false en desarrollo
584
+ */
585
+ function isProduction() {
586
+ return isProductionMode;
587
+ }
588
+ /**
589
+ * Muestra mensajes de log solo en modo desarrollo
590
+ * @param args - Argumentos a mostrar en consola
591
+ *
592
+ * @example
593
+ * ```typescript
594
+ * devLog('Usuario cargado:', usuario);
595
+ * devLog('Estado:', { activo: true, rol: 'admin' });
596
+ * ```
597
+ */
598
+ function devLog(...args) {
599
+ if (!isProductionMode) {
600
+ console.log(...args);
601
+ }
602
+ }
603
+ /**
604
+ * Muestra advertencias solo en modo desarrollo
605
+ * @param args - Argumentos a mostrar como advertencia
606
+ *
607
+ * @example
608
+ * ```typescript
609
+ * devWarn('Función deprecada, usar nuevaFuncion() en su lugar');
610
+ * ```
611
+ */
612
+ function devWarn(...args) {
613
+ if (!isProductionMode) {
614
+ console.warn(...args);
615
+ }
616
+ }
617
+ /**
618
+ * Muestra errores en consola (siempre, incluso en producción)
619
+ * @param args - Argumentos a mostrar como error
620
+ *
621
+ * @example
622
+ * ```typescript
623
+ * devError('Error al cargar datos:', error);
624
+ * ```
625
+ */
626
+ function devError(...args) {
627
+ console.error(...args);
628
+ }
629
+ /**
630
+ * Crea un grupo de logs solo en modo desarrollo
631
+ * @param label - Etiqueta del grupo
632
+ * @param collapsed - Si el grupo debe estar colapsado por defecto
633
+ *
634
+ * @example
635
+ * ```typescript
636
+ * devGroup('Datos del usuario');
637
+ * devLog('Nombre:', usuario.nombre);
638
+ * devLog('Email:', usuario.email);
639
+ * devGroupEnd();
640
+ * ```
641
+ */
642
+ function devGroup(label, collapsed = false) {
643
+ if (!isProductionMode) {
644
+ if (collapsed) {
645
+ console.groupCollapsed(label);
646
+ }
647
+ else {
648
+ console.group(label);
649
+ }
650
+ }
651
+ }
652
+ /**
653
+ * Cierra el grupo de logs actual
654
+ */
655
+ function devGroupEnd() {
656
+ if (!isProductionMode) {
657
+ console.groupEnd();
658
+ }
659
+ }
660
+ /**
661
+ * Muestra una tabla en consola solo en modo desarrollo
662
+ * @param data - Datos a mostrar en formato tabla
663
+ *
664
+ * @example
665
+ * ```typescript
666
+ * devTable([
667
+ * { nombre: 'Juan', edad: 25 },
668
+ * { nombre: 'María', edad: 30 }
669
+ * ]);
670
+ * ```
671
+ */
672
+ function devTable(data) {
673
+ if (!isProductionMode) {
674
+ console.table(data);
675
+ }
676
+ }
677
+ /**
678
+ * Inicia un temporizador solo en modo desarrollo
679
+ * @param label - Etiqueta del temporizador
680
+ *
681
+ * @example
682
+ * ```typescript
683
+ * devTime('carga-datos');
684
+ * // ... código a medir
685
+ * devTimeEnd('carga-datos'); // Muestra: carga-datos: 123.45ms
686
+ * ```
687
+ */
688
+ function devTime(label) {
689
+ if (!isProductionMode) {
690
+ console.time(label);
691
+ }
692
+ }
693
+ /**
694
+ * Finaliza un temporizador y muestra el tiempo transcurrido
695
+ * @param label - Etiqueta del temporizador
696
+ */
697
+ function devTimeEnd(label) {
698
+ if (!isProductionMode) {
699
+ console.timeEnd(label);
700
+ }
701
+ }
702
+
645
703
  function maskEmail(email) {
646
704
  const [user, domain] = email.split("@");
647
705
  if (user.length <= 2) {
@@ -1589,10 +1647,10 @@ class DataEnListaPipe {
1589
1647
  }
1590
1648
  return null;
1591
1649
  }
1592
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DataEnListaPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1593
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DataEnListaPipe, isStandalone: true, name: "dataEnLista" });
1650
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: DataEnListaPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1651
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: DataEnListaPipe, isStandalone: true, name: "dataEnLista" });
1594
1652
  }
1595
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DataEnListaPipe, decorators: [{
1653
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: DataEnListaPipe, decorators: [{
1596
1654
  type: Pipe,
1597
1655
  args: [{
1598
1656
  name: 'dataEnLista',
@@ -1681,10 +1739,10 @@ class DateDiffStringPipe {
1681
1739
  seconds,
1682
1740
  };
1683
1741
  }
1684
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateDiffStringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1685
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DateDiffStringPipe, isStandalone: true, name: "dateDiffString" });
1742
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: DateDiffStringPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1743
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: DateDiffStringPipe, isStandalone: true, name: "dateDiffString" });
1686
1744
  }
1687
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DateDiffStringPipe, decorators: [{
1745
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: DateDiffStringPipe, decorators: [{
1688
1746
  type: Pipe,
1689
1747
  args: [{
1690
1748
  name: 'dateDiffString',
@@ -1736,10 +1794,10 @@ class FiltroPipe {
1736
1794
  return false;
1737
1795
  });
1738
1796
  }
1739
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FiltroPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1740
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FiltroPipe, isStandalone: true, name: "filtro" });
1797
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: FiltroPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1798
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: FiltroPipe, isStandalone: true, name: "filtro" });
1741
1799
  }
1742
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FiltroPipe, decorators: [{
1800
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: FiltroPipe, decorators: [{
1743
1801
  type: Pipe,
1744
1802
  args: [{
1745
1803
  name: 'filtro'
@@ -1753,10 +1811,10 @@ class FormControlIsRequiredPipe {
1753
1811
  }
1754
1812
  return formControl.hasValidator(Validators.required);
1755
1813
  }
1756
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FormControlIsRequiredPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1757
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: FormControlIsRequiredPipe, isStandalone: true, name: "formControlIsRequired" });
1814
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: FormControlIsRequiredPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1815
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: FormControlIsRequiredPipe, isStandalone: true, name: "formControlIsRequired" });
1758
1816
  }
1759
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: FormControlIsRequiredPipe, decorators: [{
1817
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: FormControlIsRequiredPipe, decorators: [{
1760
1818
  type: Pipe,
1761
1819
  args: [{
1762
1820
  name: 'formControlIsRequired'
@@ -1779,10 +1837,10 @@ class JsonParsePipe {
1779
1837
  return [];
1780
1838
  }
1781
1839
  }
1782
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: JsonParsePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1783
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: JsonParsePipe, isStandalone: true, name: "jsonParse" });
1840
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: JsonParsePipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1841
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: JsonParsePipe, isStandalone: true, name: "jsonParse" });
1784
1842
  }
1785
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: JsonParsePipe, decorators: [{
1843
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: JsonParsePipe, decorators: [{
1786
1844
  type: Pipe,
1787
1845
  args: [{
1788
1846
  name: 'jsonParse'
@@ -1797,10 +1855,10 @@ class NoSanitizePipe {
1797
1855
  transform(html) {
1798
1856
  return this.domSanitizer.bypassSecurityTrustHtml(html);
1799
1857
  }
1800
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NoSanitizePipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
1801
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: NoSanitizePipe, isStandalone: true, name: "noSanitize" });
1858
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: NoSanitizePipe, deps: [{ token: i1.DomSanitizer }], target: i0.ɵɵFactoryTarget.Pipe });
1859
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: NoSanitizePipe, isStandalone: true, name: "noSanitize" });
1802
1860
  }
1803
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: NoSanitizePipe, decorators: [{
1861
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: NoSanitizePipe, decorators: [{
1804
1862
  type: Pipe,
1805
1863
  args: [{ name: 'noSanitize' }]
1806
1864
  }], ctorParameters: () => [{ type: i1.DomSanitizer }] });
@@ -1815,10 +1873,10 @@ class TipoValorFuncionPipe {
1815
1873
  }
1816
1874
  return datoParam;
1817
1875
  }
1818
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TipoValorFuncionPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1819
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: TipoValorFuncionPipe, isStandalone: true, name: "tipoValorFuncion" });
1876
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: TipoValorFuncionPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1877
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: TipoValorFuncionPipe, isStandalone: true, name: "tipoValorFuncion" });
1820
1878
  }
1821
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: TipoValorFuncionPipe, decorators: [{
1879
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: TipoValorFuncionPipe, decorators: [{
1822
1880
  type: Pipe,
1823
1881
  args: [{
1824
1882
  name: 'tipoValorFuncion',
@@ -1837,10 +1895,10 @@ class ZeroFillPipe {
1837
1895
  }
1838
1896
  return s;
1839
1897
  }
1840
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ZeroFillPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1841
- static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: ZeroFillPipe, isStandalone: true, name: "zeroFill" });
1898
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: ZeroFillPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe });
1899
+ static ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.16", ngImport: i0, type: ZeroFillPipe, isStandalone: true, name: "zeroFill" });
1842
1900
  }
1843
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: ZeroFillPipe, decorators: [{
1901
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.16", ngImport: i0, type: ZeroFillPipe, decorators: [{
1844
1902
  type: Pipe,
1845
1903
  args: [{
1846
1904
  name: 'zeroFill',
@@ -1863,5 +1921,5 @@ function zeroFill(value, digitos, ...args) {
1863
1921
  * Generated bundle index. Do not edit.
1864
1922
  */
1865
1923
 
1866
- export { DataEnListaPipe, DataModel, DateDiffStringPipe, FiltroPipe, FormControlIsRequiredPipe, JsonParsePipe, NoSanitizePipe, TipoValorFuncionPipe, ZeroFillPipe, b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, changeSelectReformateado, convertirBytes, dataEnLista, dateDiffString, decodeBase64Object, decodeBase64String, decodeBase64ToBlob, deepClone, deepMerge, delLocalStorage, desencriptar, downLoadFileStream, eliminarColumnaPorIndex, eliminarDuplicados, eliminarElementos, encodeBase64File, encodeBase64Object, encodeBase64String, encriptar, esNumero, establecerQuitarRequired, extraerDominio, formControlIsRequired, formatearFecha, formatearFechaCadena, formatearFechaFormato, generateRandomString, getBrowserName, getCambiarPwd, getDataArchivoFromPath, getFormValidationErrors, getLocalStorage, getUniqueValues, getUniqueValuesByProperty, groupBy, inicializarVariablesSessionStorage, isEmail, jwtToken, jwtTokenData, jwtTokenExpiracion, jwtTokenExpiracionFaltante, localStorageKeys, markAsTouchedWithoutEmitEvent, maskEmail, mensajeAlerta, mensajeConfirmacion, mensajeTimer, mensajeToast, mensajesDeError, mensajesErrorFormControl, mostrarValorEnBusqueda, nestGroupsBy, numberToWords, objectPropertiesBoolean, objectPropertiesToType, obtenerHostDesdeUrl, obtenerMimeType, obtenerUltimoOrden, ordenarArray, ordenarPorPropiedad, ordenarPorPropiedades, roundToDecimal, sanitizarNombreArchivo, seleccionarTextoInput, setCambiarPwd, setControlDesdeLista, setJwtTokenData, setLocalStorage, sumarObjetos, sumarPropiedades, tipoValorFuncion, toFormData, transformarFechasPorNombreDeCampo, verificarRUC, zeroFill };
1924
+ export { DataEnListaPipe, DataModel, DateDiffStringPipe, FiltroPipe, FormControlIsRequiredPipe, JsonParsePipe, NoSanitizePipe, TipoValorFuncionPipe, ZeroFillPipe, b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, convertirBytes, dataEnLista, dateDiffString, deepClone, deepMerge, delLocalStorage, desencriptar, devError, devGroup, devGroupEnd, devLog, devTable, devTime, devTimeEnd, devWarn, downLoadFileStream, eliminarColumnaPorIndex, eliminarDuplicados, eliminarElementos, encriptar, esNumero, establecerQuitarRequired, extraerDominio, formControlIsRequired, formatearFecha, formatearFechaCadena, formatearFechaFormato, generateRandomString, getBrowserName, getCambiarPwd, getDataArchivoFromPath, getFormValidationErrors, getLocalStorage, getUniqueValues, getUniqueValuesByProperty, groupBy, inicializarVariablesSessionStorage, isEmail, isProduction, jwtToken, jwtTokenData, jwtTokenExpiracion, jwtTokenExpiracionFaltante, localStorageKeys, markAsTouchedWithoutEmitEvent, maskEmail, mensajeAlerta, mensajeConfirmacion, mensajeTimer, mensajeToast, mensajesDeError, mensajesErrorFormControl, mostrarValorEnBusqueda, nestGroupsBy, numberToWords, objectPropertiesBoolean, objectPropertiesToType, obtenerHostDesdeUrl, obtenerMimeType, obtenerUltimoOrden, ordenarArray, ordenarPorPropiedad, ordenarPorPropiedades, roundToDecimal, sanitizarNombreArchivo, seleccionarTextoInput, setCambiarPwd, setControlDesdeLista, setJwtTokenData, setLocalStorage, setProductionMode, sumarObjetos, sumarPropiedades, tipoValorFuncion, toFormData, transformarFechasPorNombreDeCampo, verificarRUC, zeroFill };
1867
1925
  //# sourceMappingURL=jvsoft-utils.mjs.map