ngx-dsxlibrary 2.21.2 → 2.21.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.
@@ -3258,7 +3258,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
3258
3258
  /**
3259
3259
  * Servicio genérico para consumir endpoints REST de la API.
3260
3260
  *
3261
- * @typeParam T Tipo de dato que representa el recurso manejado por el endpoint.
3261
+ * @typeParam T Tipo base de la entidad. Puede sobrescribirse por método.
3262
3262
  */
3263
3263
  class EndpointService {
3264
3264
  /** Cliente HTTP de Angular utilizado para realizar las peticiones. */
@@ -3277,9 +3277,10 @@ class EndpointService {
3277
3277
  /**
3278
3278
  * Obtiene un listado de recursos del endpoint especificado.
3279
3279
  *
3280
+ * @typeParam TList Tipo de cada elemento del listado (por defecto `T`).
3280
3281
  * @param endpoint Segmento del endpoint (sin la parte de `api/`).
3281
3282
  * @param invalidateCache Indica si se invalida la caché del lado del servidor.
3282
- * @returns Observable con un arreglo de elementos del tipo `T`.
3283
+ * @returns Observable con un arreglo de elementos del tipo `TList`.
3283
3284
  */
3284
3285
  list(endpoint, invalidateCache = false) {
3285
3286
  return this.http.get(`${this.getUrl(endpoint)}/listar/${invalidateCache}`);
@@ -3287,9 +3288,10 @@ class EndpointService {
3287
3288
  /**
3288
3289
  * Obtiene un recurso por su identificador.
3289
3290
  *
3291
+ * @typeParam TEdit Tipo del recurso retornado (por defecto `T`).
3290
3292
  * @param endpoint Segmento del endpoint (sin la parte de `api/`).
3291
3293
  * @param id Identificador del recurso a recuperar.
3292
- * @returns Observable con el elemento del tipo `T`.
3294
+ * @returns Observable con el elemento del tipo `TEdit`.
3293
3295
  */
3294
3296
  edit(endpoint, id) {
3295
3297
  return this.http.get(`${this.getUrl(endpoint)}/get-id/${id}`);
@@ -3297,9 +3299,11 @@ class EndpointService {
3297
3299
  /**
3298
3300
  * Crea o actualiza un recurso en el endpoint especificado.
3299
3301
  *
3302
+ * @typeParam TSave Tipo del payload enviado al backend (por defecto `T`).
3303
+ * @typeParam TResponse Tipo de la entidad en la respuesta (por defecto `T`).
3300
3304
  * @param endpoint Segmento del endpoint (sin la parte de `api/`).
3301
3305
  * @param values Objeto con los datos del recurso a guardar.
3302
- * @returns Observable con el resultado del servicio que envuelve al tipo `T`.
3306
+ * @returns Observable con el resultado del servicio que envuelve `TResponse`.
3303
3307
  */
3304
3308
  save(endpoint, values) {
3305
3309
  return this.http.put(`${this.getUrl(endpoint)}/save`, values);
@@ -3308,7 +3312,7 @@ class EndpointService {
3308
3312
  * Elimina un recurso del endpoint especificado.
3309
3313
  *
3310
3314
  * @param endpoint Segmento del endpoint (sin la parte de `api/`).
3311
- * @param id Identificador del recurso a eliminar.
3315
+ * @param id Identificador del recurso a eliminar (numérico o textual).
3312
3316
  * @param softDelete Indica si la eliminación es lógica (por defecto) o física.
3313
3317
  * @returns Observable con el modelo de respuesta HTTP genérico.
3314
3318
  */
@@ -3325,19 +3329,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
3325
3329
  }]
3326
3330
  }] });
3327
3331
 
3332
+ /**
3333
+ * Clase base para servicios CRUD tipados.
3334
+ *
3335
+ * @typeParam TBase Modelo base que representa la entidad principal.
3336
+ * @typeParam TEdit Modelo de respuesta para la operación de edición/consulta por id.
3337
+ * @typeParam TSave Modelo de entrada para guardar (create/update).
3338
+ * @typeParam TList Modelo de cada elemento del listado.
3339
+ * @typeParam TDelete Tipo del identificador usado para eliminar (number o string).
3340
+ */
3328
3341
  class BaseCRUDService {
3329
- _endpointService = inject((EndpointService));
3342
+ /** Servicio HTTP base para operaciones REST. */
3343
+ _endpointService = inject(EndpointService);
3344
+ /**
3345
+ * Obtiene el listado de registros.
3346
+ * @param invalidateCache Si es true, solicita invalidar caché en backend.
3347
+ */
3330
3348
  list(invalidateCache = false) {
3331
3349
  return this._endpointService.list(this.endpoint, invalidateCache);
3332
3350
  }
3351
+ /**
3352
+ * Obtiene un registro por id para edición.
3353
+ * @param id Identificador numérico del registro.
3354
+ */
3333
3355
  edit(id) {
3334
3356
  return this._endpointService.edit(this.endpoint, id);
3335
3357
  }
3358
+ /**
3359
+ * Guarda un registro (creación o actualización).
3360
+ * @param values Payload tipado para persistir.
3361
+ */
3336
3362
  save(values) {
3337
3363
  return this._endpointService.save(this.endpoint, values);
3338
3364
  }
3339
- delete(id, softDelete = true) {
3340
- return this._endpointService.delete(this.endpoint, id, softDelete);
3365
+ /**
3366
+ * Elimina un registro por identificador.
3367
+ * @param value Identificador del registro a eliminar.
3368
+ * @param softDelete Si es true, realiza eliminación lógica.
3369
+ */
3370
+ delete(value, softDelete = true) {
3371
+ return this._endpointService.delete(this.endpoint, value, softDelete);
3341
3372
  }
3342
3373
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: BaseCRUDService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3343
3374
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: BaseCRUDService, providedIn: 'root' });
@@ -3627,6 +3658,223 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImpor
3627
3658
  }]
3628
3659
  }] });
3629
3660
 
3661
+ function pickByKeys(source, keys) {
3662
+ const result = {};
3663
+ for (const key of keys) {
3664
+ result[key] = source[key];
3665
+ }
3666
+ return result;
3667
+ }
3668
+ function tryParseDateValue(value) {
3669
+ if (value instanceof Date) {
3670
+ const time = value.getTime();
3671
+ return Number.isNaN(time) ? null : time;
3672
+ }
3673
+ if (typeof value === 'string' || typeof value === 'number') {
3674
+ const parsed = new Date(value);
3675
+ const time = parsed.getTime();
3676
+ return Number.isNaN(time) ? null : time;
3677
+ }
3678
+ return null;
3679
+ }
3680
+ function normalizeComparableValue(base, current) {
3681
+ const baseDateValue = tryParseDateValue(base);
3682
+ if (baseDateValue !== null) {
3683
+ const currentDateValue = tryParseDateValue(current);
3684
+ return currentDateValue ?? current;
3685
+ }
3686
+ return current;
3687
+ }
3688
+ function normalizeBaseEntity(baseValue, keys) {
3689
+ const normalized = {};
3690
+ const selectedKeys = keys ?? Object.keys(baseValue);
3691
+ for (const key of selectedKeys) {
3692
+ const base = baseValue[key];
3693
+ normalized[key] = normalizeComparableValue(base, base);
3694
+ }
3695
+ return normalized;
3696
+ }
3697
+ function normalizeRawEntityAgainstBase(baseValue, rawValue, keys, useBaseValueForNullish = true) {
3698
+ const coerceByBaseType = (base, current) => {
3699
+ if (typeof base === 'number') {
3700
+ if (typeof current === 'string' && current.trim() !== '') {
3701
+ const normalizedText = current.trim();
3702
+ const direct = Number(normalizedText);
3703
+ if (!Number.isNaN(direct))
3704
+ return direct;
3705
+ const commaDecimal = Number(normalizedText.replace(',', '.'));
3706
+ if (!Number.isNaN(commaDecimal))
3707
+ return commaDecimal;
3708
+ const noSpaces = normalizedText.replace(/\s+/g, '');
3709
+ const noThousandsDots = Number(noSpaces.replace(/\./g, '').replace(',', '.'));
3710
+ if (!Number.isNaN(noThousandsDots))
3711
+ return noThousandsDots;
3712
+ return current;
3713
+ }
3714
+ return current;
3715
+ }
3716
+ if (typeof base === 'boolean') {
3717
+ if (typeof current === 'string') {
3718
+ const lowered = current.trim().toLowerCase();
3719
+ if (lowered === 'true')
3720
+ return true;
3721
+ if (lowered === 'false')
3722
+ return false;
3723
+ }
3724
+ return current;
3725
+ }
3726
+ if (base instanceof Date) {
3727
+ if (current instanceof Date)
3728
+ return current;
3729
+ if (typeof current === 'string' || typeof current === 'number') {
3730
+ const parsed = new Date(current);
3731
+ return Number.isNaN(parsed.getTime()) ? current : parsed;
3732
+ }
3733
+ return current;
3734
+ }
3735
+ if (typeof base === 'string') {
3736
+ if (current === null || current === undefined)
3737
+ return current;
3738
+ return typeof current === 'string' ? current : String(current);
3739
+ }
3740
+ return current;
3741
+ };
3742
+ const normalized = {};
3743
+ const selectedKeys = keys ?? Object.keys(baseValue);
3744
+ for (const key of selectedKeys) {
3745
+ const base = baseValue[key];
3746
+ const current = rawValue[key];
3747
+ const isNullish = current === null || current === undefined;
3748
+ const isEmptyString = typeof current === 'string' && current.trim() === '';
3749
+ const treatEmptyAsNullish = isEmptyString && typeof base !== 'string';
3750
+ const shouldUseBase = useBaseValueForNullish && (isNullish || treatEmptyAsNullish);
3751
+ normalized[key] = shouldUseBase ? base : coerceByBaseType(base, current);
3752
+ normalized[key] = normalizeComparableValue(base, normalized[key]);
3753
+ }
3754
+ return normalized;
3755
+ }
3756
+ /**
3757
+ * Servicio utilitario para detectar cambios en formularios o vistas
3758
+ * maestro-detalle sin acoplarse a una estructura de dominio concreta.
3759
+ */
3760
+ class MasterDetailChangeService {
3761
+ static findFirstDifference(leftValue, rightValue, path = 'root') {
3762
+ if (leftValue instanceof Date && rightValue instanceof Date) {
3763
+ return leftValue.getTime() === rightValue.getTime()
3764
+ ? null
3765
+ : { path, leftValue, rightValue };
3766
+ }
3767
+ if (Array.isArray(leftValue) && Array.isArray(rightValue)) {
3768
+ if (leftValue.length !== rightValue.length) {
3769
+ return {
3770
+ path: `${path}.length`,
3771
+ leftValue: leftValue.length,
3772
+ rightValue: rightValue.length,
3773
+ };
3774
+ }
3775
+ for (let index = 0; index < leftValue.length; index += 1) {
3776
+ const difference = MasterDetailChangeService.findFirstDifference(leftValue[index], rightValue[index], `${path}[${index}]`);
3777
+ if (difference) {
3778
+ return difference;
3779
+ }
3780
+ }
3781
+ return null;
3782
+ }
3783
+ if (leftValue != null &&
3784
+ rightValue != null &&
3785
+ typeof leftValue === 'object' &&
3786
+ typeof rightValue === 'object') {
3787
+ const leftKeys = Object.keys(leftValue);
3788
+ const rightKeys = Object.keys(rightValue);
3789
+ if (leftKeys.length !== rightKeys.length) {
3790
+ return {
3791
+ path: `${path}.__keys__`,
3792
+ leftValue: leftKeys,
3793
+ rightValue: rightKeys,
3794
+ };
3795
+ }
3796
+ for (const key of leftKeys) {
3797
+ const difference = MasterDetailChangeService.findFirstDifference(leftValue[key], rightValue[key], `${path}.${key}`);
3798
+ if (difference) {
3799
+ return difference;
3800
+ }
3801
+ }
3802
+ return null;
3803
+ }
3804
+ return Object.is(leftValue, rightValue)
3805
+ ? null
3806
+ : { path, leftValue, rightValue };
3807
+ }
3808
+ /**
3809
+ * Version simplificada para formularios: recibe `getRawValue()` directamente
3810
+ * y compara por claves sin requerir mapeo manual en el consumidor.
3811
+ */
3812
+ createSimpleFormTracker(initialState, options) {
3813
+ const defaultMasterKeys = Object.keys(initialState.master);
3814
+ const defaultDetailKeys = initialState.detail.length > 0
3815
+ ? Object.keys(initialState.detail[0])
3816
+ : [];
3817
+ const masterKeys = options?.masterKeys ?? defaultMasterKeys;
3818
+ const detailKeys = options?.detailKeys ?? defaultDetailKeys;
3819
+ const useBaseValueForNullish = options?.useBaseValueForNullish ?? true;
3820
+ let initialMaster = structuredClone(initialState.master);
3821
+ let initialDetail = structuredClone(initialState.detail);
3822
+ const buildComparisonPayload = (currentState) => {
3823
+ const normalizedBaseMaster = normalizeBaseEntity(initialMaster, masterKeys);
3824
+ const normalizedCurrentMaster = normalizeRawEntityAgainstBase(initialMaster, currentState.master, masterKeys, useBaseValueForNullish);
3825
+ const normalizedBaseDetail = initialDetail.map((item) => normalizeBaseEntity(item, detailKeys));
3826
+ const normalizedCurrentDetail = currentState.detail.map((item, index) => normalizeRawEntityAgainstBase(initialDetail[index] ?? {}, item, detailKeys, useBaseValueForNullish));
3827
+ return {
3828
+ normalizedBaseMaster,
3829
+ normalizedCurrentMaster,
3830
+ normalizedBaseDetail,
3831
+ normalizedCurrentDetail,
3832
+ };
3833
+ };
3834
+ const resolveChanges = (currentState) => {
3835
+ const { normalizedBaseMaster, normalizedCurrentMaster, normalizedBaseDetail, normalizedCurrentDetail, } = buildComparisonPayload(currentState);
3836
+ const masterDifference = MasterDetailChangeService.findFirstDifference(normalizedBaseMaster, normalizedCurrentMaster, 'master');
3837
+ const detailDifference = MasterDetailChangeService.findFirstDifference(normalizedBaseDetail, normalizedCurrentDetail, 'detail');
3838
+ return {
3839
+ hasChanges: masterDifference !== null || detailDifference !== null,
3840
+ masterChanged: masterDifference !== null,
3841
+ detailChanged: detailDifference !== null,
3842
+ firstDifference: masterDifference
3843
+ ? {
3844
+ scope: 'master',
3845
+ path: masterDifference.path,
3846
+ baseValue: masterDifference.leftValue,
3847
+ currentValue: masterDifference.rightValue,
3848
+ }
3849
+ : detailDifference
3850
+ ? {
3851
+ scope: 'detail',
3852
+ path: detailDifference.path,
3853
+ baseValue: detailDifference.leftValue,
3854
+ currentValue: detailDifference.rightValue,
3855
+ }
3856
+ : null,
3857
+ };
3858
+ };
3859
+ return {
3860
+ hasChanges: (currentState) => resolveChanges(currentState).hasChanges,
3861
+ getChanges: (currentState) => resolveChanges(currentState),
3862
+ reset: (state) => {
3863
+ initialMaster = structuredClone(state.master);
3864
+ initialDetail = structuredClone(state.detail);
3865
+ },
3866
+ };
3867
+ }
3868
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MasterDetailChangeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
3869
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MasterDetailChangeService, providedIn: 'root' });
3870
+ }
3871
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: MasterDetailChangeService, decorators: [{
3872
+ type: Injectable,
3873
+ args: [{
3874
+ providedIn: 'root',
3875
+ }]
3876
+ }] });
3877
+
3630
3878
  class UtilityAddService {
3631
3879
  _serviceAlerta = inject(AlertaService);
3632
3880
  environment = inject(ENVIRONMENT);
@@ -4348,7 +4596,6 @@ class UtilityAddService {
4348
4596
  checkFormValid(control) {
4349
4597
  if (!control)
4350
4598
  return;
4351
- console.log(control);
4352
4599
  // Marca todos los controles (incluyendo anidados en FormGroup/FormArray)
4353
4600
  control.markAllAsTouched();
4354
4601
  // Muestra un mensaje de alerta
@@ -4957,5 +5204,5 @@ function CUICorrecto(cui) {
4957
5204
  * Generated bundle index. Do not edit.
4958
5205
  */
4959
5206
 
4960
- export { AlertaService, AppMessageErrorComponent, ArrowNavigationDirective, AuthorizeService, BaseCRUDService, CACHE_KEYS, CacheService, CssV2Component, DocxPreviewComponent, DsxAddToolsModule, DteService, ENVIRONMENT, EndpointService, ErrorHandlerService, FileComponent, GTQFormatter, HttpHelpersService, INITIAL_PARAMETERS, IconDsxComponent, JoinByPipe, JsonHighlightPipe, JsonValuesDebujComponent, JsonViewerComponent, KpicardComponent, LoadingComponent, LoadingLottieComponent, LogoDsxComponent, NavbarDsxComponent, NetworkStatusComponent, OnlyRangoPatternDirective, ParameterValuesService, PrimeNgModule, SWEET_ALERT_THEMES, SecurityService, SelectAllOnFocusDirective, SpinnerLoadingService, TruncatePipe, UtilityAddService, atLeastOneFieldRequiredValidator, createCurrencyFormatter, createInitialCache, createTypedCacheProvider, cuiValidator, dateMinMaxValidator, dateRangeValidator, dateRangeValidatorFromTo, developmentEnvironment, getZeroBasedRolIndex, httpAuthorizeInterceptor, nitValidator, productionEnvironment, provideEnvironment, validateEnvironmentConfig };
5207
+ export { AlertaService, AppMessageErrorComponent, ArrowNavigationDirective, AuthorizeService, BaseCRUDService, CACHE_KEYS, CacheService, CssV2Component, DocxPreviewComponent, DsxAddToolsModule, DteService, ENVIRONMENT, EndpointService, ErrorHandlerService, FileComponent, GTQFormatter, HttpHelpersService, INITIAL_PARAMETERS, IconDsxComponent, JoinByPipe, JsonHighlightPipe, JsonValuesDebujComponent, JsonViewerComponent, KpicardComponent, LoadingComponent, LoadingLottieComponent, LogoDsxComponent, MasterDetailChangeService, NavbarDsxComponent, NetworkStatusComponent, OnlyRangoPatternDirective, ParameterValuesService, PrimeNgModule, SWEET_ALERT_THEMES, SecurityService, SelectAllOnFocusDirective, SpinnerLoadingService, TruncatePipe, UtilityAddService, atLeastOneFieldRequiredValidator, createCurrencyFormatter, createInitialCache, createTypedCacheProvider, cuiValidator, dateMinMaxValidator, dateRangeValidator, dateRangeValidatorFromTo, developmentEnvironment, getZeroBasedRolIndex, httpAuthorizeInterceptor, nitValidator, productionEnvironment, provideEnvironment, validateEnvironmentConfig };
4961
5208
  //# sourceMappingURL=ngx-dsxlibrary.mjs.map