@jvsoft/utils 0.0.13-alpha.2 → 0.0.13-alpha.3

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.
@@ -7,8 +7,8 @@ import { formatDate } from '@angular/common';
7
7
  import { saveAs } from 'file-saver';
8
8
  import { Validators, FormGroup, FormControl, FormArray } from '@angular/forms';
9
9
  import { ReactiveFormConfig } from '@rxweb/reactive-form-validators';
10
- import swal from 'sweetalert2';
11
10
  import moment from 'moment';
11
+ import swal from 'sweetalert2';
12
12
  import { jwtDecode } from 'jwt-decode';
13
13
 
14
14
  function deepMerge(source, target) {
@@ -206,6 +206,59 @@ function obtenerUltimoOrden(data, campo, incrementar = true) {
206
206
  }, 0);
207
207
  return incrementar ? max + 1 : max;
208
208
  }
209
+ /**
210
+ * Elimina una o varias columnas específicas (por índice) de una tabla representada como array de arrays.
211
+ *
212
+ * @param data - Array de filas (cada fila debe ser un array).
213
+ * @param columnIndex - Índice o lista de índices de las columnas a eliminar.
214
+ * @returns Nuevo array con las columnas eliminadas.
215
+ */
216
+ function eliminarColumnaPorIndex(data, columnIndex) {
217
+ if (!Array.isArray(data))
218
+ return [];
219
+ // Normalizar a array único y ordenado (descendente para evitar reindexación al splicing)
220
+ const indices = Array.isArray(columnIndex)
221
+ ? [...new Set(columnIndex)].filter((i) => (typeof i === 'number' && i >= 0)).sort((a, b) => b - a)
222
+ : [columnIndex];
223
+ return data.map((row) => {
224
+ if (!Array.isArray(row))
225
+ return row;
226
+ const newRow = [...row];
227
+ for (const index of indices) {
228
+ if (index >= 0 && index < newRow.length) {
229
+ newRow.splice(index, 1);
230
+ }
231
+ }
232
+ return newRow;
233
+ });
234
+ }
235
+ function eliminarDuplicados(array, claves) {
236
+ const unicos = new Map();
237
+ for (const item of array) {
238
+ const claveUnica = claves && claves.length > 0
239
+ ? claves.map(k => item[k]).join('|')
240
+ : JSON.stringify(item);
241
+ if (!unicos.has(claveUnica)) {
242
+ unicos.set(claveUnica, item);
243
+ }
244
+ }
245
+ return Array.from(unicos.values());
246
+ }
247
+ function eliminarElementos(origen, elementosAEliminar, claves) {
248
+ const clavesSet = new Set();
249
+ for (const item of elementosAEliminar) {
250
+ const key = claves && claves.length > 0
251
+ ? claves.map(k => item[k]).join('|')
252
+ : JSON.stringify(item);
253
+ clavesSet.add(key);
254
+ }
255
+ return origen.filter(item => {
256
+ const key = claves && claves.length > 0
257
+ ? claves.map(k => item[k]).join('|')
258
+ : JSON.stringify(item);
259
+ return !clavesSet.has(key);
260
+ });
261
+ }
209
262
 
210
263
  function mostrarValorEnBusqueda(campos, idxSel) {
211
264
  const impDataMostrar = () => {
@@ -764,6 +817,87 @@ function markAsTouchedWithoutEmitEvent(control) {
764
817
  control.updateValueAndValidity({ emitEvent: false });
765
818
  }
766
819
  }
820
+ function setControlDesdeLista(config) {
821
+ const lista = config.lista$?.getValue() ?? [];
822
+ if (!lista.length)
823
+ return;
824
+ let seleccionado;
825
+ if (config.idProp && config.idValor !== undefined) {
826
+ seleccionado = lista.find((item) => item[config.idProp] === config.idValor);
827
+ }
828
+ else if (config.usarPrimeraOpcion) {
829
+ seleccionado = lista[0];
830
+ }
831
+ console.log(seleccionado);
832
+ if (!seleccionado)
833
+ return;
834
+ let valor;
835
+ if (config.selector) {
836
+ valor = config.selector(seleccionado);
837
+ }
838
+ else if (config.idProp) {
839
+ valor = seleccionado[config.idProp];
840
+ }
841
+ else {
842
+ valor = seleccionado;
843
+ }
844
+ if (config.formControl) {
845
+ config.formControl.setValue(valor);
846
+ }
847
+ else if (config.formGroup && config.controlName) {
848
+ const control = config.formGroup.get(config.controlName);
849
+ if (control instanceof FormControl) {
850
+ control.setValue(valor);
851
+ }
852
+ }
853
+ }
854
+ function transformarFechasPorNombreDeCampo(formValue) {
855
+ const retData = {};
856
+ Object.entries(formValue).forEach(([key, value]) => {
857
+ // ✅ No procesar Blob o File
858
+ if (value instanceof Blob || value instanceof File) {
859
+ retData[key] = value;
860
+ return;
861
+ }
862
+ if (value instanceof Date) {
863
+ if (/^d[A-Za-z]+/.test(key)) {
864
+ retData[key] = moment(value).format('YYYY-MM-DD');
865
+ }
866
+ else {
867
+ retData[key] = new Date(Date.UTC(value.getFullYear(), value.getMonth(), value.getDate(), value.getHours(), value.getMinutes(), value.getSeconds()));
868
+ }
869
+ }
870
+ else if (/^dt[A-Za-z]+/.test(key) && typeof value === 'string') {
871
+ if (/\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}\.\d{3}/.test(value)) {
872
+ retData[key] = new Date(value + 'Z');
873
+ }
874
+ else if (/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}/.test(value) || /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}/.test(value)) {
875
+ retData[key] = new Date(value + ':00.000Z');
876
+ }
877
+ else {
878
+ console.warn('FECHA INVALIDA AL ENVIAR (FormInter):', key, value);
879
+ retData[key] = value;
880
+ }
881
+ }
882
+ else if (Array.isArray(value)) {
883
+ if (value.every(v => v instanceof File || v instanceof Blob)) {
884
+ retData[key] = value;
885
+ }
886
+ else {
887
+ retData[key] = value.map(item => typeof item === 'object' && item !== null
888
+ ? transformarFechasPorNombreDeCampo(item)
889
+ : item);
890
+ }
891
+ }
892
+ else if (typeof value === 'object' && value !== null) {
893
+ retData[key] = transformarFechasPorNombreDeCampo(value);
894
+ }
895
+ else {
896
+ retData[key] = value;
897
+ }
898
+ });
899
+ return retData;
900
+ }
767
901
 
768
902
  function mensajeAlerta(tipo, titulo, mensaje, opciones) {
769
903
  opciones = {
@@ -1038,6 +1172,84 @@ function setLocalStorage(key, value = '') {
1038
1172
  function roundToDecimal(number, decimal) {
1039
1173
  return parseFloat(number.toFixed(decimal));
1040
1174
  }
1175
+ function numberToWords(num) {
1176
+ if (num === 0)
1177
+ return 'cero';
1178
+ if (num < 0)
1179
+ return 'menos ' + numberToWords(Math.abs(num));
1180
+ if (num > 999999999999)
1181
+ return 'número demasiado grande';
1182
+ const ones = ['', 'un', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'];
1183
+ const onesFinal = ['', 'uno', 'dos', 'tres', 'cuatro', 'cinco', 'seis', 'siete', 'ocho', 'nueve'];
1184
+ const tens = ['', 'diez', 'veinte', 'treinta', 'cuarenta', 'cincuenta', 'sesenta', 'setenta', 'ochenta', 'noventa'];
1185
+ const teens = ['diez', 'once', 'doce', 'trece', 'catorce', 'quince', 'dieciséis', 'diecisiete', 'dieciocho', 'diecinueve'];
1186
+ const hundreds = ['', 'ciento', 'doscientos', 'trescientos', 'cuatrocientos', 'quinientos', 'seiscientos', 'setecientos', 'ochocientos', 'novecientos'];
1187
+ // Función auxiliar para "un"/"uno"
1188
+ const getOneForm = (n, isFinal) => n === 1 ? (isFinal ? 'uno' : 'un') : onesFinal[n];
1189
+ let words = '';
1190
+ // Miles de millones (ahora sí se ejecuta)
1191
+ if (num >= 1000000000) {
1192
+ const billions = Math.floor(num / 1000000000);
1193
+ words += (billions === 1 ? 'mil' : numberToWords(billions) + ' mil') + ' millones ';
1194
+ num %= 1000000000;
1195
+ }
1196
+ // Millones
1197
+ if (num >= 1000000) {
1198
+ const millions = Math.floor(num / 1000000);
1199
+ words += millions === 1 ? 'un millón ' : numberToWords(millions) + ' millones ';
1200
+ num %= 1000000;
1201
+ }
1202
+ // Miles
1203
+ if (num >= 1000) {
1204
+ const thousands = Math.floor(num / 1000);
1205
+ words += thousands === 1 ? 'mil ' : numberToWords(thousands) + ' mil ';
1206
+ num %= 1000;
1207
+ }
1208
+ // Centenas (con tratamiento especial para "ciento uno")
1209
+ if (num >= 100) {
1210
+ if (num === 100) {
1211
+ words += 'cien';
1212
+ num = 0;
1213
+ }
1214
+ else {
1215
+ words += hundreds[Math.floor(num / 100)];
1216
+ num %= 100;
1217
+ if (num > 0) {
1218
+ words += ' ' + getOneForm(num, true); // Siempre "uno" después de ciento
1219
+ num = 0;
1220
+ }
1221
+ }
1222
+ }
1223
+ // Decenas y unidades
1224
+ if (num > 0) {
1225
+ const isFinalWord = words === '';
1226
+ if (num < 10) {
1227
+ words += getOneForm(num, isFinalWord);
1228
+ }
1229
+ else if (num < 20) {
1230
+ words += teens[num - 10];
1231
+ }
1232
+ else if (num < 30) {
1233
+ if (num === 21)
1234
+ words += 'veintiuno';
1235
+ else if (num === 22)
1236
+ words += 'veintidós';
1237
+ else if (num === 23)
1238
+ words += 'veintitrés';
1239
+ else if (num === 26)
1240
+ words += 'veintiséis';
1241
+ else
1242
+ words += `veinti${onesFinal[num % 10]}`;
1243
+ }
1244
+ else {
1245
+ words += tens[Math.floor(num / 10)];
1246
+ if (num % 10 > 0) {
1247
+ words += ' y ' + getOneForm(num % 10, isFinalWord);
1248
+ }
1249
+ }
1250
+ }
1251
+ return words.trim().replace(/\s+/g, ' ');
1252
+ }
1041
1253
 
1042
1254
  function objectPropertiesToType(formFields) {
1043
1255
  Object.keys(formFields).filter(control => !!formFields[control]).forEach(control => {
@@ -1081,6 +1293,52 @@ function generateRandomString(length) {
1081
1293
  }
1082
1294
  return result;
1083
1295
  }
1296
+ /**
1297
+ * Obtiene el host (hostname) de una URL o cadena host[:port].
1298
+ * Opcionalmente puede devolver también el puerto si está presente.
1299
+ *
1300
+ * - Acepta inputs como:
1301
+ * 'https://example.com/path', 'example.com:3000', 'localhost', 'http://[::1]:4200'
1302
+ * - Usa la API URL cuando es posible (más robusta) y cae a un regex de respaldo.
1303
+ *
1304
+ * @param url Cadena que representa una URL o host
1305
+ * @param options
1306
+ * @param options.includePort Si es true, incluye":puerto" cuando exista (por defecto false)
1307
+ * @param options.includeProtocol Incluye "http://" o "https://" según corresponda (por defecto false)
1308
+ */
1309
+ function obtenerHostDesdeUrl(url, options) {
1310
+ if (!url)
1311
+ return null;
1312
+ const includePort = !!options?.includePort;
1313
+ const includeProtocol = !!options?.includeProtocol;
1314
+ // Intentar con API URL
1315
+ try {
1316
+ const candidate = url.includes('://') ? url : `http://${url}`;
1317
+ const parsed = new URL(candidate);
1318
+ const protocol = includeProtocol ? `${parsed.protocol}//` : '';
1319
+ const hostname = parsed.hostname;
1320
+ const port = parsed.port;
1321
+ if (!hostname)
1322
+ return null;
1323
+ return `${protocol}${hostname}${includePort && port ? `:${port}` : ''}`;
1324
+ }
1325
+ catch {
1326
+ // Regex fallback
1327
+ const regex = /^(?:(?<protocol>[a-z]+):\/\/)?(?:www\.)?(?<host>\[[^\]]+\]|[A-Za-z0-9.-]+)(?::(?<port>\d{1,5}))?/;
1328
+ const match = String(url).match(regex);
1329
+ if (!match?.groups)
1330
+ return null;
1331
+ let host = match.groups['host'];
1332
+ if (host.startsWith('[') && host.endsWith(']')) {
1333
+ host = host.slice(1, -1);
1334
+ }
1335
+ const port = match.groups['port'];
1336
+ const protocol = includeProtocol ? `${match.groups['protocol'] ?? 'http'}://` : '';
1337
+ return `${protocol}${host}${includePort && port ? `:${port}` : ''}`;
1338
+ }
1339
+ }
1340
+ /** @deprecated Alias compatible (deprecated) — preferir usar `obtenerHostDesdeUrl`. */
1341
+ const extraerDominio = (url, options) => obtenerHostDesdeUrl(url, options);
1084
1342
 
1085
1343
  function verificarRUC(ruc) {
1086
1344
  const f = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
@@ -1105,5 +1363,5 @@ function verificarRUC(ruc) {
1105
1363
  * Generated bundle index. Do not edit.
1106
1364
  */
1107
1365
 
1108
- export { b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, convertirBytes, deepClone, deepMerge, delLocalStorage, desencriptar, downLoadFileStream, encriptar, esNumero, establecerQuitarRequired, 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, objectPropertiesBoolean, objectPropertiesToType, obtenerMimeType, obtenerUltimoOrden, ordenarArray, ordenarPorPropiedad, ordenarPorPropiedades, roundToDecimal, sanitizarNombreArchivo, seleccionarTextoInput, setCambiarPwd, setJwtTokenData, setLocalStorage, sumarObjetos, sumarPropiedades, toFormData, verificarRUC };
1366
+ export { b64Decode, b64Encode, buscarPorCampo, changeSelect, changeSelectApi, changeSelectData, changeSelectDataApi, convertirBytes, deepClone, deepMerge, delLocalStorage, desencriptar, downLoadFileStream, eliminarColumnaPorIndex, eliminarDuplicados, eliminarElementos, encriptar, esNumero, establecerQuitarRequired, extraerDominio, 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, toFormData, transformarFechasPorNombreDeCampo, verificarRUC };
1109
1367
  //# sourceMappingURL=jvsoft-utils-src-functions.mjs.map